diff --git a/api/js/etemplate/Et2Portlet/Et2Portlet.ts b/api/js/etemplate/Et2Portlet/Et2Portlet.ts
new file mode 100644
index 0000000000..1e8862da18
--- /dev/null
+++ b/api/js/etemplate/Et2Portlet/Et2Portlet.ts
@@ -0,0 +1,452 @@
+/**
+ * EGroupware eTemplate2 - Portlet base
+ *
+ * @license http://opensource.org/licenses/gpl-license.php GPL - GNU General Public License
+ * @package etemplate
+ * @subpackage api
+ * @link https://www.egroupware.org
+ * @author Nathan Gray
+ * @copyright 2022 Nathan Gray
+ */
+
+
+import {Et2Widget} from "../Et2Widget/Et2Widget";
+import {SlCard} from "@shoelace-style/shoelace";
+import interact from "@interactjs/interactjs";
+import {egw} from "../../jsapi/egw_global";
+import {classMap, css, html} from "@lion/core";
+import {HasSlotController} from "@shoelace-style/shoelace/dist/internal/slot";
+import shoelace from "../Styles/shoelace";
+import {Et2Dialog} from "../Et2Dialog/Et2Dialog";
+import {et2_IResizeable} from "../et2_core_interfaces";
+import {HomeApp} from "../../../../home/js/app";
+
+/**
+ * Participate in Home
+ */
+
+export class Et2Portlet extends (Et2Widget(SlCard))
+{
+ static get properties()
+ {
+ return {
+ ...super.properties,
+
+
+ /**
+ * Give a title
+ * Goes in the header at the top with the icons
+ */
+ title: {type: String},
+
+ /**
+ * Custom etemplate used to customize / set up the portlet
+ */
+ editTemplate: {type: String},
+ /**
+ * Set the portlet color
+ */
+ color: {type: String},
+
+ /**
+ * Array of customization settings, similar in structure to preference settings
+ */
+ settings: {type: Object},
+ actions: {type: Object},
+ }
+ }
+
+ static get styles()
+ {
+ return [
+ ...shoelace,
+ ...(super.styles || []),
+ css`
+ .portlet__header {
+ flex: 1 0 auto;
+ display: flex;
+ font-style: inherit;
+ font-variant: inherit;
+ font-weight: inherit;
+ font-stretch: inherit;
+ font-family: inherit;
+ font-size: var(--sl-font-size-medium);
+ line-height: var(--sl-line-height-dense);
+ padding: var(--header-spacing);
+ margin: 0px;
+ }
+
+ .portlet__title {
+ flex: 1 1 auto;
+ font-size: var(--sl-font-size-medium);
+ user-select: none;
+ }
+
+ .portlet__header et2-button-icon {
+ display: none;
+ order: 99;
+ margin-left: auto;
+ }
+
+ .portlet__header:hover et2-button-icon {
+ display: initial;
+ }
+
+ .card {
+ width: 100%;
+ height: 100%
+ }
+
+ .card__body {
+ /* display block to prevent overflow from our size */
+ display: block;
+ overflow: hidden;
+ flex: 1 1 auto;
+ }
+
+
+ ::slotted(div) {
+ }
+ `
+ ]
+ }
+
+ protected readonly hasSlotController = new HasSlotController(this, 'footer', 'header', 'image');
+
+ /**
+ * 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.
+ */
+ protected static default_actions : any = {
+ edit_settings: {
+ icon: "edit",
+ caption: "Configure",
+ "default": true,
+ hideOnDisabled: true,
+ group: "portlet"
+ },
+ remove_portlet: {
+ icon: "delete",
+ caption: "Remove",
+ group: "portlet"
+ }
+ };
+
+ constructor()
+ {
+ super();
+ this.editTemplate = egw.webserverUrl + "/home/templates/default/edit.xet"
+ this.actions = {};
+
+ this._onMoveResize = this._onMoveResize.bind(this);
+ this._onMoveResizeEnd = this._onMoveResizeEnd.bind(this);
+ }
+
+ connectedCallback()
+ {
+ super.connectedCallback();
+
+ Promise.all([/* any others here...*/ this.updateComplete])
+ .then(() => this._setupMoveResize());
+ }
+
+ /**
+ * Overriden from parent to add in default actions
+ */
+ set_actions(actions)
+ {
+ // Set targets for actions
+ let defaults : any = {};
+ for(let action_name in Et2Portlet.default_actions)
+ {
+ defaults[action_name] = Et2Portlet.default_actions[action_name];
+ // Translate caption here, as translations aren't available earlier
+ defaults[action_name].caption = this.egw().lang(Et2Portlet.default_actions[action_name].caption);
+ if(typeof this[action_name] == "function")
+ {
+ defaults[action_name].onExecute = this[action_name].bind(this);
+ }
+ }
+
+ // Add in defaults, but let provided actions override them
+ this.actions = jQuery.extend(true, {}, defaults, actions);
+ }
+
+ /**
+ * Set up moving & resizing
+ */
+ _setupMoveResize()
+ {
+ // Quick calculation of min size - dialog is made up of header, content & buttons
+ let minHeight = 0;
+ for(let e of this.children)
+ {
+ minHeight += e.getBoundingClientRect().height + parseFloat(getComputedStyle(e).marginTop) + parseFloat(getComputedStyle(e).marginBottom)
+ }
+
+ // Get parent's dimensions
+ let style = getComputedStyle(this.parentElement);
+ let parentDimensions = {
+ width: parseInt(style.gridAutoColumns) + parseInt(style.gap) || HomeApp.GRID,
+ height: parseInt(style.gridAutoRows) + parseInt(style.gap) || HomeApp.GRID
+ };
+
+ let gridTarget = interact.snappers.grid({
+ x: parentDimensions.width,
+ y: parentDimensions.height
+ });
+
+ interact(this)
+ .resizable({
+ edges: {bottom: true, right: true},
+ listeners: {
+ move: this._onMoveResize,
+ end: this._onMoveResizeEnd
+ },
+ modifiers: [
+ // Snap to grid
+ interact.modifiers.snap({
+ targets: [gridTarget],
+ offset: "startCoords",
+ limits: {top: 0, left: 0}
+ }),
+
+ // keep the edges inside the parent
+ interact.modifiers.restrictEdges({
+ outer: 'parent'
+ })
+ ]
+ })
+ .draggable({
+ allowFrom: ".portlet__header",
+ autoScroll: true,
+ listeners: {
+ move: this._onMoveResize,
+ end: this._onMoveResizeEnd
+ },
+ modifiers: [
+ // Restrict interferes with grid making it act strangely
+ //interact.modifiers.restrict({
+ // restriction: 'parent'
+ //}),
+ // Snap to grid
+ interact.modifiers.snap({
+ targets: [gridTarget],
+ offset: "startCoords",
+ limits: {top: 0, left: 0}
+ })
+ ]
+ });
+ }
+
+ /**
+ * Handle moving and resizing
+ *
+ * @param event
+ */
+ _onMoveResize(event : InteractEvent)
+ {
+ let target = event.target
+ let x = (parseFloat(target.getAttribute('data-x')) || 0) + (event.deltaRect ? 0 : event.dx);
+ let y = (parseFloat(target.getAttribute('data-y')) || 0) + (event.deltaRect ? 0 : event.dy);
+
+ // update the element's style
+ // Size
+ target.style.width = event.rect.width + 'px'
+ target.style.height = event.rect.height + 'px'
+
+ // Position
+ target.style.transform = 'translate(' + x + 'px,' + y + 'px)';
+
+ target.setAttribute('data-x', x);
+ target.setAttribute('data-y', y);
+ }
+
+ /**
+ * Move or resize has completed. Now into parent grid and update settings.
+ *
+ * @param {InteractEvent} event
+ */
+ _onMoveResizeEnd(event : InteractEvent)
+ {
+
+ // Get parent's dimensions
+ let style = getComputedStyle(this.parentElement);
+ let parentDimensions = {
+ x: parseInt(style.gridAutoColumns) || 1,
+ y: parseInt(style.gridAutoRows) || 1
+ }
+ let target = event.target
+ let dx = Math.round((parseInt(target.getAttribute('data-x')) || 0) / parentDimensions.x);
+ let dy = Math.round((parseInt(target.getAttribute('data-y')) || 0) / parentDimensions.y);
+ let dwidth = Math.round((event.deltaRect?.width || 1) / parentDimensions.x);
+ let dheight = Math.round((event.deltaRect?.height || 1) / parentDimensions.y);
+ let [o_x, o_width] = this.style.gridColumn.split(" / span");
+ let [o_y, o_height] = this.style.gridRow.split(" / span");
+
+ // Clear temp stuff from moving
+ target.style.transform = "";
+ target.style.width = "";
+ target.style.height = "";
+ target.removeAttribute('data-x');
+ target.removeAttribute('data-y');
+
+ let col = Math.max(1, (dx + (parseInt(o_x) || 0)));
+ let row = Math.max(1, (dy + (parseInt(o_y) || 0)));
+ let width = (dwidth + parseInt(o_width)) || 1;
+ let height = (dheight + parseInt(o_height)) || 1;
+
+ // Set grid position
+ target.style.gridArea = row + " / " +
+ col + "/ span " +
+ height + " / span " +
+ width;
+
+ // Update position settings
+ this.update_settings({row: row, col: col, width: width, height: height});
+ }
+
+
+ imageTemplate()
+ {
+ return '';
+ }
+
+ headerTemplate()
+ {
+ return html`
+ `;
+ }
+
+ footerTemplate()
+ {
+ return '';
+ }
+
+
+ /**
+ * Create & show a dialog for customizing this portlet
+ *
+ * Properties for customization are sent in the 'settings' attribute
+ */
+ edit_settings()
+ {
+ let dialog = new Et2Dialog(this.egw());
+ dialog.transformAttributes({
+ callback: this._process_edit.bind(this),
+ template: this.editTemplate,
+ value: {
+ content: this.settings
+ },
+ buttons: Et2Dialog.BUTTONS_OK_CANCEL
+ });
+ // Set separately to avoid translation
+ dialog.title = this.egw().lang("Edit") + " " + (this.title || '');
+ this.appendChild(dialog);
+ }
+
+ _process_edit(button_id, value)
+ {
+ if(button_id != Et2Dialog.OK_BUTTON)
+ {
+ return;
+ }
+
+ // Pass updated settings, unless we're removing
+ this.update_settings((typeof value == 'string') ? {} : this.settings || {});
+
+ // Extend, not replace, because settings has types while value has just value
+ if(typeof value == 'object')
+ {
+ this.settings = {...this.settings, value};
+ }
+ }
+
+ protected update_settings(settings)
+ {
+ // Save settings - server might reply with new content if the portlet needs an update,
+ // but ideally it doesn't
+ this.classList.add("loading");
+
+ this.egw().jsonq("home.home_ui.ajax_set_properties", [this.id, [], settings, this.settings ? this.settings.group : false],
+ function(data)
+ {
+ // This section not for us
+ if(!data || typeof data.attributes == 'undefined')
+ {
+ return false;
+ }
+
+ this.classList.remove("loading");
+
+ this.transformAttributes(data.attributes);
+
+ // 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)
+ {
+ this.style.columnSpan = data.attributes.width;
+ this.style.rowSpan = data.attributes.height;
+
+ // Tell children
+ try
+ {
+ this.iterateOver(function(widget)
+ {
+ if(typeof widget.resize === 'function')
+ {
+ widget.resize();
+ }
+ }, null, et2_IResizeable);
+ }
+ catch(e)
+ {
+ // Something went wrong, but do not stop
+ this.egw().debug('warn', e, this);
+ }
+ }
+ },
+ this);
+
+ }
+
+ render()
+ {
+ return html`
+
+
+ ${this.imageTemplate()}
+
+
+
+
+ `;
+ }
+
+}
+
+if(!customElements.get("et2-portlet"))
+{
+ customElements.define("et2-portlet", Et2Portlet);
+}
\ No newline at end of file
diff --git a/calendar/js/calendar_favorite_portlet.js b/calendar/js/calendar_favorite_portlet.js
index 5889890f97..a0c139b181 100644
--- a/calendar/js/calendar_favorite_portlet.js
+++ b/calendar/js/calendar_favorite_portlet.js
@@ -17,7 +17,7 @@
* views, we need some custom handling to detect and handle refreshes.
*
* Note we put the class in home.
- */
+ *
app.classes.home.calendar_favorite_portlet = app.classes.home.home_favorite_portlet.extend({
observer: function(_msg, _app, _id, _type, _msg_type, _targetapp)
@@ -70,4 +70,4 @@ observer: function(_msg, _app, _id, _type, _msg_type, _targetapp)
}
}
}
-});
\ No newline at end of file
+});*/
\ No newline at end of file
diff --git a/calendar/js/et2_widget_timegrid.ts b/calendar/js/et2_widget_timegrid.ts
index d90e24472c..59cb08c56c 100644
--- a/calendar/js/et2_widget_timegrid.ts
+++ b/calendar/js/et2_widget_timegrid.ts
@@ -30,6 +30,7 @@ import {EGW_AI_DRAG_ENTER, EGW_AI_DRAG_OUT} from "../../api/js/egw_action/egw_ac
import {formatDate, formatTime, parseTime} from "../../api/js/etemplate/Et2Date/Et2Date";
import interact from "@interactjs/interactjs/index";
import type {InteractEvent} from "@interactjs/core/InteractEvent";
+import {CalendarApp} from "./app";
/**
* Class which implements the "calendar-timegrid" XET-Tag for displaying a span of days
diff --git a/home/inc/class.home_ui.inc.php b/home/inc/class.home_ui.inc.php
index 724350ee36..fb32e05bf1 100644
--- a/home/inc/class.home_ui.inc.php
+++ b/home/inc/class.home_ui.inc.php
@@ -290,7 +290,7 @@ class home_ui
$appname = $app;
}
}
- Framework::includeJS('', $classname, $appname ? $appname : 'home');
+ //Framework::includeJS('', $classname, $appname ? $appname : 'home');
if($full_exec)
{
diff --git a/home/js/Et2PortletFavorite.ts b/home/js/Et2PortletFavorite.ts
new file mode 100644
index 0000000000..2b91ae5964
--- /dev/null
+++ b/home/js/Et2PortletFavorite.ts
@@ -0,0 +1,10 @@
+import {Et2Portlet} from "../../api/js/etemplate/Et2Portlet/Et2Portlet";
+
+export class Et2PortletFavorite extends Et2Portlet
+{
+}
+
+if(!customElements.get("et2-portlet-favorite"))
+{
+ customElements.define("et2-portlet-favorite", Et2PortletFavorite);
+}
\ No newline at end of file
diff --git a/home/js/app.js b/home/js/app.ts
similarity index 65%
rename from home/js/app.js
rename to home/js/app.ts
index fdcd9fb83b..028c14884c 100644
--- a/home/js/app.js
+++ b/home/js/app.ts
@@ -8,17 +8,12 @@
* @license http://opensource.org/licenses/gpl-license.php GPL - GNU General Public License
*/
-/*egw:uses
- /vendor/bower-asset/jquery/dist/jquery.js;
- /vendor/bower-asset/jquery-ui/jquery-ui.js;
- /vendor/npm-asset/gridster/dist/jquery.gridster.js;
-*/
import {AppJS} from "../../api/js/jsapi/app_base.js";
import {et2_createWidget} from "../../api/js/etemplate/et2_core_widget";
-import {et2_dialog} from "../../api/js/etemplate/et2_widget_dialog";
-import {et2_button} from "../../api/js/etemplate/et2_widget_button";
-// need legacy loading (uses this instead of window): import "../../vendor/npm-asset/gridster/dist/jquery.gridster.js";
-import "../../api/js/jsapi/egw_inheritance.js"; // Class
+import {EgwApp} from "../../api/js/jsapi/egw_app";
+import {etemplate2} from "../../api/js/etemplate/etemplate2";
+import {Et2Portlet} from "../../api/js/etemplate/Et2Portlet/Et2Portlet";
+import {Et2PortletFavorite} from "./Et2PortletFavorite";
/**
* JS for home application
@@ -29,45 +24,40 @@ import "../../api/js/jsapi/egw_inheritance.js"; // Class
* @see http://gridster.net
* @augments AppJS
*/
-app.classes.home = (function(){ "use strict"; return AppJS.extend(
+export class HomeApp extends EgwApp
{
- /**
- * AppJS requires overwriting this with the actual application name
- */
- appname: "home",
/**
* Grid resolution. Must match et2_portlet GRID
*/
- GRID: 50,
+ public static GRID = 50;
/**
* Default size for new portlets
*/
- DEFAULT: {
- WIDTH: 4,
- HEIGHT: 1
- },
+ public static DEFAULT = {
+ WIDTH: 4,
+ HEIGHT: 1
+ };
// List of portlets
- portlets: {},
+ private portlets = {};
+ portlet_container : any;
/**
* Constructor
*
- * @memberOf app.home
*/
- init: function()
+ constructor()
{
// call parent
- this._super.apply(this, arguments);
- },
+ super("home");
+ }
/**
* Destructor
- * @memberOf app.home
*/
- destroy: function()
+ destroy()
{
delete this.et2;
delete this.portlet_container;
@@ -75,15 +65,15 @@ app.classes.home = (function(){ "use strict"; return AppJS.extend(
this.portlets = {};
// call parent
- this._super.apply(this, arguments);
+ super.destroy(this.appname);
// Make sure all other sub-etemplates in portlets are done
- var others = etemplate2.getByApplication(this.appname);
- for(var i = 0; i < others.length; i++)
+ let others = etemplate2.getByApplication(this.appname);
+ for(let i = 0; i < others.length; i++)
{
others[i].clear();
}
- },
+ }
/**
* This function is called when the etemplate2 object is loaded
@@ -93,13 +83,13 @@ app.classes.home = (function(){ "use strict"; return AppJS.extend(
* @param {etemplate2} et2 Newly ready object
* @param {string} Template name
*/
- et2_ready: function(et2, name)
+ et2_ready(et2, name)
{
// Top level
if(name == 'home.index')
{
// call parent
- this._super.apply(this, arguments);
+ super.et2_ready(et2, name);
this.et2.set_id('home.index');
this.et2.set_actions(this.et2.getArrayMgr('modifications').getEntry('home.index')['actions']);
@@ -107,22 +97,24 @@ app.classes.home = (function(){ "use strict"; return AppJS.extend(
this.portlet_container = this.et2.getWidgetById("portlets");
// Set up sorting of portlets
- this._do_ordering();
+ //this._do_ordering();
// Accept drops of favorites, which aren't part of action system
jQuery(this.et2.getDOMNode().parentNode).droppable({
hoverClass: 'drop-hover',
- accept: function(draggable) {
+ accept: function(draggable)
+ {
// Check for direct support for that application
if(draggable[0].dataset && draggable[0].dataset.appname)
{
- return egw_getActionManager('home',false,1).getActionById('drop_'+draggable[0].dataset.appname +'_favorite_portlet') != null;
+ return egw_getActionManager('home', false, 1).getActionById('drop_' + draggable[0].dataset.appname + '_favorite_portlet') != null;
}
return false;
},
- drop: function(event, ui) {
+ drop: function(event, ui)
+ {
// Favorite dropped on home - fake an action and divert to normal handler
- var action = {
+ let action = {
data: {
class: 'add_home_favorite_portlet'
}
@@ -131,38 +123,38 @@ app.classes.home = (function(){ "use strict"; return AppJS.extend(
// Check for direct support for that application
if(ui.helper.context.dataset && ui.helper.context.dataset.appname)
{
- var action = egw_getActionManager('home',false,1).getActionById('drop_'+ui.helper.context.dataset.appname +'_favorite_portlet') || {}
+ action = egw_getActionManager('home', false, 1).getActionById('drop_' + ui.helper.context.dataset.appname + '_favorite_portlet') || {}
}
action.ui = ui;
app.home.add_from_drop(action, [{data: ui.helper.context.dataset}])
}
})
- // Bind to unload to remove it from our list
- .on('clear','.et2_container[id]', jQuery.proxy(function(e) {
+ // Bind to unload to remove it from our list
+ .on('clear', '.et2_container[id]', jQuery.proxy(function(e)
+ {
if(e.target && e.target.id && this.portlets[e.target.id])
{
this.portlets[e.target.id].destroy();
delete this.portlets[e.target.id];
}
- },this));
+ }, this));
}
- else if (et2.uniqueId)
+ else if(et2.uniqueId)
{
let portlet_container = this.portlet_container || window.app.home?.portlet_container;
// Handle bad timing - a sub-template was finished first
if(!portlet_container)
{
- window.setTimeout(jQuery.proxy(function() {this.et2_ready(et2, name);},this),200);
+ window.setTimeout(() => {this.et2_ready(et2, name);}, 200);
return;
}
-
- var portlet = portlet_container.getWidgetById(et2.uniqueId);
+ let portlet = portlet_container.getWidgetById(et2.uniqueId);
// Check for existing etemplate, this one loaded over it
// NOTE: Moving them around like this can cause problems with event handlers
- var existing = etemplate2.getById(et2.uniqueId);
+ let existing = etemplate2.getById(et2.uniqueId);
if(portlet && existing)
{
- for(var i = 0; i < portlet._children.length; i++)
+ for(let i = 0; i < portlet._children.length; i++)
{
if(typeof portlet._children[i]._init == 'undefined')
{
@@ -170,11 +162,14 @@ app.classes.home = (function(){ "use strict"; return AppJS.extend(
}
}
}
+ // Set size & position
+ let settings = portlet_container.getArrayMgr("content").data.find(e => e.id == et2.uniqueId) || {};
+ portlet.style.gridArea = settings.row + "/" + settings.col + "/ span " + (settings.height || 1) + "/ span " + (settings.width || 1);
+
// It's in the right place for original load, but move it into portlet
- var misplaced = jQuery(etemplate2.getById('home-index').DOMContainer).siblings('#'+et2.DOMContainer.id);
+ let misplaced = jQuery(etemplate2.getById('home-index').DOMContainer).siblings('#' + et2.DOMContainer.id);
if(portlet)
{
- portlet.content = jQuery(et2.DOMContainer).appendTo(portlet.content);
portlet.addChild(et2.widgetContainer);
et2.resize();
}
@@ -186,17 +181,7 @@ app.classes.home = (function(){ "use strict"; return AppJS.extend(
// Instanciate custom code for this portlet
this._get_portlet_code(portlet);
}
-
- // Special handling to deal with legacy (non-et2) calendar links
- if(name == 'home.legacy')
- {
- jQuery('.calendar_calDayColHeader a, .calendar_plannerDayScale a, .calendar_plannerWeekScale a, .calendar_plannerMonthScale a, .calendar_calGridHeader a', et2.DOMContainer)
- .on('click', function(e) {
- egw.link_handler(this.href,'calendar');
- return false;
- });
- }
- },
+ }
/**
* Observer method receives update notifications from all applications
@@ -217,9 +202,9 @@ app.classes.home = (function(){ "use strict"; return AppJS.extend(
* @param {string} _targetapp which app's window should be refreshed, default current
* @return {false|*} false to stop regular refresh, thought all observers are run
*/
- observer: function(_msg, _app, _id, _type, _msg_type, _targetapp)
+ observer(_msg, _app, _id, _type, _msg_type, _targetapp)
{
- for(var id in this.portlets)
+ for(let id in this.portlets)
{
// App is home, refresh all portlets
if(_app == 'home')
@@ -231,68 +216,56 @@ app.classes.home = (function(){ "use strict"; return AppJS.extend(
// Ask the portlets if they're interested
try
{
- var code = this.portlets[id];
+ let code = this.portlets[id];
if(code)
{
- code.observer(_msg,_app,_id,_type,_msg_type,_targetapp);
+ code.observer(_msg, _app, _id, _type, _msg_type, _targetapp);
}
}
catch(e)
{
- this.egw.debug("error", "Error trying to update portlet " + id,e);
+ this.egw.debug("error", "Error trying to update portlet " + id, e);
}
}
return false;
- },
+ }
/**
* Add a new portlet from the context menu
*/
- add: function(action, source) {
+ add(action, source)
+ {
// Basic portlet attributes
- var attrs = {
- id: this._create_id(),
- class: action.data.class,
- width: this.DEFAULT.WIDTH,
- height: this.DEFAULT.HEIGHT
+ let attrs = {
+ ...HomeApp.DEFAULT, ...{
+ id: this._create_id(),
+ class: action.data.class
+ }
};
// Try to put it about where the menu was opened
if(action.menu_context)
{
- var $portlet_container = jQuery(this.portlet_container.getDOMNode());
- attrs.row = Math.max(1,Math.round((action.menu_context.posy - $portlet_container.offset().top )/ this.GRID)+1);
- attrs.col = Math.max(1,Math.round((action.menu_context.posx - $portlet_container.offset().left) / this.GRID)+1);
+ let $portlet_container = jQuery(this.portlet_container.getDOMNode());
+ attrs.row = Math.max(1, Math.round((action.menu_context.posy - $portlet_container.offset().top) / HomeApp.GRID) + 1);
+ attrs.col = Math.max(1, Math.round((action.menu_context.posx - $portlet_container.offset().left) / HomeApp.GRID) + 1);
}
- // Don't pass default width & height so class can set it
- delete attrs.width;
- delete attrs.height;
- var portlet = et2_createWidget('portlet',jQuery.extend({},attrs), this.portlet_container);
+ let portlet = et2_createWidget('et2-portlet', attrs, this.portlet_container);
portlet.loadingFinished();
- // Immediately add content ID so etemplate loads into the right place
- portlet.content.append('');
-
// Get actual attributes & settings, since they're not available client side yet
- portlet._process_edit(et2_dialog.OK_BUTTON, attrs);
-
- // Set up sorting/grid of new portlet
- var $portlet_container = jQuery(this.portlet_container.getDOMNode());
- $portlet_container.data("gridster").add_widget(
- portlet.getDOMNode(),
- this.DEFAULT.WIDTH, this.DEFAULT.HEIGHT,
- attrs.col, attrs.row
- );
+ portlet.update_settings(attrs);
// Instanciate custom code for this portlet
this._get_portlet_code(portlet);
- },
+ }
/**
* User dropped something on home. Add a new portlet
*/
- add_from_drop: function(action,source) {
+ add_from_drop(action, source)
+ {
// Actions got confused drop vs popup
if(source[0].id == 'portlets')
@@ -300,10 +273,10 @@ app.classes.home = (function(){ "use strict"; return AppJS.extend(
return this.add(action);
}
- var $portlet_container = jQuery(this.portlet_container.getDOMNode());
+ let $portlet_container = jQuery(this.portlet_container.getDOMNode());
// Basic portlet attributes
- var attrs = {
+ let attrs = {
id: this._create_id(),
class: action.data.class || action.id.substr(5),
width: this.DEFAULT.WIDTH,
@@ -313,18 +286,18 @@ app.classes.home = (function(){ "use strict"; return AppJS.extend(
// Try to find where the drop was
if(action != null && action.ui && action.ui.position)
{
- attrs.row = Math.max(1,Math.round((action.ui.position.top - $portlet_container.offset().top )/ this.GRID));
- attrs.col = Math.max(1,Math.round((action.ui.position.left - $portlet_container.offset().left) / this.GRID));
+ attrs.row = Math.max(1, Math.round((action.ui.position.top - $portlet_container.offset().top) / this.GRID));
+ attrs.col = Math.max(1, Math.round((action.ui.position.left - $portlet_container.offset().left) / this.GRID));
}
- var portlet = et2_createWidget('portlet',jQuery.extend({},attrs), this.portlet_container);
+ let portlet = et2_createWidget('portlet', jQuery.extend({}, attrs), this.portlet_container);
portlet.loadingFinished();
// Immediately add content ID so etemplate loads into the right place
- portlet.content.append('');
+ portlet.content.append('');
// Get actual attributes & settings, since they're not available client side yet
- var drop_data = [];
- for(var i = 0; i < source.length; i++)
+ let drop_data = [];
+ for(let i = 0; i < source.length; i++)
{
if(source[i].id)
{
@@ -338,7 +311,7 @@ app.classes.home = (function(){ "use strict"; return AppJS.extend(
// Don't pass default width & height so class can set it
delete attrs.width;
delete attrs.height;
- portlet._process_edit(et2_dialog.OK_BUTTON, jQuery.extend({dropped_data: drop_data},attrs));
+ 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(
@@ -349,7 +322,7 @@ app.classes.home = (function(){ "use strict"; return AppJS.extend(
// Instanciate custom code for this portlet
this._get_portlet_code(portlet);
- },
+ }
/**
* Set the current selection as default for other users
@@ -360,25 +333,27 @@ app.classes.home = (function(){ "use strict"; return AppJS.extend(
* @param {egwAction} action
* @param {egwActionObject[]} selected
*/
- set_default: function(action, selected) {
+ set_default(action, selected)
+ {
// Gather just IDs, server will handle the details
- var portlet_ids = [];
- var group = action.data.portlet_group || false;
+ let portlet_ids = [];
+ let group = action.data.portlet_group || false;
if(selected[0].id == 'home.index')
{
// Set all
- this.portlet_container.iterateOver(function(portlet) {
+ this.portlet_container.iterateOver(function(portlet)
+ {
portlet_ids.push(portlet.id);
- },this,et2_portlet);
+ }, this, et2_portlet);
}
else
{
- for(var i = 0; i < selected.length; i++)
+ for(let i = 0; i < selected.length; i++)
{
portlet_ids.push(selected[i].id);
// Read the associated group so we can properly remove it
- var portlet = egw.preference(selected[i].id,'home');
+ let portlet = egw.preference(selected[i].id, 'home');
if(!group && portlet && portlet.group)
{
group = portlet.group;
@@ -395,33 +370,38 @@ app.classes.home = (function(){ "use strict"; return AppJS.extend(
egw.json('home_ui::ajax_set_default', ['delete', portlet_ids, group]).sendRequest(true);
return;
}
- var dialog = et2_createWidget("dialog",{
+ let dialog = et2_createWidget("dialog", {
// If you use a template, the second parameter will be the value of the template, as if it were submitted.
- callback: function(button_id, value) {
- if(button_id != et2_dialog.OK_BUTTON) return;
+ callback: function(button_id, value)
+ {
+ if(button_id != et2_dialog.OK_BUTTON)
+ {
+ return;
+ }
// Pass them to server
- egw.json('home_ui::ajax_set_default', ['add', portlet_ids, value.group||false]).sendRequest(true);
+ egw.json('home_ui::ajax_set_default', ['add', portlet_ids, value.group || false]).sendRequest(true);
},
buttons: et2_dialog.BUTTONS_OK_CANCEL,
title: action.caption,
- template:"home.set_default",
- value: {content:{}, sel_options: {group:{default: egw.lang('All'), forced: egw.lang('Forced')}}}
+ template: "home.set_default",
+ value: {content: {}, sel_options: {group: {default: egw.lang('All'), forced: egw.lang('Forced')}}}
});
- },
+ }
/**
* Allow a refresh from anywhere by triggering an update with no changes
*
* @param {string} id
*/
- refresh: function(id) {
- var p = this.portlet_container.getWidgetById(id);
+ refresh(id)
+ {
+ let p = this.portlet_container.getWidgetById(id);
if(p)
{
p._process_edit(et2_dialog.OK_BUTTON, '~reload~');
}
- },
+ }
/**
* Determine the best fitting code to use for the given portlet, instanciate
@@ -430,33 +410,36 @@ app.classes.home = (function(){ "use strict"; return AppJS.extend(
* @param {et2_portlet} portlet
* @returns {home_portlet}
*/
- _get_portlet_code: function(portlet) {
- var classname = portlet.class;
+ _get_portlet_code(portlet)
+ {
+ let classname = portlet.class;
// Freshly added portlets can have 'add_' prefix
if(classname.indexOf('add_') == 0)
{
- classname = classname.replace('add_','');
+ classname = classname.replace('add_', '');
}
// Prefer a specific match
- var _class = app.classes.home[classname] ||
+ let _class = app.classes.home[classname] ||
+ (typeof customElements.get(classname) != "undefined" ? customElements.get(classname).class : false) ||
// If it has a nextmatch, use favorite base class
- (portlet.getWidgetById('nm') ? app.classes.home.home_favorite_portlet : false) ||
+ (portlet.getWidgetById('nm') ? Et2PortletFavorite : false) ||
// Fall back to base class
- app.classes.home.home_portlet;
+ Et2Portlet;
this.portlets[portlet.id] = new _class(portlet);
return this.portlets[portlet.id];
- },
+ }
/**
* For link_portlet - opens the configured record when the user
* double-clicks or chooses view from the context menu
*/
- open_link: function(action) {
+ open_link(action)
+ {
// Get widget
- var widget = null;
+ let widget = null;
while(action.parent != null)
{
if(action.data && action.data.widget)
@@ -468,26 +451,25 @@ app.classes.home = (function(){ "use strict"; return AppJS.extend(
}
if(widget == null)
{
- egw().log("warning", "Could not find widget");
+ this.egw.log("warning", "Could not find widget");
return;
}
- egw().open(widget.options.settings.entry, "", 'view',null,widget.options.settings.entry.app);
- },
+ this.egw.open(widget.options.settings.entry, "", 'view', null, widget.options.settings.entry.app);
+ }
/**
* Set up the drag / drop / re-order of portlets
*/
- _do_ordering: function() {
- var $portlet_container = jQuery(this.portlet_container.getDOMNode());
+ _do_ordering()
+ {
+ let $portlet_container = jQuery(this.portlet_container.getDOMNode());
$portlet_container
- .addClass("home ui-helper-clearfix")
- .disableSelection()
/* Gridster */
.gridster({
widget_selector: 'div.et2_portlet',
// Dimensions + margins = grid spacing
- widget_base_dimensions: [this.GRID-5, this.GRID-5],
- widget_margins: [5,5],
+ widget_base_dimensions: [home.GRID - 5, home.GRID - 5],
+ widget_margins: [5, 5],
extra_rows: 1,
extra_cols: 1,
min_cols: 3,
@@ -498,9 +480,10 @@ app.classes.home = (function(){ "use strict"; return AppJS.extend(
* @param grid Object Grid settings
* @return Object - will be returned by gridster.serialize()
*/
- serialize_params: function($w, grid) {
+ serialize_params: function($w, grid)
+ {
return {
- id: $w.attr('id').replace(app.home.portlet_container.getInstanceManager().uniqueId+'_',''),
+ id: $w.attr('id').replace(app.home.portlet_container.getInstanceManager().uniqueId + '_', ''),
row: grid.row,
col: grid.col,
width: grid.size_x,
@@ -512,24 +495,31 @@ app.classes.home = (function(){ "use strict"; return AppJS.extend(
*/
draggable: {
handle: '.ui-widget-header',
- stop: function(event,ui) {
+ stop: function(event, ui)
+ {
// Update widget(s)
- var changed = this.serialize_changed();
+ let changed = this.serialize_changed();
// Reset changed, or they keep accumulating
this.$changed = jQuery([]);
- for (var key in changed)
+ for(let key in changed)
{
- if(!changed[key].id) continue;
+ if(!changed[key].id)
+ {
+ continue;
+ }
// Changed ID is the ID
- var widget = window.app.home.portlet_container.getWidgetById(changed[key].id);
- if(!widget || widget == window.app.home.portlet_container) continue;
+ let widget = window.app.home.portlet_container.getWidgetById(changed[key].id);
+ if(!widget || widget == window.app.home.portlet_container)
+ {
+ continue;
+ }
- egw().jsonq("home.home_ui.ajax_set_properties",[changed[key].id, {},{
+ egw().jsonq("home.home_ui.ajax_set_properties", [changed[key].id, {}, {
row: changed[key].row,
col: changed[key].col
- },widget.settings?widget.settings.group:false],
+ }, widget.settings ? widget.settings.group : false],
null,
widget, true, widget
);
@@ -540,52 +530,57 @@ app.classes.home = (function(){ "use strict"; return AppJS.extend(
});
// Rescue selectboxes from Firefox
- $portlet_container.on('mousedown touchstart', 'select', function(e) {
+ $portlet_container.on('mousedown touchstart', 'select', function(e)
+ {
e.stopPropagation();
});
// Bind window resize to re-layout gridster
- jQuery(window).one("resize."+this.et2._inst.uniqueId, function() {
+ jQuery(window).one("resize." + this.et2._inst.uniqueId, function()
+ {
// Note this doesn't change the positions, just makes them invalid
$portlet_container.data('gridster').recalculate_faux_grid();
});
// 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) {
+ .on("resizestop", function(event, ui)
+ {
$portlet_container.data("gridster").resize_widget(
ui.element,
Math.round(ui.size.width / app.home.GRID),
Math.round(ui.size.height / app.home.GRID)
);
});
- },
+ }
/**
* Create an ID that should be unique, at least amoung a single user's portlets
*/
- _create_id: function() {
- var id = '';
+ _create_id()
+ {
+ let id = '';
do
{
id = Math.floor((1 + Math.random()) * 0x10000)
- .toString(16)
- .substring(1);
+ .toString(16)
+ .substring(1);
}
- while(this.portlet_container.getWidgetById('portlet_'+id));
- return 'portlet_'+id;
- },
+ while(this.portlet_container.getWidgetById('portlet_' + id));
+ return 'portlet_' + id;
+ }
/**
* Functions for the list portlet
*/
/**
- * 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) {
+ * 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(action, source, target_action)
+ {
// Actions got confused drop vs popup
if(source[0].id == 'portlets')
{
@@ -593,7 +588,7 @@ app.classes.home = (function(){ "use strict"; return AppJS.extend(
}
// Get widget
- var widget = null;
+ let widget = null;
while(action.parent != null)
{
if(action.data && action.data.widget)
@@ -606,14 +601,18 @@ app.classes.home = (function(){ "use strict"; return AppJS.extend(
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++)
+ let splitted = 'home.edit'.split('.');
+ let path = app.home.portlet_container.getRoot()._inst.template_base_url + splitted.shift() + "/templates/default/" +
+ splitted.join('.') + ".xet";
+ let dialog = et2_createWidget("dialog", {
+ callback: function(button_id, value)
+ {
+ if(button_id == et2_dialog.CANCEL_BUTTON)
+ {
+ return;
+ }
+ let new_list = widget.options.settings.list || [];
+ for(let i = 0; i < new_list.length; i++)
{
if(new_list[i].app == value.add.app && new_list[i].id == value.add.id)
{
@@ -624,9 +623,9 @@ app.classes.home = (function(){ "use strict"; return AppJS.extend(
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});
+ widget._process_edit(button_id, {list: new_list});
// Update client side
- var list = widget.getWidgetById('list');
+ let list = widget.getWidgetById('list');
if(list)
{
list.set_value(new_list);
@@ -634,41 +633,41 @@ app.classes.home = (function(){ "use strict"; return AppJS.extend(
},
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:''}]}
+ 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++)
+ let new_list = widget.options.settings.list || [];
+ let changed = false;
+ for(let i = 0; i < new_list.length; i++)
{
// Avoid duplicates
- for(var j = 0; j < source.length; j++)
+ for(let j = 0; j < source.length; j++)
{
- if(!source[j].id || new_list[i].app+"::"+new_list[i].id == source[j].id)
+ if(!source[j].id || new_list[i].app + "::" + new_list[i].id == source[j].id)
{
// Duplicate - skip it
- source.splice(j,1);
+ source.splice(j, 1);
}
}
}
- for(var i = 0; i < source.length; i++)
+ for(let 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(':')});
+ let 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,{
+ 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++)
+ for(let i = 0; i < new_list.length; i++)
{
if(new_list[i]['app'] == 'filemanager')
{
@@ -679,20 +678,21 @@ app.classes.home = (function(){ "use strict"; return AppJS.extend(
widget.getWidgetById('list').set_value(new_list);
}
- },
+ }
/**
* Remove a link from the list
*/
- link_change: function(list, link_id, row) {
+ link_change(list, link_id, row)
+ {
// Quick response client side
row.slideUp(row.remove);
// Actual removal
- var portlet = list._parent._parent;
+ let portlet = list._parent._parent;
portlet.options.settings.list.splice(row.index(), 1);
- portlet._process_edit(et2_dialog.OK_BUTTON,{list: portlet.options.settings.list || {}});
- },
+ portlet._process_edit(et2_dialog.OK_BUTTON, {list: portlet.options.settings.list || {}});
+ }
/**
* Functions for the note portlet
@@ -704,33 +704,34 @@ app.classes.home = (function(){ "use strict"; return AppJS.extend(
* @param {egwAction} action
* @param {egwActionObject[]} Selected
*/
- note_edit: function(action, selected) {
+ note_edit(action, selected)
+ {
if(!selected && typeof action == 'string')
{
- var id = action;
+ let id = action;
}
else
{
- var id = selected[0].id;
+ let id = selected[0].id;
}
// Aim to match the size
- var portlet_dom = jQuery('[id$='+id+'][data-sizex]',this.portlet_container.getDOMNode());
- var width = portlet_dom.attr('data-sizex') * this.GRID;
- var height = portlet_dom.attr('data-sizey') * this.GRID;
+ let portlet_dom = jQuery('[id$=' + id + '][data-sizex]', this.portlet_container.getDOMNode());
+ let width = portlet_dom.attr('data-sizex') * this.GRID;
+ let height = portlet_dom.attr('data-sizey') * this.GRID;
// CKEditor is impossible to use below a certain size
// Add 35px for the toolbar, 35px for the buttons
- var window_width = Math.max(580, width+20);
- var window_height = Math.max(350, height+70);
+ let window_width = Math.max(580, width + 20);
+ let window_height = Math.max(350, height + 70);
// Open popup, but add 70 to the height for the toolbar
- egw.open_link(egw.link('/index.php',{
+ this.egw.open_link(this.egw.link('/index.php', {
menuaction: 'home.home_note_portlet.edit',
id: id,
height: window_height - 70
- }),'home_'+id, window_width+'x'+window_height,'home');
- },
+ }), 'home_' + id, window_width + 'x' + window_height, 'home');
+ }
/**
* Favorites / nextmatch
@@ -741,18 +742,24 @@ app.classes.home = (function(){ "use strict"; return AppJS.extend(
* @param {Event} event
* @param {et2_button} widget
*/
- nextmatch_toggle_header: function(event, widget) {
+ nextmatch_toggle_header(event, widget)
+ {
widget.set_class(widget.class == 'opened' ? 'closed' : 'opened');
// We operate on the DOM here, nm should be unaware of our fiddling
- var nm = widget.getParent().getWidgetById('nm');
- if(!nm) return;
+ let nm = widget.getParent().getWidgetById('nm');
+ if(!nm)
+ {
+ return;
+ }
// Hide header
nm.div.toggleClass('header_hidden');
nm.set_hide_header(nm.div.hasClass('header_hidden'));
nm.resize();
}
-})}).call(window);
+}
+
+app.classes.home = HomeApp;
/// Base class code
@@ -763,15 +770,19 @@ app.classes.home = (function(){ "use strict"; return AppJS.extend(
*
* @type @exp;Class@call;extend
*/
-app.classes.home.home_portlet = Class.extend({
- portlet: null,
+export class HomePortlet
+{
+ protected portlet = null;
- init: function(portlet) {
+ init(portlet)
+ {
this.portlet = portlet;
- },
- destroy: function() {
+ }
+
+ destroy()
+ {
this.portlet = null;
- },
+ }
/**
* Handle framework refresh messages to determine if the portlet needs to
@@ -780,12 +791,13 @@ app.classes.home.home_portlet = Class.extend({
* App is responsible for only reacting to "messages" it is interested in!
*
*/
- observer: function(_msg, _app, _id, _type, _msg_type, _targetapp)
+ observer(_msg, _app, _id, _type, _msg_type, _targetapp)
{
// Not interested
}
-});
+}
+/*
app.classes.home.home_link_portlet = app.classes.home.home_portlet.extend({
init: function(portlet) {
// call parent
@@ -794,7 +806,7 @@ app.classes.home.home_link_portlet = app.classes.home.home_portlet.extend({
// Check for tooltip
if(this.portlet)
{
- var content = jQuery('.tooltip',this.portlet.content);
+ let content = jQuery('.tooltip', this.portlet.content);
if(content.length && content.children().length)
{
//Check if the tooltip is already initialized
@@ -831,7 +843,7 @@ app.classes.home.home_link_portlet = app.classes.home.home_portlet.extend({
{
if(this.portlet && this.portlet.settings)
{
- var value = this.portlet.settings.entry || {};
+ let value = this.portlet.settings.entry || {};
if(value.app && value.app == _app && value.id && value.id == _id)
{
// We don't just get the updated title, in case there's a custom
@@ -846,8 +858,8 @@ app.classes.home.home_list_portlet = app.classes.home.home_portlet.extend({
{
if(this.portlet && this.portlet.getWidgetById('list'))
{
- var list = this.portlet.getWidgetById('list').options.value;
- for(var i = 0; i < list.length; i++)
+ let list = this.portlet.getWidgetById('list').options.value;
+ for(let i = 0; i < list.length; i++)
{
if(list[i].app == _app && list[i].id == _id)
{
diff --git a/home/templates/default/app.css b/home/templates/default/app.css
index df4198e1aa..bd1ad0ff6b 100644
--- a/home/templates/default/app.css
+++ b/home/templates/default/app.css
@@ -10,6 +10,16 @@
}
#home-index_portlets {
background-color: inherit;
+ display: grid;
+ grid-auto-columns: 50ex;
+ grid-auto-rows: 50ex;
+
+ gap: 2ex;
+
+ justify-content: stretch;
+ align-content: stretch;
+ justify-items: stretch;
+ align-items: stretch;
}
#portlets {
border: 1px solid silver;
@@ -26,14 +36,8 @@
.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 */
- position: absolute;
- bottom: 0px;
- top: 20px;
- width: 100%;
- overflow: hidden;
-}
+
+
.et2_portlet .et2_container {
height: 100%;
}
diff --git a/home/templates/default/index.xet b/home/templates/default/index.xet
index 54d65f7fac..b4341be2de 100644
--- a/home/templates/default/index.xet
+++ b/home/templates/default/index.xet
@@ -17,11 +17,16 @@
-
-
-
-
-
+
+
+
+
+
+
+
diff --git a/home/templates/pixelegg/app.css b/home/templates/pixelegg/app.css
deleted file mode 100755
index c012d241d6..0000000000
--- a/home/templates/pixelegg/app.css
+++ /dev/null
@@ -1,11 +0,0 @@
-/**
- * EGroupware: CSS with less preprocessor
- *
- * Please do NOT change app.css directly, instead change app.less and compile it!
- *
- * @link http://www.egroupware.org
- * @license http://opensource.org/licenses/gpl-license.php GPL - GNU General Public License
- * @author Stefan Reinhardt
- * @package home
- * @version $Id$
- */
diff --git a/home/templates/pixelegg/app.less b/home/templates/pixelegg/app.less
deleted file mode 100755
index 7e4d0aff38..0000000000
--- a/home/templates/pixelegg/app.less
+++ /dev/null
@@ -1,18 +0,0 @@
-/**
- * EGroupware: CSS with less preprocessor
- *
- * Please do NOT change app.css directly, instead change app.less and compile it!
- *
- * @link http://www.egroupware.org
- * @license http://opensource.org/licenses/gpl-license.php GPL - GNU General Public License
- * @author Stefan Reinhardt
- * @package home
- * @version $Id$
- */
-
-@import (reference) "../../../pixelegg/less/def_buttons.less";
-@import (reference) "../../../pixelegg/less/def_design_pattern_color_font_shadow.less";
-
-@import (reference) "../default/app.css";
-
-