egroupware_official/api/js/etemplate/et2_widget_favorites.js
2021-06-08 14:11:59 +02:00

331 lines
14 KiB
JavaScript

/**
* EGroupware eTemplate2 - JS Favorite widget
*
* @license http://opensource.org/licenses/gpl-license.php GPL - GNU General Public License
* @package etemplate
* @subpackage api
* @link https://www.egroupware.org
* @author Nathan Gray
* @copyright Nathan Gray 2013
*/
/*egw:uses
et2_dropdown_button;
et2_extension_nextmatch;
*/
import { et2_register_widget } from "./et2_core_widget";
import { et2_dropdown_button } from "./et2_widget_dropdown_button";
import { ClassWithAttributes } from "./et2_core_inheritance";
import { egw, egw_getFramework } from "../jsapi/egw_global";
/**
* 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'=>'<span id="favorite_sidebox"/>',
* '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_<name>. The favorite favorite used for clicking on
* the filter button is stored in nextmatch-<columnselection_pref>-favorite.
*
* @augments et2_dropdown_button
*/
export class et2_favorites extends et2_dropdown_button {
/**
* Constructor
*
* @memberOf et2_favorites
*/
constructor(_parent, _attrs, _child) {
super(_parent, _attrs, ClassWithAttributes.extendAttributes(et2_favorites._attributes, _child || {}));
// 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) {
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");
// Make sure we have an app
if (!this.options.app) {
this.options.app = this.getInstanceManager().app;
}
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) {
jQuery("div.ui-icon-heart", self.sidebox_target)
.replaceWith("<div class='sideboxstar'/>");
jQuery("li[data-id='" + self.preferred + "'] div.sideboxstar", self.sidebox_target)
.replaceWith("<div class='ui-icon ui-icon-heart'/>");
}
// 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,
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("<span class='ui-state-active'/>"); })
.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 = {
'blank': {
name: this.egw().lang("No filters"),
state: {}
}
};
// Load saved favorites
let preferences = egw.preference("*", app);
for (let pref_name in preferences) {
if (pref_name.indexOf(et2_favorites.PREFIX) == 0 && typeof preferences[pref_name] == 'object') {
let name = pref_name.substr(et2_favorites.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] = "<input type='radio' name='" + this.internal_ids.menu + "[button][favorite]' value='" + name + "' title='" +
this.egw().lang('Set as default') + "'/>" +
(filters[name].name != undefined ? filters[name].name : name) +
(filters[name].group != false && !et2_favorites.is_admin || name == 'blank' ? "" :
"<div class='ui-icon ui-icon-trash' title='" + this.egw().lang('Delete') + "'/>");
}
// Only add 'Add current' if we have a nextmatch
if (this.nextmatch) {
options["add"] = "<img src='" + this.egw().image("new") + "'/>" + 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) {
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_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" }
};
et2_favorites.PREFIX = "favorite_";
et2_register_widget(et2_favorites, ["favorites"]);
//# sourceMappingURL=et2_widget_favorites.js.map