WIP et2 based site configuration

This commit is contained in:
Ralf Becker 2016-04-20 18:52:55 +00:00
parent 88338c2c8c
commit 22701ce83f
7 changed files with 617 additions and 3 deletions

View File

@ -0,0 +1,157 @@
<?php
/**
* EGgroupware admin - New et2 site configuration
*
* @link http://www.egroupware.org
* @author Ralf Becker <rb@stylite.de>
* @package admin
* @copyright (c) 2016 by Ralf Becker <rb@stylite.de>
* @license http://opensource.org/licenses/gpl-license.php GPL - GNU General Public License
* @version $Id$
*/
use EGroupware\Api;
/**
* New site configuration for all apps using eTemplate2 $app/templates/default/config.xet
*/
class admin_config
{
var $public_functions = array('index' => True);
function index($_content=null)
{
if (is_array($_content))
{
$_appname = $_content['appname'];
}
elseif (!empty($_GET['appname']) && isset($GLOBALS['egw_info']['apps'][$_GET['appname']]))
{
$_appname = $_GET['appname'];
}
else
{
throw new Api\Exception\WrongParameter("Wrong or missing appname parameter!");
}
if ($GLOBALS['egw']->acl->check('site_config_acce',1,'admin'))
{
egw::redirect_link('/index.php');
}
// load the translations of the app we show too, so they dont need to be in admin!
if ($_appname != 'admin')
{
Api\Translation::add_app($_appname);
}
switch($_appname)
{
case 'admin':
case 'addressbook':
case 'calendar':
case 'preferences':
$appname = $_appname;
$config_appname = 'phpgwapi';
break;
case 'phpgwapi':
case '':
/* This keeps the admin from getting into what is a setup-only config */
egw::redirect_link('/admin/index.php');
break;
default:
$appname = $_appname;
$config_appname = $appname;
break;
}
$c = new Api\Config($config_appname);
$c->read_repository();
if ($_content['cancel'] || ($_content['save'] || $_content['apply']) && $GLOBALS['egw']->acl->check('site_config_acce',2,'admin'))
{
egw::redirect_link('/admin/index.php?ajax=true');
}
if ($_content['save'] || $_content['apply'])
{
// support old validation hooks
$_POST = array('newsettings' => &$_content['newsettings']);
/* Load hook file with functions to validate each config (one/none/all) */
Api\Hooks::single(array(
'location' => 'config_validate',
)+$_content['newsettings'], $appname);
foreach($_content['newsettings'] as $key => $config)
{
if ($config)
{
$c->config_data[$key] = $config;
if (in_array($key, (array)$GLOBALS['egw_info']['server']['found_validation_hook'], true) && function_exists($key))
{
call_user_func($key, $config, $c);
if($GLOBALS['config_error'])
{
$errors .= lang($GLOBALS['config_error']) . "\n";
$GLOBALS['config_error'] = False;
}
}
}
// don't erase passwords, since we also don't print them
elseif(strpos($key,'passwd') === false && strpos($key,'password') === false && strpos($key,'root_pw') === false)
{
unset($c->config_data[$key]);
}
}
if(in_array('final_validation', (array)$GLOBALS['egw_info']['server']['found_validation_hook']) &&
function_exists('final_validation'))
{
final_validation($_content['newsettings']);
if($GLOBALS['config_error'])
{
$errors .= lang($GLOBALS['config_error']) . "\n";
$GLOBALS['config_error'] = False;
}
unset($GLOBALS['egw_info']['server']['found_validation_hook']);
}
$c->save_repository();
if(!$errors && !$_content['apply'])
{
egw_framework::message(lang('Configuration saved.'), 'success');
egw::redirect_link('/index.php', array(
'menuaction' => 'admin.admin_ui.index',
'ajax' => 'true'
), 'admin');
}
}
if($errors)
{
egw_framework::message(lang('Error') . ': ' . $errors, 'error');
unset($errors);
unset($GLOBALS['config_error']);
}
elseif ($_content['apply'])
{
egw_framework::message(lang('Configuration saved.'), 'success');
}
Api\Hooks::single('config', $appname);
$tmpl = new Api\Etemplate($appname.'.config');
$path = (parse_url($tmpl->rel_path, PHP_URL_SCHEME) !== 'vfs' ? EGW_SERVER_ROOT : '').$tmpl->rel_path;
$content = array('newsettings' => array());
$config = $c->read_repository();
// for security reasons we do not send all config to client-side, but only ones mentioned in templates
$matches = null;
preg_match_all('/id="newsettings\[([^]]+)\]"/', file_get_contents($path), $matches, PREG_PATTERN_ORDER);
foreach($matches[1] as $name)
{
$content['newsettings'][$name] = $config[$name];
}
$tmpl->exec('admin.admin_config.index', $content, array(), array(), array('appname' => $appname));
}
}

View File

@ -153,3 +153,11 @@ span#admin-mailaccount_acc_id {
select#admin-mailaccount_ident_id {
width: 95%;
}
/**
* new et2 site configuration
*/
table.admin-config td.subHeader span {
font-weight: bold;
font-size: 110%;
}

View File

@ -233,7 +233,7 @@
<td>{lang_Passwords_require_a_minimum_number_of_characters}:</td>
<td>
<select name="newsettings[force_pwd_length]">
<option value="">{lang_None}</options>
<option value="">{lang_None}</option>
<option value="6"{selected_force_pwd_length_6}>6</option>
<option value="7"{selected_force_pwd_length_7}>7</option>
<option value="8"{selected_force_pwd_length_8}>8</option>

View File

@ -0,0 +1,263 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE overlay PUBLIC "-//Stylite AG//eTemplate 2//EN" "http://www.egroupware.org/etemplate2.dtd">
<!-- $Id$ -->
<overlay>
<template id="admin.config" template="" lang="" group="0" version="16.1">
<grid width="100%" class="admin-config egwGridView_grid">
<columns>
<column width="70%"/>
<column/>
</columns>
<rows>
<row>
<description value="Site configuration" span="all" class="subHeader"/>
</row>
<row>
<description value="Should the login page include a language selectbox (useful for demo-sites) ?" label="%s:"/>
<select id="newsettings[login_show_language_selection]">
<option value="">No</option>
<option value="True">Yes</option>
</select>
</row>
<row>
<description value="How should EMail addresses for new users be constructed?" label="%s:"/>
<select id="newsettings[email_address_format]">
<option value="first-dot-last">{Firstname}.{Lastname}@domain.com</option>
<option value="first-last">{Firstname}{Lastname}@domain.com</option>
<option value="first-underscore-last">{Firstname}_{Lastname}@domain.com</option>
<option value="initial-last">{Initial}{Lastname}@domain.com</option>
<option value="initial-dot-last">{Initial}.{Lastname}@domain.com</option>
<option value="last-dot-first">{Lastname}.{Firstname}@domain.com</option>
<option value="last-first">{Lastname}{Firstname}@domain.com</option>
<option value="last-underscore-first">{Lastname}_{Firstname}@domain.com</option>
<option value="last">{Lastname}@domain.com</option>
<option value="first">{Firstname}@domain.com</option>
<option value="account">{Username}@domain.com</option>
</select>
</row>
<row>
<description value="Enter the VFS-Path where additional images, icons or logos can be placed (and found by EGroupwares applications). The path MUST start with /,and be readable by all users" label="%s:"/>
<textbox id="newsettings[vfs_image_dir]" size="40"/>
</row>
<row>
<description value="Log user-agent and action of changes in history-log of entries" label="%s:"/>
<select id="newsettings[log_user_agent_action]">
<option value="">No</option>
<option value="True">Yes</option>
</select>
</row>
<row>
<description value="appearance" span="all" class="subHeader"/>
</row>
<row>
<description value="Enter the title for your site" label="%s:"/>
<textbox id="newsettings[site_title]"/>
</row>
<row>
<description value="Enter the URL or filename (in phpgwapi/templates/default/images) of your logo" label="%s:"/>
<textbox id="newsettings[login_logo_file]"/>
</row>
<row>
<description value="Enter the url where your logo should link to" label="%s:"/>
<textbox id="newsettings[login_logo_url]"/>
</row>
<row>
<description value="Enter the title of your logo" label="%s:"/>
<textbox id="newsettings[login_logo_title]"/>
</row>
<row>
<description value="Enter the URL or filename (in your templates image directory) of your favicon (the little icon that appears in the browsers tabs)" label="%s:"/>
<textbox id="newsettings[favicon_file]"/>
</row>
<row>
<description value="How big should thumbnails for linked images be (maximum in pixels) ?" label="%s:"/>
<textbox id="newsettings[link_list_thumbnail]" size="5"/>
</row>
<row>
<description value="Enable spellcheck in rich text editor" label="%s:"/>
<select id="newsettings[enabled_spellcheck]">
<option value="">{No} - {more secure}</option>
<option value="True">Yes</option>
<option value="YesNoSCAYT">Yes, but no SCAYT</option>
<option value="YesBrowserBased">{Yes, use browser based spell checking engine} - {more secure}</option>
<option value="YesUseWebSpellCheck">Yes, use WebSpellChecker</option>
</select>
</row>
<row>
<description value="EGroupware Tutorial" label="%s:"/>
<select id="newsettings[egw_tutorial_disable]">
<option value="">Enable</option>
<option value="sidebox">Hide sidebox video tutorials</option>
<option value="intro">Do not offer introduction video</option>
<option value="all">Disable all</option>
</select>
</row>
<row>
<description value="Applications available on mobile devices" label="%s:"/>
<select-app id="newsettings[fw_mobile_app_list]" multiple="true" tags="true"/>
</row>
<row>
<description value="security" span="all" class="subHeader"/>
</row>
<row>
<description value="Cookie path (allows multiple eGW sessions with different directories, has problemes with SiteMgr!)" label="%s:"/>
<select id="newsettings[cookiepath]">
<option value="">Document root (default)</option>
<option value="egroupware">eGroupWare directory</option>
</select>
</row>
<row>
<description value="Cookie domain (default empty means use full domain name, for SiteMgr eg. &quot;.domain.com&quot; allows to use the same cookie for egw.domain.com and www.domain.com)" label="%s:"/>
<textbox id="newsettings[cookiedomain]"/>
</row>
<row>
<vbox>
<description value="check ip address of all sessions"/>
<description value="switch it off, if users are randomly thrown out" label="%s:"/>
<description value="Your session could not be verified."/>
</vbox>
<select id="newsettings[sessions_checkip]">
<option value="True">{Yes} - {more secure}</option>
<option value="">No</option>
</select>
</row>
<row>
<description value="Use secure cookies (transmitted only via https)"/>
<select id="newsettings[insecure_cookies]">
<option value="">{Yes} - {more secure}</option>
<option value="insecure">No</option>
</select>
</row>
<row>
<description value="Deny all users access to grant other users access to their entries ?" label="%s:"/>
<select id="newsettings[deny_user_grants_access]">
<option value="">No</option>
<option value="True">Yes</option>
</select>
</row>
<!--
<row>
<description value="Default file system space per user"/>
<textbox id="newsettings[vfs_default_account_size_number]" type="text" size="7"/>
<td>{Default_file_system_space_per_user}/{group_?}:</td>
<td>
<input type="text" name="newsettings[vfs_default_account_size_number]" size="7" value="{value_vfs_default_account_size_number}">&nbsp;&nbsp;
<select name="newsettings[vfs_default_account_size_type]">
<option value="gb"{selected_vfs_default_account_size_type_gb}>GB</option>
<option value="mb"{selected_vfs_default_account_size_type_mb}>MB</option>
<option value="kb"{selected_vfs_default_account_size_type_kb}>KB</option>
<option value="b"{selected_vfs_default_account_size_type_b}>B</option>
</select>
</td>
</row> -->
<row>
<description value="How many days should entries stay in the access log, before they get deleted (default 90) ?" label="%s:"/>
<textbox id="newsettings[max_access_log_age]" size="5"/>
</row>
<row>
<description value="After how many unsuccessful attempts to login, an account should be blocked (default 3) ?" label="%s:"/>
<textbox id="newsettings[num_unsuccessful_id]" size="5"/>
</row>
<row>
<description value="After how many unsuccessful attempts to login, an IP should be blocked (default 3) ?" label="%s:"/>
<textbox id="newsettings[num_unsuccessful_ip]" size="5"/>
</row>
<row>
<description value="How many minutes should an account or IP be blocked (default 30) ?" label="%s:"/>
<textbox id="newsettings[block_time]" size="5"/>
</row>
<row>
<description value="Force users to change their password regularily?(empty for no,number for after that number of days" label="%s:"/>
<textbox id="newsettings[change_pwd_every_x_days]" size="5"/>
</row>
<row>
<description value="Warn users about the need to change their password? The number set here should be lower than the value used to enforce the change of passwords every X days. Only effective when enforcing of password change is enabled. (empty for no,number for number of days before they must change)" label="%s:"/>
<textbox id="newsettings[warn_about_upcoming_pwd_change]" size="5"/>
</row>
<row>
<description value="Passwords require a minimum number of characters" label="%s:"/>
<select id="newsettings[force_pwd_length]">
<option value="">None</option>
<option value="6">6</option>
<option value="7">7</option>
<option value="8">8</option>
<option value="10">10</option>
<option value="12">12</option>
<option value="14">14</option>
<option value="16">16</option>
</select>
</row>
<row>
<vbox>
<description value="Passwords requires this number of different character classes"/>
<description value="Uppercase, lowercase, number, special char" label="(%s)"/>
</vbox>
<select id="newsettings[force_pwd_strength]">
<option value="">None</option>
<option value="2">2</option>
<option value="3">3</option>
<option value="4">4</option>
</select>
</row>
<row>
<description value="Reject passwords containing part of username or full name (3 or more characters long)" label="%s:"/>
<select id="newsettings[passwd_forbid_name]">
<option value="no">No</option>
<option value="yes">Yes</option>
</select>
</row>
<row>
<description value="Admin email addresses (comma-separated) to be notified about the blocking (empty for no notify)" label="%s:"/>
<textbox id="newsettings[admin_mails]" size="40"/>
</row>
<!-- not used at the moment RalfBecker 2007/05/17
<row>
<description value="Disable &quot;auto completion&quot; of the login form " label="%s:"/>
<select id="newsettings[autocomplete_login]">
<option value="">No</option>
<option value="True">Yes</option>
</select>
</row> -->
<row>
<vbox>
<description value="How many entries should non-admins be able to export (empty = no limit, no = no export)"/>
<description value="This controls exports and merging."/>
</vbox>
<textbox id="newsettings[export_limit]" size="5"/>
</row>
<row>
<description value="Group excepted from above export limit (admins are always excepted)" label="%s:"/>
<select-account id="newsettings[export_limit_excepted]" account_type="groups" multiple="true" tags="true"/>
</row>
<row>
<vbox>
<description value="Allow remote administration from following install ID's (comma separated)"/>
<description id="newsettings[install_id]" label="Own install ID:"/>
</vbox>
<textbox id="newsettings[allow_remote_admin]" size="40"/>
</row>
<row>
<description value="Should exceptions contain a trace (including function arguments)" label="%s:"/>
<select id="newsettings[exception_show_trace]">
<option value="">{No} - {more secure}</option>
<option value="True">Yes</option>
</select>
</row>
<row>
<description value="Disable minifying of javascript and CSS files" label="%s:"/>
<select id="newsettings[debug_minify]">
<option value="">{No} - {Default}</option>
<option value="True">Yes</option>
<option value="debug">Debug</option>
</select>
</row>
</rows>
</grid>
<hbox class="dialogFooterToolbar">
<button id="save" label="Save"/>
<button id="apply" label="Apply"/>
<button id="cancel" label="Cancel"/>
</hbox>
</template>
</overlay>

View File

@ -160,6 +160,13 @@ span#admin-mailaccount_acc_id {
select#admin-mailaccount_ident_id {
width: 95%;
}
/**
* new et2 site configuration
*/
table.admin-config td.subHeader span {
font-weight: bold;
font-size: 110%;
}
@media all {
div.dhtmlxTree td.standartTreeRow span.selectedTreeRow {
background-color: #ffdd73;

View File

@ -424,11 +424,16 @@ var et2_selectbox = (function(){ "use strict"; return et2_inputWidget.extend(
}
// Read the option-tags
var options = et2_directChildrenByTagName(_node, "options");
var options = et2_directChildrenByTagName(_node, "option");
var egw = this.egw();
for (var i = 0; i < options.length; i++)
{
this.options.select_options[et2_readAttrWithDefault(options[i], "value", options[i].textContent)] = {
"label": options[i].textContent,
// allow options to contain multiple translated sub-strings eg: {Firstname}.{Lastname}
"label": options[i].textContent.replace(/{([^}]+)}/g, function(str,p1)
{
return egw.lang(p1);
}),
"title": et2_readAttrWithDefault(options[i], "title", "")
};
}

174
doc/config2xet.php Normal file
View File

@ -0,0 +1,174 @@
<?php
/**
* EGroupware - convert old config.tpl to et2 config.xet
*
* @link http://www.egroupware.org
* @license http://opensource.org/licenses/gpl-license.php GPL - GNU General Public License
* @author Ralf Becker <rb@stylite.de>
* @copyright 2016 by Ralf Becker <rb@stylite.de>
* @version $Id$
*/
if (php_sapi_name() !== 'cli' && !empty($_GET['app']) && preg_match('/^[a-z0-9_-]+$/i', $_GET['app']))
{
$app = $_GET['app'];
}
elseif ($_SERVER['argc'] > 1)
{
$app = $_SERVER['argv'][1];
}
else
{
$app = 'admin';
}
include __DIR__.'/../phpgwapi/inc/common_functions.inc.php';
$path = EGW_SERVER_ROOT.'/'.$app.'/templates/default/config.tpl';
if (!file_exists($path) || !($content = file_get_contents($path)))
{
die("File not found: $path");
}
if (!preg_match('|<!-- BEGIN body -->(.*)<!-- END body -->|sui', $content, $table) &&
!preg_match('|\<table[^>]*\>(.*)</table\>|sui', $content, $table))
{
die('No BEGIN/END body or table tag found!');
}
$table[1] = preg_replace('/^<!-- (BEGIN|END)\s*[^ -]+-->/U', '', $table[1]);
if (!preg_match_all('|(<!--[^<-]*)?\s*<tr[^>]*>(.*)</tr>|Usui', $table[1], $trs, PREG_PATTERN_ORDER))
{
die('No tr tags found!');
}
if (php_sapi_name() !== 'cli')
{
EGroupware\Api\Header\Content::type('config.xet', 'text/plain', 0, true, false);
}
echo '<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE overlay PUBLIC "-//Stylite AG//eTemplate 2//EN" "http://www.egroupware.org/etemplate2.dtd">
<!-- $Id$ -->
<overlay>
<template id="'.$app.'.config" template="" lang="" group="0" version="16.1">
<grid width="100%" class="admin-config egwGridView_grid">
<columns>
<column width="60%"/>
<column/>
</columns>
<rows>
<row>
<description value="Site configuration" span="all" class="subHeader"/>
</row>
';
foreach($trs[2] as $n => $tr)
{
if (strpos($tr, '{title}') || strpos($tr, '<input type="submit"')) continue;
if (!preg_match_all('|<td\s*([^>]*)>(.*)</td>|Usui', $tr, $tds, PREG_PATTERN_ORDER))
{
die("No td tags found in $n. tr: $tr");
}
if (($commented = !empty($trs[1][$n])? $trs[1][$n] : ''))
{
echo "\t\t\t\t$commented\n";
}
echo "\t\t\t\t<row>\n";
foreach($tds[2] as $t => $td)
{
if (preg_match('|^\s*<([^ >]+)\s*([^/>]+)/?>(.*)\s*$|sui', $td, $matches))
{
$attrs = preg_match_all('|\s*([^=]+)="([^"]+)"|', $matches[2], $attrs) ?
array_combine($attrs[1], $attrs[2]) : array();
switch($matches[1])
{
case 'input':
echo "\t\t\t\t\t<textbox id=\"".$attrs['name'].'"';
unset($attrs['value'], $attrs['name']);
foreach($attrs as $name => $value)
{
echo " $name=\"$value\"";
}
echo "/>\n";
if (trim($matches[3]))
{
if ($commented)
{
echo $tr;
continue;
}
echo "\t\t\t\t\t<!-- ".trim($matches[3])." -->\n";
}
break;
case 'select':
echo "\t\t\t\t\t<select id=\"".$attrs['name'].'"';
unset($attrs['name']);
foreach($attrs as $name => $value)
{
echo " $name=\"$value\"";
}
echo ">\n";
if (preg_match_all('|<option\s+value="([^"]*)"\s*({selected_[^}]+})?>(.*)</option>|Usui', $matches[3], $options))
{
foreach($options[3] as $i => $label)
{
$label = preg_replace_callback('/{lang_([^}]+)}/', function($matches)
{
return '{'.str_replace('_', ' ', $matches[1]).'}';
}, $label);
// no need for spezial sub-string translation syntax
if ($label[0] == '{' && strpos($label, '{', 1) === false && substr($label, -1) == '}')
{
$label = substr($label, 1, -1);
}
echo "\t\t\t\t\t\t<option value=\"".$options[1][$i].'">'.$label."</option>\n";
}
}
else
{
echo "\t\t\t\t\t<!-- ".trim($matches[3])." -->\n";
}
echo "\t\t\t\t\t</select>\n";
break;
default:
echo "\t\t\t\t\t<!-- $tr -->\n";
break;
}
}
elseif (preg_match('/^\s*([^{]*){lang_([^}]+)}\s*(.*)$/sui',
str_replace(array('&nbsp;', '<b>', '</b>'), '', $td), $matches))
{
if (!$commented && trim($matches[1])) echo "\t\t\t\t\t<!-- $matches[1] -->\n";
echo "\t\t\t\t\t<description value=\"".htmlspecialchars(str_replace('_', ' ', $matches[2])).'"';
if (trim($matches[3]) == ':')
{
echo ' label="%s:"';
unset($matches[3]);
}
if (strpos($tds[1][$t], 'colspan='))
{
echo ' span="all" class="subHeader"';
}
echo "/>\n";
if (!$commented && !empty($matches[3]) && trim($matches[3])) echo "\t\t\t\t\t<!-- ".trim($matches[3])." -->\n";
}
elseif(!$commented)
{
echo "\t\t\t\t\t<!-- ".trim($td)." -->\n";
}
}
echo "\t\t\t\t</row>".($commented ? ' -->' : '')."\n";
}
echo
' </rows>
</grid>
<hbox class="dialogFooterToolbar">
<button id="save" label="Save"/>
<button id="apply" label="Apply"/>
<button id="cancel" label="Cancel"/>
</hbox>
</template>
</overlay>
';