From 872a11bfdb514ab752eb66bab4b058e9a960253a Mon Sep 17 00:00:00 2001 From: Nathan Gray Date: Tue, 11 Nov 2014 23:07:35 +0000 Subject: [PATCH] Home progress: - Fix some incorrect sizes on new portlets - Fix mainscreen message - Base for using other app favorites on home screen --- etemplate/js/et2_widget_portlet.js | 13 ++ home/inc/class.home_favorite_portlet.inc.php | 181 +++++++++++++++++++ home/inc/class.home_ui.inc.php | 53 ++++-- home/js/app.js | 61 +++++-- home/templates/default/app.css | 19 +- home/templates/default/edit.xet | 2 +- home/templates/default/favorite.xet | 7 + 7 files changed, 294 insertions(+), 42 deletions(-) create mode 100644 home/inc/class.home_favorite_portlet.inc.php create mode 100644 home/templates/default/favorite.xet diff --git a/etemplate/js/et2_widget_portlet.js b/etemplate/js/et2_widget_portlet.js index a5fc455b3a..5face4436a 100644 --- a/etemplate/js/et2_widget_portlet.js +++ b/etemplate/js/et2_widget_portlet.js @@ -132,6 +132,8 @@ var et2_portlet = et2_valueWidget.extend( null, self, true, self ); + // Tell children + self.iterateOver(function(widget) {widget.resize();},null,et2_IResizeable); } }); this.header = $j(document.createElement("div")) @@ -288,6 +290,13 @@ var et2_portlet = et2_valueWidget.extend( { this.edit_settings(); } + + // Only resize once, and only if needed + if(data.attributes.width || data.attributes.height) + { + // Tell children + this.iterateOver(function(widget) {widget.resize();},null,et2_IResizeable); + } }, this, true, this ); @@ -350,6 +359,8 @@ var et2_portlet = et2_valueWidget.extend( { this.options.width = value; this.div.attr("data-sizex", value); + // Clear what's there from jQuery, we get width from CSS according to sizex + this.div.css('width',''); }, /** @@ -361,6 +372,8 @@ var et2_portlet = et2_valueWidget.extend( { this.options.height = value; this.div.attr("data-sizey", value); + // Clear what's there from jQuery, we get width from CSS according to sizey + this.div.css('height',''); } }); diff --git a/home/inc/class.home_favorite_portlet.inc.php b/home/inc/class.home_favorite_portlet.inc.php new file mode 100644 index 0000000000..cce4f99375 --- /dev/null +++ b/home/inc/class.home_favorite_portlet.inc.php @@ -0,0 +1,181 @@ + '', + 'favorite' => 'blank' + ); + + /** + * Nextmatch settings + * @see etemplate_widget_nextmatch + * @var array + */ + protected $nm_settings = array( + 'no_filter' => true, + 'no_filter2' => true, + 'no_cat' => true, + 'no_search' => true, + 'lettersearch' => false, + 'favorites' => false, // Hide favorite control + ); + + /** + * Constructor sets up the portlet according to the user's saved property values + * for this particular portlet. It is possible to have multiple instances of the + * same portlet with different properties. + * + * The implementing class is allowed to modify the context, if needed, but it is + * better to use get_properties(). + * + * @param context Array portlet settings such as size, as well as values for properties + * @param boolean $need_reload Flag to indicate that the portlet needs to be reloaded (exec will be called) + */ + public function __construct(Array &$context = array(), &$need_reload = false) + { + // Process dropped data (Should be [appname => , id => ]) into something useable + if($context['dropped_data']) + { + foreach((Array)$context['dropped_data'] as $dropped) + { + // Only handle one, but dropped is an array + $context['appname'] = $dropped['appname']; + $context['favorite'] = $dropped['id']; + break; + } + + unset($context['dropped_data']); + + $need_reload = true; + } + // Title not set for new widgets created via context menu + if(!$context['title']) + { + // Set initial size to 6x3, default is way too small + $context['width'] = 6; + $context['height'] = 3; + + $need_reload = true; + } + $favorites = egw_favorites::get_favorites($context['appname']); + $this->favorite = $favorites[$context['favorite']]; + $this->title = $context['title'] = $context['title'] ? $context['title'] : lang($context['appname']) . ' ' . $this->favorite['name']; + $this->context = $context; + if($this->favorite) + { + $this->nm_settings['favorite'] = $this->context['favorite']; + $this->nm_settings['columnselection_pref'] = 'nextmatch-home'; + if(is_array($this->favorite['state'])) + { + $this->nm_settings += $this->favorite['state']; + } + } + } + public function exec($id = null, etemplate_new &$etemplate = null) + { + if($etemplate == null) + { + $etemplate = new etemplate_new(); + } + $etemplate->read('home.favorite'); + + $etemplate->set_dom_id($id); + + $content = $this->context + array('nm' => $this->nm_settings); + $etemplate->setElementAttribute('nm', 'template',$this->nm_settings['template']); + + $etemplate->exec(get_called_class() .'::process',$content); + } + + public static function process($content = array()) + { + // We need to keep the template going, thanks. + etemplate_widget::setElementAttribute('','',''); + } + + public function get_actions(){ + return array(); + } + + /** + * Some descriptive information about the portlet, so that users can decide if + * they want it or not, and for inclusion in lists, hover text, etc. + * + * These should be already translated, no further translation will be done. + * + * @return Array with keys + * - displayName: Used in lists + * - title: Put in the portlet header + * - description: A short description of what this portlet does or displays + */ + public function get_description() + { + return array( + 'displayName'=> lang('Favorite'), + 'title'=> $this->title, + 'description'=> lang('Show the entries from a favorite') + ); + } + /** + * Return a list of settings to customize the portlet. + * + * Settings should be in the same style as for preferences. It is OK to return an empty array + * for no customizable settings. + * + * These should be already translated, no further translation will be done. + * + * @see preferences/inc/class.preferences_settings.inc.php + * @return Array of settings. Each setting should have the following keys: + * - name: Internal reference + * - type: Widget type for editing + * - label: Human name + * - help: Description of the setting, and what it does + * - default: Default value, for when it's not set yet + */ + public function get_properties() + { + $properties = parent::get_properties(); + $favorites = egw_favorites::get_favorites($this->context['appname']); + $favorite_list = array(); + foreach($favorites as $id => $favorite) + { + $favorite_list[$id] = $favorite['name']; + } + $favorite = array( + 'label' => lang('Favorite'), + 'name' => 'favorite', + 'type' => 'select', + 'select_options' => $favorite_list + ); + if($this->context['favorite']) + { + $favorite['type'] = 'select_ro'; + } + $properties[] = $favorite; + $properties[] = array( + 'appname' => 'appname' + ); + return $properties; + } +} \ No newline at end of file diff --git a/home/inc/class.home_ui.inc.php b/home/inc/class.home_ui.inc.php index 8c2460b9b7..82130a7fdb 100644 --- a/home/inc/class.home_ui.inc.php +++ b/home/inc/class.home_ui.inc.php @@ -48,17 +48,6 @@ class home_ui $GLOBALS['egw_info']['flags']['app_header'] = lang('home'); $GLOBALS['egw_info']['flags']['currentapp'] = 'home'; - $template->exec('home.home_ui.index', $content); - - // Now run the portlets themselves - foreach($content['portlets'] as $portlet => $p_data) - { - $id = $p_data['id']; - - if(!$id) continue; - $portlet = $this->get_portlet($id, $p_data, $content, $attrs, true); - } - // Main screen message translation::add_app('mainscreen'); $greeting = translation::translate('mainscreen_message',false,''); @@ -72,6 +61,18 @@ class home_ui { $content['mainscreen_message'] = $greeting; } + + $template->exec('home.home_ui.index', $content); + + // Now run the portlets themselves + foreach($content['portlets'] as $portlet => $p_data) + { + $id = $p_data['id']; + + if(!$id) continue; + $portlet = $this->get_portlet($id, $p_data, $content, $attrs, true); + } + } /** @@ -86,6 +87,13 @@ class home_ui $add_portlets = $portlets; foreach($add_portlets as $id => &$add) { + if(!$add['id'] && is_array($add['children'])) + { + foreach($add['children'] as $sub_id => &$sub_add) + { + $sub_add['id'] = 'add_'.$sub_id; + } + } $add['id'] = 'add_' . $id; $add['class'] = $id; } @@ -95,7 +103,9 @@ class home_ui 'caption' => 'Add', 'onExecute' => 'javaScript:app.home.add', 'children' => $add_portlets - ) + ), + // Favorites are sortable which needs special handling, + // handled directly through jQuery ); // Add all known portlets as drop actions too. If there are multiple matches, there will be a menu @@ -113,12 +123,15 @@ class home_ui } else { - foreach($children as $portlet => $app_portlets) + foreach($children['children'] as $portlet => $app_portlet) { - $app_portlets['onExecute'] = $drop_execute; + if(!is_array($app_portlet)) continue; + $app_portlet['class'] = $portlet; + $app_portlet['id'] = 'drop_' . $app_portlet['id']; + $app_portlet['onExecute'] = $drop_execute; $app_portlet['acceptedTypes'] = $app; $app_portlet['type'] = 'drop'; - $actions["drop_$portlet"] = $app_portlets; + $actions["drop_$portlet"] = $app_portlet; } } } @@ -300,7 +313,13 @@ class home_ui $list = egw_cache::getTree('home', 'portlet_classes', function() { $list = array(); $classes = array(); - + + // Ignore some problem files and base classes that shouldn't be options + $ignore = array( + '.','..', + 'class.home_legacy_portlet.inc.php', + 'class.home_favorite_portlet.inc.php' + ); // Look through all known classes for portlets - for now, they need 'portlet' in the file name foreach($GLOBALS['egw_info']['apps'] as $appname => $app) { @@ -310,7 +329,7 @@ class home_ui foreach($files as $entry) { - if (!in_array($entry, array('.','..','class.home_legacy_portlet.inc.php')) && substr($entry,-8) == '.inc.php' && strpos($entry,'portlet')) + if (!in_array($entry, $ignore) && substr($entry,-8) == '.inc.php' && strpos($entry,'portlet')) { list(,$classname) = explode('.', $entry); if(class_exists($classname) && diff --git a/home/js/app.js b/home/js/app.js index aa5c046ae8..498bc73f26 100644 --- a/home/js/app.js +++ b/home/js/app.js @@ -101,6 +101,35 @@ app.classes.home = AppJS.extend( // Set up sorting of portlets this._do_ordering(); + + // Accept drops of favorites, which aren't part of action system + $j(this.et2.getDOMNode()).droppable({ + hoverClass: 'drop-hover', + accept: function(draggable) { + // Check for direct support for that application + if(draggable[0].dataset && draggable[0].dataset.appname) + { + return egw_getActionManager('home',false,1).getActionById('drop_'+draggable[0].dataset.appname +'_favorite_portlet') != null; + } + return false; + }, + drop: function(event, ui) { + // Favorite dropped on home - fake an action and divert to normal handler + var action = { + data: { + class: 'add_home_favorite_portlet' + } + } + + // Check for direct support for that application + if(ui.helper.context.dataset && ui.helper.context.dataset.appname) + { + var action = egw_getActionManager('home',false,1).getActionById('drop_'+ui.helper.context.dataset.appname +'_favorite_portlet') || {} + } + action.ui = ui; + app.home.add_from_drop(action, [{data: ui.helper.context.dataset}]) + } + }) } else if (et2.uniqueId) { @@ -127,37 +156,24 @@ app.classes.home = AppJS.extend( var misplaced = $j(etemplate2.getById('home-index').DOMContainer).siblings('#'+et2.DOMContainer.id); if(portlet) { + portlet.content = $j(et2.DOMContainer).appendTo(portlet.content); portlet.addChild(et2.widgetContainer); + et2.resize(); } if(portlet && misplaced.length) { - // etemplate->exec() always adds a new div, so if there's an extra one, move it - $j(et2.DOMContainer).remove(); - et2.DOMContainer = portlet.getDOMNode(et2); et2.DOMContainer.id = et2.uniqueId; } } }, - /** - * Set top level actions - * - * @param {type} action - * @param {type} source - * @returns {undefined} - */ - set_actions: function() { - - }, - /** * Add a new portlet from the context menu */ add: function(action, source) { - // Put it in the last row, first column, since the mouse position is unknown - var max_row = Math.max.apply(null,$j('div',this.portlet_container.div).map(function() {return $j(this).attr('data-row');})); - var attrs = {id: this._create_id(), row: 1, col: 1}; + + // Try to put it about where the menu was opened if(action.menu_context) { var $portlet_container = $j(this.portlet_container.getDOMNode()); @@ -185,7 +201,7 @@ app.classes.home = AppJS.extend( /** * User dropped something on home. Add a new portlet */ - add_from_drop: function(action,source,target_action) { + add_from_drop: function(action,source) { // Actions got confused drop vs popup if(source[0].id == 'portlets') @@ -214,7 +230,14 @@ app.classes.home = AppJS.extend( var drop_data = []; for(var i = 0; i < source.length; i++) { - if(source[i].id) drop_data.push(source[i].id); + if(source[i].id) + { + drop_data.push(source[i].id); + } + else + { + drop_data.push(source[i].data); + } } portlet._process_edit(et2_dialog.OK_BUTTON, {dropped_data: drop_data, class: action.data.class || action.id.substr(5)}); diff --git a/home/templates/default/app.css b/home/templates/default/app.css index 1ba1399620..e581a5fce4 100644 --- a/home/templates/default/app.css +++ b/home/templates/default/app.css @@ -24,10 +24,16 @@ overflow: hidden; } .et2_portlet.ui-widget-content > div:last-of-type { + /* Allow space for header, as the whole portlet is sized by auto-generated css */ + position: absolute; + bottom: 0px; + top: 20px; + width: 100%; +} +.et2_portlet .et2_container { height: 100%; } .et2_portlet.ui-widget-content > div:last-of-type > div { - height: 100%; background: linear-gradient(to bottom, rgba(255,255,255,.9) 10%,rgba(255,255,255,.75) 90%) /* W3C */ } @@ -38,10 +44,13 @@ .home .et2_portlet { position: absolute; } -#portlets .preview-holder { +.home .et2_portlet.dragging { + z-index: 99; +} +.preview-holder { margin: 5px; list-style: none; - background: transparent; - border: 1px dashed silver; + background: rgba(0,0,0,.3); + border: 1px solid silver; position: absolute; -} +} \ No newline at end of file diff --git a/home/templates/default/edit.xet b/home/templates/default/edit.xet index 4c2eb1c850..d32587feed 100644 --- a/home/templates/default/edit.xet +++ b/home/templates/default/edit.xet @@ -10,7 +10,7 @@ diff --git a/home/templates/default/favorite.xet b/home/templates/default/favorite.xet new file mode 100644 index 0000000000..54a374b7b4 --- /dev/null +++ b/home/templates/default/favorite.xet @@ -0,0 +1,7 @@ + + + + +