diff --git a/api/js/etemplate/et2_widget_dropdown_button.js b/api/js/etemplate/et2_widget_dropdown_button.js index e72c219211..f08c16eec3 100644 --- a/api/js/etemplate/et2_widget_dropdown_button.js +++ b/api/js/etemplate/et2_widget_dropdown_button.js @@ -377,5 +377,6 @@ var et2_dropdown_button = /** @class */ (function (_super) { }; return et2_dropdown_button; }(et2_core_inputWidget_1.et2_inputWidget)); +exports.et2_dropdown_button = et2_dropdown_button; et2_core_widget_1.et2_register_widget(et2_dropdown_button, ["dropdown_button"]); //# sourceMappingURL=et2_widget_dropdown_button.js.map \ No newline at end of file diff --git a/api/js/etemplate/et2_widget_dropdown_button.ts b/api/js/etemplate/et2_widget_dropdown_button.ts index c21efdaa89..150851f387 100644 --- a/api/js/etemplate/et2_widget_dropdown_button.ts +++ b/api/js/etemplate/et2_widget_dropdown_button.ts @@ -34,7 +34,7 @@ import {ClassWithAttributes} from "./et2_core_inheritance"; * * @augments et2_inputWidget */ -class et2_dropdown_button extends et2_inputWidget +export class et2_dropdown_button extends et2_inputWidget { static readonly attributes : any = { "label": { diff --git a/api/js/etemplate/et2_widget_favorites.js b/api/js/etemplate/et2_widget_favorites.js index c455e1d847..962fbfe0a3 100644 --- a/api/js/etemplate/et2_widget_favorites.js +++ b/api/js/etemplate/et2_widget_favorites.js @@ -1,3 +1,4 @@ +"use strict"; /** * EGroupware eTemplate2 - JS Favorite widget * @@ -7,14 +8,28 @@ * @link http://www.egroupware.org * @author Nathan Gray * @copyright Nathan Gray 2013 - * @version $Id$ */ - +var __extends = (this && this.__extends) || (function () { + var extendStatics = function (d, b) { + extendStatics = Object.setPrototypeOf || + ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) || + function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; }; + return extendStatics(d, b); + }; + return function (d, b) { + extendStatics(d, b); + function __() { this.constructor = d; } + d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); + }; +})(); +Object.defineProperty(exports, "__esModule", { value: true }); /*egw:uses - et2_dropdown_button; - et2_extension_nextmatch; + et2_dropdown_button; + et2_extension_nextmatch; */ - +var et2_core_widget_1 = require("./et2_core_widget"); +var et2_widget_dropdown_button_1 = require("./et2_widget_dropdown_button"); +var et2_core_inheritance_1 = require("./et2_core_inheritance"); /** * Favorites widget, designed for use with a nextmatch widget * @@ -42,369 +57,287 @@ * * @augments et2_dropdown_button */ -var et2_favorites = (function(){ "use strict"; return et2_dropdown_button.extend([et2_INextmatchHeader], -{ - attributes: { - "default_pref": { - "name": "Default preference key", - "type": "string", - "description": "The preference key where default favorite is stored (not the value)" - }, - "sidebox_target": { - "name": "Sidebox target", - "type": "string", - "description": "ID of element to insert favorite list into", - "default": "favorite_sidebox" - }, - "app": { - "name": "Application", - "type": "string", - "description": "Application to show favorites for" - }, - "filters": { - "name": "Extra filters", - "type": "any", - "description": "Array of extra filters to include in the saved favorite" - }, - - // These are particular to favorites - id: {"default": "favorite"}, - label: {"default": ""}, - label_updates: { "default": false}, - image: {"default": this.egw().image('fav_filter')}, - statustext: {"default": "Favorite queries", "type": "string"} - }, - - // Some convenient variables, used in closures / event handlers - header: null, - nextmatch: null, - favorite_prefix: "favorite_", - stored_filters: {}, - - // If filter was set server side, we need to remember it until nm is created - nm_filter: false, - - /** - * Constructor - * - * @memberOf et2_favorites - */ - init: function() { - this._super.apply(this, arguments); - this.sidebox_target = jQuery("#"+this.options.sidebox_target); - if(this.sidebox_target.length == 0 && egw_getFramework() != null) - { - var egw_fw = egw_getFramework(); - this.sidebox_target = jQuery("#"+this.options.sidebox_target,egw_fw.sidemenuDiv); - } - // Store array of sorted items - this.favSortedList = ['blank']; - - var apps = egw().user('apps'); - this.is_admin = (typeof apps['admin'] != "undefined"); - - this.stored_filters = this.load_favorites(this.options.app); - - this.preferred = egw.preference(this.options.default_pref,this.options.app); - if(!this.preferred || typeof this.stored_filters[this.preferred] == "undefined") - { - this.preferred = "blank"; - } - - // It helps to have the ID properly set before we get too far - this.set_id(this.id); - - this.init_filters(this); - - this.menu.addClass("favorites"); - - // Set the default (button) value - this.set_value(this.preferred,true); - - var self = this; - - // Add a listener on the radio buttons to set default filter - jQuery(this.menu).on("click","input:radio", function(event){ - // Don't do the menu - event.stopImmediatePropagation(); - - // Save as default favorite - used when you click the button - self.egw().set_preference(self.options.app,self.options.default_pref,jQuery(this).val()); - self.preferred = jQuery(this).val(); - - // Update sidebox, if there - if(self.sidebox_target.length) - { - self.sidebox_target.find("div.ui-icon-heart") - .replaceWith("
"); - jQuery("li[data-id='"+self.preferred+"'] div.sideboxstar",self.sidebox_target) - .replaceWith("
"); - } - - // Close the menu - self.menu.hide(); - - // Some user feedback - self.button.addClass("ui-state-active", 500,"swing",function(){ - self.button.removeClass("ui-state-active",2000); - }); - }); - - //Sort DomNodes of sidebox fav. menu - var sideBoxDOMNodeSort = function (_favSList) { - var favS = jQuery.isArray(_favSList)?_favSList.slice(0).reverse():[]; - - for (var i=0; i < favS.length;i++) - { - self.sidebox_target.children().find('[data-id$="' + favS[i] + '"]').prependTo(self.sidebox_target.children()); - } - }; - - //Add Sortable handler to nm fav. menu - jQuery(this.menu).sortable({ - - items:'li:not([data-id$="add"])', - placeholder:'ui-fav-sortable-placeholder', - delay: 250, //(millisecond) delay before the sorting should start - update: function (event, ui) - { - self.favSortedList = jQuery(this).sortable('toArray', {attribute:'data-id'}); - - self.egw().set_preference(self.options.app,'fav_sort_pref',self.favSortedList); - - sideBoxDOMNodeSort(self.favSortedList); - } - }); - - // Add a listener on the delete to remove - this.menu.on("click","div.ui-icon-trash", app[self.options.app], function() { - // App instance might not be ready yet, so don't bind directly - app[self.options.app].delete_favorite.apply(this,arguments); - }) - // Wrap and unwrap because jQueryUI styles use a parent, and we don't want to change the state of the menu item - // Wrap in a span instead of a div because div gets a border - .on("mouseenter","div.ui-icon-trash", function() {jQuery(this).wrap("");}) - .on("mouseleave","div.ui-icon-trash", function() {jQuery(this).unwrap();}); - - // Trigger refresh of menu options now that events are registered - // to update sidebox - if(this.sidebox_target.length > 0) - { - this.init_filters(this); - } - }, - - /** - * Load favorites from preferences - * - * @param app String Load favorites from this application - */ - load_favorites: function(app) { - - // Default blank filter - var stored_filters = { - 'blank': { - name: this.egw().lang("No filters"), - state: {} - } - }; - - // Load saved favorites - var preferences = egw.preference("*",app); - for(var pref_name in preferences) - { - if(pref_name.indexOf(this.favorite_prefix) == 0 && typeof preferences[pref_name] == 'object') - { - var name = pref_name.substr(this.favorite_prefix.length); - stored_filters[name] = preferences[pref_name]; - // Keep older favorites working - they used to store nm filters in 'filters',not state - if(preferences[pref_name].filters) - { - stored_filters[pref_name].state = preferences[pref_name].filters; - } - } - if (pref_name == 'fav_sort_pref') - { - this.favSortedList = preferences[pref_name]; - //Make sure sorted list is always an array, seems some old fav are not array - if (!jQuery.isArray(this.favSortedList)) this.favSortedList = this.favSortedList.split(','); - } - } - if(typeof stored_filters == "undefined" || !stored_filters) - { - stored_filters = {}; - } - else - { - for(var name in stored_filters) - { - if (this.favSortedList.indexOf(name) < 0) - { - this.favSortedList.push(name); - } - } - this.egw().set_preference (this.options.app,'fav_sort_pref',this.favSortedList); - if (this.favSortedList.length > 0) - { - var sortedListObj = {}; - - for (var i=0;i < this.favSortedList.length;i++) - { - if (typeof stored_filters[this.favSortedList[i]] != 'undefined') - { - sortedListObj[this.favSortedList[i]] = stored_filters[this.favSortedList[i]]; - } - else - { - this.favSortedList.splice(i,1); - this.egw().set_preference (this.options.app,'fav_sort_pref',this.favSortedList); - } - } - stored_filters = jQuery.extend(sortedListObj,stored_filters); - } - } - return stored_filters; - }, - - // Create & set filter options for dropdown menu - init_filters: function(widget, filters) - { - if(typeof filters == "undefined") - { - filters = this.stored_filters; - } - - var options = {}; - for(var name in filters) - { - options[name] = ""+ - (filters[name].name != undefined ? filters[name].name : name) + - (filters[name].group != false && !this.is_admin || name == 'blank' ? "" : - "
"); - } - - // Only add 'Add current' if we have a nextmatch - if(this.nextmatch) - { - options.add = ""+this.egw().lang('Add current'); - } - widget.set_select_options.call(widget,options); - - // Set radio to current value - jQuery("input[value='"+ this.preferred +"']:radio", this.menu).attr("checked",true); - }, - - set_nm_filters: function(filters) - { - if(this.nextmatch) - { - this.nextmatch.applyFilters(filters); - } - else - { - console.log(filters); - } - }, - - onclick: function(node) { - // Apply preferred filter - make sure it's an object, and not a reference - if(this.preferred && this.stored_filters[this.preferred]) - { - // use app[appname].setState if available to allow app to overwrite it (eg. change to non-listview in calendar) - if (typeof app[this.options.app] != 'undefined') - { - app[this.options.app].setState(this.stored_filters[this.preferred]); - } - else - { - this.set_nm_filters(jQuery.extend({},this.stored_filters[this.preferred].state)); - } - } - else - { - alert(this.egw().lang("No default set")); - } - }, - - // Apply the favorite when you pick from the list - change: function(selected_node) { - this.value = jQuery(selected_node).attr("data-id"); - if(this.value == "add" && this.nextmatch) - { - // Get current filters - var current_filters = jQuery.extend({},this.nextmatch.activeFilters); - - // Add in extras - for(var extra in this.options.filters) - { - // Don't overwrite what nm has, chances are nm has more up-to-date value - if(typeof current_filters == 'undefined') - { - current_filters[extra] = this.nextmatch.options.settings[extra]; - } - } - - // Skip columns for now - delete current_filters.selcolumns; - - // Add in application's settings - if(this.filters != true) - { - for(var i = 0; i < this.filters.length; i++) - { - current_filters[this.options.filters[i]] = this.nextmatch.options.settings[this.options.filters[i]]; - } - } - - // Call framework - app[this.options.app].add_favorite(current_filters); - - // Reset value - this.set_value(this.preferred,true); - } - else if (this.value == 'blank') - { - // Reset filters when select no filters - this.set_nm_filters({}); - } - }, - - set_value: function(filter_name, parent) { - if(parent) - { - return this._super.call(this, filter_name); - } - - if(filter_name == 'add') return false; - - app[this.options.app].setState(this.stored_filters[filter_name]); - return false; - }, - - getValue: function() - { - return null; - }, - - /** - * Set the nextmatch to filter - * From et2_INextmatchHeader interface - * - * @param {et2_nextmatch} nextmatch - */ - setNextmatch: function(nextmatch) - { - this.nextmatch = nextmatch; - - if(this.nm_filter) - { - this.set_value(this.nm_filter); - this.nm_filter = false; - } - - // Re-generate filter list so we can add 'Add current' - this.init_filters(this); - } -});}).call(this); -et2_register_widget(et2_favorites, ["favorites"]); +var et2_favorites = /** @class */ (function (_super) { + __extends(et2_favorites, _super); + /** + * Constructor + * + * @memberOf et2_favorites + */ + function et2_favorites(_parent, _attrs, _child) { + var _this = _super.call(this, _parent, _attrs, et2_core_inheritance_1.ClassWithAttributes.extendAttributes(et2_favorites._attributes, _child || {})) || this; + // Some convenient variables, used in closures / event handlers + _this.header = null; + _this.nextmatch = null; + _this.favSortedList = null; + _this.sidebox_target = null; + // If filter was set server side, we need to remember it until nm is created + _this.nm_filter = false; + _this.sidebox_target = jQuery("#" + _this.options.sidebox_target); + if (_this.sidebox_target.length == 0 && egw_getFramework() != null) { + var egw_fw = egw_getFramework(); + _this.sidebox_target = jQuery("#" + _this.options.sidebox_target, egw_fw.sidemenuDiv); + } + // Store array of sorted items + _this.favSortedList = ['blank']; + var apps = egw().user('apps'); + et2_favorites.is_admin = (typeof apps['admin'] != "undefined"); + _this.stored_filters = _this.load_favorites(_this.options.app); + _this.preferred = egw.preference(_this.options.default_pref, _this.options.app); + if (!_this.preferred || typeof _this.stored_filters[_this.preferred] == "undefined") { + _this.preferred = "blank"; + } + // It helps to have the ID properly set before we get too far + _this.set_id(_this.id); + _this.init_filters(_this); + _this.menu.addClass("favorites"); + // Set the default (button) value + _this.set_value(_this.preferred, true); + var self = _this; + // Add a listener on the radio buttons to set default filter + jQuery(_this.menu).on("click", "input:radio", function (event) { + // Don't do the menu + event.stopImmediatePropagation(); + // Save as default favorite - used when you click the button + self.egw().set_preference(self.options.app, self.options.default_pref, jQuery(this).val()); + self.preferred = jQuery(this).val(); + // Update sidebox, if there + if (self.sidebox_target.length) { + self.sidebox_target.find("div.ui-icon-heart") + .replaceWith("
"); + jQuery("li[data-id='" + self.preferred + "'] div.sideboxstar", self.sidebox_target) + .replaceWith("
"); + } + // Close the menu + self.menu.hide(); + // Some user feedback + self.button.addClass("ui-state-active", 500, "swing", function () { + self.button.removeClass("ui-state-active", 2000); + }); + }); + //Sort DomNodes of sidebox fav. menu + var sideBoxDOMNodeSort = function (_favSList) { + var favS = jQuery.isArray(_favSList) ? _favSList.slice(0).reverse() : []; + for (var i = 0; i < favS.length; i++) { + self.sidebox_target.children().find('[data-id$="' + favS[i] + '"]').prependTo(self.sidebox_target.children()); + } + }; + //Add Sortable handler to nm fav. menu + jQuery(_this.menu).sortable({ + items: 'li:not([data-id$="add"])', + placeholder: 'ui-fav-sortable-placeholder', + delay: 250, + update: function () { + self.favSortedList = jQuery(this).sortable('toArray', { attribute: 'data-id' }); + self.egw().set_preference(self.options.app, 'fav_sort_pref', self.favSortedList); + sideBoxDOMNodeSort(self.favSortedList); + } + }); + // Add a listener on the delete to remove + _this.menu.on("click", "div.ui-icon-trash", app[self.options.app], function () { + // App instance might not be ready yet, so don't bind directly + app[self.options.app].delete_favorite.apply(this, arguments); + }) + // Wrap and unwrap because jQueryUI styles use a parent, and we don't want to change the state of the menu item + // Wrap in a span instead of a div because div gets a border + .on("mouseenter", "div.ui-icon-trash", function () { jQuery(this).wrap(""); }) + .on("mouseleave", "div.ui-icon-trash", function () { jQuery(this).unwrap(); }); + // Trigger refresh of menu options now that events are registered + // to update sidebox + if (_this.sidebox_target.length > 0) { + _this.init_filters(_this); + } + return _this; + } + /** + * Load favorites from preferences + * + * @param app String Load favorites from this application + */ + et2_favorites.prototype.load_favorites = function (app) { + // Default blank filter + var stored_filters = { + 'blank': { + name: this.egw().lang("No filters"), + state: {} + } + }; + // Load saved favorites + var preferences = egw.preference("*", app); + for (var pref_name in preferences) { + if (pref_name.indexOf(this.favorite_prefix) == 0 && typeof preferences[pref_name] == 'object') { + var name_1 = pref_name.substr(this.favorite_prefix.length); + stored_filters[name_1] = preferences[pref_name]; + // Keep older favorites working - they used to store nm filters in 'filters',not state + if (preferences[pref_name]["filters"]) { + stored_filters[pref_name]["state"] = preferences[pref_name]["filters"]; + } + } + if (pref_name == 'fav_sort_pref') { + this.favSortedList = preferences[pref_name]; + //Make sure sorted list is always an array, seems some old fav are not array + if (!jQuery.isArray(this.favSortedList)) + this.favSortedList = this.favSortedList.split(','); + } + } + if (typeof stored_filters == "undefined" || !stored_filters) { + stored_filters = {}; + } + else { + for (var name_2 in stored_filters) { + if (this.favSortedList.indexOf(name_2) < 0) { + this.favSortedList.push(name_2); + } + } + this.egw().set_preference(this.options.app, 'fav_sort_pref', this.favSortedList); + if (this.favSortedList.length > 0) { + var sortedListObj = {}; + for (var i = 0; i < this.favSortedList.length; i++) { + if (typeof stored_filters[this.favSortedList[i]] != 'undefined') { + sortedListObj[this.favSortedList[i]] = stored_filters[this.favSortedList[i]]; + } + else { + this.favSortedList.splice(i, 1); + this.egw().set_preference(this.options.app, 'fav_sort_pref', this.favSortedList); + } + } + stored_filters = jQuery.extend(sortedListObj, stored_filters); + } + } + return stored_filters; + }; + // Create & set filter options for dropdown menu + et2_favorites.prototype.init_filters = function (widget, filters) { + if (typeof filters == "undefined") { + filters = this.stored_filters; + } + var options = {}; + for (var name_3 in filters) { + options[name_3] = "" + + (filters[name_3].name != undefined ? filters[name_3].name : name_3) + + (filters[name_3].group != false && !et2_favorites.is_admin || name_3 == 'blank' ? "" : + "
"); + } + // Only add 'Add current' if we have a nextmatch + if (this.nextmatch) { + options["add"] = "" + this.egw().lang('Add current'); + } + widget.set_select_options.call(widget, options); + // Set radio to current value + jQuery("input[value='" + this.preferred + "']:radio", this.menu).attr("checked", 1); + }; + et2_favorites.prototype.set_nm_filters = function (filters) { + if (this.nextmatch) { + this.nextmatch.applyFilters(filters); + } + else { + console.log(filters); + } + }; + et2_favorites.prototype.onclick = function (node) { + // Apply preferred filter - make sure it's an object, and not a reference + if (this.preferred && this.stored_filters[this.preferred]) { + // use app[appname].setState if available to allow app to overwrite it (eg. change to non-listview in calendar) + if (typeof app[this.options.app] != 'undefined') { + app[this.options.app].setState(this.stored_filters[this.preferred]); + } + else { + this.set_nm_filters(jQuery.extend({}, this.stored_filters[this.preferred].state)); + } + } + else { + alert(this.egw().lang("No default set")); + } + }; + // Apply the favorite when you pick from the list + et2_favorites.prototype.change = function (selected_node) { + this.value = jQuery(selected_node).attr("data-id"); + if (this.value == "add" && this.nextmatch) { + // Get current filters + var current_filters = jQuery.extend({}, this.nextmatch.activeFilters); + // Add in extras + for (var extra in this.options.filters) { + // Don't overwrite what nm has, chances are nm has more up-to-date value + if (typeof current_filters == 'undefined') { + current_filters[extra] = this.nextmatch.options.settings[extra]; + } + } + // Skip columns for now + delete current_filters.selcolumns; + // Add in application's settings + if (this.filters != true) { + for (var i = 0; i < this.filters.length; i++) { + current_filters[this.options.filters[i]] = this.nextmatch.options.settings[this.options.filters[i]]; + } + } + // Call framework + app[this.options.app].add_favorite(current_filters); + // Reset value + this.set_value(this.preferred, true); + } + else if (this.value == 'blank') { + // Reset filters when select no filters + this.set_nm_filters({}); + } + }; + et2_favorites.prototype.set_value = function (filter_name, parent) { + if (parent) { + return _super.prototype.set_value.call(this, filter_name); + } + if (filter_name == 'add') + return false; + app[this.options.app].setState(this.stored_filters[filter_name]); + return false; + }; + et2_favorites.prototype.getValue = function () { + return null; + }; + /** + * Set the nextmatch to filter + * From et2_INextmatchHeader interface + * + * @param {et2_nextmatch} nextmatch + */ + et2_favorites.prototype.setNextmatch = function (nextmatch) { + this.nextmatch = nextmatch; + if (this.nm_filter) { + this.set_value(this.nm_filter); + this.nm_filter = false; + } + // Re-generate filter list so we can add 'Add current' + this.init_filters(this); + }; + et2_favorites._attributes = { + "default_pref": { + "name": "Default preference key", + "type": "string", + "description": "The preference key where default favorite is stored (not the value)" + }, + "sidebox_target": { + "name": "Sidebox target", + "type": "string", + "description": "ID of element to insert favorite list into", + "default": "favorite_sidebox" + }, + "app": { + "name": "Application", + "type": "string", + "description": "Application to show favorites for" + }, + "filters": { + "name": "Extra filters", + "type": "any", + "description": "Array of extra filters to include in the saved favorite" + }, + // These are particular to favorites + id: { "default": "favorite" }, + label: { "default": "" }, + label_updates: { "default": false }, + image: { "default": egw().image('fav_filter') }, + statustext: { "default": "Favorite queries", "type": "string" } + }; + return et2_favorites; +}(et2_widget_dropdown_button_1.et2_dropdown_button)); +et2_core_widget_1.et2_register_widget(et2_favorites, ["favorites"]); +//# sourceMappingURL=et2_widget_favorites.js.map \ No newline at end of file diff --git a/api/js/etemplate/et2_widget_favorites.ts b/api/js/etemplate/et2_widget_favorites.ts new file mode 100644 index 0000000000..0144e52f7f --- /dev/null +++ b/api/js/etemplate/et2_widget_favorites.ts @@ -0,0 +1,425 @@ +/** + * EGroupware eTemplate2 - JS Favorite widget + * + * @license http://opensource.org/licenses/gpl-license.php GPL - GNU General Public License + * @package etemplate + * @subpackage api + * @link http://www.egroupware.org + * @author Nathan Gray + * @copyright Nathan Gray 2013 + */ + +/*egw:uses + et2_dropdown_button; + et2_extension_nextmatch; +*/ + +import {et2_register_widget, WidgetConfig} from "./et2_core_widget"; +import {et2_INextmatchHeader} from "./et2_extension_nextmatch"; +import {et2_dropdown_button} from "./et2_widget_dropdown_button"; +import {ClassWithAttributes} from "./et2_core_inheritance"; + +/** + * Favorites widget, designed for use with a nextmatch widget + * + * The primary control is a split/dropdown button. Clicking on the left side of the button filters the + * nextmatch list by the user's default filter. The right side of the button gives a list of + * saved filters, pulled from preferences. Clicking a filter from the dropdown list sets the + * filters as saved. + * + * Favorites can also automatically be shown in the sidebox, using the special ID favorite_sidebox. + * Use the following code to generate the sidebox section: + * display_sidebox($appname,lang('Favorites'),array( + * array( + * 'no_lang' => true, + * 'text'=>'', + * 'link'=>false, + * 'icon' => false + * ) + * )); + * This sidebox list will be automatically generated and kept up to date. + * + * + * Favorites are implemented by saving the values for [column] filters. Filters are stored + * in preferences, with the name favorite_. The favorite favorite used for clicking on + * the filter button is stored in nextmatch--favorite. + * + * @augments et2_dropdown_button + */ +class et2_favorites extends et2_dropdown_button implements et2_INextmatchHeader +{ + static readonly _attributes : any = { + "default_pref": { + "name": "Default preference key", + "type": "string", + "description": "The preference key where default favorite is stored (not the value)" + }, + "sidebox_target": { + "name": "Sidebox target", + "type": "string", + "description": "ID of element to insert favorite list into", + "default": "favorite_sidebox" + }, + "app": { + "name": "Application", + "type": "string", + "description": "Application to show favorites for" + }, + "filters": { + "name": "Extra filters", + "type": "any", + "description": "Array of extra filters to include in the saved favorite" + }, + + // These are particular to favorites + id: {"default": "favorite"}, + label: {"default": ""}, + label_updates: { "default": false}, + image: {"default": egw().image('fav_filter')}, + statustext: {"default": "Favorite queries", "type": "string"} + }; + + // Some convenient variables, used in closures / event handlers + header = null; + nextmatch = null; + favorite_prefix: "favorite_"; + private stored_filters: {}; + private favSortedList : any = null; + private sidebox_target : JQuery = null; + private preferred; + static is_admin : boolean; + private filters : any; + + // If filter was set server side, we need to remember it until nm is created + nm_filter = false; + + /** + * Constructor + * + * @memberOf et2_favorites + */ + constructor(_parent?, _attrs? : WidgetConfig, _child? : object) + { + super(_parent, _attrs, ClassWithAttributes.extendAttributes(et2_favorites._attributes, _child || {})); + this.sidebox_target = jQuery("#"+this.options.sidebox_target); + if(this.sidebox_target.length == 0 && egw_getFramework() != null) + { + let egw_fw = egw_getFramework(); + this.sidebox_target = jQuery("#"+this.options.sidebox_target,egw_fw.sidemenuDiv); + } + // Store array of sorted items + this.favSortedList = ['blank']; + + let apps = egw().user('apps'); + et2_favorites.is_admin = (typeof apps['admin'] != "undefined"); + + this.stored_filters = this.load_favorites(this.options.app); + + this.preferred = egw.preference(this.options.default_pref,this.options.app); + if(!this.preferred || typeof this.stored_filters[this.preferred] == "undefined") + { + this.preferred = "blank"; + } + + // It helps to have the ID properly set before we get too far + this.set_id(this.id); + + this.init_filters(this); + + this.menu.addClass("favorites"); + + // Set the default (button) value + this.set_value(this.preferred,true); + + let self = this; + + // Add a listener on the radio buttons to set default filter + jQuery(this.menu).on("click","input:radio", function(event){ + // Don't do the menu + event.stopImmediatePropagation(); + + // Save as default favorite - used when you click the button + self.egw().set_preference(self.options.app,self.options.default_pref,jQuery(this).val()); + self.preferred = jQuery(this).val(); + + // Update sidebox, if there + if(self.sidebox_target.length) + { + self.sidebox_target.find("div.ui-icon-heart") + .replaceWith("
"); + jQuery("li[data-id='"+self.preferred+"'] div.sideboxstar",self.sidebox_target) + .replaceWith("
"); + } + + // Close the menu + self.menu.hide(); + + // Some user feedback + self.button.addClass("ui-state-active", 500,"swing",function(){ + self.button.removeClass("ui-state-active",2000); + }); + }); + + //Sort DomNodes of sidebox fav. menu + let sideBoxDOMNodeSort = function (_favSList) { + let favS = jQuery.isArray(_favSList)?_favSList.slice(0).reverse():[]; + + for (let i=0; i < favS.length;i++) + { + self.sidebox_target.children().find('[data-id$="' + favS[i] + '"]').prependTo(self.sidebox_target.children()); + } + }; + + //Add Sortable handler to nm fav. menu + jQuery(this.menu).sortable({ + + items:'li:not([data-id$="add"])', + placeholder:'ui-fav-sortable-placeholder', + delay: 250, //(millisecond) delay before the sorting should start + update: function () + { + self.favSortedList = jQuery(this).sortable('toArray', {attribute:'data-id'}); + + self.egw().set_preference(self.options.app,'fav_sort_pref',self.favSortedList); + + sideBoxDOMNodeSort(self.favSortedList); + } + }); + + // Add a listener on the delete to remove + this.menu.on("click","div.ui-icon-trash", app[self.options.app], function() { + // App instance might not be ready yet, so don't bind directly + app[self.options.app].delete_favorite.apply(this,arguments); + }) + // Wrap and unwrap because jQueryUI styles use a parent, and we don't want to change the state of the menu item + // Wrap in a span instead of a div because div gets a border + .on("mouseenter","div.ui-icon-trash", function() {jQuery(this).wrap("");}) + .on("mouseleave","div.ui-icon-trash", function() {jQuery(this).unwrap();}); + + // Trigger refresh of menu options now that events are registered + // to update sidebox + if(this.sidebox_target.length > 0) + { + this.init_filters(this); + } + } + + /** + * Load favorites from preferences + * + * @param app String Load favorites from this application + */ + load_favorites(app) + { + + // Default blank filter + let stored_filters : any = { + 'blank': { + name: this.egw().lang("No filters"), + state: {} + } + }; + + // Load saved favorites + let preferences : any = egw.preference("*",app); + for(let pref_name in preferences) + { + if(pref_name.indexOf(this.favorite_prefix) == 0 && typeof preferences[pref_name] == 'object') + { + let name = pref_name.substr(this.favorite_prefix.length); + stored_filters[name] = preferences[pref_name]; + // Keep older favorites working - they used to store nm filters in 'filters',not state + if(preferences[pref_name]["filters"]) + { + stored_filters[pref_name]["state"] = preferences[pref_name]["filters"]; + } + } + if (pref_name == 'fav_sort_pref') + { + this.favSortedList = preferences[pref_name]; + //Make sure sorted list is always an array, seems some old fav are not array + if (!jQuery.isArray(this.favSortedList)) this.favSortedList = this.favSortedList.split(','); + } + } + if(typeof stored_filters == "undefined" || !stored_filters) + { + stored_filters = {}; + } + else + { + for(let name in stored_filters) + { + if (this.favSortedList.indexOf(name) < 0) + { + this.favSortedList.push(name); + } + } + this.egw().set_preference (this.options.app,'fav_sort_pref',this.favSortedList); + if (this.favSortedList.length > 0) + { + let sortedListObj = {}; + + for (let i=0; i < this.favSortedList.length; i++) + { + if (typeof stored_filters[this.favSortedList[i]] != 'undefined') + { + sortedListObj[this.favSortedList[i]] = stored_filters[this.favSortedList[i]]; + } + else + { + this.favSortedList.splice(i,1); + this.egw().set_preference (this.options.app,'fav_sort_pref',this.favSortedList); + } + } + stored_filters = jQuery.extend(sortedListObj,stored_filters); + } + } + return stored_filters; + } + + // Create & set filter options for dropdown menu + init_filters(widget, filters?) + { + if(typeof filters == "undefined") + { + filters = this.stored_filters; + } + + let options = {}; + for(let name in filters) + { + options[name] = ""+ + (filters[name].name != undefined ? filters[name].name : name) + + (filters[name].group != false && !et2_favorites.is_admin || name == 'blank' ? "" : + "
"); + } + + // Only add 'Add current' if we have a nextmatch + if(this.nextmatch) + { + options["add"] = ""+this.egw().lang('Add current'); + } + widget.set_select_options.call(widget,options); + + // Set radio to current value + jQuery("input[value='"+ this.preferred +"']:radio", this.menu).attr("checked",1); + } + + set_nm_filters(filters) + { + if(this.nextmatch) + { + this.nextmatch.applyFilters(filters); + } + else + { + console.log(filters); + } + } + + onclick(node) + { + // Apply preferred filter - make sure it's an object, and not a reference + if(this.preferred && this.stored_filters[this.preferred]) + { + // use app[appname].setState if available to allow app to overwrite it (eg. change to non-listview in calendar) + if (typeof app[this.options.app] != 'undefined') + { + app[this.options.app].setState(this.stored_filters[this.preferred]); + } + else + { + this.set_nm_filters(jQuery.extend({},this.stored_filters[this.preferred].state)); + } + } + else + { + alert(this.egw().lang("No default set")); + } + } + + // Apply the favorite when you pick from the list + change(selected_node) + { + this.value = jQuery(selected_node).attr("data-id"); + if(this.value == "add" && this.nextmatch) + { + // Get current filters + let current_filters = jQuery.extend({},this.nextmatch.activeFilters); + + // Add in extras + for(let extra in this.options.filters) + { + // Don't overwrite what nm has, chances are nm has more up-to-date value + if(typeof current_filters == 'undefined') + { + current_filters[extra] = this.nextmatch.options.settings[extra]; + } + } + + // Skip columns for now + delete current_filters.selcolumns; + + // Add in application's settings + if(this.filters != true) + { + for(let i = 0; i < this.filters.length; i++) + { + current_filters[this.options.filters[i]] = this.nextmatch.options.settings[this.options.filters[i]]; + } + } + + // Call framework + app[this.options.app].add_favorite(current_filters); + + // Reset value + this.set_value(this.preferred,true); + } + else if (this.value == 'blank') + { + // Reset filters when select no filters + this.set_nm_filters({}); + } + } + + set_value(filter_name, parent? : boolean) : void | boolean + { + if(parent) + { + return super.set_value(filter_name); + } + + if(filter_name == 'add') return false; + + app[this.options.app].setState(this.stored_filters[filter_name]); + return false; + } + + getValue() + { + return null; + } + + /** + * Set the nextmatch to filter + * From et2_INextmatchHeader interface + * + * @param {et2_nextmatch} nextmatch + */ + setNextmatch(nextmatch) + { + this.nextmatch = nextmatch; + + if(this.nm_filter) + { + this.set_value(this.nm_filter); + this.nm_filter = false; + } + + // Re-generate filter list so we can add 'Add current' + this.init_filters(this); + } +} +et2_register_widget(et2_favorites, ["favorites"]); +