diff --git a/etemplate/inc/class.etemplate_widget_nextmatch.inc.php b/etemplate/inc/class.etemplate_widget_nextmatch.inc.php
index 6161a2ea4d..331104e1b8 100644
--- a/etemplate/inc/class.etemplate_widget_nextmatch.inc.php
+++ b/etemplate/inc/class.etemplate_widget_nextmatch.inc.php
@@ -795,7 +795,10 @@ class etemplate_widget_nextmatch extends etemplate_widget
*/
public static function ajax_set_favorite($app, $name, $action, $group, $filters = array())
{
- $pref_name = "favorite_".$name;
+ // Only use alphanumeric for preference name, so it can be used directly as DOM ID
+ $name = strip_tags($name);
+ $pref_name = "favorite_".preg_replace('/[^A-Za-z0-9-_]/','_',$name);
+
if($group && $GLOBALS['egw_info']['apps']['admin'])
{
$prefs = new preferences(is_numeric($group) ? $group: $GLOBALS['egw_info']['user']['account_id']);
@@ -809,7 +812,9 @@ class etemplate_widget_nextmatch extends etemplate_widget
if($action == "add")
{
$filters = array(
- 'group' => $group,
+ // This is the name as user entered it, minus tags
+ 'name' => $name,
+ 'group' => $group ? $group : false,
'filter' => $filters
);
$result = $prefs->add($app,$pref_name,$filters,$type);
diff --git a/etemplate/js/et2_extension_nextmatch.js b/etemplate/js/et2_extension_nextmatch.js
index 53cf8897b6..443d1c2b5b 100644
--- a/etemplate/js/et2_extension_nextmatch.js
+++ b/etemplate/js/et2_extension_nextmatch.js
@@ -1367,6 +1367,18 @@ var et2_nextmatch_header_bar = et2_DOMWidget.extend(et2_INextmatchHeader, {
* 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.
@@ -1375,28 +1387,34 @@ var et2_nextmatch_header_bar = et2_DOMWidget.extend(et2_INextmatchHeader, {
* additional fields/settings to add in to the favorite.
*/
_setup_favorites: function(filters) {
- // 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 apps = this.egw().user('apps');
- var is_admin = (typeof apps['admin'] != "undefined");
-
if(typeof filters == "undefined")
{
// No favorites configured
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)
{
@@ -1411,6 +1429,40 @@ var et2_nextmatch_header_bar = et2_DOMWidget.extend(et2_INextmatchHeader, {
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());
+
+ };
+
// Create & set filter options for dropdown menu
var init_filters = function(widget)
{
@@ -1418,7 +1470,8 @@ var et2_nextmatch_header_bar = et2_DOMWidget.extend(et2_INextmatchHeader, {
for(var name in stored_filters)
{
options[name] = ""+
- name + (stored_filters[name].group != false ? " ♦" :"") +
+ (stored_filters[name].name != undefined ? stored_filters[name].name : name) +
+ (stored_filters[name].group != false ? " ♦" :"") +
(stored_filters[name].group != false && !is_admin ? "" :
"");
}
@@ -1427,6 +1480,21 @@ var et2_nextmatch_header_bar = et2_DOMWidget.extend(et2_INextmatchHeader, {
// 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
@@ -1438,12 +1506,12 @@ var et2_nextmatch_header_bar = et2_DOMWidget.extend(et2_INextmatchHeader, {
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
- var favorite = header.egw().preference(favorite_preference,app);
- if(favorite && stored_filters[favorite])
+ if(preferred && stored_filters[preferred])
{
- nextmatch.activeFilters = jQuery.extend({},stored_filters[favorite].filter);
+ nextmatch.activeFilters = jQuery.extend({},stored_filters[preferred].filter);
nextmatch.applyFilters();
}
else
@@ -1452,10 +1520,53 @@ var et2_nextmatch_header_bar = et2_DOMWidget.extend(et2_INextmatchHeader, {
}
};
- var preferred = this.egw().preference(favorite_preference, app);
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")
@@ -1507,6 +1618,9 @@ var et2_nextmatch_header_bar = et2_DOMWidget.extend(et2_INextmatchHeader, {
// Popup
header.favorites_popup.dialog("open");
+
+ // Reset value
+ this.set_value(preferred && stored_filters[preferred] ? preferred : "");
}
else if(stored_filters[this.get_value()])
{
@@ -1516,58 +1630,24 @@ var et2_nextmatch_header_bar = et2_DOMWidget.extend(et2_INextmatchHeader, {
}
};
-
- // 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());
-
- // 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);
- });
- });
// Add a listener on the delete to remove
- $j(this.favorites.menu).on("click","div.ui-icon-trash", function(event) {
- // Don't do the menu
- event.stopImmediatePropagation();
-
- // Hide the trash
- $j(this).hide();
-
- var name = $j(this).parentsUntil("li").parent().attr("id");
-
- // 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());
-
- })
+ $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
- // Use a span instead of a div because div gets a border
+ // 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();});
// 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('