diff --git a/classes/pref/prefs.php b/classes/pref/prefs.php index 89783bd38c2f74e4b1cdda26bcd324537406ef16..1ba8ff3c4b231a12a1060eb1eaacacf14daeec17 100644 --- a/classes/pref/prefs.php +++ b/classes/pref/prefs.php @@ -802,7 +802,7 @@ class Pref_Prefs extends Handler_Protected { if ($about[3] ?? false) { $is_checked = in_array($name, $system_enabled) ? "checked" : ""; ?> - <fieldset class='prefs plugin'> + <fieldset class='prefs plugin' data-plugin-name='<?= htmlspecialchars($name) ?>'> <label><?= $name ?>:</label> <label class='checkbox description text-muted' id="PLABEL-<?= htmlspecialchars($name) ?>"> <input disabled='1' dojoType='dijit.form.CheckBox' <?= $is_checked ?> type='checkbox'><?= htmlspecialchars($about[1]) ?> @@ -867,7 +867,7 @@ class Pref_Prefs extends Handler_Protected { } ?> - <fieldset class='prefs plugin'> + <fieldset class='prefs plugin' data-plugin-name='<?= htmlspecialchars($name) ?>'> <label><?= $name ?>:</label> <label class='checkbox description text-muted' id="PLABEL-<?= htmlspecialchars($name) ?>"> <input name='plugins[]' value="<?= htmlspecialchars($name) ?>" @@ -1353,14 +1353,20 @@ class Pref_Prefs extends Handler_Protected { } } - private function _get_available_plugins() { + private function _get_available_plugins(array $installed = []) { if ($_SESSION["access_level"] >= 10 && Config::get(Config::ENABLE_PLUGIN_INSTALLER)) { - return json_decode(UrlHelper::fetch(['url' => 'https://tt-rss.org/plugins.json']), true); + $obj = json_decode(UrlHelper::fetch(['url' => 'https://tt-rss.org/plugins.json'])); + + // TODO: filter installed, we'll need class names in the plugins.json + + return $obj; } } function getAvailablePlugins() { + $installed = $_REQUEST['installed']; + if ($_SESSION["access_level"] >= 10) { - print json_encode($this->_get_available_plugins()); + print json_encode($this->_get_available_plugins($installed)); } } diff --git a/js/PrefHelpers.js b/js/PrefHelpers.js index b599e7e3f494a7b48162d5ca723e7c17e7a44ca3..889d93232ac35d5a102a62b7e8204002f54feef4 100644 --- a/js/PrefHelpers.js +++ b/js/PrefHelpers.js @@ -370,8 +370,10 @@ const Helpers = { PI_ERR_NO_TEMPDIR: "PI_ERR_NO_TEMPDIR", PI_ERR_PLUGIN_NOT_FOUND: "PI_ERR_PLUGIN_NOT_FOUND", PI_ERR_NO_WORKDIR: "PI_ERR_NO_WORKDIR", - title: __("List of plugins"), + title: __("Available plugins"), need_refresh: false, + entries: false, + search_query: "", onHide: function() { if (this.need_refresh) { Helpers.Prefs.refresh(); @@ -428,18 +430,26 @@ const Helpers = { install_dialog.show(); }, - refresh: function() { + search: function() { + this.search_query = this.attr('value').search.toLowerCase(); + this.render_contents(); + }, + render_contents: function() { const container = dialog.domNode.querySelector(".contents"); - container.innerHTML = `<li class='text-center'>${__("Looking for plugins...")}</li>`; - xhr.json("backend.php", {op: "pref-prefs", method: "getAvailablePlugins"}, (reply) => { + if (!dialog.entries) { + container.innerHTML = `<li class='text-center text-error'>${__("Operation failed: check event log.")}</li>`; + } else { + container.innerHTML = ""; - if (!reply) { - container.innerHTML = `<li class='text-center text-error'>${__("Operation failed: check event log.")}</li>`; - } else { - container.innerHTML = ""; + let results_rendered = 0; + + dialog.entries.forEach((plugin) => { + if (!dialog.search_query || + (plugin.name.toLowerCase().indexOf(dialog.search_query) != -1 || plugin.description.toLowerCase().indexOf(dialog.search_query) != -1)) { + + ++results_rendered; - reply.forEach((plugin) => { container.innerHTML += ` <li data-row-value="${App.escapeHtml(plugin.name)}"> <h3 style="margin-top: 0">${plugin.name} @@ -456,17 +466,39 @@ const Helpers = { <hr/> </li> ` - }); + } + }); - dojo.parser.parse(container); + if (results_rendered == 0) { + container.innerHTML = `<li class='text-center text-info'>${__("Could not find any plugins for this search query.")}</li>`; } + + dojo.parser.parse(container); + } + }, + reload: function() { + const container = dialog.domNode.querySelector(".contents"); + container.innerHTML = `<li class='text-center'>${__("Looking for plugins...")}</li>`; + + const installed = [...document.querySelectorAll('*[data-plugin-name]')].map((p) => p.getAttribute('data-plugin-name')); + + xhr.json("backend.php", {op: "pref-prefs", method: "getAvailablePlugins", 'installed[]': installed}, (reply) => { + dialog.entries = reply; + dialog.render_contents(); }); }, content: ` - <ul class="panel panel-scrollable contents"> </ul> + <div dojoType='fox.Toolbar'> + <div class='pull-right'> + <input name="search" placeholder="${__("Search...")}" type="search" dojoType="dijit.form.TextBox" onkeyup="App.dialogOf(this).search()"> + </div> + <div style='height : 16px'> </div> <!-- disgusting --> + </div> + + <ul style='clear : both' class="panel panel-scrollable-400px contents"> </ul> <footer> - ${App.FormFields.button_tag(__("Refresh"), "", {class: 'alt-primary', onclick: 'App.dialogOf(this).refresh()'})} + ${App.FormFields.button_tag(__("Refresh"), "", {class: 'alt-primary', onclick: 'App.dialogOf(this).reload()'})} ${App.FormFields.cancel_dialog_tag(__("Close"))} </footer> `, @@ -474,7 +506,7 @@ const Helpers = { const tmph = dojo.connect(dialog, 'onShow', function () { dojo.disconnect(tmph); - dialog.refresh(); + dialog.reload(); }); dialog.show(); diff --git a/themes/compact.css b/themes/compact.css index 230aab7da1f7f836760eb68567370dd9f7c07892..1f9d457dcd660c6d96d0236a92849dd4fdd558fc 100644 --- a/themes/compact.css +++ b/themes/compact.css @@ -988,6 +988,10 @@ body.ttrss_main .panel-scrollable { overflow: auto; height: 200px; } +body.ttrss_main .panel-scrollable-400px { + overflow: auto; + height: 400px; +} body.ttrss_main ul.list li { padding: 2px; } @@ -1715,9 +1719,13 @@ body.ttrss_utility.share_popup .content { font-size: 13px; padding: 0px; } -.flat .dijitToolbar .dijitTextBox .dijitInputInner { +.flat .dijitToolbar .dijitTextBox .dijitInputContainer .dijitInputInner { line-height: 10px; } +.flat .dijitToolbar .dijitTextBox .dijitInputContainer .dijitPlaceHolder { + line-height: 15px; + margin-left: 0; +} .flat .dijitToolbar label { position: relative; top: 2px; diff --git a/themes/compact_night.css b/themes/compact_night.css index 12737d903bd83c1823bff224f715ad3e4f19a803..8f2507f4fbad0fc9a76931080964cadd27b84241 100644 --- a/themes/compact_night.css +++ b/themes/compact_night.css @@ -988,6 +988,10 @@ body.ttrss_main .panel-scrollable { overflow: auto; height: 200px; } +body.ttrss_main .panel-scrollable-400px { + overflow: auto; + height: 400px; +} body.ttrss_main ul.list li { padding: 2px; } @@ -1617,9 +1621,13 @@ body.ttrss_utility fieldset > label.checkbox { font-size: 13px; padding: 0px; } -.flat .dijitToolbar .dijitTextBox .dijitInputInner { +.flat .dijitToolbar .dijitTextBox .dijitInputContainer .dijitInputInner { line-height: 10px; } +.flat .dijitToolbar .dijitTextBox .dijitInputContainer .dijitPlaceHolder { + line-height: 15px; + margin-left: 0; +} .flat .dijitToolbar label { position: relative; top: 2px; diff --git a/themes/light.css b/themes/light.css index f8da03ca0ac1215afc8b28b4c8c0657a00975646..c1e5201d21673522cf5d9bdbe0dc4d5b900102a9 100644 --- a/themes/light.css +++ b/themes/light.css @@ -988,6 +988,10 @@ body.ttrss_main .panel-scrollable { overflow: auto; height: 200px; } +body.ttrss_main .panel-scrollable-400px { + overflow: auto; + height: 400px; +} body.ttrss_main ul.list li { padding: 2px; } @@ -1715,9 +1719,13 @@ body.ttrss_utility.share_popup .content { font-size: 13px; padding: 0px; } -.flat .dijitToolbar .dijitTextBox .dijitInputInner { +.flat .dijitToolbar .dijitTextBox .dijitInputContainer .dijitInputInner { line-height: 10px; } +.flat .dijitToolbar .dijitTextBox .dijitInputContainer .dijitPlaceHolder { + line-height: 15px; + margin-left: 0; +} .flat .dijitToolbar label { position: relative; top: 2px; diff --git a/themes/light/dijit_basic.less b/themes/light/dijit_basic.less index 0582ddc5c7170a3aaf4611e0f5de78d0ce52694d..da1138986d725340dad890aeb4771f02c852764d 100644 --- a/themes/light/dijit_basic.less +++ b/themes/light/dijit_basic.less @@ -41,8 +41,15 @@ font-size: 13px; padding: 0px; - .dijitTextBox .dijitInputInner { - line-height : 10px; + .dijitTextBox .dijitInputContainer { + .dijitInputInner { + line-height : 10px; + } + + .dijitPlaceHolder { + line-height : 15px; + margin-left : 0; + } } label { diff --git a/themes/light/tt-rss.less b/themes/light/tt-rss.less index 9044afb246210aff6996db21911912664353b3d8..36b73b4f474623976f79390c5ca0d4c2e70e33de 100644 --- a/themes/light/tt-rss.less +++ b/themes/light/tt-rss.less @@ -1171,6 +1171,11 @@ body.ttrss_main { height : 200px; } + .panel-scrollable-400px { + overflow : auto; + height : 400px; + } + ul.list li { padding : 2px; } diff --git a/themes/night.css b/themes/night.css index b30c260f6ab7f961bc8611b5b29db0fd0a08bbbf..1000586cfe69ed063482c6d6d48a1233f1340f0a 100644 --- a/themes/night.css +++ b/themes/night.css @@ -989,6 +989,10 @@ body.ttrss_main .panel-scrollable { overflow: auto; height: 200px; } +body.ttrss_main .panel-scrollable-400px { + overflow: auto; + height: 400px; +} body.ttrss_main ul.list li { padding: 2px; } @@ -1618,9 +1622,13 @@ body.ttrss_utility fieldset > label.checkbox { font-size: 13px; padding: 0px; } -.flat .dijitToolbar .dijitTextBox .dijitInputInner { +.flat .dijitToolbar .dijitTextBox .dijitInputContainer .dijitInputInner { line-height: 10px; } +.flat .dijitToolbar .dijitTextBox .dijitInputContainer .dijitPlaceHolder { + line-height: 15px; + margin-left: 0; +} .flat .dijitToolbar label { position: relative; top: 2px; diff --git a/themes/night_blue.css b/themes/night_blue.css index 46f1aa124b1f4c1f4cc086a9ccf1487199b87e22..4df5f54abf13148a8d2d368d03108802528e2b1d 100644 --- a/themes/night_blue.css +++ b/themes/night_blue.css @@ -989,6 +989,10 @@ body.ttrss_main .panel-scrollable { overflow: auto; height: 200px; } +body.ttrss_main .panel-scrollable-400px { + overflow: auto; + height: 400px; +} body.ttrss_main ul.list li { padding: 2px; } @@ -1618,9 +1622,13 @@ body.ttrss_utility fieldset > label.checkbox { font-size: 13px; padding: 0px; } -.flat .dijitToolbar .dijitTextBox .dijitInputInner { +.flat .dijitToolbar .dijitTextBox .dijitInputContainer .dijitInputInner { line-height: 10px; } +.flat .dijitToolbar .dijitTextBox .dijitInputContainer .dijitPlaceHolder { + line-height: 15px; + margin-left: 0; +} .flat .dijitToolbar label { position: relative; top: 2px;