forked from extern/egroupware
Move favorites js into its own widget, add support for favorites in sidebox when there is no nextmatch on the page
This commit is contained in:
parent
5c0a8fa721
commit
dee286e2e0
@ -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 = "<span id='$target' class='ui-helper-clearfix'><ul class='ui-menu ui-widget-content ui-corner-all favorites' role='listbox'>\n";
|
||||
foreach($GLOBALS['egw_info']['user']['preferences'][$app] as $pref_name => $pref)
|
||||
{
|
||||
if(strpos($pref_name, $pref_prefix) === 0)
|
||||
{
|
||||
if(!is_array($pref))
|
||||
{
|
||||
$GLOBALS['egw']->preferences->delete($app,$pref_name);
|
||||
$GLOBALS['egw']->preferences->save_repository(false);
|
||||
continue;
|
||||
}
|
||||
$filters[substr($pref_name,strlen($pref_prefix))] = $pref;
|
||||
}
|
||||
}
|
||||
foreach($filters as $name => $filter)
|
||||
{
|
||||
$href = egw::link('/index.php', (array)egw_link::get_registry($app,'list') + array('favorite'=>$name),$app);
|
||||
$html .= "<li id='$name' class='ui-menu-item' role='menuitem'>\n";
|
||||
$html .= "<a href=\"$href\" class='ui-corner-all' tabindex='-1'>";
|
||||
$html .= "<div class='" . ($name == $default_filter ? 'ui-icon ui-icon-heart' : 'sideboxstar') . "'></div>".
|
||||
$filter['name'] .($filter['group'] != false ? " ♦" :"");
|
||||
$html .= "</a></li>\n";
|
||||
|
||||
}
|
||||
|
||||
$html .= '</ul></span>';
|
||||
return $html;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create or delete a favorite for multiple users
|
||||
*
|
||||
|
@ -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'=>'<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.
|
||||
*
|
||||
* @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] = "<input type='radio' name='favorite[button][favorite]' value='"+name+"' title='" + header.egw().lang('Set as default') + "'/>"+
|
||||
(stored_filters[name].name != undefined ? stored_filters[name].name : name) +
|
||||
(stored_filters[name].group != false ? " ♦" :"") +
|
||||
(stored_filters[name].group != false && !is_admin ? "" :
|
||||
"<div class='ui-icon ui-icon-trash' title='" + header.egw().lang('Delete') + "'/>");
|
||||
}
|
||||
options.add = "<img src='"+header.egw().image("new") +"'/>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("<div class='ui-icon ui-icon-heart'/>");
|
||||
sidebox_clone
|
||||
.find("input").replaceWith("<img class='sideboxstar'/>");
|
||||
|
||||
}
|
||||
};
|
||||
|
||||
// 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("<span class='ui-state-active'/>");})
|
||||
.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("<img class='sideboxstar'/>");
|
||||
$j("li#"+preferred+" img",sidebox_target)
|
||||
.replaceWith("<div class='ui-icon ui-icon-heart'/>");
|
||||
|
||||
}
|
||||
|
||||
// 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("<ul>");
|
||||
jQuery.each(arr, function(index, filter) {
|
||||
filter_list.push("<li id='index'><span class='filter_id'>"+index+"</span>" +
|
||||
(typeof filter != "object" ? "<span class='filter_value'>"+filter+"</span>": "")
|
||||
);
|
||||
if(typeof filter == "object" && filter != null) add_to_popup(filter);
|
||||
filter_list.push("</li>");
|
||||
});
|
||||
filter_list.push("</ul>");
|
||||
}
|
||||
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("<span class='ui-state-active'/>");})
|
||||
.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('<div id="nm_favorites_popup" title="' + egw().lang("New favorite") + '">\
|
||||
<form>\
|
||||
<label for="name">'+
|
||||
this.egw().lang("name") +
|
||||
'</label>' +
|
||||
|
||||
'<input type="text" name="name" id="name"/>\
|
||||
<div id="nm_favorites_popup_admin"/>\
|
||||
<span>'+ this.egw().lang("Details") + '</span><span style="float:left;" class="ui-icon ui-icon-circle-plus" />\
|
||||
<ul id="nm_favorites_popup_filters"/>\
|
||||
</form>\
|
||||
</div>');
|
||||
$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];
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
|
551
etemplate/js/et2_widget_favorites.js
Normal file
551
etemplate/js/et2_widget_favorites.js
Normal file
@ -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'=>'<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.
|
||||
*/
|
||||
|
||||
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("<span class='ui-state-active'/>");})
|
||||
.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("<img class='sideboxstar'/>");
|
||||
$j("li#"+preferred+" img",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);
|
||||
});
|
||||
});
|
||||
|
||||
// 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("<span class='ui-state-active'/>");})
|
||||
.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] = "<input type='radio' name='favorite[button][favorite]' value='"+name+"' title='" +
|
||||
this.egw().lang('Set as default') + "'/>"+
|
||||
(filters[name].name != undefined ? filters[name].name : name) +
|
||||
(filters[name].group != false ? " ♦" :"") +
|
||||
(filters[name].group != false && !this.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") +"'/>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("<div class='ui-icon ui-icon-heart'/>");
|
||||
sidebox_clone
|
||||
.find("input").replaceWith("<img class='sideboxstar'/>");
|
||||
|
||||
}
|
||||
},
|
||||
|
||||
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("<ul>");
|
||||
jQuery.each(arr, function(index, filter) {
|
||||
filter_list.push("<li id='index'><span class='filter_id'>"+index+"</span>" +
|
||||
(typeof filter != "object" ? "<span class='filter_value'>"+filter+"</span>": "")
|
||||
);
|
||||
if(typeof filter == "object" && filter != null) add_to_popup(filter);
|
||||
filter_list.push("</li>");
|
||||
});
|
||||
filter_list.push("</ul>");
|
||||
}
|
||||
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('<div id="nm_favorites_popup" title="' + egw().lang("New favorite") + '">\
|
||||
<form>\
|
||||
<label for="name">'+
|
||||
this.egw().lang("name") +
|
||||
'</label>' +
|
||||
|
||||
'<input type="text" name="name" id="name"/>\
|
||||
<div id="nm_favorites_popup_admin"/>\
|
||||
<span>'+ this.egw().lang("Details") + '</span><span style="float:left;" class="ui-icon ui-icon-circle-plus" />\
|
||||
<ul id="nm_favorites_popup_filters"/>\
|
||||
</form>\
|
||||
</div>'
|
||||
).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"]);
|
@ -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;
|
||||
|
Loading…
Reference in New Issue
Block a user