wip: feed editor client-side
This commit is contained in:
parent
545bcc3e4b
commit
9586c72a17
|
@ -464,8 +464,10 @@ class Pref_Feeds extends Handler_Protected {
|
||||||
if (is_uploaded_file($_FILES['icon_file']['tmp_name'])) {
|
if (is_uploaded_file($_FILES['icon_file']['tmp_name'])) {
|
||||||
$tmp_file = tempnam(CACHE_DIR . '/upload', 'icon');
|
$tmp_file = tempnam(CACHE_DIR . '/upload', 'icon');
|
||||||
|
|
||||||
$result = move_uploaded_file($_FILES['icon_file']['tmp_name'],
|
if (!$tmp_file)
|
||||||
$tmp_file);
|
return;
|
||||||
|
|
||||||
|
$result = move_uploaded_file($_FILES['icon_file']['tmp_name'], $tmp_file);
|
||||||
|
|
||||||
if (!$result) {
|
if (!$result) {
|
||||||
return;
|
return;
|
||||||
|
@ -478,7 +480,7 @@ class Pref_Feeds extends Handler_Protected {
|
||||||
$feed_id = clean($_REQUEST["feed_id"]);
|
$feed_id = clean($_REQUEST["feed_id"]);
|
||||||
$rc = 2; // failed
|
$rc = 2; // failed
|
||||||
|
|
||||||
if (is_file($icon_file) && $feed_id) {
|
if ($icon_file && is_file($icon_file) && $feed_id) {
|
||||||
if (filesize($icon_file) < 65535) {
|
if (filesize($icon_file) < 65535) {
|
||||||
|
|
||||||
$sth = $this->pdo->prepare("SELECT id FROM ttrss_feeds
|
$sth = $this->pdo->prepare("SELECT id FROM ttrss_feeds
|
||||||
|
@ -486,8 +488,12 @@ class Pref_Feeds extends Handler_Protected {
|
||||||
$sth->execute([$feed_id, $_SESSION['uid']]);
|
$sth->execute([$feed_id, $_SESSION['uid']]);
|
||||||
|
|
||||||
if ($row = $sth->fetch()) {
|
if ($row = $sth->fetch()) {
|
||||||
@unlink(ICONS_DIR . "/$feed_id.ico");
|
$new_filename = ICONS_DIR . "/$feed_id.ico";
|
||||||
if (rename($icon_file, ICONS_DIR . "/$feed_id.ico")) {
|
|
||||||
|
if (file_exists($new_filename)) unlink($new_filename);
|
||||||
|
|
||||||
|
if (rename($icon_file, $new_filename)) {
|
||||||
|
chmod($new_filename, 644);
|
||||||
|
|
||||||
$sth = $this->pdo->prepare("UPDATE ttrss_feeds SET
|
$sth = $this->pdo->prepare("UPDATE ttrss_feeds SET
|
||||||
favicon_avg_color = ''
|
favicon_avg_color = ''
|
||||||
|
@ -502,7 +508,9 @@ class Pref_Feeds extends Handler_Protected {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (is_file($icon_file)) @unlink($icon_file);
|
if ($icon_file && is_file($icon_file)) {
|
||||||
|
unlink($icon_file);
|
||||||
|
}
|
||||||
|
|
||||||
print $rc;
|
print $rc;
|
||||||
return;
|
return;
|
||||||
|
@ -512,12 +520,62 @@ class Pref_Feeds extends Handler_Protected {
|
||||||
global $purge_intervals;
|
global $purge_intervals;
|
||||||
global $update_intervals;
|
global $update_intervals;
|
||||||
|
|
||||||
$feed_id = clean($_REQUEST["id"]);
|
$feed_id = (int)clean($_REQUEST["id"]);
|
||||||
|
|
||||||
$sth = $this->pdo->prepare("SELECT * FROM ttrss_feeds WHERE id = ? AND
|
$sth = $this->pdo->prepare("SELECT * FROM ttrss_feeds WHERE id = ? AND
|
||||||
owner_uid = ?");
|
owner_uid = ?");
|
||||||
$sth->execute([$feed_id, $_SESSION['uid']]);
|
$sth->execute([$feed_id, $_SESSION['uid']]);
|
||||||
|
|
||||||
|
if ($row = $sth->fetch(PDO::FETCH_ASSOC)) {
|
||||||
|
|
||||||
|
ob_start();
|
||||||
|
PluginHost::getInstance()->run_hooks(PluginHost::HOOK_PREFS_EDIT_FEED, $feed_id);
|
||||||
|
$plugin_data = trim((string)ob_get_contents());
|
||||||
|
ob_end_clean();
|
||||||
|
|
||||||
|
$row["icon"] = Feeds::_get_icon($feed_id);
|
||||||
|
|
||||||
|
$local_update_intervals = $update_intervals;
|
||||||
|
$local_update_intervals[0] .= sprintf(" (%s)", $update_intervals[get_pref("DEFAULT_UPDATE_INTERVAL")]);
|
||||||
|
|
||||||
|
if (FORCE_ARTICLE_PURGE == 0) {
|
||||||
|
$local_purge_intervals = $purge_intervals;
|
||||||
|
$default_purge_interval = get_pref("PURGE_OLD_DAYS");
|
||||||
|
|
||||||
|
if ($default_purge_interval > 0)
|
||||||
|
$local_purge_intervals[0] .= " " . T_nsprintf('(%d day)', '(%d days)', $default_purge_interval, $default_purge_interval);
|
||||||
|
else
|
||||||
|
$local_purge_intervals[0] .= " " . sprintf("(%s)", __("Disabled"));
|
||||||
|
|
||||||
|
} else {
|
||||||
|
$purge_interval = FORCE_ARTICLE_PURGE;
|
||||||
|
$local_purge_intervals = [ T_nsprintf('%d day', '%d days', $purge_interval, $purge_interval) ];
|
||||||
|
}
|
||||||
|
|
||||||
|
print json_encode([
|
||||||
|
"feed" => $row,
|
||||||
|
"cats" => [
|
||||||
|
"enabled" => get_pref('ENABLE_FEED_CATS'),
|
||||||
|
"select" => \Controls\select_feeds_cats("cat_id", $row["cat_id"]),
|
||||||
|
],
|
||||||
|
"plugin_data" => $plugin_data,
|
||||||
|
"force_purge" => (int)FORCE_ARTICLE_PURGE,
|
||||||
|
"intervals" => [
|
||||||
|
"update" => $local_update_intervals,
|
||||||
|
"purge" => $local_purge_intervals,
|
||||||
|
],
|
||||||
|
"lang" => [
|
||||||
|
"enabled" => DB_TYPE == "pgsql",
|
||||||
|
"default" => get_pref('DEFAULT_SEARCH_LANGUAGE'),
|
||||||
|
"all" => $this::get_ts_languages(),
|
||||||
|
]
|
||||||
|
]);
|
||||||
|
} else {
|
||||||
|
print json_encode(["error" => "FEED_NOT_FOUND"]);
|
||||||
|
}
|
||||||
|
|
||||||
|
return;
|
||||||
|
|
||||||
if ($row = $sth->fetch()) {
|
if ($row = $sth->fetch()) {
|
||||||
print '<div dojoType="dijit.layout.TabContainer" style="height : 450px">
|
print '<div dojoType="dijit.layout.TabContainer" style="height : 450px">
|
||||||
<div dojoType="dijit.layout.ContentPane" title="'.__('General').'">';
|
<div dojoType="dijit.layout.ContentPane" title="'.__('General').'">';
|
||||||
|
|
22
js/App.js
22
js/App.js
|
@ -343,16 +343,20 @@ const App = {
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
// htmlspecialchars()-alike for headlines data-content attribute
|
// htmlspecialchars()-alike for headlines data-content attribute
|
||||||
escapeHtml: function(text) {
|
escapeHtml: function(p) {
|
||||||
const map = {
|
if (typeof p == "string") {
|
||||||
'&': '&',
|
const map = {
|
||||||
'<': '<',
|
'&': '&',
|
||||||
'>': '>',
|
'<': '<',
|
||||||
'"': '"',
|
'>': '>',
|
||||||
"'": '''
|
'"': '"',
|
||||||
};
|
"'": '''
|
||||||
|
};
|
||||||
|
|
||||||
return text.replace(/[&<>"']/g, function(m) { return map[m]; });
|
return p.replace(/[&<>"']/g, function(m) { return map[m]; });
|
||||||
|
} else {
|
||||||
|
return p;
|
||||||
|
}
|
||||||
},
|
},
|
||||||
displayIfChecked: function(checkbox, elemId) {
|
displayIfChecked: function(checkbox, elemId) {
|
||||||
if (checkbox.checked) {
|
if (checkbox.checked) {
|
||||||
|
|
|
@ -389,19 +389,20 @@ const CommonDialogs = {
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
},
|
},
|
||||||
editFeed: function (feed) {
|
editFeed: function (feed_id) {
|
||||||
if (feed <= 0)
|
if (feed_id <= 0)
|
||||||
return alert(__("You can't edit this kind of feed."));
|
return alert(__("You can't edit this kind of feed."));
|
||||||
|
|
||||||
const query = {op: "pref-feeds", method: "editfeed", id: feed};
|
const query = {op: "pref-feeds", method: "editfeed", id: feed_id};
|
||||||
|
|
||||||
console.log("editFeed", query);
|
console.log("editFeed", query);
|
||||||
|
|
||||||
const dialog = new fox.SingleUseDialog({
|
const dialog = new fox.SingleUseDialog({
|
||||||
id: "feedEditDlg",
|
id: "feedEditDlg",
|
||||||
title: __("Edit Feed"),
|
title: __("Edit Feed"),
|
||||||
unsubscribeFeed: function(feed_id, title) {
|
feed_title: "",
|
||||||
if (confirm(__("Unsubscribe from %s?").replace("%s", title))) {
|
unsubscribe: function() {
|
||||||
|
if (confirm(__("Unsubscribe from %s?").replace("%s", this.feed_title))) {
|
||||||
dialog.hide();
|
dialog.hide();
|
||||||
CommonDialogs.unsubscribeFeed(feed_id);
|
CommonDialogs.unsubscribeFeed(feed_id);
|
||||||
}
|
}
|
||||||
|
@ -430,8 +431,212 @@ const CommonDialogs = {
|
||||||
const tmph = dojo.connect(dialog, 'onShow', function () {
|
const tmph = dojo.connect(dialog, 'onShow', function () {
|
||||||
dojo.disconnect(tmph);
|
dojo.disconnect(tmph);
|
||||||
|
|
||||||
xhr.post("backend.php", {op: "pref-feeds", method: "editfeed", id: feed}, (reply) => {
|
xhr.json("backend.php", {op: "pref-feeds", method: "editfeed", id: feed_id}, (reply) => {
|
||||||
dialog.attr('content', reply);
|
const feed = reply.feed;
|
||||||
|
|
||||||
|
// for unsub prompt
|
||||||
|
dialog.feed_title = feed.title;
|
||||||
|
|
||||||
|
dialog.attr('content',
|
||||||
|
`
|
||||||
|
<div dojoType="dijit.layout.TabContainer" style="height : 450px">
|
||||||
|
<div dojoType="dijit.layout.ContentPane" title="${__('General')}">
|
||||||
|
|
||||||
|
${App.FormFields.hidden_tag("id", feed_id)}
|
||||||
|
${App.FormFields.hidden_tag("op", "pref-feeds")}
|
||||||
|
${App.FormFields.hidden_tag("method", "editSave")}
|
||||||
|
|
||||||
|
<section>
|
||||||
|
<fieldset>
|
||||||
|
<input dojoType='dijit.form.ValidationTextBox' required='1'
|
||||||
|
placeHolder="${__("Feed Title")}"
|
||||||
|
style='font-size : 16px; width: 500px' name='title' value="${App.escapeHtml(feed.title)}">
|
||||||
|
</fieldset>
|
||||||
|
|
||||||
|
<fieldset>
|
||||||
|
<label>${__('URL:')}</label>
|
||||||
|
<input dojoType='dijit.form.ValidationTextBox' required='1'
|
||||||
|
placeHolder="${__("Feed URL")}"
|
||||||
|
regExp='^(http|https)://.*' style='width : 300px'
|
||||||
|
name='feed_url' value="${App.escapeHtml(feed.feed_url)}">
|
||||||
|
|
||||||
|
${feed.last_error ?
|
||||||
|
`<i class="material-icons"
|
||||||
|
title="${App.escapeHtml(feed.last_error)}">error</i>
|
||||||
|
` : ""}
|
||||||
|
</fieldset>
|
||||||
|
|
||||||
|
${reply.cats.enabled ?
|
||||||
|
`
|
||||||
|
<fieldset>
|
||||||
|
<label>${__('Place in category:')}</label>
|
||||||
|
${reply.cats.select}
|
||||||
|
</fieldset>
|
||||||
|
` : ""}
|
||||||
|
|
||||||
|
<fieldset>
|
||||||
|
<label>${__('Site URL:')}</label>
|
||||||
|
<input dojoType='dijit.form.ValidationTextBox' required='1'
|
||||||
|
placeHolder="${__("Site URL")}"
|
||||||
|
regExp='^(http|https)://.*' style='width : 300px'
|
||||||
|
name='site_url' value="${App.escapeHtml(feed.site_url)}">
|
||||||
|
</fieldset>
|
||||||
|
|
||||||
|
${reply.lang.enabled ?
|
||||||
|
`
|
||||||
|
<fieldset>
|
||||||
|
<label>${__('Language:')}</label>
|
||||||
|
${App.FormFields.select_tag("feed_language", feed.feed_language, reply.lang.all)}
|
||||||
|
</fieldset>
|
||||||
|
` : ""}
|
||||||
|
|
||||||
|
<hr/>
|
||||||
|
|
||||||
|
<fieldset>
|
||||||
|
<label>${__("Update interval:")}</label>
|
||||||
|
${App.FormFields.select_hash("update_interval", feed.update_interval, reply.intervals.update)}
|
||||||
|
</fieldset>
|
||||||
|
<fieldset>
|
||||||
|
<label>${__('Article purging:')}</label>
|
||||||
|
|
||||||
|
${App.FormFields.select_hash("purge_interval",
|
||||||
|
feed.purge_interval,
|
||||||
|
reply.intervals.purge,
|
||||||
|
reply.force_purge ? {disabled: 1} : {})}
|
||||||
|
|
||||||
|
</fieldset>
|
||||||
|
</section>
|
||||||
|
</div>
|
||||||
|
<div dojoType="dijit.layout.ContentPane" title="${__('Authentication')}">
|
||||||
|
<section>
|
||||||
|
<fieldset>
|
||||||
|
<label>${__("Login:")}</label>
|
||||||
|
<input dojoType='dijit.form.TextBox'
|
||||||
|
autocomplete='new-password'
|
||||||
|
name='auth_login' value="${App.escapeHtml(feed.auth_login)}">
|
||||||
|
</fieldset>
|
||||||
|
<fieldset>
|
||||||
|
<label>${__("Password:")}</label>
|
||||||
|
<input dojoType='dijit.form.TextBox' type='password' name='auth_pass'
|
||||||
|
autocomplete='new-password'
|
||||||
|
value="${App.escapeHtml(feed.auth_pass)}">
|
||||||
|
</fieldset>
|
||||||
|
</section>
|
||||||
|
</div>
|
||||||
|
<div dojoType="dijit.layout.ContentPane" title="'.__('Options').'">
|
||||||
|
|
||||||
|
<section class='narrow'>
|
||||||
|
|
||||||
|
$include_in_digest = $row["include_in_digest"];
|
||||||
|
|
||||||
|
if ($include_in_digest) {
|
||||||
|
$checked = "checked="1"
|
||||||
|
} else {
|
||||||
|
$checked = "
|
||||||
|
}
|
||||||
|
|
||||||
|
<fieldset class='narrow'>
|
||||||
|
|
||||||
|
<label class='checkbox'><input dojoType="dijit.form.CheckBox" type="checkbox" id="include_in_digest"
|
||||||
|
name="include_in_digest"
|
||||||
|
$checked> ".__('Include in e-mail digest')."</label>
|
||||||
|
|
||||||
|
</fieldset>
|
||||||
|
|
||||||
|
$always_display_enclosures = $row["always_display_enclosures"];
|
||||||
|
|
||||||
|
if ($always_display_enclosures) {
|
||||||
|
$checked = "checked
|
||||||
|
} else {
|
||||||
|
$checked = "
|
||||||
|
}
|
||||||
|
|
||||||
|
<fieldset class='narrow'>
|
||||||
|
|
||||||
|
<label class='checkbox'><input dojoType="dijit.form.CheckBox" type="checkbox" id="always_display_enclosures"
|
||||||
|
name="always_display_enclosures"
|
||||||
|
$checked> ".__('Always display image attachments')."</label>
|
||||||
|
|
||||||
|
</fieldset>
|
||||||
|
|
||||||
|
$hide_images = $row["hide_images"];
|
||||||
|
|
||||||
|
if ($hide_images) {
|
||||||
|
$checked = "checked="1"
|
||||||
|
} else {
|
||||||
|
$checked = "
|
||||||
|
}
|
||||||
|
|
||||||
|
<fieldset class='narrow'>
|
||||||
|
|
||||||
|
<label class='checkbox'><input dojoType='dijit.form.CheckBox' type='checkbox' id='hide_images'
|
||||||
|
name='hide_images' $checked> ".__('Do not embed media')."</label>
|
||||||
|
|
||||||
|
</fieldset>
|
||||||
|
|
||||||
|
$cache_images = $row["cache_images"];
|
||||||
|
|
||||||
|
if ($cache_images) {
|
||||||
|
$checked = "checked="1"
|
||||||
|
} else {
|
||||||
|
$checked = "
|
||||||
|
}
|
||||||
|
|
||||||
|
<fieldset class='narrow'>
|
||||||
|
|
||||||
|
<label class='checkbox'><input dojoType='dijit.form.CheckBox' type='checkbox' id='cache_images'
|
||||||
|
name='cache_images' $checked> ". __('Cache media')."</label>
|
||||||
|
|
||||||
|
</fieldset>
|
||||||
|
|
||||||
|
$mark_unread_on_update = $row["mark_unread_on_update"];
|
||||||
|
|
||||||
|
if ($mark_unread_on_update) {
|
||||||
|
$checked = "checked
|
||||||
|
} else {
|
||||||
|
$checked = "
|
||||||
|
}
|
||||||
|
|
||||||
|
<fieldset class='narrow'>
|
||||||
|
|
||||||
|
<label class='checkbox'><input dojoType='dijit.form.CheckBox' type='checkbox' id='mark_unread_on_update'
|
||||||
|
name='mark_unread_on_update' $checked> ".__('Mark updated articles as unread')."</label>
|
||||||
|
|
||||||
|
</fieldset>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div dojoType="dijit.layout.ContentPane" title="${__('Icon')}">
|
||||||
|
|
||||||
|
<img class='feedIcon feed-editor-icon' src="${feed.icon ? App.escapeHtml(feed.icon) : ""}">
|
||||||
|
|
||||||
|
<form onsubmit="return false" id="feed_icon_upload_form" enctype="multipart/form-data" method="post">
|
||||||
|
<label class="dijitButton">${__("Choose file...")}
|
||||||
|
<input style="display: none" id="icon_file" size="10" name="icon_file" type="file">
|
||||||
|
</label>
|
||||||
|
|
||||||
|
${App.FormFields.hidden_tag("op", "pref-feeds")}
|
||||||
|
${App.FormFields.hidden_tag("feed_id", feed_id)}
|
||||||
|
${App.FormFields.hidden_tag("method", "uploadIcon")}
|
||||||
|
${App.FormFields.hidden_tag("csrf_token", App.getInitParam("csrf_token"))}
|
||||||
|
|
||||||
|
${App.FormFields.submit_tag(__("Replace"), {onclick: "return CommonDialogs.uploadFeedIcon()"})}
|
||||||
|
${App.FormFields.submit_tag(__("Remove"), {class: "alt-danger", onclick: "return CommonDialogs.removeFeedIcon("+feed_id+")"})}
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div dojoType="dijit.layout.ContentPane" title="${__('Plugins')}">
|
||||||
|
${reply.plugin_data}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<footer>
|
||||||
|
${App.FormFields.button_tag(__("Unsubscribe"), "", {class: "pull-left alt-danger", onclick: "App.dialogOf(this).unsubscribe()"})}
|
||||||
|
${App.FormFields.submit_tag(__("Save"), {onclick: "return App.dialogOf(this).execute()"})}
|
||||||
|
${App.FormFields.cancel_dialog_tag(__("Cancel"))}
|
||||||
|
</footer>
|
||||||
|
`);
|
||||||
})
|
})
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue