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