diff options
Diffstat (limited to 'js/CommonDialogs.js')
-rw-r--r-- | js/CommonDialogs.js | 633 |
1 files changed, 424 insertions, 209 deletions
diff --git a/js/CommonDialogs.js b/js/CommonDialogs.js index 70596539b..321ddf6d3 100644 --- a/js/CommonDialogs.js +++ b/js/CommonDialogs.js @@ -3,7 +3,7 @@ /* eslint-disable new-cap */ /* eslint-disable no-new */ -/* global __, dojo, dijit, Notify, App, Feeds, $$, xhrPost, xhrJson, Tables, Effect, fox */ +/* global __, dojo, dijit, Notify, App, Feeds, xhrPost, xhr, Tables, fox */ /* exported CommonDialogs */ const CommonDialogs = { @@ -11,89 +11,99 @@ const CommonDialogs = { const dialog = dijit.byId("infoBox"); if (dialog) dialog.hide(); }, - removeFeedIcon: function(id) { - if (confirm(__("Remove stored feed icon?"))) { - Notify.progress("Removing feed icon...", true); - - const query = {op: "pref-feeds", method: "removeicon", feed_id: id}; - - xhrPost("backend.php", query, () => { - Notify.info("Feed icon removed."); - - if (App.isPrefs()) - dijit.byId("feedTree").reload(); - else - Feeds.reload(); - - const icon = $$(".feed-editor-icon")[0]; - - if (icon) - icon.src = icon.src.replace(/\?[0-9]+$/, "?" + new Date().getTime()); - - }); - } - - return false; - }, - uploadFeedIcon: function() { - const file = $("icon_file"); - - if (file.value.length == 0) { - alert(__("Please select an image file to upload.")); - } else if (confirm(__("Upload new icon for this feed?"))) { - Notify.progress("Uploading, please wait...", true); - - const xhr = new XMLHttpRequest(); - - xhr.open( 'POST', 'backend.php', true ); - xhr.onload = function () { - switch (parseInt(this.responseText)) { - case 0: - { - Notify.info("Upload complete."); - - if (App.isPrefs()) - dijit.byId("feedTree").reload(); - else - Feeds.reload(); - - const icon = $$(".feed-editor-icon")[0]; - - if (icon) - icon.src = icon.src.replace(/\?[0-9]+$/, "?" + new Date().getTime()); - - } - break; - case 1: - Notify.error("Upload failed: icon is too big."); - break; - case 2: - Notify.error("Upload failed."); - break; - } - }; - xhr.send(new FormData($("feed_icon_upload_form"))); - } - - return false; - }, - quickAddFeed: function() { - xhrPost("backend.php", - {op: "feeds", method: "quickAddFeed"}, - (transport) => { - + subscribeToFeed: function() { + xhr.json("backend.php", + {op: "feeds", method: "subscribeToFeed"}, + (reply) => { const dialog = new fox.SingleUseDialog({ - id: "feedAddDlg", title: __("Subscribe to Feed"), - content: transport.responseText, + content: ` + <form onsubmit='return false'> + + ${App.FormFields.hidden_tag("op", "feeds")} + ${App.FormFields.hidden_tag("method", "add")} + + <div id='fadd_error_message' style='display : none' class='alert alert-danger'></div> + + <div id='fadd_multiple_notify' style='display : none'> + <div class='alert alert-info'> + ${__("Provided URL is a HTML page referencing multiple feeds, please select required feed from the dropdown menu below.")} + </div> + </div> + + <section> + <fieldset> + <div style='float : right'><img style='display : none' id='feed_add_spinner' src='images/indicator_white.gif'></div> + <input style='font-size : 16px; width : 500px;' + placeHolder="${__("Feed or site URL")}" + dojoType='dijit.form.ValidationTextBox' + required='1' name='feed' id='feedDlg_feedUrl'> + </fieldset> + + ${App.getInitParam('enable_feed_cats') ? + ` + <fieldset> + <label class='inline'>${__('Place in category:')}</label> + ${reply.cat_select} + </fieldset> + ` : ''} + </section> + + <div id="feedDlg_feedsContainer" style="display : none"> + <header>${__('Available feeds')}</header> + <section> + <fieldset> + <select id="feedDlg_feedContainerSelect" + dojoType="fox.form.Select" size="3"> + <script type="dojo/method" event="onChange" args="value"> + dijit.byId("feedDlg_feedUrl").attr("value", value); + </script> + </select> + </fieldset> + </section> + </div> + + <div id='feedDlg_loginContainer' style='display : none'> + <section> + <fieldset> + <input dojoType="dijit.form.TextBox" name='login'" + placeHolder="${__("Login")}" + autocomplete="new-password" + style="width : 10em;"> + <input + placeHolder="${__("Password")}" + dojoType="dijit.form.TextBox" type='password' + autocomplete="new-password" + style="width : 10em;" name='pass'"> + </fieldset> + </section> + </div> + + <section> + <label class='checkbox'> + <input type='checkbox' name='need_auth' dojoType='dijit.form.CheckBox' id='feedDlg_loginCheck' + onclick='App.displayIfChecked(this, "feedDlg_loginContainer")'> + ${__('This feed requires authentication.')} + </label> + </section> + + <footer> + <button dojoType='dijit.form.Button' class='alt-primary' type='submit' + onclick='App.dialogOf(this).execute()'> + ${__('Subscribe')} + </button> + <button dojoType='dijit.form.Button' onclick='App.dialogOf(this).hide()'> + ${__('Cancel')} + </button> + </footer> + </form> + `, show_error: function (msg) { - const elem = $("fadd_error_message"); + const elem = App.byId("fadd_error_message"); elem.innerHTML = msg; - if (!Element.visible(elem)) - new Effect.Appear(elem); - + Element.show(elem); }, execute: function () { if (this.validate()) { @@ -104,17 +114,12 @@ const CommonDialogs = { Element.show("feed_add_spinner"); Element.hide("fadd_error_message"); - xhrPost("backend.php", this.attr('value'), (transport) => { + xhr.json("backend.php", this.attr('value'), (reply) => { try { - let reply; - - try { - reply = JSON.parse(transport.responseText); - } catch (e) { + if (!reply) { Element.hide("feed_add_spinner"); alert(__("Failed to parse output. This can indicate server timeout and/or network issues. Backend output was logged to browser console.")); - console.log('quickAddFeed, backend returned:' + transport.responseText); return; } @@ -161,7 +166,7 @@ const CommonDialogs = { } } - Effect.Appear('feedDlg_feedsContainer', {duration: 0.5}); + Element.show('feedDlg_feedsContainer'); } break; case 5: @@ -188,69 +193,103 @@ const CommonDialogs = { }); }, showFeedsWithErrors: function() { - const dialog = new fox.SingleUseDialog({ - id: "errorFeedsDlg", - title: __("Feeds with update errors"), - getSelectedFeeds: function () { - return Tables.getSelected("error-feeds-list"); - }, - removeSelected: function () { - const sel_rows = this.getSelectedFeeds(); - if (sel_rows.length > 0) { - if (confirm(__("Remove selected feeds?"))) { - Notify.progress("Removing selected feeds...", true); + xhr.json("backend.php", {op: "pref-feeds", method: "feedsWithErrors"}, (reply) => { - const query = { - op: "pref-feeds", method: "remove", - ids: sel_rows.toString() - }; + const dialog = new fox.SingleUseDialog({ + id: "errorFeedsDlg", + title: __("Feeds with update errors"), + getSelectedFeeds: function () { + return Tables.getSelected("error-feeds-list"); + }, + removeSelected: function () { + const sel_rows = this.getSelectedFeeds(); - xhrPost("backend.php", query, () => { - Notify.close(); - dialog.hide(); + if (sel_rows.length > 0) { + if (confirm(__("Remove selected feeds?"))) { + Notify.progress("Removing selected feeds...", true); - if (App.isPrefs()) - dijit.byId("feedTree").reload(); - else - Feeds.reload(); + const query = { + op: "pref-feeds", method: "remove", + ids: sel_rows.toString() + }; - }); - } + xhr.post("backend.php", query, () => { + Notify.close(); + dialog.hide(); - } else { - alert(__("No feeds selected.")); - } - }, - content: __("Loading, please wait...") - }); + if (App.isPrefs()) + dijit.byId("feedTree").reload(); + else + Feeds.reload(); - const tmph = dojo.connect(dialog, 'onShow', function () { - dojo.disconnect(tmph); + }); + } - xhrPost("backend.php", {op: "pref-feeds", method: "feedsWithErrors"}, (transport) => { - dialog.attr('content', transport.responseText); - }) - }); + } else { + alert(__("No feeds selected.")); + } + }, + content: ` + <div dojoType="fox.Toolbar"> + <div dojoType="fox.form.DropDownButton"> + <span>${__('Select')}</span> + <div dojoType="dijit.Menu" style="display: none"> + <div onclick="Tables.select('error-feeds-list', true)" + dojoType="dijit.MenuItem">${__('All')}</div> + <div onclick="Tables.select('error-feeds-list', false)" + dojoType="dijit.MenuItem">${__('None')}</div> + </div> + </div> + </div> + + <div class='panel panel-scrollable'> + <table width='100%' id='error-feeds-list'> + + ${reply.map((row) => ` + <tr data-row-id='${row.id}'> + <td width='5%' align='center'> + <input onclick='Tables.onRowChecked(this)' dojoType="dijit.form.CheckBox" + type="checkbox"> + </td> + <td> + <a href="#" title="${__("Click to edit feed")}" onclick="CommonDialogs.editFeed(${row.id})"> + ${App.escapeHtml(row.title)} + </a> + </td> + <td class='text-muted small' align='right' width='50%'> + ${App.escapeHtml(row.last_error)} + </td> + </tr> + `).join("")} + </table> + </div> + + <footer> + <button style='float : left' class='alt-danger' dojoType='dijit.form.Button' onclick='App.dialogOf(this).removeSelected()'> + ${__('Unsubscribe from selected feeds')} + </button> + <button dojoType='dijit.form.Button' class='alt-primary' type='submit'> + ${__('Close this window')} + </button> + </footer> + ` + }); - dialog.show(); + dialog.show(); + }) }, - addLabel: function(select, callback) { + addLabel: function() { const caption = prompt(__("Please enter label caption:"), ""); if (caption != undefined && caption.trim().length > 0) { const query = {op: "pref-labels", method: "add", caption: caption.trim()}; - if (select) - Object.extend(query, {output: "select"}); - Notify.progress("Loading, please wait...", true); - xhrPost("backend.php", query, (transport) => { - if (callback) { - callback(transport); - } else if (App.isPrefs()) { + xhr.post("backend.php", query, () => { + if (dijit.byId("labelTree")) { dijit.byId("labelTree").reload(); } else { Feeds.reload(); @@ -267,13 +306,13 @@ const CommonDialogs = { const query = {op: "pref-feeds", quiet: 1, method: "remove", ids: feed_id}; - xhrPost("backend.php", query, () => { + xhr.post("backend.php", query, () => { if (App.isPrefs()) { dijit.byId("feedTree").reload(); } else { if (feed_id == Feeds.getActive()) setTimeout(() => { - Feeds.open({feed: -5}) + Feeds.openDefaultFeed(); }, 100); @@ -284,28 +323,109 @@ const CommonDialogs = { return false; }, - editFeed: function (feed) { - if (feed <= 0) + editFeed: function (feed_id) { + if (feed_id <= 0) 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); const dialog = new fox.SingleUseDialog({ id: "feedEditDlg", title: __("Edit Feed"), - unsubscribeFeed: function(feed_id, title) { - if (confirm(__("Unsubscribe from %s?").replace("%s", title))) { + feed_title: "", + unsubscribe: function() { + if (confirm(__("Unsubscribe from %s?").replace("%s", this.feed_title))) { dialog.hide(); CommonDialogs.unsubscribeFeed(feed_id); } }, + uploadIcon: function(input) { + if (input.files.length != 0) { + const icon_file = input.files[0]; + + if (icon_file.type.indexOf("image/") == -1) { + alert(__("Please select an image file.")); + return; + } + + const fd = new FormData(); + fd.append('icon_file', icon_file) + fd.append('feed_id', feed_id); + fd.append('op', 'pref-feeds'); + fd.append('method', 'uploadIcon'); + fd.append('csrf_token', App.getInitParam("csrf_token")); + + const xhr = new XMLHttpRequest(); + + xhr.open( 'POST', 'backend.php', true ); + xhr.onload = function () { + console.log(this.responseText); + + // TODO: make a notice box within panel content + switch (parseInt(this.responseText)) { + case 1: + Notify.error("Upload failed: icon is too big."); + break; + case 2: + Notify.error("Upload failed."); + break; + default: + { + Notify.info("Upload complete."); + + if (App.isPrefs()) + dijit.byId("feedTree").reload(); + else + Feeds.reload(); + + const icon = dialog.domNode.querySelector(".feedIcon"); + + if (icon) { + icon.src = this.responseText; + icon.show(); + } + + input.value = ""; + } + } + }; + + xhr.send(fd); + + } + }, + removeIcon: function(id) { + if (confirm(__("Remove stored feed icon?"))) { + Notify.progress("Removing feed icon...", true); + + const query = {op: "pref-feeds", method: "removeicon", feed_id: id}; + + xhr.post("backend.php", query, () => { + Notify.info("Feed icon removed."); + + if (App.isPrefs()) + dijit.byId("feedTree").reload(); + else + Feeds.reload(); + + const icon = dialog.domNode.querySelector(".feedIcon"); + + if (icon) { + icon.src = ""; + icon.hide(); + } + }); + } + + return false; + }, execute: function () { if (this.validate()) { Notify.progress("Saving data...", true); - xhrPost("backend.php", dialog.attr('value'), () => { + xhr.post("backend.php", dialog.attr('value'), () => { dialog.hide(); Notify.close(); @@ -315,7 +435,9 @@ const CommonDialogs = { Feeds.reload(); }); + return true; } + return false; }, content: __("Loading, please wait...") }); @@ -323,102 +445,195 @@ const CommonDialogs = { const tmph = dojo.connect(dialog, 'onShow', function () { dojo.disconnect(tmph); - xhrPost("backend.php", {op: "pref-feeds", method: "editfeed", id: feed}, (transport) => { - dialog.attr('content', transport.responseText); + xhr.json("backend.php", {op: "pref-feeds", method: "editfeed", id: feed_id}, (reply) => { + const feed = reply.feed; + + // for unsub prompt + dialog.feed_title = feed.title; + + // options tab + const options = { + include_in_digest: [ feed.include_in_digest, __('Include in e-mail digest') ], + always_display_enclosures: [ feed.always_display_enclosures, __('Always display image attachments') ], + hide_images: [ feed.hide_images, __('Do not embed media') ], + cache_images: [ feed.cache_images, __('Cache media') ], + mark_unread_on_update: [ feed.mark_unread_on_update, __('Mark updated articles as unread') ] + }; + + dialog.attr('content', + ` + <form onsubmit="return false"> + <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 ? feed.feed_language : reply.lang.default, + 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"> + ${Object.keys(options).map((name) => + ` + <fieldset class='narrow'> + <label class="checkbox"> + ${App.FormFields.checkbox_tag(name, options[name][0])} + ${options[name][1]} + </label> + </fieldset> + `).join("")} + </section> + </div> + <div dojoType="dijit.layout.ContentPane" title="${__('Icon')}"> + <div><img class='feedIcon' style="${feed.icon ? "" : "display : none"}" src="${feed.icon ? App.escapeHtml(feed.icon) : ""}"></div> + + <label class="dijitButton">${__("Upload new icon...")} + <input style="display: none" type="file" onchange="App.dialogOf(this).uploadIcon(this)"> + </label> + + ${App.FormFields.submit_tag(__("Remove"), {class: "alt-danger", onclick: "App.dialogOf(this).removeIcon("+feed_id+")"})} + </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: "App.dialogOf(this).execute()"})} + ${App.FormFields.cancel_dialog_tag(__("Cancel"))} + </footer> + </form> + `); }) }); dialog.show(); }, - genUrlChangeKey: function(feed, is_cat) { - if (confirm(__("Generate new syndication address for this feed?"))) { - - Notify.progress("Trying to change address...", true); - - const query = {op: "pref-feeds", method: "regenFeedKey", id: feed, is_cat: is_cat}; - - xhrJson("backend.php", query, (reply) => { - const new_link = reply.link; - const e = $('gen_feed_url'); - - if (new_link) { - e.innerHTML = e.innerHTML.replace(/&key=.*$/, - "&key=" + new_link); - - e.href = e.href.replace(/&key=.*$/, - "&key=" + new_link); - - new Effect.Highlight(e); - - Notify.close(); - - } else { - Notify.error("Could not change feed URL."); - } - }); - } - return false; - }, - publishedOPML: function() { + generatedFeed: function(feed, is_cat, search = "") { Notify.progress("Loading, please wait...", true); - xhrJson("backend.php", {op: "pref-feeds", method: "getOPMLKey"}, (reply) => { + xhr.json("backend.php", {op: "pref-feeds", method: "getsharedurl", id: feed, is_cat: is_cat, search: search}, (reply) => { try { const dialog = new fox.SingleUseDialog({ - title: __("Public OPML URL"), - content: ` - <header>${__("Your Public OPML URL is:")}</header> - <section> - <div class='panel text-center'> - <a id='pub_opml_url' href="${App.escapeHtml(reply.link)}" target='_blank'>${reply.link}</a> - </div> - </section> - <footer class='text-center'> - <button dojoType='dijit.form.Button' onclick="return Helpers.OPML.changeKey()"> - ${__('Generate new URL')} - </button> - <button dojoType='dijit.form.Button' type='submit' class='alt-primary'> - ${__('Close this window')} - </button> - </footer> - ` - }); + title: __("Show as feed"), + regenFeedKey: function(feed, is_cat) { + if (confirm(__("Generate new syndication address for this feed?"))) { - dialog.show(); + Notify.progress("Trying to change address...", true); - Notify.close(); + const query = {op: "pref-feeds", method: "regenFeedKey", id: feed, is_cat: is_cat}; - } catch (e) { - App.Error.report(e); - } - }); - }, - generatedFeed: function(feed, is_cat, rss_url, feed_title) { + xhr.json("backend.php", query, (reply) => { + const new_link = reply.link; + const target = this.domNode.querySelector(".generated_url"); - Notify.progress("Loading, please wait...", true); + if (new_link && target) { + target.innerHTML = target.innerHTML.replace(/&key=.*$/, + "&key=" + new_link); - xhrJson("backend.php", {op: "pref-feeds", method: "getFeedKey", id: feed, is_cat: is_cat}, (reply) => { - try { - if (!feed_title && typeof Feeds != "undefined") - feed_title = Feeds.getName(feed, is_cat); + target.href = target.href.replace(/&key=.*$/, + "&key=" + new_link); - const secret_url = rss_url + "&key=" + encodeURIComponent(reply.link); + Notify.close(); - const dialog = new fox.SingleUseDialog({ - title: __("Show as feed"), + } else { + Notify.error("Could not change feed URL."); + } + }); + } + return false; + }, content: ` - <header>${__("%s can be accessed via the following secret URL:").replace("%s", feed_title)}</header> + <header>${__("%s can be accessed via the following secret URL:").replace("%s", App.escapeHtml(reply.title))}</header> <section> <div class='panel text-center'> - <a id='gen_feed_url' href="${App.escapeHtml(secret_url)}" target='_blank'>${secret_url}</a> + <a class='generated_url' href="${App.escapeHtml(reply.link)}" target='_blank'>${App.escapeHtml(reply.link)}</a> </div> </section> <footer> <button dojoType='dijit.form.Button' style='float : left' class='alt-info' onclick='window.open("https://tt-rss.org/wiki/GeneratedFeeds")'> <i class='material-icons'>help</i> ${__("More info...")}</button> - <button dojoType='dijit.form.Button' onclick="return CommonDialogs.genUrlChangeKey('${feed}', '${is_cat}')"> + <button dojoType='dijit.form.Button' onclick="return App.dialogOf(this).regenFeedKey('${feed}', '${is_cat}')"> ${__('Generate new URL')} </button> <button dojoType='dijit.form.Button' class='alt-primary' type='submit'> |