Home progress:

- Fix some incorrect sizes on new portlets
- Fix mainscreen message
- Base for using other app favorites on home screen
This commit is contained in:
Nathan Gray 2014-11-11 23:07:35 +00:00
parent af1bf822a9
commit 872a11bfdb
7 changed files with 294 additions and 42 deletions

View File

@ -132,6 +132,8 @@ var et2_portlet = et2_valueWidget.extend(
null, null,
self, true, self self, true, self
); );
// Tell children
self.iterateOver(function(widget) {widget.resize();},null,et2_IResizeable);
} }
}); });
this.header = $j(document.createElement("div")) this.header = $j(document.createElement("div"))
@ -288,6 +290,13 @@ var et2_portlet = et2_valueWidget.extend(
{ {
this.edit_settings(); 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 this, true, this
); );
@ -350,6 +359,8 @@ var et2_portlet = et2_valueWidget.extend(
{ {
this.options.width = value; this.options.width = value;
this.div.attr("data-sizex", 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.options.height = value;
this.div.attr("data-sizey", 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','');
} }
}); });

View File

@ -0,0 +1,181 @@
<?php
/**
* EGroupware - Home - A simple portlet for displaying a list of entries
*
* @link www.egroupware.org
* @author Nathan Gray
* @copyright (c) 2013 by Nathan Gray
* @package home
* @subpackage portlet
* @license http://opensource.org/licenses/gpl-license.php GPL - GNU General Public License
* @version $Id: class.home_list_portlet.inc.php 49321 2014-11-06 21:40:03Z nathangray $
*/
/**
* The home_favorite_portlet extends the list portlet to display the entries for a particular
* favorite, for a given app.
*/
class home_favorite_portlet extends home_portlet
{
/**
* Context for this portlet - the application and favorite name
*/
protected $context = array(
'appname' => '',
'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 => <appname>, id => <favorite 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;
}
}

View File

@ -48,17 +48,6 @@ class home_ui
$GLOBALS['egw_info']['flags']['app_header'] = lang('home'); $GLOBALS['egw_info']['flags']['app_header'] = lang('home');
$GLOBALS['egw_info']['flags']['currentapp'] = '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 // Main screen message
translation::add_app('mainscreen'); translation::add_app('mainscreen');
$greeting = translation::translate('mainscreen_message',false,''); $greeting = translation::translate('mainscreen_message',false,'');
@ -72,6 +61,18 @@ class home_ui
{ {
$content['mainscreen_message'] = $greeting; $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; $add_portlets = $portlets;
foreach($add_portlets as $id => &$add) 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['id'] = 'add_' . $id;
$add['class'] = $id; $add['class'] = $id;
} }
@ -95,7 +103,9 @@ class home_ui
'caption' => 'Add', 'caption' => 'Add',
'onExecute' => 'javaScript:app.home.add', 'onExecute' => 'javaScript:app.home.add',
'children' => $add_portlets '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 // 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 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['acceptedTypes'] = $app;
$app_portlet['type'] = 'drop'; $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 = egw_cache::getTree('home', 'portlet_classes', function() {
$list = array(); $list = array();
$classes = 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 // Look through all known classes for portlets - for now, they need 'portlet' in the file name
foreach($GLOBALS['egw_info']['apps'] as $appname => $app) foreach($GLOBALS['egw_info']['apps'] as $appname => $app)
{ {
@ -310,7 +329,7 @@ class home_ui
foreach($files as $entry) 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); list(,$classname) = explode('.', $entry);
if(class_exists($classname) && if(class_exists($classname) &&

View File

@ -101,6 +101,35 @@ app.classes.home = AppJS.extend(
// Set up sorting of portlets // Set up sorting of portlets
this._do_ordering(); 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) 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); var misplaced = $j(etemplate2.getById('home-index').DOMContainer).siblings('#'+et2.DOMContainer.id);
if(portlet) if(portlet)
{ {
portlet.content = $j(et2.DOMContainer).appendTo(portlet.content);
portlet.addChild(et2.widgetContainer); portlet.addChild(et2.widgetContainer);
et2.resize();
} }
if(portlet && misplaced.length) 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; 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 a new portlet from the context menu
*/ */
add: function(action, source) { 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}; var attrs = {id: this._create_id(), row: 1, col: 1};
// Try to put it about where the menu was opened
if(action.menu_context) if(action.menu_context)
{ {
var $portlet_container = $j(this.portlet_container.getDOMNode()); 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 * 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 // Actions got confused drop vs popup
if(source[0].id == 'portlets') if(source[0].id == 'portlets')
@ -214,7 +230,14 @@ app.classes.home = AppJS.extend(
var drop_data = []; var drop_data = [];
for(var i = 0; i < source.length; i++) 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)}); portlet._process_edit(et2_dialog.OK_BUTTON, {dropped_data: drop_data, class: action.data.class || action.id.substr(5)});

View File

@ -24,10 +24,16 @@
overflow: hidden; overflow: hidden;
} }
.et2_portlet.ui-widget-content > div:last-of-type { .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%; height: 100%;
} }
.et2_portlet.ui-widget-content > div:last-of-type > div { .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 */ 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 { .home .et2_portlet {
position: absolute; position: absolute;
} }
#portlets .preview-holder { .home .et2_portlet.dragging {
z-index: 99;
}
.preview-holder {
margin: 5px; margin: 5px;
list-style: none; list-style: none;
background: transparent; background: rgba(0,0,0,.3);
border: 1px dashed silver; border: 1px solid silver;
position: absolute; position: absolute;
} }

View File

@ -10,7 +10,7 @@
<rows> <rows>
<row> <row>
<label for="@${row}[name]" value="@${row}[label]"/> <label for="@${row}[name]" value="@${row}[label]"/>
<widget type="@${row}[type]" id="@${row}[name]" no_lang="1" options="@${row}[size]"/> <widget type="@${row}[type]" id="@${row}[name]" no_lang="1" select_options="@${row}[select_options]"/>
</row> </row>
</rows> </rows>
</grid> </grid>

View File

@ -0,0 +1,7 @@
<?xml version="1.0"?>
<!-- $Id$ -->
<overlay>
<template id="home.favorite" template="" lang="" group="0" version="1.9.001">
<nextmatch id="nm"/>
</template>
</overlay>