From aa6c36ba3fe561d803491d87c1325daa0113defd Mon Sep 17 00:00:00 2001 From: Nathan Gray Date: Tue, 30 Sep 2014 21:37:45 +0000 Subject: [PATCH] Work on app-entry widgets (contact-value) --- etemplate/inc/class.contact_widget.inc.php | 30 +++-- .../inc/class.etemplate_widget_entry.inc.php | 111 ++++++++++++++++++ etemplate/js/et2_core_widget.js | 6 +- etemplate/js/et2_widget_entry.js | 103 ++++++++++++++++ etemplate/js/etemplate2.js | 1 + 5 files changed, 238 insertions(+), 13 deletions(-) create mode 100644 etemplate/inc/class.etemplate_widget_entry.inc.php create mode 100644 etemplate/js/et2_widget_entry.js diff --git a/etemplate/inc/class.contact_widget.inc.php b/etemplate/inc/class.contact_widget.inc.php index ce19d7eddc..293beed990 100644 --- a/etemplate/inc/class.contact_widget.inc.php +++ b/etemplate/inc/class.contact_widget.inc.php @@ -15,7 +15,7 @@ * * This widget can be used to fetch fields of a contact specified by contact-id */ -class contact_widget extends etemplate_widget_transformer +class contact_widget extends etemplate_widget_entry { /** * exported methods of this class @@ -89,19 +89,12 @@ class contact_widget extends etemplate_widget_transformer 'options' => 'None', ), 'contact-template' => array( - 'value' => array('__callback__' => 'get_contact'), 'type' => 'template', 'options' => '', - 'label' => array( - '' => array('id' => '@value[@id]'), - '__default__' => array('id' => '@label@value[@id]'), // non-empty label prefixes value - ), + 'template' => array('__callback__' => 'parse_template'), ), '__default__' => array( - 'value' => array('__callback__' => 'get_contact'), - 'id' => '@id[@options]', 'options' => array( - '' => array('id' => '@value[@id]'), 'bday' => array('type' => 'date', 'options' => 'Y-m-d'), 'owner' => array('type' => 'select-account', 'options' => ''), 'modifier' => array('type' => 'select-account', 'options' => ''), @@ -111,7 +104,6 @@ class contact_widget extends etemplate_widget_transformer 'cat_id' => array('type' => 'select-cat', 'options' => ''), '__default__' => array('type' => 'label', 'options' => ''), ), - 'readonly' => true, 'no_lang' => 1, ), ), @@ -131,6 +123,16 @@ class contact_widget extends etemplate_widget_transformer $this->contacts = $GLOBALS['egw']->contacts; } + /** + * Legacy support for putting the template name in 'label' param + * @param string $label + * @param array $attrs + */ + public function parse_template($template, &$attrs) + { + return sprintf($template ? $template : $attrs['label'], $attrs['value']); + } + /** * Get all contact-fields * @@ -148,6 +150,10 @@ class contact_widget extends etemplate_widget_transformer return $options; } + public function get_entry($value, array $attrs) + { + return $this->get_contact($value, $attrs); + } /** * Get contact data, if $value not already contains them * @@ -157,8 +163,10 @@ class contact_widget extends etemplate_widget_transformer */ public function get_contact($value, array $attrs) { - if (is_array($value)) return $value; + $field = $attrs['field'] ? $attrs['field'] : ''; + if (is_array($value) && !(array_key_exists('app',$value) && array_key_exists('id', $value))) return $value; + if(is_array($value) && array_key_exists('app', $value) && array_key_exists('id', $value)) $value = $value['id']; switch($attrs['type']) { case 'contact-account': diff --git a/etemplate/inc/class.etemplate_widget_entry.inc.php b/etemplate/inc/class.etemplate_widget_entry.inc.php new file mode 100644 index 0000000000..31eb341470 --- /dev/null +++ b/etemplate/inc/class.etemplate_widget_entry.inc.php @@ -0,0 +1,111 @@ + + * @version $Id: class.contact_widget.inc.php 46844 2014-05-07 09:00:59Z ralfbecker $ + */ + +/** + * eTemplate Extension: Entry widget + * + * This widget can be used to fetch fields of any entry specified by its ID. + * The entry is loaded once and shared amoung widget that need it. + */ +abstract class etemplate_widget_entry extends etemplate_widget_transformer +{ + + /** + * @var $prefix string Prefix for the ID to avoid conflicts between the + * record and the original value + */ + const ID_PREFIX = '~'; + + /** + * (Array of) comma-separated list of legacy options to automatically replace when parsing with set_attrs + * + * @var string|array + */ + protected $legacy_options = 'field'; + + /** + * Array with a transformation description, based on attributes to modify. + * + * @see etemplate_widget_transformer::$transformation + * + * @var array + */ + protected static $transformation = array( + 'type' => array( + 'entry-fields' => array( // List of fields + 'sel_options' => array('__callback__' => 'get_field_list'), + 'type' => 'select', + 'no_lang' => true, + 'options' => 'None', + ), + ) + ); + + /** + * Load entry + * + * @param string $cname + */ + public function beforeSendToClient($cname) + { + $attrs = $this->attrs; + + $attrs['type'] = $this->type; + $attrs['id'] = $this->id; + + $form_name = self::form_name($cname, $this->id); + $data_id = $this->value ? self::form_name($cname, $this->value) : self::form_name($cname, self::ID_PREFIX . $this->id); + + // Find out which record to load + $value = self::get_array(self::$request->content, $form_name, false, true); + + // Get the record itself + $data =& self::get_array(self::$request->content, $data_id, true, false); + if(!$data) + { + $data =& static::get_entry($value, $attrs); + } + $this->id = self::ID_PREFIX . $this->id . "[{$attrs['field']}]"; + + $old_type = self::getElementAttribute($this->id, 'type'); + + parent::beforeSendToClient($cname); + + // Check for conflict - more than one with same id/field and different type + if($old_type && $old_type != $this->type) + { + self::set_validation_error($this->id, lang('%1, duplicate ID', $this)); + } + } + + /** + * Get entry data + * + * @param int|string|array $value + * @param array $attrs + * @return array + */ + abstract function get_entry($value, array $attrs); + + /** + * Get a list of fields available for display + * + * @return Array + */ + protected static function get_field_list() + { + return array(); + } +} \ No newline at end of file diff --git a/etemplate/js/et2_core_widget.js b/etemplate/js/et2_core_widget.js index cc1fdf5fcf..9473d6d05d 100644 --- a/etemplate/js/et2_core_widget.js +++ b/etemplate/js/et2_core_widget.js @@ -666,7 +666,8 @@ var et2_widget = ClassWithAttributes.extend( } if(entry && entry.type) { - _nodeName = attributes["type"] = entry.type; + _nodeName = entry.type; + _node.setAttribute("type", entry.type); } entry = null; } @@ -675,7 +676,8 @@ var et2_widget = ClassWithAttributes.extend( // we need to expand it now as it defines the constructor and by that attributes parsed via parseXMLAttrs! if (_nodeName.charAt(0) == '@' || _nodeName.indexOf('$') >= 0) { - _nodeName = attributes["type"] = this.getArrayMgr('content').expandName(_nodeName); + _nodeName = this.getArrayMgr('content').expandName(_nodeName); + _node.setAttribute("type", _nodeName); } // Get the constructor - if the widget is readonly, use the special "_ro" diff --git a/etemplate/js/et2_widget_entry.js b/etemplate/js/et2_widget_entry.js new file mode 100644 index 0000000000..873dd4ca10 --- /dev/null +++ b/etemplate/js/et2_widget_entry.js @@ -0,0 +1,103 @@ +/* + * Egroupware etemplate2 JS Entry widget + * @license http://opensource.org/licenses/gpl-license.php GPL - GNU General Public License + * @package etemplate + * @subpackage api + * @link http://www.egroupware.org + * @author Nathan Gray + * @version $Id$ + */ + + +"use strict"; + +/*egw:uses + et2_core_valueWidget; +*/ + +/** + * A widget to display a value from an entry + * + * Since we have etemplate_widget_transformer, this client side widget exists + * mostly to resolve the problem where the ID for the entry widget is the same + * as the widget where you actually set the value, which prevents transformer + * from working. + * + * Server side will find the associated entry, and load it into ~ to + * avoid overwriting the widget with id="entry_id". This widget will reverse + * that, and the modifications from transformer will be applied. + * + * @augments et2_valueWidget + */ +var et2_entry = et2_valueWidget.extend( +{ + attributes: { + field: { + 'name': 'Fields', + 'description': 'Which entry field to display', + 'type': 'string' + }, + value: { + type: 'any' + }, + readonly: { + default: true + } + }, + + legacyOptions: ["field"], + + // Doesn't really need a namespace, but this simplifies the sub-widgets + createNamespace: true, + + prefix: '~', + + /** + * Constructor + * + * @memberOf et2_customfields_list + */ + init: function(parent, attrs) { + // Often the ID conflicts, so check prefix + if(attrs.id && attrs.id.indexOf(this.prefix) < 0 && typeof attrs.value == 'undefined') + { + attrs.id = this.prefix + attrs.id; + } + + this._super.apply(this, arguments); + + this.widget = null; + this.setDOMNode(document.createElement('span')); + }, + + loadFromXML: function(_node) { + // Load the nodes as usual + this._super.apply(this, arguments); + + // Do the magic + this.loadField(); + }, + + /** + * Initialize widget for entry field + */ + loadField: function() { + // Create widget of correct type + var modifications = this.getArrayMgr("modifications"); + if(modifications && this.options.field) { + var entry = modifications.getEntry(this.options.field); + if(entry == null) + { + entry = {type: 'label'}; + } + } + var attrs = { + id: this.options.field, + type: entry.type, + readonly: this.options.readonly + }; + var widget = et2_createWidget(attrs.type, attrs, this); + } +}); + +et2_register_widget(et2_entry, ["entry", 'contact-value', 'contact-account', 'contact-template']); \ No newline at end of file diff --git a/etemplate/js/etemplate2.js b/etemplate/js/etemplate2.js index 9c5afc2ec0..5507f3bd30 100644 --- a/etemplate/js/etemplate2.js +++ b/etemplate/js/etemplate2.js @@ -21,6 +21,7 @@ et2_widget_button; et2_widget_color; et2_widget_description; + et2_widget_entry; et2_widget_textbox; et2_widget_number; et2_widget_url;