From dee286e2e0131c2b7358f2e897452627628a0e82 Mon Sep 17 00:00:00 2001 From: Nathan Gray Date: Tue, 12 Mar 2013 22:57:42 +0000 Subject: [PATCH] Move favorites js into its own widget, add support for favorites in sidebox when there is no nextmatch on the page --- .../class.etemplate_widget_nextmatch.inc.php | 101 ++++ etemplate/js/et2_extension_nextmatch.js | 402 +------------ etemplate/js/et2_widget_favorites.js | 551 ++++++++++++++++++ etemplate/js/etemplate2.js | 1 + 4 files changed, 669 insertions(+), 386 deletions(-) create mode 100644 etemplate/js/et2_widget_favorites.js diff --git a/etemplate/inc/class.etemplate_widget_nextmatch.inc.php b/etemplate/inc/class.etemplate_widget_nextmatch.inc.php index 331104e1b8..f8170d6cbd 100644 --- a/etemplate/inc/class.etemplate_widget_nextmatch.inc.php +++ b/etemplate/inc/class.etemplate_widget_nextmatch.inc.php @@ -114,8 +114,49 @@ class etemplate_widget_nextmatch extends etemplate_widget $value['rows'] = array(); $send_value = $value; + + // Check for a favorite in URL + if($_GET['favorite'] && $value['favorites']) + { + list($app) = explode('.',$value['get_rows']); + $safe_name = preg_replace('/[^A-Za-z0-9-_]/','_',strip_tags($_GET['favorite'])); + $pref_name = "favorite_" .$safe_name; + + // Do some easy applying of filters server side + $favorite = $GLOBALS['egw_info']['user']['preferences'][$app][$pref_name]; + if(!$favorite && $_GET['favorite'] == 'blank') + { + // Have to go through each of these + foreach(array('search','cat_id','filter','filter2') as $filter) + { + $send_value[$filter] = ''; + } + unset($send_value['col_filter']); + } + if($favorite && $favorite['filter']) + { + $send_value = array_merge($value, $favorite['filter']); + + // Ajax call can handle the saved sort here, but this can't + if($favorite['filter']['sort']) + { + unset($send_value['sort']); + $send_value['order'] = $favorite['filter']['sort']['id']; + $send_value['sort'] = $favorite['filter']['sort']['asc'] ? 'ASC' : 'DESC'; + } + } + } + // Make sure it's not set + unset($send_value['favorite']); + $total = self::call_get_rows($send_value, $send_value['rows'], self::$request->readonlys); $value =& self::get_array(self::$request->content, $form_name, true); + + // Add favorite here so app doesn't save it in the session + if($_GET['favorite']) + { + $send_value['favorite'] = $safe_name; + } $value = $send_value; $value['total'] = $total; @@ -725,6 +766,7 @@ class etemplate_widget_nextmatch extends etemplate_widget return $cat_actions; } + /** * Validate input * @@ -746,6 +788,9 @@ class etemplate_widget_nextmatch extends etemplate_widget $value = self::get_array($content, $form_name); list($app) = explode('.',$this->attrs['template']); + unset($value['favorite']); +_debug_array($value); + // On client, rows does not get its own namespace, but all apps are expecting it $value['rows'] = $value; @@ -756,6 +801,7 @@ class etemplate_widget_nextmatch extends etemplate_widget $validated += $content[$value[$preserve['action_var']].'_popup']; } + // Save current column settings as default (admins only) if($value['as_default']) { @@ -780,6 +826,61 @@ class etemplate_widget_nextmatch extends etemplate_widget } + /** + * Include favorites when generating the page server-side + * + * Use this function in your sidebox (or anywhere else, I suppose) to + * get the favorite list when a nextmatch is _not_ on the page. If + * a nextmatch is on the page, it will update / replace this list. + * + * @param $app String Current application, needed to find preferences + * @param $default String Preference name for default favorite + * + * @return String HTML fragment for the list + */ + public static function favorite_list($app, $default) + { + if(!$app) return ''; + $target = 'favorite_sidebox'; + $pref_prefix = 'favorite_'; + $filters = array( + 'blank' => array( + 'name' => lang('Blank'), + 'filters' => array() + ) + ); + $default_filter = $GLOBALS['egw_info']['user']['preferences'][$app][$default]; + if(!$default_filter) $default_filter = "blank"; + + $html = "'; + return $html; + } + /** * Create or delete a favorite for multiple users * diff --git a/etemplate/js/et2_extension_nextmatch.js b/etemplate/js/et2_extension_nextmatch.js index 443d1c2b5b..bb20e73d7e 100644 --- a/etemplate/js/et2_extension_nextmatch.js +++ b/etemplate/js/et2_extension_nextmatch.js @@ -32,6 +32,7 @@ et2_widget_template; et2_widget_grid; et2_widget_selectbox; + et2_widget_selectAccount; et2_extension_customfields; // Include all nextmatch subclasses @@ -122,6 +123,10 @@ var et2_nextmatch = et2_DOMWidget.extend([et2_IResizeable, et2_IInput], { // Create the outer grid container this.dataview = new et2_dataview(this.innerDiv, this.egw()); + // Blank placeholder + this.blank = $j(document.createElement("div")) + .appendTo(this.dataview.table); + // We cannot create the grid controller now, as this depends on the grid // instance, which can first be created once we have the columns this.controller = null; @@ -265,6 +270,9 @@ var et2_nextmatch = et2_DOMWidget.extend([et2_IResizeable, et2_IInput], { // Update any column filters this.iterateOver(function(column) { + // Skip favorites - it implements et2_INextmatchHeader, but we don't want it in the filter + if(typeof column.id != "undefined" && column.id.indexOf('favorite') == 0) return; + if(typeof column.set_value != "undefined" && column.id) { column.set_value(typeof this[column.id] == "undefined" || this[column.id] == null ? "" : this[column.id]); @@ -1085,9 +1093,6 @@ var et2_nextmatch_header_bar = et2_DOMWidget.extend(et2_INextmatchHeader, { destroy: function() { this.nextmatch = null; this.div = null; - this.favorites = null; - this.favorites_popup.dialog("destroy"); - this.favorites_popup = null; }, setNextmatch: function(nextmatch) { @@ -1362,27 +1367,6 @@ var et2_nextmatch_header_bar = et2_DOMWidget.extend(et2_INextmatchHeader, { /** * Set up the favorites UI control * - * The 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: - * 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. - * * @param filters Array|boolean The nextmatch setting for favorites. Either true, or a list of * additional fields/settings to add in to the favorite. */ @@ -1393,366 +1377,16 @@ var et2_nextmatch_header_bar = et2_DOMWidget.extend(et2_INextmatchHeader, { return; } - // Some convenient variables, used in closures / event handlers - var header = this; - var nextmatch = this.nextmatch; - var nm_div = this.nextmatch.div; - var favorite_prefix = "favorite_"; - var favorite_preference = "nextmatch-" + nextmatch.options.settings.columnselection_pref + "-favorite"; - var sidebox_target = $j("#favorite_sidebox"); - if(sidebox_target.length == 0 && egw_getFramework() != null) - { - var egw_fw = egw_getFramework(); - sidebox_target = $j("#favorite_sidebox",egw_fw.sidemenuDiv); - } - - var apps = this.egw().user('apps'); - var is_admin = (typeof apps['admin'] != "undefined"); - var list = et2_csvSplit(this.options.get_rows, 2, "."); - var app = list[0]; - - // Load saved favorites - var stored_filters = {}; - var preferred = this.egw().preference(favorite_preference, app); - var preferences = this.egw().preference("*",app); - for(var pref_name in preferences) - { - if(pref_name.indexOf(favorite_prefix) == 0) - { - var name = pref_name.substr(favorite_prefix.length); - stored_filters[name] = preferences[pref_name]; - } - } - if(typeof stored_filters == "undefined" || !stored_filters) - { - stored_filters = {}; - } - - /** - * Delete a favorite from the list and update preferences - * Registered as a handler on the delete icons - */ - var delete_favorite = function(event) - { - // Don't do the menu - event.stopImmediatePropagation(); - - var name = $j(this).parentsUntil("li").parent().attr("id"); - - // Make sure first - if(!confirm(header.egw().lang("Delete") + " " +stored_filters[name].name +"?")) return; - - // Hide the trash - $j(this).hide(); - - // Delete preference server side - var request = new egw_json_request("etemplate_widget_nextmatch::ajax_set_favorite::etemplate", - [app, name, "delete", stored_filters[name].group ? stored_filters[name].group : '', ''], - header - ); - request.sendRequest(true, function(result) { - if(result) - { - // Remove line from list - this.slideUp("slow", function() { header.favorites.menu.hide();}); - delete stored_filters[name]; - init_filters(header.favorites); - } - }, $j(this).parentsUntil("li").parent()); - + var widget_options = { + default_pref: "nextmatch-" + this.nextmatch.options.settings.columnselection_pref + "-favorite", + app: list[0], + filters: filters }; - - // Create & set filter options for dropdown menu - var init_filters = function(widget) - { - var options = {}; - for(var name in stored_filters) - { - options[name] = ""+ - (stored_filters[name].name != undefined ? stored_filters[name].name : name) + - (stored_filters[name].group != false ? " ♦" :"") + - (stored_filters[name].group != false && !is_admin ? "" : - "
"); - } - options.add = "Add current"; - widget.set_select_options(options); - - // Set radio to current value - $j("input[value='"+ preferred +"']:radio", header.favorites.menu).attr("checked",true); - - // Clone for sidebox - if(sidebox_target.length) - { - sidebox_target.empty(); - var sidebox_clone = widget.menu.clone(); - sidebox_clone - .appendTo(sidebox_target) - .menu() - .show() - .find("input:checked").replaceWith("
"); - sidebox_clone - .find("input").replaceWith(""); - - } - }; - - // Favorite dropdown button - this.favorites = et2_createWidget("dropdown_button", { - id: "favorite[button]", - label: "", - label_updates: false, - image: "etemplate/fav_filter", - tooltip: "Favorite queries" - }, this); - - this.favorites.menu.addClass("favorites"); - this.favorites.onclick= function(node) { - // Apply preferred filter - make sure it's an object, and not a reference - if(preferred && stored_filters[preferred]) - { - nextmatch.activeFilters = jQuery.extend({},stored_filters[preferred].filter); - nextmatch.applyFilters(); - } - else - { - alert(this.egw().lang("No default set")); - } - }; - - this.favorites.set_value(preferred && stored_filters[preferred] ? preferred : ""); - init_filters(this.favorites); - - // Initialize sidebox - if(sidebox_target.length) - { - sidebox_target - .on("mouseenter","div.ui-icon-trash", function() {$j(this).wrap("");}) - .on("mouseleave","div.ui-icon-trash", function() {$j(this).unwrap();}) - .on("click","div.ui-icon-trash", delete_favorite); - if(header.favorites) - { - sidebox_target.on("click","li",function() { - header.favorites.set_value($j(this).attr("id")); - header.favorites.change(this); - }); - } - } - - // Add a listener on the radio buttons to set default filter - $j(this.favorites.menu).on("click","input:radio",function(event){ - // Don't do the menu - event.stopImmediatePropagation(); - - // Save as default favorite - used when you click the button - header.egw().set_preference(app,favorite_preference,$j(this).val()); - preferred = $j(this).val(); - - // Update sidebox, if there - if(sidebox_target.length) - { - sidebox_target.find(".ui-icon-heart") - .replaceWith(""); - $j("li#"+preferred+" img",sidebox_target) - .replaceWith("
"); - - } - - // Close the menu - header.favorites.menu.hide(); - - // Some user feedback - header.favorites.button.addClass("ui-state-active", 500,"swing",function(){ - header.favorites.button.removeClass("ui-state-active",2000); - }); - }); - - // Apply the favorite when you pick from the list - this.favorites.change = function(selected_node) { - if(this.get_value() == "add") - { - // Get current filters - header.favorites_popup.current_filters = $j.extend({},nextmatch.activeFilters); - - // Add in application's settings - if(filters != true) - { - for(var i = 0; i < filters.length; i++) - { - header.favorites_popup.current_filters[filters[i]] = nextmatch.options.settings[filters[i]]; - } - } - - // Remove some internal values - delete header.favorites_popup.current_filters[header.favorites.id]; - if(header.favorites_popup.group != undefined) - { - delete header.favorites_popup.current_filters[header.favorites_popup.group.id]; - } - - // Make sure it's an object - deep copy to prevent references in sub-objects (col_filters) - header.favorites_popup.current_filters = jQuery.extend(true,{},header.favorites_popup.current_filters); - - // Update popup with current set filters (more for debug than user) - var filter_list = []; - var add_to_popup = function(arr) { - filter_list.push("
    "); - jQuery.each(arr, function(index, filter) { - filter_list.push("
  • "+index+"" + - (typeof filter != "object" ? ""+filter+"": "") - ); - if(typeof filter == "object" && filter != null) add_to_popup(filter); - filter_list.push("
  • "); - }); - filter_list.push("
"); - } - add_to_popup(header.favorites_popup.current_filters); - $j("#nm_favorites_popup_filters",header.favorites_popup) - .replaceWith( - $j(filter_list.join("")).attr("id","nm_favorites_popup_filters") - ); - $j("#nm_favorites_popup_filters",header.favorites_popup) - .hide() - .siblings(".ui-icon-circle-plus") - .removeClass("ui-icon-circle-minus"); - - // Popup - header.favorites_popup.dialog("open"); - - // Reset value - this.set_value(preferred && stored_filters[preferred] ? preferred : ""); - } - else if(stored_filters[this.get_value()]) - { - // Apply selected filter - make sure it's an object, and not a reference - nextmatch.activeFilters = jQuery.extend(true, {},stored_filters[this.get_value()].filter); - nextmatch.applyFilters(); - } - }; - - - // Add a listener on the delete to remove - $j(this.favorites.menu).on("click","div.ui-icon-trash", delete_favorite) - // 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() {$j(this).wrap("");}) - .on("mouseleave","div.ui-icon-trash", function() {$j(this).unwrap();}); + this.favorites = et2_createWidget('favorites', widget_options, this); // Add into header $j(this.favorites.getDOMNode(this.favorites)).insertAfter(this.count).css("float","right"); - - // Trigger refresh of menu options now that events are registered - // to update sidebox - if(sidebox_target.length > 0) - { - init_filters(this.favorites); - } - - // Create popup - this.favorites_popup = $j('
\ -
\ - ' + - - '\ -
\ - '+ this.egw().lang("Details") + '\ -
    \ -\ -
'); - $j(".ui-icon-circle-plus",this.favorites_popup).prev().andSelf().click(function() { - var details = $j("#nm_favorites_popup_filters",header.favorites_popup) - .slideToggle() - .siblings(".ui-icon-circle-plus") - .toggleClass("ui-icon-circle-minus"); - }); - this.favorites_popup.name = et2_createWidget("text",{id:"favorite[name]"},this); - $j("name",this.favorites_popup).replaceWith(this.favorites_popup.name.getDOMNode()); - - // Add some controls if user is an admin - if(apps['admin']) - { - this.favorites_popup.group = et2_createWidget("select-account",{ - id: "favorite[group]", - account_type: "groups", - empty_label: "Groups", - no_lang: true - },this); - $j("#nm_favorites_popup_admin",this.favorites_popup) - .append(this.favorites_popup.group.getDOMNode(this.favorites_popup.group)); - - } - var buttons = {}; - buttons[this.egw().lang("save")] = function() { - // Add a new favorite - var name = $j("#name",this); - - if(name.val()) - { - // Add to the list - name.val(name.val().replace(/(<([^>]+)>)/ig,"")); - var safe_name = name.val().replace(/[^A-Za-z0-9-_]/g,"_"); - stored_filters[safe_name] = { - name: name.val(), - group: (typeof header.favorites_popup.group != "undefined" && - header.favorites_popup.group.get_value() ? header.favorites_popup.group.get_value() : false), - filter: header.favorites_popup.current_filters - }; - init_filters(header.favorites); - - var favorite_pref = favorite_prefix+safe_name; - - // Save to preferences - if(typeof header.favorites_popup.group != "undefined") - { - // Admin stuff - save preference server side - var request = new egw_json_request("etemplate_widget_nextmatch::ajax_set_favorite::etemplate", - [ - app, - name.val(), - "add", - header.favorites_popup.group.get_value(), - header.favorites_popup.current_filters - ], - header - ); - request.sendRequest(true, function(result) { - if(result) - { - // Do something nice and confirmy if result is true - not really needed - } - }, header); - header.favorites_popup.group.set_value(""); - } - else - { - // Normal user - just save to preferences client side - header.egw().set_preference(app,favorite_pref,{ - name: name.val(), - group: false, - filter:header.favorites_popup.current_filters - }); - } - delete header.favorites_popup.current_filters; - } - // Reset form - name.val(""); - $j("#filters",header.favorites_popup).empty(); - - $j(this).dialog("close"); - }; - buttons[this.egw().lang("cancel")] = function() { - $j(this).dialog("close"); - }; - - this.favorites_popup.dialog({ - autoOpen: false, - modal: true, - buttons: buttons, - close: function() { - } - }); }, /** @@ -1765,6 +1399,9 @@ var et2_nextmatch_header_bar = et2_DOMWidget.extend(et2_INextmatchHeader, { */ setFilters: function(filters) { this.iterateOver(function(child) { + // Skip favorites, don't want them in the filter + if(typeof child.id != "undefined" && child.id.indexOf("favorite") == 0) return; + if(typeof child.set_value != "undefined" && child.id) { child.set_value(typeof this[child.id] == "undefined" || this[child.id] == null ? "" : this[child.id]); @@ -1784,13 +1421,6 @@ var et2_nextmatch_header_bar = et2_DOMWidget.extend(et2_INextmatchHeader, { // Set activeFilters to current value filters.searchletter = $j("td.lettersearch_active").attr("id") } - - // Remove some internal values - if(typeof this.favorites != "undefined") - { - delete filters[this.favorites.id]; - if(this.favorites_popup.group != undefined) delete filters[this.favorites_popup.group.id]; - } }, /** diff --git a/etemplate/js/et2_widget_favorites.js b/etemplate/js/et2_widget_favorites.js new file mode 100644 index 0000000000..5677fc28d3 --- /dev/null +++ b/etemplate/js/et2_widget_favorites.js @@ -0,0 +1,551 @@ +/** + * 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 + * @version $Id$ + */ + +"use strict"; + +/*egw:uses + et2_dropdown_button; + et2_extension_nextmatch; +*/ + +/** + * 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. + */ + +var et2_favorites = 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": "etemplate/fav_filter"}, + tooltip: {"default": "Favorite queries"} + }, + + // Some convenient variables, used in closures / event handlers + header: null, + nextmatch: null, + favorite_prefix: "favorite_", + + // If filter was set server side, we need to remember it until nm is created + nm_filter: false, + + init: function() { + this._super.apply(this, arguments); + this.sidebox_target = $j("#"+this.options.sidebox_target); + if(this.sidebox_target.length == 0 && egw_getFramework() != null) + { + var egw_fw = egw_getFramework(); + this.sidebox_target = $j("#favorite_sidebox",egw_fw.sidemenuDiv); + } + + var apps = egw().user('apps'); + this.is_admin = (typeof apps['admin'] != "undefined"); + + this.preferred = this.egw().preference(this.options.default_pref,this.options.app); + if(!this.preferred || typeof this.stored_filters[this.preferred] == "undefined") + { + this.preferred = "blank"; + } + + this.stored_filters = this.load_favorites(this.options.app); + + this.init_filters(this); + + this.menu.addClass("favorites"); + + // Set the default (button) value + this.set_value(this.preferred,true); + + // If a pre-selected filter was passed from server + if(typeof this.options.value != "undefined") + { + this.set_value(this.options.value); + } + + var self = this; + + // Initialize sidebox + if(this.sidebox_target.length) + { + this.sidebox_target + .on("mouseenter","div.ui-icon-trash", function() {$j(this).wrap("");}) + .on("mouseleave","div.ui-icon-trash", function() {$j(this).unwrap();}) + .on("click","div.ui-icon-trash", this, this.delete_favorite) + .addClass("ui-helper-clearfix"); + this.sidebox_target.on("click","li",function() { + self.set_value($j(this).attr("id")); + self.change(this); + }); + } + + // Add a listener on the radio buttons to set default filter + $j(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.favorite_preference,$j(this).val()); + preferred = $j(this).val(); + + // Update sidebox, if there + if(self.sidebox_target.length) + { + self.sidebox_target.find(".ui-icon-heart") + .replaceWith(""); + $j("li#"+preferred+" img",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); + }); + }); + + // Add a listener on the delete to remove + this.menu.on("click","div.ui-icon-trash", this, this.delete_favorite) + // 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() {$j(this).wrap("");}) + .on("mouseleave","div.ui-icon-trash", function() {$j(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); + } + }, + + destroy: function() { + this.popup.dialog("destroy"); + this.popup = null; + this._super.apply(this, arguments); + }, + + /** + * 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("Blank"), + filters: {}, + } + }; + + // Load saved favorites + var preferences = this.egw().preference("*",app); + for(var pref_name in preferences) + { + if(pref_name.indexOf(this.favorite_prefix) == 0) + { + var name = pref_name.substr(this.favorite_prefix.length); + stored_filters[name] = preferences[pref_name]; + } + } + if(typeof stored_filters == "undefined" || !stored_filters) + { + stored_filters = {}; + } + + return stored_filters; + }, + + /** + * Delete a favorite from the list and update preferences + * Registered as a handler on the delete icons + */ + delete_favorite: function(event) + { + // Don't do the menu + event.stopImmediatePropagation(); + + var header = event.data; + var name = $j(this).parentsUntil("li").parent().attr("id"); + + // Make sure first + if(!confirm(header.egw().lang("Delete") + " " +header.stored_filters[name].name +"?")) return; + + // Hide the trash + $j(this).hide(); + + // Delete preference server side + var request = new egw_json_request("etemplate_widget_nextmatch::ajax_set_favorite::etemplate", + [header.app, name, "delete", header.stored_filters[name].group ? header.stored_filters[name].group : '', ''], + header + ); + request.sendRequest(true, function(result) { + if(result) + { + // Remove line from list + this.slideUp("slow", function() { header.menu.hide();}); + delete header.stored_filters[name]; + header.init_filters(header); + } + }, $j(this).parentsUntil("li").parent()); + + }, + + // 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 ? " ♦" :"") + + (filters[name].group != false && !this.is_admin || name == 'blank' ? "" : + "
"); + } + + // Only add 'Add current' if we have a nextmatch + if(this.nextmatch) + { + options.add = "Add current"; + } + widget.set_select_options(options); + + // Set radio to current value + $j("input[value='"+ this.preferred +"']:radio", this.menu).attr("checked",true); + + // Clone for sidebox + if(this.sidebox_target.length) + { + this.sidebox_target.empty(); + var sidebox_clone = widget.menu.clone(); + sidebox_clone + .appendTo(this.sidebox_target) + .menu() + .removeClass("ui-widget") + .show() + .find("input:checked").replaceWith("
"); + sidebox_clone + .find("input").replaceWith(""); + + } + }, + + set_nm_filters: function(filters) + { + if(this.nextmatch) + { + this.nextmatch.activeFilters = filters; + this.nextmatch.applyFilters(); + } + 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]) + { + this.set_nm_filters(jQuery.extend({},this.stored_filters[this.preferred].filter)); + } + else + { + alert(this.egw().lang("No default set")); + } + }, + + + // Apply the favorite when you pick from the list + change: function(selected_node) { + this.value = $j(selected_node).attr("id"); + if(this.value == "add" && this.nextmatch) + { + // Get current filters + this.popup.current_filters = $j.extend({},this.nextmatch.activeFilters); + + // Skip columns for now + delete this.popup.current_filters.selcolumns; + + // Add in application's settings + if(this.filters != true) + { + for(var i = 0; i < this.filters.length; i++) + { + this.popup.current_filters[this.options.filters[i]] = this.nextmatch.options.settings[this.options.filters[i]]; + } + } + + // Remove some internal values + delete this.popup.current_filters[this.id]; + if(this.popup.group != undefined) + { + delete this.popup.current_filters[this.popup.group.id]; + } + + // Make sure it's an object - deep copy to prevent references in sub-objects (col_filters) + this.popup.current_filters = jQuery.extend(true,{},this.popup.current_filters); + + // Update popup with current set filters (more for debug than user) + var filter_list = []; + var add_to_popup = function(arr) { + filter_list.push("
    "); + jQuery.each(arr, function(index, filter) { + filter_list.push("
  • "+index+"" + + (typeof filter != "object" ? ""+filter+"": "") + ); + if(typeof filter == "object" && filter != null) add_to_popup(filter); + filter_list.push("
  • "); + }); + filter_list.push("
"); + } + add_to_popup(this.popup.current_filters); + $j("#nm_favorites_popup_filters",this.popup) + .replaceWith( + $j(filter_list.join("")).attr("id","nm_favorites_popup_filters") + ); + $j("#nm_favorites_popup_filters",this.popup) + .hide() + .siblings(".ui-icon-circle-plus") + .removeClass("ui-icon-circle-minus"); + + // Popup + this.popup.dialog("open"); + + // Reset value + this.set_value(this.preferred,true); + } + }, + + + /** + * Create the "Add new" popup dialog + */ + create_popup: function() + { + var self = this; + + // Create popup + this.popup = $j('
\ +
\ + ' + + + '\ +
\ + '+ this.egw().lang("Details") + '\ +
    \ + \ +
' + ).appendTo(this.div); + + $j(".ui-icon-circle-plus",this.popup).prev().andSelf().click(function() { + var details = $j("#nm_favorites_popup_filters",this.popup) + .slideToggle() + .siblings(".ui-icon-circle-plus") + .toggleClass("ui-icon-circle-minus"); + }); + + // Add some controls if user is an admin + if(this.is_admin) + { + this.popup.group = et2_createWidget("select-account",{ + id: "favorite[group]", + account_type: "groups", + empty_label: "Groups", + no_lang: true, + parent_node: 'nm_favorites_popup_admin' + },this); +/* + $j("#nm_favorites_popup_admin",this.popup) + .append(this.popup.group.getDOMNode(this.popup.group)); +*/ + + } + + var buttons = {}; + buttons[this.egw().lang("save")] = function() { + // Add a new favorite + var name = $j("#name",this); + + if(name.val()) + { + // Add to the list + name.val(name.val().replace(/(<([^>]+)>)/ig,"")); + var safe_name = name.val().replace(/[^A-Za-z0-9-_]/g,"_"); + self.stored_filters[safe_name] = { + name: name.val(), + group: (typeof self.popup.group != "undefined" && + self.popup.group.get_value() ? self.popup.group.get_value() : false), + filter: self.popup.current_filters + }; + self.init_filters(self); + + var favorite_pref = self.favorite_prefix+safe_name; + + // Save to preferences + if(typeof self.popup.group != "undefined") + { + // Admin stuff - save preference server side + var request = new egw_json_request("etemplate_widget_nextmatch::ajax_set_favorite::etemplate", + [ + self.options.app, + name.val(), + "add", + self.popup.group.get_value(), + self.popup.current_filters + ], + self + ); + request.sendRequest(true, function(result) { + if(result) + { + // Do something nice and confirmy if result is true - not really needed + } + }, self); + self.popup.group.set_value(null); + } + else + { + // Normal user - just save to preferences client side + self.egw().set_preference(self.options.app,favorite_pref,{ + name: name.val(), + group: false, + filter:self.popup.current_filters + }); + } + delete self.popup.current_filters; + } + // Reset form + name.val(""); + $j("#filters",self.popup).empty(); + + $j(this).dialog("close"); + }; + buttons[this.egw().lang("cancel")] = function() { + self.popup.group.set_value(null); + $j(this).dialog("close"); + }; + + this.popup.dialog({ + autoOpen: false, + modal: true, + buttons: buttons, + close: function() { + } + }); + }, + + set_value: function(filter_name, parent) { + if(parent) + { + return this._super.apply(filter_name); + } + if(this.nextmatch) + { + if(this.stored_filters[filter_name]) + { + // Apply selected filter - make sure it's an object, and not a reference + this.set_nm_filters(jQuery.extend(true, {},this.stored_filters[filter_name].filter)); + } + } + else + { + // Too soon - nm doesn't exist yet + this.nm_filter = filter_name; + } + }, + + getValue: function() + { + return null; + }, + + /** + * Set the nextmatch to filter + * From et2_INextmatchHeader interface + */ + 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); + + // With no Add current, this is only needed when there's a nm + this.create_popup(); + } +}); +et2_register_widget(et2_favorites, ["favorites"]); diff --git a/etemplate/js/etemplate2.js b/etemplate/js/etemplate2.js index fb9674b38b..69412345d6 100644 --- a/etemplate/js/etemplate2.js +++ b/etemplate/js/etemplate2.js @@ -31,6 +31,7 @@ et2_widget_diff; et2_widget_dropdown_button; et2_widget_styles; + et2_widget_favorites; et2_widget_html; et2_widget_htmlarea; et2_widget_tabs;