2 commits - lib/client lib/kolab_client_task.php lib/locale public_html/js public_html/skins
Aleksander Machniak
machniak at kolabsys.com
Fri Sep 7 13:25:41 CEST 2012
lib/client/kolab_client_task_main.php | 24 ++
lib/kolab_client_task.php | 18 +
lib/locale/en.php | 2
public_html/js/kolab_admin.js | 218 +++++++++++++++++++++++-
public_html/skins/default/images/domain_ico.png |binary
public_html/skins/default/images/user_ico.png |binary
public_html/skins/default/style.css | 74 +++++++-
public_html/skins/default/templates/main.html | 9
public_html/skins/default/ui.js | 36 +++
9 files changed, 352 insertions(+), 29 deletions(-)
New commits:
commit 98b5aef5b20451da9b1ca01993e21e92bbef35ff
Merge: e9ca874 f2a2fd1
Author: Aleksander Machniak <alec at alec.pl>
Date: Fri Sep 7 13:25:26 2012 +0200
Merge branch 'master' of ssh://git.kolab.org/git/kolab-wap
commit e9ca874fc54fb6bc8429a3a6e7866e6e6e92820b
Author: Aleksander Machniak <alec at alec.pl>
Date: Fri Sep 7 13:24:49 2012 +0200
Added select widget
Added domain selector
diff --git a/lib/client/kolab_client_task_main.php b/lib/client/kolab_client_task_main.php
index 8abb186..6921377 100644
--- a/lib/client/kolab_client_task_main.php
+++ b/lib/client/kolab_client_task_main.php
@@ -27,15 +27,27 @@ class kolab_client_task_main extends kolab_client_task
protected $_menu = array(
'user' => 'users',
'group' => 'groups',
- 'resource' => 'resources',
'domain' => 'domains',
'role' => 'roles',
+ 'resource' => 'resources',
'about' => 'about',
);
public function action_default()
{
+ // handle domain change
+ if ($domain = $this->get_input('domain', 'GET')) {
+ $result = $this->api->get('system.select_domain', array('domain' => $domain));
+
+ if (!$result->get_error_code()) {
+ $_SESSION['user']['domain'] = $domain;
+ }
+ else {
+ $this->output->command('display_message', $this->translate('error.domainselect'), 'error');
+ }
+ }
+
// assign token
$this->output->set_env('token', $_SESSION['user']['token']);
@@ -50,7 +62,7 @@ class kolab_client_task_main extends kolab_client_task
// @TODO: check capabilities
$capabilities = $this->capabilities();
- $this->menu = Array();
+ $this->menu = array();
foreach ($this->_menu as $task => $api_task) {
if ($task !== "about") {
@@ -64,9 +76,13 @@ class kolab_client_task_main extends kolab_client_task
}
$this->output->assign('tasks', $this->menu);
-
$this->output->assign('main_menu', $this->menu());
$this->output->assign('user', $_SESSION['user']);
- }
+ // Domains list
+ if ($domains = $this->get_domains()) {
+ sort($domains, SORT_LOCALE_STRING);
+ $this->output->set_env('domains', $domains);
+ }
+ }
}
diff --git a/lib/kolab_client_task.php b/lib/kolab_client_task.php
index 910de6c..7e21852 100644
--- a/lib/kolab_client_task.php
+++ b/lib/kolab_client_task.php
@@ -181,8 +181,6 @@ class kolab_client_task
if ($login['username']) {
$result = $this->api->login($login['username'], $login['password'], $login['domain']);
- //console($result);
-
if ($token = $result->get('session_token')) {
$user = array(
'token' => $token,
@@ -401,6 +399,18 @@ class kolab_client_task
}
/**
+ * Returns output environment variable value
+ *
+ * @param string $name Variable name
+ *
+ * @return mixed Variable value
+ */
+ public function get_env($name)
+ {
+ return $this->output->get_env($name);
+ }
+
+ /**
* Returns configuration option value.
*
* @param string $name Option name
@@ -661,10 +671,6 @@ class kolab_client_task
{
$post = $this->get_input('login', 'POST');
- $auth = Auth::get_instance();
- $conf = Conf::get_instance();
- $auth->connect();
-
$username = kolab_html::label(array(
'for' => 'login_name',
'content' => $this->translate('login.username')), true)
diff --git a/lib/locale/en.php b/lib/locale/en.php
index a9fde09..cbf9d62 100644
--- a/lib/locale/en.php
+++ b/lib/locale/en.php
@@ -23,6 +23,7 @@ $LANG['domain.system'] = 'System';
$LANG['domain.type_id'] = 'Standard Domain';
$LANG['error'] = 'Error';
+$LANG['error.domainselect'] = 'Unable to change domain!';
$LANG['form.required.empty'] = 'Some of the required fields are empty!';
$LANG['form.maxcount.exceeded'] = 'Maximum count of items exceeded!';
@@ -46,6 +47,7 @@ $LANG['group.uniquemember'] = 'Members';
$LANG['info'] = 'Information';
$LANG['internalerror'] = 'Internal system error!';
$LANG['loading'] = 'Loading...';
+
$LANG['login.username'] = 'Username:';
$LANG['login.password'] = 'Password:';
$LANG['login.domain'] = 'Domain:';
diff --git a/public_html/js/kolab_admin.js b/public_html/js/kolab_admin.js
index be08f8a..701f818 100644
--- a/public_html/js/kolab_admin.js
+++ b/public_html/js/kolab_admin.js
@@ -413,6 +413,47 @@ function kolab_admin()
}
};
+ // position and display popup
+ this.popup_show = function(e, popup)
+ {
+ var popup = $(popup),
+ pos = this.mouse_pos(e),
+ win = $(window),
+ w = popup.width(),
+ h = popup.height(),
+ left = pos.left - w,
+ top = pos.top;
+
+ if (top + h > win.height())
+ top -= h;
+ if (left + w > win.width())
+ left -= w;
+
+ popup.css({left: left + 'px', top: top + 'px'}).show();
+ e.stopPropagation();
+ };
+
+ // Return absolute mouse position of an event
+ this.mouse_pos = function(e)
+ {
+ if (!e) e = window.event;
+
+ var mX = (e.pageX) ? e.pageX : e.clientX,
+ mY = (e.pageY) ? e.pageY : e.clientY;
+
+ if (document.body && document.all) {
+ mX += document.body.scrollLeft;
+ mY += document.body.scrollTop;
+ }
+
+ if (e._offset) {
+ mX += e._offset.left;
+ mY += e._offset.top;
+ }
+
+ return { left:mX, top:mY };
+ };
+
/*********************************************************/
/********* keyboard autocomplete methods *********/
@@ -682,7 +723,10 @@ function kolab_admin()
// replace some textarea fields with pretty/smart input lists
$('textarea[data-type="list"]', form)
- .each(function() { kadm.form_element_wrapper(this); });
+ .each(function() { kadm.form_list_element_wrapper(this); });
+ // create smart select fields
+ $('input[data-type="select"]', form)
+ .each(function() { kadm.form_select_element_wrapper(this); });
};
// Form serialization
@@ -690,7 +734,7 @@ function kolab_admin()
{
var form = $(data.id);
- // replace some textarea fields with pretty/smart input lists
+ // smart list fields
$('textarea[data-type="list"]', form).not('disabled').each(function() {
var i, v, value = [],
re = RegExp('^' + RegExp.escape(this.name) + '\[[0-9-]+\]$');
@@ -713,6 +757,11 @@ function kolab_admin()
data.json[this.name] = value;
});
+ // smart selects
+ $('input[data-type="select"]', form).each(function() {
+ delete data.json[this.name];
+ });
+
return data;
};
@@ -728,12 +777,12 @@ function kolab_admin()
// remove old wrapper
$('span[class="listarea"]', elem.parent()).remove();
// insert new list element
- this.form_element_wrapper(elem.get(0));
+ this.form_list_element_wrapper(elem.get(0));
}
};
- // Replaces form element with smart element
- this.form_element_wrapper = function(form_element)
+ // Replaces form element with smart list element
+ this.form_list_element_wrapper = function(form_element)
{
var i = 0, j = 0, list = [], elem, e = $(form_element),
form = form_element.form,
@@ -746,7 +795,7 @@ function kolab_admin()
e.hide();
if (autocomplete)
- list = this.env.assoc_fields[form_element.name];
+ list = this.env.assoc_fields ? this.env.assoc_fields[form_element.name] : [];
else if (form_element.value)
list = form_element.value.split("\n");
@@ -835,7 +884,7 @@ function kolab_admin()
content = '<span class="listelement"><span class="actions">'
+ (!ac ? '<span title="" class="add"></span>' : ac && idx == -1 ? '<span title="" class="search"></span>' : '')
+ (!ac || idx >= 0 ? '<span title="" class="reset"></span>' : '')
- + '</span><input></span>';
+ + '</span><input type="text" autocomplete="off"></span>';
elem = $(content);
input = $('input', elem);
@@ -928,6 +977,147 @@ function kolab_admin()
af[name][key] = val;
};
+ // Replaces form element with smart select element
+ this.form_select_element_wrapper = function(form_element)
+ {
+ var e = $(form_element),
+ form = form_element.form,
+ elem = $('<span class="link"></span>'),
+ area = $('<span class="listarea autocomplete select popup"></span>'),
+ content = $('<span class="listcontent"></span>');
+ list = this.env.assoc_fields ? this.env.assoc_fields[form_element.name] : [];
+
+ elem.text(e.val()).css({cursor: 'pointer'})
+ .click(function(e) {
+ var popup = $('span.listarea', this.parentNode);
+ kadm.popup_show(e, popup);
+ $('input', popup).val('').focus();
+ $('span.listcontent > span.listelement', popup).removeClass('selected').show();
+ })
+ .appendTo(form_element.parentNode);
+
+ if (list.length <= 1)
+ return;
+
+ if (form_element.type != 'hidden') e.hide();
+ area.hide();
+
+ elem = this.form_list_element(form, {
+ autocomplete: true,
+ element: e
+ }, -1);
+
+ elem.appendTo(area);
+ content.appendTo(area);
+
+ // popup events
+ $('input', area)
+ .click(function(e) {
+ // stop click on the popup
+ e.stopPropagation();
+ })
+ .keypress(function(e) {
+ // prevent form submission with Enter key
+ if (e.which == 13)
+ e.preventDefault();
+ })
+ .keyup(function(e) {
+ // filtering
+ var s = this.value,
+ options = $('span.listcontent > span.listelement', area);
+
+ // Enter key
+ if (e.which == 13) {
+ options.filter('.selected').click()
+ return;
+ }
+ // Escape
+ else if (e.which == 27) {
+ area.hide();
+ this.value = s = '';
+ }
+ // UP/Down arrows
+ else if (e.which == 38 || e.which == 40) {
+ options = options.not(':hidden');
+ var selected = options.filter('.selected');
+
+ if (!selected.length) {
+ if (e.which == 40) // Down key
+ options.first().addClass('selected').parent().get(0).scrollTop = 0;
+ }
+ else {
+ var focused = selected[e.which == 40 ? 'next' : 'prev']();
+
+ while (focused.length && focused.is(':hidden'))
+ focused = selected[e.which == 40 ? 'next' : 'prev']();
+
+ if (!focused.length)
+ focused = options[e.which == 40 ? 'first' : 'last']();
+
+ if (focused.length) {
+ selected.removeClass('selected');
+ focused.addClass('selected');
+
+ var parent = focused.parent(),
+ parent_height = parent.height(),
+ parent_top = parent.get(0).scrollTop,
+ top = focused.offset().top - parent.offset().top,
+ height = focused.height();
+
+ if (top < 0)
+ parent.get(0).scrollTop = 0;
+ else if (top >= parent_height)
+ parent.get(0).scrollTop = top - parent_height + height + parent_top;
+ }
+ }
+
+ return;
+ }
+
+ if (!s) {
+ options.show().removeClass('selected');
+ return;
+ }
+
+ options.each(function() {
+ var o = $(this), v = o.data('value');
+ o[v.indexOf(s) != -1 ? 'show' : 'hide']().removeClass('selected');
+ });
+
+ options = options.not(':hidden');
+ if (options.length == 1)
+ options.addClass('selected');
+ });
+
+ // add option rows
+ $.each(list, function(i, v) {
+ var elem = kadm.form_select_option_element(form, {value: v, key: v, element: e});
+ elem.appendTo(content);
+ });
+
+ area.appendTo(form_element.parentNode);
+ };
+
+ // Creates option element for smart select
+ this.form_select_option_element = function(form, data)
+ {
+ // build element content
+ var elem = $('<span class="listelement"></span>')
+ .data('value', data.key).text(data.value)
+ .click(function(e) {
+ var val = $(this).data('value'),
+ elem = $(data.element),
+ old_val = elem.val();
+
+ $('span.link', elem.parent()).text(val);
+ elem.val(val);
+ if (val != old_val)
+ elem.change();
+ });
+
+ return elem;
+ };
+
/*********************************************************/
/********* Forms *********/
@@ -1458,6 +1648,19 @@ RegExp.escape = function(str)
return String(str).replace(/([.*+?^=!:${}()|[\]\/\\])/g, '\\$1');
};
+// make a string URL safe (and compatible with PHP's rawurlencode())
+function urlencode(str)
+{
+ if (window.encodeURIComponent)
+ return encodeURIComponent(str).replace('*', '%2A');
+
+ return escape(str)
+ .replace('+', '%2B')
+ .replace('*', '%2A')
+ .replace('/', '%2F')
+ .replace('@', '%40');
+};
+
// Initialize application object (don't change var name!)
var kadm = new kolab_admin();
@@ -1465,4 +1668,5 @@ var kadm = new kolab_admin();
$(document).click(function() {
// destroy autocompletion
kadm.ac_stop();
+ $('.popup').hide();
});
diff --git a/public_html/skins/default/images/domain_ico.png b/public_html/skins/default/images/domain_ico.png
new file mode 100644
index 0000000..98d4c3b
Binary files /dev/null and b/public_html/skins/default/images/domain_ico.png differ
diff --git a/public_html/skins/default/images/user_ico.png b/public_html/skins/default/images/user_ico.png
new file mode 100644
index 0000000..89badba
Binary files /dev/null and b/public_html/skins/default/images/user_ico.png differ
diff --git a/public_html/skins/default/style.css b/public_html/skins/default/style.css
index 64d0ef1..be30a88 100644
--- a/public_html/skins/default/style.css
+++ b/public_html/skins/default/style.css
@@ -140,6 +140,12 @@ td.label {
white-space: nowrap;
}
+#topmenu > span {
+ color: #f8fcff;
+ font-size: 11px;
+ text-shadow: black 1px 1px;
+}
+
#navigation {
margin: 0 15px;
text-align: right;
@@ -243,14 +249,20 @@ td.label {
#topmenu .logout {
background: url(images/logout.png) center 10px no-repeat;
- padding: 9px 16px;
+ padding: 10px 16px;
margin: 0px 5px 0px 10px;
}
#topmenu .login {
- color: #f8fcff;
- font-size: 11px;
- text-shadow: black 1px 1px;
+ padding-left: 20px;
+ margin-right: 20px;
+ background: url(images/user_ico.png) 0 1px no-repeat;
+}
+
+#topmenu .domain {
+ padding-left: 20px;
+ margin-right: 10px;
+ background: url(images/domain_ico.png) 0 2px no-repeat;
}
#navigation ul {
@@ -515,6 +527,17 @@ pre.debug {
overflow: auto;
}
+.popup {
+ display: none;
+ position: absolute;
+ border: none;
+ border-radius: 3px;
+ box-shadow: 0 2px 6px 0 #333;
+ -moz-box-shadow: 0 2px 6px 0 #333;
+ -webkit-box-shadow: 0 2px 6px 0 #333;
+ -o-box-shadow: 0 2px 6px 0 #333;
+}
+
/********* Form smart inputs *********/
span.listarea {
@@ -523,13 +546,16 @@ span.listarea {
max-height: 209px;
overflow-y: auto;
overflow-x: hidden;
- margin: 0 0 1px;
+ margin: 0;
padding: 0;
background-color: white;
border: 1px solid #d0d0d0;
border-radius: 3px;
-moz-border-radius: 3px;
-webkit-border-radius: 3px;
+ text-align: left;
+ text-shadow: none;
+ color: black;
}
span.listelement {
@@ -639,6 +665,39 @@ span.listarea.autocomplete span.listelement input.autocomplete {
background-color: #f0f0f0;
}
+span.listarea.select {
+ width: 200px;
+}
+
+span.listarea.select > span.listelement input {
+ width: 180px;
+}
+
+span.listcontent {
+ display: block;
+ padding: 0;
+ margin: 0;
+ overflow: hidden;
+ max-height: 94px;
+ overflow-x: hidden;
+ overflow-y: auto;
+ border-top: 1px solid #d0d0d0;
+ background-color: #f5f5f5;
+ cursor: default;
+}
+
+span.listcontent span.listelement {
+ padding-left: 3px;
+}
+
+span.listcontent span.listelement:hover {
+ background-color: #d6efff;
+}
+
+span.listcontent span.listelement.selected {
+ background-color: #d6efff;
+}
+
/***** autocomplete list *****/
#autocompletepane
@@ -840,6 +899,11 @@ fieldset.tabbed
vertical-align: top;
}
+#domain-selector span.link {
+ color: #1E90FF;
+ text-decoration: none;
+}
+
/**** User/Group task elements ****/
#userlist table,
diff --git a/public_html/skins/default/templates/main.html b/public_html/skins/default/templates/main.html
index ed902c6..7dbdf55 100644
--- a/public_html/skins/default/templates/main.html
+++ b/public_html/skins/default/templates/main.html
@@ -13,6 +13,15 @@
<div id="logo" onclick="document.location='.'"></div>
<div id="topmenu">
<span class="login">{$user.fullname}</span>
+ <span id="domain-selector" class="domain">
+ {if count($engine->get_env('domains')) > 1}
+ <form id="domain-selector-form" method="post" style="display: inline">
+ <input data-type="select" name="domain" type="hidden" value="{$user.domain}" />
+ </form>
+ {else}
+ {$user.domain}
+ {/if}
+ </span>
<span class="logout link" title="{$engine->translate('Logout')}" onclick="kadm.main_logout()"></span>
</div>
<div id="navigation">{$main_menu}</div>
diff --git a/public_html/skins/default/ui.js b/public_html/skins/default/ui.js
index 7c598e6..94891c6 100644
--- a/public_html/skins/default/ui.js
+++ b/public_html/skins/default/ui.js
@@ -45,7 +45,7 @@ function search_init(task)
if (this.value == kadm.t('search'))
$(this).val('').removeClass('inactive');
});
-}
+};
function search_reset()
{
@@ -54,7 +54,7 @@ function search_reset()
input.val(kadm.t('search')).addClass('inactive');
kadm.command(kadm.env.search_task + '.list', {search: ''});
-}
+};
function search_details()
{
@@ -64,7 +64,7 @@ function search_details()
div.slideDown(200);
else
div.slideUp(200);
-}
+};
/**
* Fieldsets-to-tabs converter
@@ -110,7 +110,7 @@ function init_tabs(id, current)
// add the tab to container
tab.append(a).appendTo(tabs);
});
-}
+};
function show_tab(id, index)
{
@@ -122,14 +122,30 @@ function show_tab(id, index)
// Select/unselect tab
$('#tab'+idx).toggleClass('tablink-selected', idx == index);
});
-}
+};
+
+// Domain selector initializer
+function domain_selector()
+{
+ // domain selector
+ if (kadm.env.domains && kadm.env.domains.length > 1) {
+ var form = $('#domain-selector-form');
+
+ kadm.env.assoc_fields = {domain: kadm.env.domains};
+ kadm.form_init('domain-selector-form');
+
+ $('input[name="domain"]', form).change(function() {
+ window.location = '?domain=' + urlencode(this.value);
+ });
+ }
+};
// Form "onload" handler
function form_load(id)
{
if (id != 'search-form')
init_tabs(id);
-}
+};
// UI resize handler
function ui_resize()
@@ -139,8 +155,13 @@ function ui_resize()
if (h > 100) {
$('#taskcontent').height(h - 22);
}
-}
+};
+// UI loader
+function ui_load()
+{
+ domain_selector();
+}
/**
* UI Initialization
@@ -148,3 +169,4 @@ function ui_resize()
kadm.add_event_listener('form-load', form_load);
kadm.add_event_listener('http-response', ui_resize);
//$(window).resize(function() { ui_resize(); });
+$(window).load(function() { ui_load(); });
More information about the commits
mailing list