egroupware/api/js/etemplate/et2_widget_portlet.js

346 lines
13 KiB
JavaScript
Raw Normal View History

/**
* 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 https://www.egroupware.org
* @author Nathan Gray
*/
/*egw:uses
2016-06-06 17:38:20 +02:00
/vendor/bower-asset/jquery/dist/jquery.js;
et2_core_baseWidget;
*/
import { et2_createWidget, et2_register_widget } from "./et2_core_widget";
import { et2_valueWidget } from "./et2_core_valueWidget";
import { ClassWithAttributes } from "./et2_core_inheritance";
import { et2_action_object_impl } from "./et2_core_DOMWidget";
import { egw } from "../jsapi/egw_global";
import { et2_no_init } from "./et2_core_common";
import { et2_IResizeable } from "./et2_core_interfaces";
import { et2_dialog } from "./et2_widget_dialog";
/**
* 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
*/
export class et2_portlet extends et2_valueWidget {
2020-02-13 15:27:27 +01:00
/**
* Constructor
*
* @memberOf et2_portlet
*/
constructor(_parent, _attrs, _child) {
2020-02-13 15:27:27 +01:00
// Call the inherited constructor
super(_parent, _attrs, ClassWithAttributes.extendAttributes(et2_portlet._attributes, _child || {}));
this.GRID = 55;
2020-02-13 15:27:27 +01:00
/**
* 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.
*/
this.default_actions = {
2020-02-13 15:27:27 +01:00
edit_settings: {
icon: "edit",
caption: "Configure",
"default": true,
hideOnDisabled: true,
group: "portlet"
},
remove_portlet: {
icon: "delete",
caption: "Remove",
group: "portlet"
}
};
let self = this;
2020-02-13 15:27:27 +01:00
// Create DOM nodes
this.div = jQuery(document.createElement("div"))
.addClass(this.options.class)
2020-02-13 15:27:27 +01:00
.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)
2020-02-13 15:27:27 +01:00
.resizable({
autoHide: true,
grid: this.GRID,
2020-02-13 15:27:27 +01:00
//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().jsonq("home.home_ui.ajax_set_properties", [self.id, {}, {
width: self.options.width,
height: self.options.height
}], null, self);
// Tell children
self.iterateOver(function (widget) { widget.resize(); }, null, et2_IResizeable);
}
});
this.header = jQuery(document.createElement("div"))
.attr('id', this.getInstanceManager().uniqueId + '_' + this.id.replace(/\./g, '-') + '_header')
2020-02-13 15:27:27 +01:00
.addClass("ui-widget-header ui-corner-all")
.appendTo(this.div)
.html(this.options.title);
this.content = jQuery(document.createElement("div"))
.attr('id', this.getInstanceManager().uniqueId + '_' + this.id.replace(/\./g, '-') + '_content')
.appendTo(this.div);
this.setDOMNode(this.div[0]);
2020-02-13 15:27:27 +01:00
}
destroy() {
for (let i = 0; i < this._children.length; i++) {
2020-02-13 15:27:27 +01:00
// Check for child is a different template and clear it,
// since it won't be cleared by destroy()
if (this._children[i].getInstanceManager() != this.getInstanceManager()) {
this._children[i].getInstanceManager().clear();
}
}
super.destroy();
}
doLoadingFinished() {
2020-02-13 15:27:27 +01:00
this.set_color(this.options.color);
return true;
}
2020-02-13 15:27:27 +01:00
/**
* If anyone asks, return the content node, so content goes inside
*/
getDOMNode(_sender) {
2020-02-13 15:27:27 +01:00
if (typeof _sender != 'undefined' && _sender != this) {
return this.content[0];
}
return super.getDOMNode(_sender);
}
2020-02-13 15:27:27 +01:00
/**
* Overriden from parent to add in default actions
*/
set_actions(actions) {
2020-02-13 15:27:27 +01:00
// Set targets for actions
let defaults = {};
for (let action_name in this.default_actions) {
2020-02-13 15:27:27 +01:00
defaults[action_name] = this.default_actions[action_name];
// Translate caption here, as translations aren't available earlier
defaults[action_name].caption = this.egw().lang(this.default_actions[action_name].caption);
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);
super.set_actions(this.options.actions);
}
2020-02-13 15:27:27 +01:00
/**
* Override _link_actions to remove edit action, if there is no settings
*
* @param actions
*/
_link_actions(actions) {
2020-02-13 15:27:27 +01:00
// Get the top level element
let objectManager = egw_getAppObjectManager(true);
let widget_object = objectManager.getObjectById(this.id);
2020-02-13 15:27:27 +01:00
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).getAOI(), this._actionManager || objectManager.manager.getActionById(this.id) || objectManager.manager));
2020-02-13 15:27:27 +01:00
}
// 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
let action_links = [];
for (let i in actions) {
let id = typeof actions[i].id != 'undefined' ? actions[i].id : i;
let action = {
2020-02-13 15:27:27 +01:00
actionId: id,
enabled: true
};
// If there are no settings, there can be no customization, so remove the edit action
if (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);
}
2020-02-13 15:27:27 +01:00
/**
* Create & show a dialog for customizing this portlet
*
* Properties for customization are sent in the 'settings' attribute
*/
edit_settings() {
let dialog = et2_createWidget("dialog", {
2020-02-13 15:27:27 +01:00
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(button_id, value) {
2020-02-13 15:27:27 +01:00
if (button_id != et2_dialog.OK_BUTTON)
return;
// Save settings - server might reply with new content if the portlet needs an update,
// but ideally it doesn't
this.div.addClass("loading");
// Pass updated settings, unless we're removing
let settings = (typeof value == 'string') ? {} : this.options.settings || {};
2020-02-13 15:27:27 +01:00
this.egw().jsonq("home.home_ui.ajax_set_properties", [this.id, settings, value, this.settings ? this.settings.group : false], function (data) {
// This section not for us
if (!data || typeof data.attributes == 'undefined')
return false;
this.div.removeClass("loading");
this.set_value(data.content);
for (let key in data.attributes) {
2020-02-13 15:27:27 +01:00
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();
}
// Only resize once, and only if needed
if (data.attributes.width || data.attributes.height) {
// Tell children
try {
this.iterateOver(function (widget) { widget.resize(); }, null, et2_IResizeable);
}
catch (e) {
// Something went wrong, but do not stop
egw.debug('warn', e, this);
}
}
}, this);
// Extend, not replace, because settings has types while value has just value
if (typeof value == 'object') {
jQuery.extend(this.options.settings, value);
}
}
2020-02-13 15:27:27 +01:00
/**
* Remove this portlet from the home page
*/
remove_portlet() {
let self = this;
2020-02-13 15:27:27 +01:00
et2_dialog.show_dialog(function (button_id) {
if (button_id != et2_dialog.OK_BUTTON)
return;
self._process_edit(button_id, '~remove~');
self.getParent().removeChild(self);
self.destroy();
}, this.egw().lang("Remove"), this.options.title, {}, et2_dialog.BUTTONS_OK_CANCEL, et2_dialog.QUESTION_MESSAGE);
}
2020-02-13 15:27:27 +01:00
/**
* Set the HTML content of the portlet
*
* @param value String HTML fragment
*/
set_value(value) {
2020-02-13 15:27:27 +01:00
this.content.html(value);
}
2020-02-13 15:27:27 +01:00
/**
* Set the content of the header
*
* @param value String HTML fragment
*/
set_title(value) {
2020-02-13 15:27:27 +01:00
this.header.contents()
.filter(function () {
return this.nodeType === 3;
})
.remove();
this.options.title = value;
this.header.append(value);
}
2020-02-13 15:27:27 +01:00
/**
* Let this portlet stand out a little by allowing a custom color
*/
set_color(color) {
2020-02-13 15:27:27 +01:00
this.options.color = color;
this.header.css("backgroundColor", color);
this.header.css('color', jQuery.Color(this.header.css("backgroundColor")).lightness() > 0.5 ? 'black' : 'white');
this.content.css("backgroundColor", color);
}
2020-02-13 15:27:27 +01:00
/**
* Set the number of grid cells this widget spans
*
* @param value int Number of horizontal grid cells
*/
set_width(value) {
2020-02-13 15:27:27 +01:00
this.options.width = value;
this.div.attr("data-sizex", value);
// Clear what's there from jQuery, we get width from CSS according to sizex
this.div.css('width', '');
}
2020-02-13 15:27:27 +01:00
/**
* Set the number of vertical grid cells this widget spans
*
* @param value int Number of vertical grid cells
*/
set_height(value) {
2020-02-13 15:27:27 +01:00
this.options.height = value;
this.div.attr("data-sizey", value);
// Clear what's there from jQuery, we get width from CSS according to sizey
this.div.css('height', '');
}
}
et2_portlet._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": egw.webserverUrl + "/home/templates/default/edit.xet"
},
"color": {
"name": "Color",
"description": "Set the portlet color",
"type": "string",
"default": ''
},
"settings": {
"name": "Customization settings",
"description": "Array of customization settings, similar in structure to preference settings",
"type": "any",
"default": et2_no_init
},
"actions": {
default: {}
},
"width": { "default": 2, "ignore": true },
"height": { "default": 1, "type": "integer" },
"rows": { "ignore": true, default: et2_no_init },
"cols": { "ignore": true, default: et2_no_init },
"resize_ratio": { "ignore": true, default: et2_no_init },
"row": {
"name": "Row",
"description": "Home page location (row) - handled by home app",
"default": 1
},
"col": {
"name": "Column",
"description": "Home page location(column) - handled by home app",
"default": 1
}
};
et2_register_widget(et2_portlet, ["portlet"]);
2020-02-13 15:27:27 +01:00
//# sourceMappingURL=et2_widget_portlet.js.map