mirror of
https://github.com/EGroupware/egroupware.git
synced 2024-11-07 08:34:42 +01:00
First run at a new home app. Just structure so far, more to come.
This commit is contained in:
parent
44f4e1ba3b
commit
e8a21565ab
320
etemplate/js/et2_widget_portlet.js
Normal file
320
etemplate/js/et2_widget_portlet.js
Normal 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"]);
|
@ -44,6 +44,7 @@
|
||||
et2_widget_file;
|
||||
et2_widget_link;
|
||||
et2_widget_progress;
|
||||
et2_widget_portlet;
|
||||
et2_widget_selectAccount;
|
||||
et2_widget_ajaxSelect;
|
||||
et2_widget_vfs;
|
||||
|
@ -927,6 +927,29 @@ label input, label span, label div, label select, label textarea {
|
||||
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 {
|
||||
display: inline-block;
|
||||
border: 1px solid black;
|
||||
@ -1113,3 +1136,4 @@ div.ui-dialog .ui-dialog-buttonpane .ui-dialog-buttonset button {
|
||||
.et2_prompt #value {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
|
123
home/inc/class.home_link_portlet.inc.php
Normal file
123
home/inc/class.home_link_portlet.inc.php
Normal 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;
|
||||
}
|
||||
}
|
91
home/inc/class.home_portlet.inc.php
Normal file
91
home/inc/class.home_portlet.inc.php
Normal 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();
|
||||
}
|
276
home/inc/class.home_ui.inc.php
Normal file
276
home/inc/class.home_ui.inc.php
Normal 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);
|
||||
}
|
||||
}
|
||||
}
|
@ -26,9 +26,13 @@
|
||||
);
|
||||
|
||||
include('../header.inc.php');
|
||||
auth::check_password_age('home','index');
|
||||
$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
|
||||
*/
|
||||
|
225
home/js/app.js
Normal file
225
home/js/app.js
Normal 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;
|
||||
}
|
||||
});
|
49
home/templates/default/app.css
Normal file
49
home/templates/default/app.css
Normal 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;
|
||||
}
|
18
home/templates/default/edit.xet
Normal file
18
home/templates/default/edit.xet
Normal 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>
|
20
home/templates/default/index.xet
Normal file
20
home/templates/default/index.xet
Normal 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>
|
Loading…
Reference in New Issue
Block a user