mirror of
https://github.com/EGroupware/egroupware.git
synced 2024-11-25 01:13:25 +01:00
Framework WIP:
- Some work on taking over framework duties
This commit is contained in:
parent
24e265ef6e
commit
4271b5c72c
@ -53,6 +53,11 @@ class kdots_framework extends Api\Framework\Ajax
|
||||
$data['open_app_name'] = $open_app['name'];
|
||||
$data['open_app_url'] = $open_app['url'];
|
||||
}
|
||||
if($data['open_app_name'] && !$this->sidebox_done)
|
||||
{
|
||||
$this->do_sidebox();
|
||||
$data['setSidebox'] = htmlentities(json_encode(static::$extra['setSidebox'], JSON_HEX_QUOT | JSON_HEX_AMP), ENT_QUOTES, 'UTF-8');
|
||||
}
|
||||
return $data;
|
||||
}
|
||||
|
||||
|
@ -103,6 +103,21 @@ export class EgwFramework extends LitElement
|
||||
|
||||
private get tabs() : SlTabGroup { return this.shadowRoot.querySelector("sl-tab-group");}
|
||||
|
||||
connectedCallback()
|
||||
{
|
||||
super.connectedCallback();
|
||||
if(this.egw.window && this.egw.window.opener == null && !this.egw.window.framework)
|
||||
{
|
||||
// This works, but stops a lot else from working
|
||||
//this.egw.window.framework = this;
|
||||
}
|
||||
if(this.egw.window?.framework && this.egw.window?.framework !== this)
|
||||
{
|
||||
// Override framework setSidebox, use arrow function to force context
|
||||
this.egw.framework.setSidebox = (applicationName, sideboxData, hash?) => this.setSidebox(applicationName, sideboxData, hash);
|
||||
}
|
||||
}
|
||||
|
||||
get egw() : typeof egw
|
||||
{
|
||||
return window.egw ?? <typeof egw>{
|
||||
@ -138,13 +153,37 @@ export class EgwFramework extends LitElement
|
||||
(menuaction ? '.' + menuaction[1] : '');
|
||||
};
|
||||
|
||||
public loadApp(appname, active = false)
|
||||
/**
|
||||
* Load an application into the framework
|
||||
*
|
||||
* Loading is done by name, and we look up everything we need in the applicationList
|
||||
*
|
||||
* @param {string} appname
|
||||
* @param {boolean} active
|
||||
* @param {string} url
|
||||
* @returns {EgwFrameworkApp}
|
||||
*/
|
||||
public loadApp(appname : string, active = false, url = null) : EgwFrameworkApp
|
||||
{
|
||||
const existing : EgwFrameworkApp = this.querySelector(`egw-app[name="${appname}"]`);
|
||||
if(existing)
|
||||
{
|
||||
if(active)
|
||||
{
|
||||
this.tabs.show(appname);
|
||||
}
|
||||
if(url)
|
||||
{
|
||||
existing.url = url;
|
||||
}
|
||||
return existing;
|
||||
}
|
||||
|
||||
const app = this.applicationList.find(a => a.name == appname);
|
||||
let appComponent = <EgwFrameworkApp>document.createElement("egw-app");
|
||||
appComponent.id = appname;
|
||||
appComponent.name = appname;
|
||||
appComponent.url = app?.url;
|
||||
appComponent.url = url ?? app?.url;
|
||||
|
||||
this.append(appComponent);
|
||||
// App was not in the tab list
|
||||
@ -166,6 +205,110 @@ export class EgwFramework extends LitElement
|
||||
return appComponent;
|
||||
}
|
||||
|
||||
/**
|
||||
* Load a link into the framework
|
||||
*
|
||||
* @param {string} _link
|
||||
* @param {string} _app
|
||||
* @returns {undefined}
|
||||
*/
|
||||
public linkHandler(_link : string, _app : string)
|
||||
{
|
||||
//Determine the app string from the application parameter
|
||||
let app = null;
|
||||
if(_app && typeof _app == 'string')
|
||||
{
|
||||
app = this.applicationList.find(a => a.name == _app);
|
||||
}
|
||||
|
||||
if(!app)
|
||||
{
|
||||
//The app parameter was false or not a string or the application specified did not exists.
|
||||
//Determine the target application from the link that had been passed to this function
|
||||
app = this.parseAppFromUrl(_link);
|
||||
}
|
||||
|
||||
if(app)
|
||||
{
|
||||
if(_app == '_tab')
|
||||
{
|
||||
// add target flag
|
||||
_link += '&target=_tab';
|
||||
const appname = app.appName + ":" + btoa(_link);
|
||||
this.applicationList[appname] = {...app};
|
||||
this.applicationList[appname]['name'] = appname;
|
||||
this.applicationList[appname]['indexUrl'] = _link;
|
||||
this.applicationList[appname]['tab'] = null;
|
||||
this.applicationList[appname]['browser'] = null;
|
||||
this.applicationList[appname]['title'] = 'view';
|
||||
app = this.applicationList[appname];
|
||||
}
|
||||
this.loadApp(app.name, true, _link);
|
||||
}
|
||||
else
|
||||
{
|
||||
//Display some error messages to have visible feedback
|
||||
if(typeof _app == 'string')
|
||||
{
|
||||
egw_alertHandler('Application "' + _app + '" not found.',
|
||||
'The application "' + _app + '" the link "' + _link + '" points to is not registered.');
|
||||
}
|
||||
else
|
||||
{
|
||||
egw_alertHandler("No appropriate target application has been found.",
|
||||
"Target link: " + _link);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Tries to obtain the application from a menuaction
|
||||
* @param {string} _url
|
||||
*/
|
||||
protected parseAppFromUrl(_url : string)
|
||||
{
|
||||
let _app = null;
|
||||
|
||||
// Check the menuaction parts from the url
|
||||
let matches = _url.match(/menuaction=([a-z0-9_-]+)\./i) ||
|
||||
// Check the url for a scheme of "/app/something.php"
|
||||
_url.match(/\/([^\/]+)\/[^\/]+\.php/i);
|
||||
if(matches)
|
||||
{
|
||||
// check if this is a regular app-name
|
||||
_app = this.applicationList.find(a => a.name == matches[1]);
|
||||
}
|
||||
|
||||
return _app;
|
||||
}
|
||||
|
||||
/**
|
||||
* Print
|
||||
*/
|
||||
public async print()
|
||||
{
|
||||
const appElement : EgwFrameworkApp = this.querySelector("egw-app[active]");
|
||||
try
|
||||
{
|
||||
if(appElement)
|
||||
{
|
||||
await appElement.print();
|
||||
}
|
||||
const appWindow = this.egw.window;
|
||||
appWindow.setTimeout(appWindow.print, 0);
|
||||
}
|
||||
catch
|
||||
{
|
||||
// Ignore rejected
|
||||
}
|
||||
}
|
||||
|
||||
public async setSidebox(appname, sideboxData, hash)
|
||||
{
|
||||
const app = this.loadApp(appname);
|
||||
app.setSidebox(sideboxData, hash);
|
||||
}
|
||||
|
||||
protected getBaseUrl() {return "";}
|
||||
|
||||
/**
|
||||
|
@ -10,6 +10,7 @@ import {SlSplitPanel} from "@shoelace-style/shoelace";
|
||||
import {HasSlotController} from "../../api/js/etemplate/Et2Widget/slot";
|
||||
import type {EgwFramework} from "./EgwFramework";
|
||||
import {etemplate2} from "../../api/js/etemplate/etemplate2";
|
||||
import {et2_IPrint} from "../../api/js/etemplate/et2_core_interfaces";
|
||||
|
||||
/**
|
||||
* @summary Application component inside EgwFramework
|
||||
@ -156,6 +157,14 @@ export class EgwFrameworkApp extends LitElement
|
||||
this.load(this.url);
|
||||
}
|
||||
|
||||
protected async getUpdateComplete() : Promise<boolean>
|
||||
{
|
||||
const result = await super.updateComplete;
|
||||
await this.loadingPromise;
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
protected load(url)
|
||||
{
|
||||
if(!url)
|
||||
@ -231,6 +240,11 @@ export class EgwFrameworkApp extends LitElement
|
||||
}
|
||||
}
|
||||
|
||||
public setSidebox(sideboxData, hash?)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public showLeft()
|
||||
{
|
||||
this.showSide("left");
|
||||
@ -251,6 +265,85 @@ export class EgwFrameworkApp extends LitElement
|
||||
this.hideSide("right");
|
||||
}
|
||||
|
||||
public async print()
|
||||
{
|
||||
|
||||
let template;
|
||||
let deferred = [];
|
||||
let et2_list = [];
|
||||
const appWindow = this.framework.egw.window;
|
||||
|
||||
if((template = appWindow.etemplate2.getById(this.id)) && this == template.DOMContainer)
|
||||
{
|
||||
deferred = deferred.concat(template.print());
|
||||
et2_list.push(template);
|
||||
}
|
||||
else
|
||||
{
|
||||
// et2 inside, let its widgets prepare
|
||||
this.querySelectorAll(":scope > *").forEach((domNode : HTMLElement) =>
|
||||
{
|
||||
let et2 = appWindow.etemplate2.getById(domNode.id);
|
||||
if(et2 && (domNode.offsetWidth > 0 || domNode.offsetHeight > 0 || domNode.getClientRects().length > 0))
|
||||
{
|
||||
deferred = deferred.concat(et2.print());
|
||||
et2_list.push(et2);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
if(et2_list.length)
|
||||
{
|
||||
// Try to clean up after - not guaranteed
|
||||
let afterPrint = () =>
|
||||
{
|
||||
this.egw.loading_prompt(this.name, true, this.egw.lang('please wait...'), this, egwIsMobile() ? 'horizental' : 'spinner');
|
||||
|
||||
// Give framework a chance to deal, then reset the etemplates
|
||||
appWindow.setTimeout(function()
|
||||
{
|
||||
for(var i = 0; i < et2_list.length; i++)
|
||||
{
|
||||
et2_list[i].widgetContainer.iterateOver(function(_widget)
|
||||
{
|
||||
_widget.afterPrint();
|
||||
}, et2_list[i], et2_IPrint);
|
||||
}
|
||||
this.egw.loading_prompt(this.name, false);
|
||||
}, 100);
|
||||
appWindow.onafterprint = null;
|
||||
};
|
||||
/* Not sure what this did, it triggers while preview is still up
|
||||
if(appWindow.matchMedia)
|
||||
{
|
||||
var mediaQueryList = appWindow.matchMedia('print');
|
||||
var listener = function(mql)
|
||||
{
|
||||
if(!mql.matches)
|
||||
{
|
||||
mediaQueryList.removeListener(listener);
|
||||
afterPrint();
|
||||
}
|
||||
};
|
||||
mediaQueryList.addListener(listener);
|
||||
}
|
||||
|
||||
*/
|
||||
|
||||
appWindow.addEventListener("afterprint", afterPrint, {once: true});
|
||||
|
||||
// Wait for everything to be ready
|
||||
return Promise.all(deferred).catch((e) =>
|
||||
{
|
||||
afterPrint();
|
||||
if(typeof e == "undefined")
|
||||
{
|
||||
throw "rejected";
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
protected showSide(side)
|
||||
{
|
||||
const attribute = `${side}Collapsed`;
|
||||
@ -396,6 +489,44 @@ export class EgwFrameworkApp extends LitElement
|
||||
</aside>`;
|
||||
}
|
||||
|
||||
/**
|
||||
* Top right header, contains application action buttons (reload, print, config)
|
||||
* @returns {TemplateResult<1>}
|
||||
* @protected
|
||||
*/
|
||||
protected _rightHeaderTemplate()
|
||||
{
|
||||
return html`
|
||||
<sl-button-group>
|
||||
<et2-button-icon nosubmit name="arrow-clockwise"
|
||||
label=${this.egw.lang("Reload %1", this.egw.lang(this.name))}
|
||||
statustext=${this.egw.lang("Reload %1", this.egw.lang(this.name))}
|
||||
@click=${(e) =>
|
||||
{
|
||||
this.egw.refresh("", this.name);
|
||||
/* Could also be this.load(false); this.load(this.url) */
|
||||
}}
|
||||
></et2-button-icon>
|
||||
<et2-button-icon nosubmit name="printer"
|
||||
label=${this.egw.lang("Print")}
|
||||
statustext=${this.egw.lang("Print")}
|
||||
@click=${(e) => this.framework.print()}
|
||||
></et2-button-icon>
|
||||
${this.egw.user('apps')['waffles'] !== "undefined" ? html`
|
||||
<et2-button-icon nosubmit name="gear-wide"
|
||||
label=${this.egw.lang("Site configuration for %1", this.egw.lang(this.name))}
|
||||
statustext=${this.egw.lang("App configuration")}
|
||||
@click=${(e) =>
|
||||
{
|
||||
// @ts-ignore
|
||||
egw_link_handler(`/egroupware/index.php?menuaction=admin.admin_ui.index&load=admin.uiconfig.index&appname=${this.name}&ajax=true`, 'admin');
|
||||
}}
|
||||
></et2-button-icon>` : nothing
|
||||
}
|
||||
</sl-button-group>
|
||||
`;
|
||||
}
|
||||
|
||||
render()
|
||||
{
|
||||
const hasLeftSlots = this.hasSideContent("left");
|
||||
@ -426,14 +557,7 @@ export class EgwFrameworkApp extends LitElement
|
||||
<header class="egw_fw_app__header" part="header">
|
||||
<slot name="main-header"><span class="placeholder"> ${this.name} main-header</span></slot>
|
||||
</header>
|
||||
<sl-button-group>
|
||||
<sl-icon-button name="arrow-clockwise"
|
||||
label=${this.egw.lang("Reload %1", this.egw.lang(this.name))}></sl-icon-button>
|
||||
<sl-icon-button name="printer"
|
||||
label=${this.egw.lang("Reload %1", this.egw.lang(this.name))}></sl-icon-button>
|
||||
<sl-icon-button name="gear-wide"
|
||||
label=${this.egw.lang("Site configuration for %1", this.egw.lang(this.name))}></sl-icon-button>
|
||||
</sl-button-group>
|
||||
${this._rightHeaderTemplate()}
|
||||
</div>
|
||||
<div class="egw_fw_app__main" part="main">
|
||||
<sl-split-panel class=${classMap({"egw_fw_app__outerSplit": true, "no-content": !hasLeftSlots})}
|
||||
|
Loading…
Reference in New Issue
Block a user