First run at a new home app. Just structure so far, more to come.

This commit is contained in:
Nathan Gray 2013-05-22 20:13:12 +00:00
parent 44f4e1ba3b
commit e8a21565ab
11 changed files with 1152 additions and 1 deletions

View File

@ -0,0 +1,320 @@
/**
* EGroupware eTemplate2 - JS Portlet object - used by Home
*
* @license http://opensource.org/licenses/gpl-license.php GPL - GNU General Public License
* @package home
* @package etemplate
* @subpackage api
* @link http://www.egroupware.org
* @author Nathan Gray
* @version $Id$
*/
"use strict";
/*egw:uses
jquery.jquery;
et2_core_baseWidget;
*/
/**
* Class which implements the UI of a Portlet
*
* This manages the frame and decoration, but also provides the UI for properties.
*
* Portlets are only internal to EGroupware.
*
* Home does not fully implement WSRP, but tries not to conflict, ether.
* @link http://docs.oasis-open.org/wsrp/v2/wsrp-2.0-spec-os-01.html
* @augments et2_baseWidget
*/
var et2_portlet = et2_valueWidget.extend(
{
attributes: {
"title": {
"name": "Title",
"description": "Goes in the little bit at the top with the icons",
"type": "string",
"default": ""
},
"edit_template": {
"name": "Edit template",
"description": "Custom eTemplate used to customize / set up the portlet",
"type": "string",
"default": window.egw_webserverUrl+"/home/templates/default/edit.xet"
},
"settings": {
"name": "Customization settings",
"description": "Array of customization settings, similar to preference settings",
"type": "any",
"default": et2_no_init
},
"width": { "default": 2, "ignore": true},
"height": { "default": 1, "ignore": true},
"rows": {"ignore": true},
"cols": {"ignore": true},
"row": { "default": 1},
"col": {"default": 1}
},
createNamespace: true,
RESIZE_TIMEOUT: 5000,
GRID: 50,
/**
* These are the "normal" actions that every portlet is expected to have.
* The widget provides default actions for all of these, but they can
* be added to or overridden if needed by setting the action attribute.
*/
default_actions: {
edit_settings: {
icon: "edit",
caption: "Configure",
default: true,
hideOnDisabled: true,
group: "portlet"
},
remove_portlet: {
icon: "delete",
caption: "Remove",
group: "portlet"
}
},
/**
* Constructor
*
* @memberOf et2_portlet
*/
init: function()
{
this._super.apply(this, arguments);
var self = this;
// Create DOM nodes
this.div = $j(document.createElement("div"))
.addClass("ui-widget ui-widget-content ui-corner-all")
.addClass("et2_portlet")
/* Gridster */
.attr("data-sizex", this.options.width)
.attr("data-sizey", this.options.height)
.attr("data-row", this.options.row)
.attr("data-col", this.options.col)
// Shapeshift
.width(this.options.width * this.GRID)
.height(this.options.height * this.GRID)
.attr("data-ss-rowspan", this.options.row)
.attr("data-ss-colspan", this.options.col)
.resizable( {
autoHide: true,
grid: this.GRID,
//containment: this.getParent().getDOMNode(),
stop: function(event, ui) {
self.set_width(Math.round(ui.size.width / self.GRID));
self.set_height(Math.round(ui.size.height / self.GRID));
self.egw().json("home.home_ui.ajax_set_properties",[self.id, self.options.settings,{
width: self.options.width,
height: self.options.height
}],
null,
self, true, self
).sendRequest();
}
});
this.header = $j(document.createElement("div"))
.addClass("ui-widget-header ui-corner-all")
.appendTo(this.div)
.html(this.options.title);
this.content = $j(document.createElement("div"))
.appendTo(this.div)
.html(this.options.value);
this.setDOMNode(this.div[0]);
},
destroy: function()
{
this._super.apply(this, arguments);
},
/**
* Overriden from parent to add in default actions
*/
set_actions: function(actions)
{
// Set targets for actions
var defaults = {};
for(var action_name in this.default_actions)
{
defaults[action_name] = this.default_actions[action_name];
if(typeof this[action_name] == "function")
{
defaults[action_name].onExecute = jQuery.proxy(this[action_name],this);
}
}
// Add in defaults, but let provided actions override them
this.options.actions = jQuery.extend(true,{},defaults,actions);
this._super.apply(this, [this.options.actions]);
},
/**
* Override _link_actions to remove edit action, if there is no settings
*/
_link_actions: function(parsed_actions)
{
// Get the top level element
var objectManager = egw_getAppObjectManager(true);
var widget_object = objectManager.getObjectById(this.id);
if (widget_object == null) {
// Add a new container to the object manager which will hold the widget
// objects
widget_object = objectManager.insertObject(false, new egwActionObject(
this.id, objectManager, new et2_action_object_impl(this),
objectManager.manager.getActionById(this.id) || objectManager.manager
));
}
// Delete all old objects
widget_object.clear();
// Go over the widget & add links - this is where we decide which actions are
// 'allowed' for this widget at this time
var action_links = [];
for(var i = 0; i < parsed_actions.length; i++)
{
var action = {
actionId: parsed_actions[i].id,
enabled: true
};
// If there are no settings, there can be no customization, so remove the edit action
if(parsed_actions[i].id == 'edit_settings' && (!this.options.settings || jQuery.isEmptyObject(this.options.settings)))
{
this.egw().debug("log", "No settings for portlet %o, edit_settings action removed", this);
action.enabled = false;
}
action_links.push(action);
}
widget_object.updateActionLinks(action_links);
},
/**
* Create & show a dialog for customizing this portlet
*
* Properties for customization are sent in the 'settings' attribute
*/
edit_settings: function(action, sender)
{
var dialog = et2_createWidget("dialog", {
callback: jQuery.proxy(this._process_edit, this),
template: this.options.edit_template,
value: {
content: this.options.settings
},
buttons: et2_dialog.BUTTONS_OK_CANCEL
},this);
// Set seperately to avoid translation
dialog.set_title(this.egw().lang("Edit") + " " + this.options.title);
},
_process_edit: function(button_id, value)
{
if(button_id != et2_dialog.OK_BUTTON) return;
// Save settings - server will reply with new content, if the portlet needs an update
this.div.addClass("loading");
this.egw().json("home.home_ui.ajax_set_properties",[this.id, this.options.settings || {}, value],
function(data) {
this.div.removeClass("loading");
this.set_value(data.content);
for(var key in data.attributes)
{
if(typeof this["set_"+key] == "function")
{
this["set_"+key].call(this, data.attributes[key]);
}
else if (this.attributes[key])
{
this.options[key] = data.attributes[key];
}
}
// Flagged as needing to edit settings? Open dialog
if(typeof data.edit_settings != 'undefined' && data.edit_settings)
{
this.edit_settings();
}
},
this, true, this
).sendRequest();
// Extend, not replace, because settings has types while value has just value
jQuery.extend(this.options.settings, value);
},
/**
* Remove this portlet from the home page
*/
remove_portlet: function() {
var self = this;
et2_dialog.show_dialog(function(button_id) {
if(button_id != et2_dialog.OK_BUTTON) return;
self._process_edit(button_id, '~remove~');
self._parent.removeChild(self);
self.destroy();
},"Remove", this.options.title,{},
et2_dialog.BUTTONS_OK_CANCEL, et2_dialog.QUESTION_MESSAGE
);
},
/**
* Set the HTML content of the portlet
*
* @param value String HTML fragment
*/
set_value: function(value)
{
this.content.html(value);
},
/**
* Set the content of the header
*
* @param value String HTML fragment
*/
set_title: function(value)
{
this.options.title = value;
this.header.html(value);
},
/**
* Set the number of grid cells this widget spans
*
* @param value int Number of horizontal grid cells
*/
set_width: function(value)
{
this.options.width = value;
this.div.attr("data-sizex", value);
},
/**
* Set the number of vertical grid cells this widget spans
*
* @param value int Number of vertical grid cells
*/
set_height: function(value)
{
this.options.height = value;
this.div.attr("data-sizey", value);
}
});
et2_register_widget(et2_portlet, ["portlet"]);

View File

@ -44,6 +44,7 @@
et2_widget_file; et2_widget_file;
et2_widget_link; et2_widget_link;
et2_widget_progress; et2_widget_progress;
et2_widget_portlet;
et2_widget_selectAccount; et2_widget_selectAccount;
et2_widget_ajaxSelect; et2_widget_ajaxSelect;
et2_widget_vfs; et2_widget_vfs;

View File

@ -927,6 +927,29 @@ label input, label span, label div, label select, label textarea {
cursor: pointer; cursor: pointer;
} }
/**
* et2_portlet
*/
div.et2_portlet {
min-width: 100px;
}
.et2_portlet .ui-widget-header {
margin: 0em;
padding-bottom: 4px;
padding-left: 0.2em;
}
.et2_portlet .ui-widget-header span.ui-icon {
display: inline-block;
float: right;
}
.et2_portlet .ui-widget-header span.ui-icon.ui-icon-gear {
display: inline-block;
float: left;
}
/**
* et2_progress
*/
div.et2_progress { div.et2_progress {
display: inline-block; display: inline-block;
border: 1px solid black; border: 1px solid black;
@ -1113,3 +1136,4 @@ div.ui-dialog .ui-dialog-buttonpane .ui-dialog-buttonset button {
.et2_prompt #value { .et2_prompt #value {
width: 100%; width: 100%;
} }

View File

@ -0,0 +1,123 @@
<?php
/**
* EGroupware - Home - A simple portlet for displaying an entry
*
* @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_link_portlet extends home_portlet
{
/**
* Context for this portlet
*/
protected $context = array();
/**
* Title of entry
*/
protected $title = 'Link';
/**
* Construct the portlet
*
*/
public function __construct(Array &$context = array())
{
$this->context = $context;
if($context['entry'])
{
$this->title = $context['entry']['title'] = egw_link::title($context['entry']['app'], $context['entry']['id']);
}
}
/**
* 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'=> 'Single Entry',
'title'=> $this->context['entry'] ? lang($this->context['entry']['app']) : lang('None'),
'description'=> lang('Show one entry')
);
}
/**
* Get a fragment of HTML for display
*
* @param content Array Values returned from a submit, if any
* @param context Settings for customizing the portlet
* @return string HTML fragment for display
*/
public function get_content()
{
return $this->title;
}
/**
* 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()
{
return array(
array(
'name' => 'entry',
'type' => 'link-entry',
'label' => lang('Entry'),
)
) + parent::get_properties();
}
/**
* Return a list of allowable actions for the portlet.
*
* These actions will be merged with the default porlet actions.
* We add an 'edit' action as default so double-clicking the widget
* opens the entry
*/
public function get_actions()
{
$actions = array(
'view' => array(
'icon' => 'view',
'caption' => lang('open'),
'default' => true,
'hideOnDisabled' => false,
'onExecute' => 'javaScript:app.home.open_link',
),
'edit_settings' => array(
'default' => false
)
);
$actions['view']['enabled'] = (bool)$this->context['entry'];
return $actions;
}
}

View File

@ -0,0 +1,91 @@
<?php
/**
* EGroupware - Home - Portlet interface
*
* @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$
*/
abstract class home_portlet
{
/**
* Attributes that are common to all portlets, but are customized indirectly
* through the UI, rather than explictly through the configure popup
*/
public static $common_attributes = array(
'width', 'height', 'row', 'col'
);
/**
* 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
*/
public abstract function __construct(Array &$context = 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 abstract function get_description();
/**
* Get a fragment of HTML for display
*
* @param content Array Values returned from a submit, if any
* @param context Settings for customizing the portlet
* @return string HTML fragment for display
*/
public abstract function get_content();
/**
* 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() {
// Include the common attributes, or they won't get saved
$properties = array();
foreach(self::$common_attributes as $prop)
{
$properties[$prop] = array('name' => $prop);
}
return $properties;
}
/**
* Return a list of allowable actions for the portlet.
*
* These actions will be merged with the default porlet actions. Use the
* same id / key to override the default action.
*/
public abstract function get_actions();
}

View File

@ -0,0 +1,276 @@
<?php
/**
* EGroupware - Home - user interface
*
* @link www.egroupware.org
* @author Nathan Gray
* @copyright (c) 2013 by Nathan Gray
* @package home
* @license http://opensource.org/licenses/gpl-license.php GPL - GNU General Public License
* @version $Id$
*/
/**
* General user interface object of the Home app
*
* For the purposes of the Home application, a Portlet is [part of] an application that provides
* a specific piece of content to be included as part of the Home page.
* See /home/js/Portlet.js.
*
* While Home is not WSRP complient, it does use many of the ideas, and may someday be,
* if someone wants to fully implement it.
* @link http://docs.oasis-open.org/wsrp/v2/wsrp-2.0-spec-os-01.html
*/
class home_ui
{
public $public_functions = array(
'index' => true
);
/**
* Main UI - generates the container, and aggregates all
* the portlets from the applications
*/
public function index($content = array())
{
// CSS for Gridster grid layout
egw_framework::includeCSS('/phpgwapi/js/jquery/gridster/jquery.gridster.css');
$template = new etemplate('home.index');
$content = array(
'portlets' => $this->get_user_portlets($template)
);
$template->setElementAttribute('portlets','actions',$this->get_actions());
//$template->setElementAttribute('portlets[1]','settings',$settings[1]);
$GLOBALS['egw_info']['flags']['app_header'] = lang('home');
$GLOBALS['egw_info']['flags']['currentapp'] = 'home';
$template->exec('home.home_ui.index', $content);
}
/**
* Get a list of actions on the whole home page. Each portlet also has
* its own actions
*
* @return array of actions
*/
protected function get_actions()
{
$actions = array(
'add' => array(
'type' => 'popup',
'caption' => 'Add',
'onExecute' => 'javaScript:app.home.add',
'children' => $this->get_portlet_list()
),
'drop_create' => array(
'type' => 'drop',
//'acceptedTypes' => 'apps?'
'onExecute' => 'javaScript:app.home.add'
)
);
return $actions;
}
/**
* Get a list of the user's portlets, and their associated values & settings, for display
*
* Actual portlet content is provided by each portlet.
* @param template etemplate so attributes can be set
*/
protected function get_user_portlets(etemplate &$template)
{
$portlets = array(
'Just a hard-coded test',
);
$attributes = array();
$attributes[] = array(
'title' => 'Has content',
);
foreach((array)$GLOBALS['egw_info']['user']['preferences']['home']['portlets'] as $id => $context)
{
$content = '';
$attrs = array();
$this->get_portlet($context, $content, $attrs);
$portlets[$id] = $content;
$attributes[$id] = $attrs;
}
foreach($portlets as $index => $portlet)
{
$template->setElementAttribute('portlets', $index, (array)$attributes[$index]);
}
return $portlets;
}
/**
* Load the needed info for one portlet, given the context
*
* @param context Array Settings to customize the portlet instance (size, entry, etc)
* These are specific values for the portlet's properties.
* @param content String HTML fragment to be displayed - will be set by the portlet
* @param attributes Array Settings that can be customized on a per-portlet basis - will be set
* @return home_portlet The portlet object that created the content
*/
protected function get_portlet(&$context, &$content, &$attributes)
{
if(!$context['class']) $context['class'] = 'home_link_portlet';
$classname = $context['class'];
$portlet = new $classname($context);
$desc = $portlet->get_description();
$content = $portlet->get_content();
// Exclude common attributes changed through UI
$settings = $portlet->get_properties() + $context;
foreach(home_portlet::$common_attributes as $attr)
{
unset($settings[$attr]);
}
$attributes = array(
'title' => $desc['title'],
'settings' => $settings,
'actions' => $portlet->get_actions(),
);
// Set any provided common attributes (size, etc)
foreach(home_portlet::$common_attributes as $name)
{
if(array_key_exists($name, $context))
{
$attributes[$name] = $context[$name];
}
}
return $portlet;
}
/**
* Get a list of all available portlets for add menu
*/
protected function get_portlet_list()
{
$list = array();
$list = egw_cache::getTree('home', 'portlet_classes', function() {
$list = array();
$classes = array();
// Look through all known classes for portlets - for now, they need 'portlet' in the file name
foreach($GLOBALS['egw_info']['apps'] as $appname => $app)
{
if(in_array($appname, array('phpgwapi', 'felamimail'))) continue;
$files = (array)@scandir(EGW_SERVER_ROOT . '/'.$appname .'/inc/');
if(!$files) continue;
foreach($files as $entry)
{
if (!in_array($entry, array('.','..')) && substr($entry,-8) == '.inc.php' && strpos($entry,'portlet'))
{
list(,$classname) = explode('.', $entry);
if(class_exists($classname) &&
in_array('home_portlet', class_parents($classname, false)))
{
$classes[$appname][] = $classname;
}
}
}
if(!$classes[$appname]) continue;
// Build 'Add' actions for each discovered portlet.
// Portlets from other apps go in sub-actions
$add_to =& $list;
if($classes[$appname] && $appname != 'home')
{
$list[$appname] = array(
'caption' => lang($appname),
);
$add_to =& $list[$appname]['children'];
}
foreach($classes[$appname] as $portlet)
{
$instance = new $portlet();
$desc = $instance->get_description();
$add_to[$portlet] = array(
'id' => $portlet,
'type' => 'popup',
'caption' => $desc['displayName'],
'hint' => $desc['description'],
'onExecute' => 'javaScript:app.home.add'
);
}
}
return $list;
}, array(), 60);
return $list;
}
/**
* Update the settings for a particular portlet, and give updated content
*
* @param portlet_id String Unique ID (for the user) for a portlet
* @param values Array List of property => value pairs
*
*/
public function ajax_set_properties($portlet_id, $attributes, $values)
{
if(!$attributes)
{
$attributes = array();
}
$response = egw_json_response::get();
if ($GLOBALS['egw_info']['user']['apps']['preferences'])
{
$prefs = $GLOBALS['egw']->preferences->read_repository();
$portlets = (array)$prefs['home']['portlets'];
if($values == '~remove~')
{
unset($portlets[$portlet_id]);
// Already removed client side
}
else
{
// Get portlet settings, and merge new with old
$content = '';
$portlet = $this->get_portlet(array_merge((array)$attributes, $values), $content, $attributes);
$context = array('class' => get_class($portlet));
foreach($portlet->get_properties() as $property)
{
if($values[$property['name']])
{
$context[$property['name']] = $values[$property['name']];
}
elseif($portlets[$portlet_id][$property['name']])
{
$context[$property['name']] = $portlets[$portlet_id][$property['name']];
}
}
// Update client side
$update = array('content' => $content, 'attributes' => $attributes);
// New portlet? Flag going straight to edit mode
if(!array_key_exists($portlet_id,$portlets) && $attributes['settings'])
{
$update['edit_settings'] = true;
}
$response->data($update);
// Store for preference update
$portlets[$portlet_id] = $context;
}
// Save updated preferences
$GLOBALS['egw']->preferences->add('home', 'portlets', $portlets);
$GLOBALS['egw']->preferences->save_repository(True);
}
}
}

View File

@ -26,9 +26,13 @@
); );
include('../header.inc.php'); include('../header.inc.php');
auth::check_password_age('home','index');
$GLOBALS['egw_info']['flags']['nonavbar']=false; $GLOBALS['egw_info']['flags']['nonavbar']=false;
// Home is treated specially, so a redirect won't work.
$home = new home_ui();
echo $home->index();
exit;
/* /*
** Initializing the template ** Initializing the template
*/ */

225
home/js/app.js Normal file
View File

@ -0,0 +1,225 @@
/**
* EGroupware - Filemanager - Javascript UI
*
* @link http://www.egroupware.org
* @package filemanager
* @author Ralf Becker <RalfBecker-AT-outdoor-training.de>
* @copyright (c) 2008-13 by Ralf Becker <RalfBecker-AT-outdoor-training.de>
* @license http://opensource.org/licenses/gpl-license.php GPL - GNU General Public License
* @version $Id$
*/
"use strict";
/*egw:uses
jquery.jquery;
jquery.jquery-ui;
/phpgwapi/js/jquery/shapeshift/core/jquery.shapeshift.js;
/phpgwapi/js/jquery/gridster/jquery.gridster.js;
*/
/**
* JS for home application
*
* Home is a collection of little bits of content (portlets) from the other applications.
*
*
* Uses Gridster for the grid layout
* @see https://github.com/dustmoo/gridster.js
* @augments AppJS
*/
app.home = AppJS.extend(
{
/**
* AppJS requires overwriting this with the actual application name
*/
appname: "home",
/**
* Constructor
*
* @memberOf app.home
*/
init: function()
{
// call parent
this._super.apply(this, arguments);
},
/**
* Destructor
* @memberOf app.home
*/
destroy: function()
{
delete this.et2;
delete this.portlet_container;
// call parent
this._super.apply(this, arguments);
},
/**
* This function is called when the etemplate2 object is loaded
* and ready. If you must store a reference to the et2 object,
* make sure to clean it up in destroy().
*
* @param et2 etemplate2 Newly ready object
*/
et2_ready: function(et2)
{
// call parent
this._super.apply(this, arguments);
this.et2 = et2.widgetContainer;
this.portlet_container = this.et2.getWidgetById("portlets");
// Add portlets
var content = this.et2.getArrayMgr("content").getEntry("portlets");
var modifications = this.et2.getArrayMgr("modifications").getEntry("portlets");
for(var key in content)
{
//var attrs = jQuery.extend({id: key}, content[key], modifications[key]);
var attrs = {id: key};
var portlet = et2_createWidget('portlet',attrs, this.portlet_container);
}
this.et2.loadingFinished();
// Set up sorting of portlets
this._do_ordering();
},
/**
* Add a new portlet
*/
add: function(action) {
var content = this.et2.getArrayMgr("content").getEntry("portlets");
var attrs = {id: this._create_id(), class: action.id};
var portlet = et2_createWidget('portlet',attrs, this.portlet_container);
portlet.loadingFinished();
// Get actual attributes & settings, since they're not available client side yet
portlet._process_edit(et2_dialog.OK_BUTTON, {});
// Set up sorting/grid of new portlet
var $portlet_container = $j(this.portlet_container.getDOMNode());
$portlet_container.data("gridster").add_widget(
portlet.getDOMNode()
);
},
/**
* For link_portlet - opens the configured record when the user
* double-clicks or chooses view from the context menu
*/
open_link: function(action) {
// Get widget
var widget = null;
while(action.parent != null)
{
if(action.data && action.data.widget)
{
widget = action.data.widget;
break;
}
action = action.parent;
}
if(widget == null)
{
egw().log("warning", "Could not find widget");
return;
}
egw().open(widget.options.settings.entry, false, 'view');
},
/**
* Set up the drag / drop / re-order of portlets
*/
_do_ordering: function() {
var $portlet_container = $j(this.portlet_container.getDOMNode());
$portlet_container
.addClass("home ui-helper-clearfix")
.disableSelection()
/* Shapeshift
.shapeshift();
*/
/* Gridster */
.wrap("<div />")
.gridster({
widget_selector: 'div.et2_portlet',
widget_base_dimensions: [45, 45],
widget_margins: [5,5],
extra_rows: 1,
extra_cols: 1,
min_cols: 3,
min_rows: 3,
/**
* Set which parameters we want when calling serialize().
* @param $w jQuery jQuery-wrapped element
* @param grid Object Grid settings
* @return Object - will be returned by gridster.serialize()
*/
serialize_params: function($w, grid) {
return {
id: $w.attr("id"),
row: grid.row,
col: grid.col,
width: grid.width,
height: grid.height
};
},
/**
* Gridster's internal drag settings
*/
draggable: {
handle: '.ui-widget-header',
stop: function(event,ui) {
// Update widget(s)
var changed = this.serialize_changed();
for (var key in changed)
{
if(!changed[key].id) continue;
var widget = window.app.home.portlet_container.getWidgetById(changed[key].id);
if(!widget || widget == window.app.home.portlet_container) continue;
egw().json("home.home_ui.ajax_set_properties",[widget.id, widget.options.settings,{
row: changed[key].row,
col: changed[key].col
}],
null,
widget, true, widget
).sendRequest();
}
}
}
});
// Bind resize to update gridster - this may happen _before_ the widget gets a
// chance to update itself, so we can't use the widget
$portlet_container
.on("resizestop", function(event, ui) {
$portlet_container.data("gridster").resize_widget(
ui.element,
Math.round(ui.size.width / 50),
Math.round(ui.size.height / 50)
);
});
},
/**
* Create an ID that should be unique, at least amoung a single user's portlets
*/
_create_id: function() {
var id = '';
do
{
id = Math.floor((1 + Math.random()) * 0x10000)
.toString(16)
.substring(1);
}
while(this.portlet_container.getWidgetById(id));
return id;
}
});

View File

@ -0,0 +1,49 @@
/**
* Home CSS
*/
#portlets {
border: 1px solid silver;
width: 100%;
min-width: 100px;
min-height: 100px;
position: relative;
}
.home .et2_portlet > .ui-widget-header {
cursor: pointer;
}
.et2_portlet.ui-widget-content {
overflow: hidden;
}
/* Shapeshift
#portlets {
position: relative;
}
.home .et2_portlet {
position: absolute;
}
#portlets .ss-placeholder-child {
background: transparent;
border: 1px dashed silver;
position: absolute;
}
*/
/* Gridster */
#portlets {
position: relative;
}
.home .et2_portlet {
position: absolute;
margin: 5px;
}
#portlets .preview-holder {
margin: 5px;
list-style: none;
background: transparent;
border: 1px dashed silver;
position: absolute;
}

View File

@ -0,0 +1,18 @@
<?xml version="1.0"?>
<!-- $Id$ -->
<overlay>
<template id="home.edit" template="" lang="" group="0" version="1.9.001">
<grid>
<columns>
<column/>
<column/>
</columns>
<rows>
<row>
<label for="@${row}[name]" value="@${row}[label]"/>
<widget type="@${row}[type]" id="@${row}[name]" no_lang="1" options="@${row}[size]"/>
</row>
</rows>
</grid>
</template>
</overlay>

View File

@ -0,0 +1,20 @@
<?xml version="1.0"?>
<!-- $Id$ -->
<overlay>
<template id="home.index" template="" lang="" group="0" version="1.9.001">
<grid width="100%">
<columns>
<column/>
</columns>
<rows>
<row disabled="!@mainscreen_message">
<html id="mainscreen_message"/>
</row>
<row>
<box id="portlets">
</box>
</row>
</rows>
</grid>
</template>
</overlay>