diff --git a/addressbook/templates/default/home.link.xet b/addressbook/templates/default/home.link.xet
new file mode 100644
index 0000000000..4c9a16ca8c
--- /dev/null
+++ b/addressbook/templates/default/home.link.xet
@@ -0,0 +1,54 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/etemplate/js/et2_widget_portlet.js b/etemplate/js/et2_widget_portlet.js
index c56f33c896..3f609782b3 100644
--- a/etemplate/js/et2_widget_portlet.js
+++ b/etemplate/js/et2_widget_portlet.js
@@ -110,6 +110,7 @@ var et2_portlet = et2_valueWidget.extend(
// Create DOM nodes
this.div = $j(document.createElement("div"))
+ .addClass(this.options.class)
.addClass("ui-widget ui-widget-content ui-corner-all")
.addClass("et2_portlet")
/* Gridster */
diff --git a/home/inc/class.home_link_portlet.inc.php b/home/inc/class.home_link_portlet.inc.php
index dc8d7773cb..b89868b46b 100644
--- a/home/inc/class.home_link_portlet.inc.php
+++ b/home/inc/class.home_link_portlet.inc.php
@@ -11,6 +11,9 @@
* @version $Id$
*/
+ /**
+ * A single entry is displayed with its application icon and title
+ */
class home_link_portlet extends home_portlet
{
@@ -24,6 +27,12 @@ class home_link_portlet extends home_portlet
*/
protected $title = 'Link';
+ /**
+ * Image shown. Leave at false to have it automatically set an icon based
+ * on the entry or customize it for the context.
+ */
+ protected $image = false;
+
/**
* Base name for template
* @var string
@@ -48,6 +57,8 @@ class home_link_portlet extends home_portlet
{
$this->title = $context['entry']['title'] = egw_link::title($context['entry']['app'], $context['entry']['id']);
+ // Reload to get the latest title
+ // TODO: This is a performance hit, it would be good to do this less
$need_reload |= (boolean)$context['entry']['id'];
}
$this->context = $context;
@@ -82,27 +93,79 @@ class home_link_portlet extends home_portlet
public function exec($id = null, etemplate_new &$etemplate = null)
{
// Check for custom template for app
+ $custom_template = false;
if($this->context && $this->context['entry'] && $this->context['entry']['app'] &&
$etemplate->read($this->context['entry']['app'] . '.' . $this->template_name))
{
// No action needed, custom template loaded as side-effect
+ $custom_template = true;
}
else
{
$etemplate->read($this->template_name);
}
+
+ $etemplate->set_dom_id($id);
+
+ $content = array(
+ 'image' => $this->image
+ );
+
+ // Try to load entry
+ if($this->context['entry'] && $this->context['entry']['app'])
+ {
+ try
+ {
+ $classname = $this->context['entry']['app'] . '_egw_record';
+ if(class_exists($classname))
+ {
+ $record = new $classname($this->context['entry']['id']);
+ if($record)
+ {
+ // If there's a custom template, send the full record
+ if($custom_template)
+ {
+ $content += $record->get_record_array();
+ }
+ if($content['image'] == false)
+ {
+ $content['image'] = $record->get_icon();
+ }
+ }
+ }
+ }
+ catch(Exception $e)
+ {
+ error_log("Problem loading record " . array2string($this->context['entry']));
+ throw $e;
+ }
+
+ // Set a fallback image
+ if($content['image'] == false)
+ {
+ if($this->context['entry'] && $this->context['entry']['app'])
+ {
+ $content['image'] = $this->context['entry']['app'] . '/navbar';
+ }
+ else
+ {
+ $content['image'] = 'home';
+ }
+ }
+ }
+
// Filemanager support - links need app = 'file' and type set
if($this->context && $this->context['entry'] && $this->context['entry']['app'] == 'filemanager')
{
$this->context['entry']['app'] = 'file';
$this->context['entry']['path'] = $this->context['entry']['title'] = $this->context['entry']['id'];
$this->context['entry']['type'] = egw_vfs::mime_content_type($this->context['entry']['id']);
+ $content['image'] = egw_framework::link('/etemplate/thumbnail.php',array('path' => $this->context['entry']['id']));
}
- $etemplate->set_dom_id($id);
-
- $content = $this->context;
+ $content += $this->context;
+
if(!is_array($content['entry']))
{
$content['entry'] = null;
diff --git a/home/js/app.js b/home/js/app.js
index 36937cc158..2a8557831a 100644
--- a/home/js/app.js
+++ b/home/js/app.js
@@ -171,7 +171,13 @@ app.classes.home = AppJS.extend(
* Add a new portlet from the context menu
*/
add: function(action, source) {
- var attrs = {id: this._create_id(), row: 1, col: 1};
+ // Basic portlet attributes
+ var attrs = {
+ id: this._create_id(),
+ class: action.data.class,
+ width: this.DEFAULT.WIDTH,
+ height: this.DEFAULT.HEIGHT
+ };
// Try to put it about where the menu was opened
if(action.menu_context)
@@ -187,7 +193,7 @@ app.classes.home = AppJS.extend(
portlet.loadingFinished();
// Get actual attributes & settings, since they're not available client side yet
- portlet._process_edit(et2_dialog.OK_BUTTON, {class: action.id});
+ portlet._process_edit(et2_dialog.OK_BUTTON, attrs);
// Set up sorting/grid of new portlet
var $portlet_container = $j(this.portlet_container.getDOMNode());
@@ -212,7 +218,12 @@ app.classes.home = AppJS.extend(
var $portlet_container = $j(this.portlet_container.getDOMNode());
// Basic portlet attributes
- var attrs = {id: this._create_id()};
+ var attrs = {
+ id: this._create_id(),
+ class: action.data.class || action.id.substr(5),
+ width: this.DEFAULT.WIDTH,
+ height: this.DEFAULT.HEIGHT
+ };
// Try to find where the drop was
if(action != null && action.ui && action.ui.position)
@@ -239,7 +250,7 @@ app.classes.home = AppJS.extend(
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, jQuery.extend({dropped_data: drop_data},attrs));
// Set up sorting/grid of new portlet
$portlet_container.data("gridster").add_widget(
@@ -287,14 +298,6 @@ app.classes.home = AppJS.extend(
egw().open(widget.options.settings.entry, "", 'edit');
},
- /**
- * For list_portlet - adds a new link
- * This is needed here so action system can find it
- */
- add_link: function(action,source,target_action) {
- this.List.add_link(action, source, target_action);
- },
-
/**
* Set up the drag / drop / re-order of portlets
*/
@@ -395,118 +398,119 @@ app.classes.home = AppJS.extend(
/**
* Functions for the list portlet
*/
- List:
- {
- /**
- * For list_portlet - opens a dialog to add a new entry to the list
- */
- add_link: function(action, source, target_action) {
- // Actions got confused drop vs popup
- if(source[0].id == 'portlets')
- {
- return this.add_link(action);
- }
+ /**
+ * For list_portlet - opens a dialog to add a new entry to the list
+ *
+ * @param {egwAction} action Drop or add action
+ * @param {egwActionObject[]} Selected entries
+ * @param {egwActionObject} target_action Drop target
+ */
+ add_link: function(action, source, target_action) {
+ // Actions got confused drop vs popup
+ if(source[0].id == 'portlets')
+ {
+ return this.add_link(action);
+ }
- // Get widget
- var widget = null;
- while(action.parent != null)
+ // Get widget
+ var widget = null;
+ while(action.parent != null)
+ {
+ if(action.data && action.data.widget)
{
- if(action.data && action.data.widget)
- {
- widget = action.data.widget;
- break;
- }
- action = action.parent;
+ widget = action.data.widget;
+ break;
}
- if(target_action == null)
- {
- // use template base url from initial template, to continue using webdav, if that was loaded via webdav
- var splitted = 'home.edit'.split('.');
- var path = app.home.portlet_container.getRoot()._inst.template_base_url + splitted.shift() + "/templates/default/" +
- splitted.join('.')+ ".xet";
- var dialog = et2_createWidget("dialog",{
- callback: function(button_id, value) {
- if(button_id == et2_dialog.CANCEL_BUTTON) return;
- var new_list = widget.options.settings.list || [];
- for(var i = 0; i < new_list.length; i++)
- {
- if(new_list[i].app == value.add.app && new_list[i].id == value.add.id)
- {
- // Duplicate - skip it
- return;
- }
- }
- value.add.link_id = value.add.app + ':' + value.add.id;
- // Update server side
- new_list.push(value.add);
- widget._process_edit(button_id,{list: new_list});
- // Update client side
- widget.getWidgetById('list').set_value(new_list);
- },
- buttons: et2_dialog.BUTTONS_OK_CANCEL,
- title: app.home.egw.lang('add'),
- template:path,
- value: { content: [{label: app.home.egw.lang('add'),type: 'link-entry',name: 'add',size:''}]}
- });
- }
- else
- {
- // Drag'n'dropped something on the list - just send action IDs
- var new_list = widget.options.settings.list || [];
- var changed = false;
- for(var i = 0; i < new_list.length; i++)
- {
- // Avoid duplicates
- for(var j = 0; j < source.length; j++)
+ action = action.parent;
+ }
+ if(target_action == null)
+ {
+ // use template base url from initial template, to continue using webdav, if that was loaded via webdav
+ var splitted = 'home.edit'.split('.');
+ var path = app.home.portlet_container.getRoot()._inst.template_base_url + splitted.shift() + "/templates/default/" +
+ splitted.join('.')+ ".xet";
+ var dialog = et2_createWidget("dialog",{
+ callback: function(button_id, value) {
+ if(button_id == et2_dialog.CANCEL_BUTTON) return;
+ var new_list = widget.options.settings.list || [];
+ for(var i = 0; i < new_list.length; i++)
{
- if(!source[j].id || new_list[i].app+"::"+new_list[i].id == source[j].id)
+ if(new_list[i].app == value.add.app && new_list[i].id == value.add.id)
{
// Duplicate - skip it
- source.splice(j,1);
+ return;
}
}
- }
- for(var i = 0; i < source.length; i++)
+ value.add.link_id = value.add.app + ':' + value.add.id;
+ // Update server side
+ new_list.push(value.add);
+ widget._process_edit(button_id,{list: new_list});
+ // Update client side
+ widget.getWidgetById('list').set_value(new_list);
+ },
+ buttons: et2_dialog.BUTTONS_OK_CANCEL,
+ title: app.home.egw.lang('add'),
+ template:path,
+ value: { content: [{label: app.home.egw.lang('add'),type: 'link-entry',name: 'add',size:''}]}
+ });
+ }
+ else
+ {
+ // Drag'n'dropped something on the list - just send action IDs
+ var new_list = widget.options.settings.list || [];
+ var changed = false;
+ for(var i = 0; i < new_list.length; i++)
+ {
+ // Avoid duplicates
+ for(var j = 0; j < source.length; j++)
{
- var explode = source[i].id.split('::');
- new_list.push({app: explode[0],id: explode[1], link_id: explode.join(':')});
- changed = true;
- }
- if(changed)
- {
- widget._process_edit(et2_dialog.OK_BUTTON,{
- list: new_list || {}
- });
- }
- // Filemanager support - links need app = 'file' and type set
- for(var i = 0; i < new_list.length; i++)
- {
- if(new_list[i]['app'] == 'filemanager')
+ if(!source[j].id || new_list[i].app+"::"+new_list[i].id == source[j].id)
{
- new_list[i]['app'] = 'file';
- new_list[i]['path'] = new_list[i]['title'] = new_list[i]['icon'] = new_list[i]['id'];
+ // Duplicate - skip it
+ source.splice(j,1);
}
}
-
- widget.getWidgetById('list').set_value(new_list);
-
}
- },
+ for(var i = 0; i < source.length; i++)
+ {
+ var explode = source[i].id.split('::');
+ new_list.push({app: explode[0],id: explode[1], link_id: explode.join(':')});
+ changed = true;
+ }
+ if(changed)
+ {
+ widget._process_edit(et2_dialog.OK_BUTTON,{
+ list: new_list || {}
+ });
+ }
+ // Filemanager support - links need app = 'file' and type set
+ for(var i = 0; i < new_list.length; i++)
+ {
+ if(new_list[i]['app'] == 'filemanager')
+ {
+ new_list[i]['app'] = 'file';
+ new_list[i]['path'] = new_list[i]['title'] = new_list[i]['icon'] = new_list[i]['id'];
+ }
+ }
- /**
- * Remove a link from the list
- */
- link_change: function(list, link_id, row) {
- // Quick response client side
- row.slideUp(row.remove);
+ widget.getWidgetById('list').set_value(new_list);
- // Actual removal
- var portlet = list._parent._parent;
- portlet.options.settings.list.splice(row.index(), 1);
- portlet._process_edit(et2_dialog.OK_BUTTON,{list: portlet.options.settings.list || {}});
}
},
+ /**
+ * Remove a link from the list
+ */
+ link_change: function(list, link_id, row) {
+ // Quick response client side
+ row.slideUp(row.remove);
+
+ // Actual removal
+ var portlet = list._parent._parent;
+ portlet.options.settings.list.splice(row.index(), 1);
+ portlet._process_edit(et2_dialog.OK_BUTTON,{list: portlet.options.settings.list || {}});
+ },
+
/**
* Functions for the note portlet
*/
@@ -543,5 +547,33 @@ app.classes.home = AppJS.extend(
id: id,
height: window_height - 70
}),'home_'+id, window_width+'x'+window_height,'home');
+ },
+
+ /**
+ * Favorites / nextmatch
+ */
+ /**
+ * Toggle the nextmatch header shown / hidden
+ *
+ * @param {Event} event
+ * @param {et2_button} widget
+ */
+ nextmatch_toggle_header: function(event, widget) {
+ widget.set_image(widget.options.image == 'arrow_down' ? 'arrow_left' : 'arrow_down');
+ // We operate on the DOM here, nm should be unaware of our fiddling
+ var nm = widget.getParent().getWidgetById('nm');
+ if(!nm) return;
+ var header = nm.header;
+ var header_height = header.div.innerHeight();
+
+ // Hide header
+ nm.div.toggleClass('header_hidden');
+
+ header_height -= header.div.height();
+
+ // Grow row space - I have no idea why it needs to be 25 pixels instead of header_height
+ var scroll_height = $j('.egwGridView_scrollarea',nm.getDOMNode()).height();
+ $j('.egwGridView_scrollarea',nm.getDOMNode()).height(scroll_height + (header_height > 0 ? 25 : -25));
+ nm.resize();
}
});
diff --git a/home/templates/default/app.css b/home/templates/default/app.css
index ddc71ae928..8a83e7be2a 100644
--- a/home/templates/default/app.css
+++ b/home/templates/default/app.css
@@ -2,6 +2,9 @@
* Home CSS
*/
+/**
+ * Basic layout and structural CSS
+ */
#home-index_home-index {
height:100%;
}
@@ -20,8 +23,8 @@
cursor: pointer;
}
-.et2_portlet.ui-widget-content {
- overflow: hidden;
+.et2_portlet.ui-widget-content > div {
+
}
.et2_portlet.ui-widget-content > div:last-of-type {
/* Allow space for header, as the whole portlet is sized by auto-generated css */
@@ -29,13 +32,11 @@
bottom: 0px;
top: 20px;
width: 100%;
+ overflow: hidden;
}
.et2_portlet .et2_container {
height: 100%;
}
-.et2_portlet.ui-widget-content > div:last-of-type > div {
- background: linear-gradient(to bottom, rgba(255,255,255,.9) 10%,rgba(255,255,255,.75) 90%) /* W3C */
-}
/* Gridster */
#portlets {
@@ -54,3 +55,65 @@
border: 1px solid silver;
position: absolute;
}
+
+/**
+ * Portlet styling (cosmetic)
+ */
+.et2_portlet.ui-widget-content > div:last-of-type > div {
+ background: linear-gradient(to bottom, rgba(255,255,255,.9) 10%,rgba(255,255,255,.75) 90%) /* W3C */
+}
+
+/* Single entry */
+.et2_portlet.home_link_portlet > .ui-widget-header {
+ display: none;
+ position: relative;
+ top: -20px;
+}
+.et2_portlet.home_link_portlet:hover > .ui-widget-header {
+ display: block;
+ z-index: 90;
+}
+.et2_portlet.home_link_portlet.ui-widget-content > div:last-of-type {
+ top: 0px
+}
+.et2_portlet.home_link_portlet > div:last-of-type > div {
+ padding: 10px;
+}
+/* Thumbnail / icon */
+.et2_portlet.home_link_portlet > div:last-of-type img:first-of-type {
+ float: left;
+ margin-right: 8px;
+ margin-bottom: 8px;
+ width: 32px;
+ height: 32px;
+}
+
+/* Favorite / nextmatch */
+.et2_portlet .et2_container > div > .et2_button {
+ float: left;
+ margin-bottom: -16px;
+ min-height: 16px;
+ min-width: 16px;
+}
+.et2_portlet .et2_nextmatch.header_hidden .nextmatch_header {
+ min-height: 6px;
+}
+.et2_portlet .et2_nextmatch.header_hidden .nextmatch_header .ui-helper-reset{
+ height: 0px;
+ padding: 0px;
+}
+.et2_portlet .et2_nextmatch.header_hidden .nextmatch_header div.nextmatch_header_row .header_count {
+ top: -11px;
+ height: 14px;
+}
+.et2_portlet .et2_nextmatch.header_hidden .nextmatch_header div.nextmatch_header_row .header_count span {
+ top: 0px;
+}
+.et2_portlet .et2_nextmatch.header_hidden .nextmatch_header div.nextmatch_header_row div.search,
+.et2_portlet .et2_nextmatch.header_hidden .nextmatch_header div.nextmatch_header_row label,
+.et2_portlet .et2_nextmatch.header_hidden .nextmatch_header div.nextmatch_header_row select {
+ display:none;
+}
+.et2_portlet .et2_nextmatch.header_hidden .egwGridView_outer:first-of-type {
+ position: absolute;
+}
\ No newline at end of file
diff --git a/home/templates/default/favorite.xet b/home/templates/default/favorite.xet
index 54a374b7b4..4db206f1f2 100644
--- a/home/templates/default/favorite.xet
+++ b/home/templates/default/favorite.xet
@@ -2,6 +2,7 @@
-
+
+
diff --git a/home/templates/default/index.xet b/home/templates/default/index.xet
index fe9fef2c6e..11d4e97a5d 100644
--- a/home/templates/default/index.xet
+++ b/home/templates/default/index.xet
@@ -13,7 +13,7 @@
-
+
diff --git a/home/templates/default/link.xet b/home/templates/default/link.xet
index 3c93690296..627ab98eea 100644
--- a/home/templates/default/link.xet
+++ b/home/templates/default/link.xet
@@ -1,6 +1,7 @@
+
\ No newline at end of file
diff --git a/home/templates/default/list.xet b/home/templates/default/list.xet
index 158e0bc0f3..b22a179472 100644
--- a/home/templates/default/list.xet
+++ b/home/templates/default/list.xet
@@ -2,6 +2,6 @@
-
+