2 commits - kolab.org/www

Torsten Grote grote at kolabsys.com
Wed Jul 3 15:30:18 CEST 2013


 kolab.org/www/drupal-7.18/sites/all/modules/i18n/INSTALL.txt                                  |   40 
 kolab.org/www/drupal-7.18/sites/all/modules/i18n/LICENSE.txt                                  |  339 ++
 kolab.org/www/drupal-7.18/sites/all/modules/i18n/README.txt                                   |   27 
 kolab.org/www/drupal-7.18/sites/all/modules/i18n/i18n.api.php                                 |  117 
 kolab.org/www/drupal-7.18/sites/all/modules/i18n/i18n.info                                    |   18 
 kolab.org/www/drupal-7.18/sites/all/modules/i18n/i18n.install                                 |   57 
 kolab.org/www/drupal-7.18/sites/all/modules/i18n/i18n.js                                      |   21 
 kolab.org/www/drupal-7.18/sites/all/modules/i18n/i18n.module                                  |  609 ++++
 kolab.org/www/drupal-7.18/sites/all/modules/i18n/i18n.pages.inc                               |   95 
 kolab.org/www/drupal-7.18/sites/all/modules/i18n/i18n.test                                    |  460 +++
 kolab.org/www/drupal-7.18/sites/all/modules/i18n/i18n.variable.inc                            |   69 
 kolab.org/www/drupal-7.18/sites/all/modules/i18n/i18n_block/i18n_block.i18n.inc               |   65 
 kolab.org/www/drupal-7.18/sites/all/modules/i18n/i18n_block/i18n_block.inc                    |   52 
 kolab.org/www/drupal-7.18/sites/all/modules/i18n/i18n_block/i18n_block.info                   |   16 
 kolab.org/www/drupal-7.18/sites/all/modules/i18n/i18n_block/i18n_block.install                |  109 
 kolab.org/www/drupal-7.18/sites/all/modules/i18n/i18n_block/i18n_block.js                     |   30 
 kolab.org/www/drupal-7.18/sites/all/modules/i18n/i18n_block/i18n_block.module                 |  345 ++
 kolab.org/www/drupal-7.18/sites/all/modules/i18n/i18n_block/i18n_block.test                   |  197 +
 kolab.org/www/drupal-7.18/sites/all/modules/i18n/i18n_contact/i18n_contact.i18n.inc           |   57 
 kolab.org/www/drupal-7.18/sites/all/modules/i18n/i18n_contact/i18n_contact.info               |   13 
 kolab.org/www/drupal-7.18/sites/all/modules/i18n/i18n_contact/i18n_contact.module             |   94 
 kolab.org/www/drupal-7.18/sites/all/modules/i18n/i18n_field/i18n_field.i18n.inc               |   90 
 kolab.org/www/drupal-7.18/sites/all/modules/i18n/i18n_field/i18n_field.inc                    |  182 +
 kolab.org/www/drupal-7.18/sites/all/modules/i18n/i18n_field/i18n_field.info                   |   14 
 kolab.org/www/drupal-7.18/sites/all/modules/i18n/i18n_field/i18n_field.install                |   85 
 kolab.org/www/drupal-7.18/sites/all/modules/i18n/i18n_field/i18n_field.module                 |  380 ++
 kolab.org/www/drupal-7.18/sites/all/modules/i18n/i18n_field/i18n_field.pages.inc              |   36 
 kolab.org/www/drupal-7.18/sites/all/modules/i18n/i18n_field/i18n_field.test                   |  129 
 kolab.org/www/drupal-7.18/sites/all/modules/i18n/i18n_forum/i18n_forum.info                   |   15 
 kolab.org/www/drupal-7.18/sites/all/modules/i18n/i18n_forum/i18n_forum.install                |   19 
 kolab.org/www/drupal-7.18/sites/all/modules/i18n/i18n_forum/i18n_forum.module                 |  164 +
 kolab.org/www/drupal-7.18/sites/all/modules/i18n/i18n_forum/i18n_forum.test                   |   30 
 kolab.org/www/drupal-7.18/sites/all/modules/i18n/i18n_menu/i18n_menu.admin.inc                |  259 +
 kolab.org/www/drupal-7.18/sites/all/modules/i18n/i18n_menu/i18n_menu.i18n.inc                 |  130 
 kolab.org/www/drupal-7.18/sites/all/modules/i18n/i18n_menu/i18n_menu.inc                      |   98 
 kolab.org/www/drupal-7.18/sites/all/modules/i18n/i18n_menu/i18n_menu.info                     |   18 
 kolab.org/www/drupal-7.18/sites/all/modules/i18n/i18n_menu/i18n_menu.install                  |   71 
 kolab.org/www/drupal-7.18/sites/all/modules/i18n/i18n_menu/i18n_menu.module                   |  952 ++++++
 kolab.org/www/drupal-7.18/sites/all/modules/i18n/i18n_menu/i18n_menu.test                     |  292 ++
 kolab.org/www/drupal-7.18/sites/all/modules/i18n/i18n_node/i18n_node.features.inc             |   26 
 kolab.org/www/drupal-7.18/sites/all/modules/i18n/i18n_node/i18n_node.i18n.inc                 |   50 
 kolab.org/www/drupal-7.18/sites/all/modules/i18n/i18n_node/i18n_node.info                     |   17 
 kolab.org/www/drupal-7.18/sites/all/modules/i18n/i18n_node/i18n_node.install                  |   64 
 kolab.org/www/drupal-7.18/sites/all/modules/i18n/i18n_node/i18n_node.js                       |   20 
 kolab.org/www/drupal-7.18/sites/all/modules/i18n/i18n_node/i18n_node.module                   |  572 ++++
 kolab.org/www/drupal-7.18/sites/all/modules/i18n/i18n_node/i18n_node.pages.inc                |  356 ++
 kolab.org/www/drupal-7.18/sites/all/modules/i18n/i18n_node/i18n_node.test                     |   99 
 kolab.org/www/drupal-7.18/sites/all/modules/i18n/i18n_node/i18n_node.variable.inc             |   79 
 kolab.org/www/drupal-7.18/sites/all/modules/i18n/i18n_object.inc                              |  248 +
 kolab.org/www/drupal-7.18/sites/all/modules/i18n/i18n_path/README.txt                         |   25 
 kolab.org/www/drupal-7.18/sites/all/modules/i18n/i18n_path/i18n_path.admin.inc                |  150 +
 kolab.org/www/drupal-7.18/sites/all/modules/i18n/i18n_path/i18n_path.inc                      |   72 
 kolab.org/www/drupal-7.18/sites/all/modules/i18n/i18n_path/i18n_path.info                     |   14 
 kolab.org/www/drupal-7.18/sites/all/modules/i18n/i18n_path/i18n_path.install                  |   92 
 kolab.org/www/drupal-7.18/sites/all/modules/i18n/i18n_path/i18n_path.module                   |  141 +
 kolab.org/www/drupal-7.18/sites/all/modules/i18n/i18n_path/i18n_path.test                     |   88 
 kolab.org/www/drupal-7.18/sites/all/modules/i18n/i18n_redirect/i18n_redirect.info             |   12 
 kolab.org/www/drupal-7.18/sites/all/modules/i18n/i18n_redirect/i18n_redirect.module           |   38 
 kolab.org/www/drupal-7.18/sites/all/modules/i18n/i18n_select/i18n_select.admin.inc            |   27 
 kolab.org/www/drupal-7.18/sites/all/modules/i18n/i18n_select/i18n_select.info                 |   14 
 kolab.org/www/drupal-7.18/sites/all/modules/i18n/i18n_select/i18n_select.module               |  314 ++
 kolab.org/www/drupal-7.18/sites/all/modules/i18n/i18n_select/i18n_select.test                 |   86 
 kolab.org/www/drupal-7.18/sites/all/modules/i18n/i18n_select/i18n_select.variable.inc         |   73 
 kolab.org/www/drupal-7.18/sites/all/modules/i18n/i18n_string/i18n_string.admin.inc            |  313 ++
 kolab.org/www/drupal-7.18/sites/all/modules/i18n/i18n_string/i18n_string.api.php              |  123 
 kolab.org/www/drupal-7.18/sites/all/modules/i18n/i18n_string/i18n_string.i18n.inc             |  105 
 kolab.org/www/drupal-7.18/sites/all/modules/i18n/i18n_string/i18n_string.inc                  | 1282 +++++++++
 kolab.org/www/drupal-7.18/sites/all/modules/i18n/i18n_string/i18n_string.info                 |   18 
 kolab.org/www/drupal-7.18/sites/all/modules/i18n/i18n_string/i18n_string.install              |  260 +
 kolab.org/www/drupal-7.18/sites/all/modules/i18n/i18n_string/i18n_string.module               |  940 ++++++
 kolab.org/www/drupal-7.18/sites/all/modules/i18n/i18n_string/i18n_string.pages.inc            |  429 +++
 kolab.org/www/drupal-7.18/sites/all/modules/i18n/i18n_string/i18n_string.test                 |   92 
 kolab.org/www/drupal-7.18/sites/all/modules/i18n/i18n_string/i18n_string.variable.inc         |   57 
 kolab.org/www/drupal-7.18/sites/all/modules/i18n/i18n_sync/README.txt                         |   22 
 kolab.org/www/drupal-7.18/sites/all/modules/i18n/i18n_sync/i18n_sync.api.php                  |   68 
 kolab.org/www/drupal-7.18/sites/all/modules/i18n/i18n_sync/i18n_sync.features.inc             |   26 
 kolab.org/www/drupal-7.18/sites/all/modules/i18n/i18n_sync/i18n_sync.info                     |   18 
 kolab.org/www/drupal-7.18/sites/all/modules/i18n/i18n_sync/i18n_sync.install                  |   32 
 kolab.org/www/drupal-7.18/sites/all/modules/i18n/i18n_sync/i18n_sync.module                   |  359 ++
 kolab.org/www/drupal-7.18/sites/all/modules/i18n/i18n_sync/i18n_sync.modules.inc              |   68 
 kolab.org/www/drupal-7.18/sites/all/modules/i18n/i18n_sync/i18n_sync.node.inc                 |  170 +
 kolab.org/www/drupal-7.18/sites/all/modules/i18n/i18n_sync/i18n_sync.test                     |   85 
 kolab.org/www/drupal-7.18/sites/all/modules/i18n/i18n_sync/i18n_sync.variable.inc             |   20 
 kolab.org/www/drupal-7.18/sites/all/modules/i18n/i18n_taxonomy/i18n_taxonomy.admin.inc        |  225 +
 kolab.org/www/drupal-7.18/sites/all/modules/i18n/i18n_taxonomy/i18n_taxonomy.i18n.inc         |  136 
 kolab.org/www/drupal-7.18/sites/all/modules/i18n/i18n_taxonomy/i18n_taxonomy.inc              |   52 
 kolab.org/www/drupal-7.18/sites/all/modules/i18n/i18n_taxonomy/i18n_taxonomy.info             |   19 
 kolab.org/www/drupal-7.18/sites/all/modules/i18n/i18n_taxonomy/i18n_taxonomy.install          |  130 
 kolab.org/www/drupal-7.18/sites/all/modules/i18n/i18n_taxonomy/i18n_taxonomy.module           | 1269 +++++++++
 kolab.org/www/drupal-7.18/sites/all/modules/i18n/i18n_taxonomy/i18n_taxonomy.pages.inc        |  169 +
 kolab.org/www/drupal-7.18/sites/all/modules/i18n/i18n_taxonomy/i18n_taxonomy.test             |  251 +
 kolab.org/www/drupal-7.18/sites/all/modules/i18n/i18n_taxonomy/i18n_taxonomy.tokens.inc       |  111 
 kolab.org/www/drupal-7.18/sites/all/modules/i18n/i18n_taxonomy/i18n_taxonomy.views.inc        |   19 
 kolab.org/www/drupal-7.18/sites/all/modules/i18n/i18n_translation/README.txt                  |   47 
 kolab.org/www/drupal-7.18/sites/all/modules/i18n/i18n_translation/i18n_translation.admin.inc  |   98 
 kolab.org/www/drupal-7.18/sites/all/modules/i18n/i18n_translation/i18n_translation.api.php    |   45 
 kolab.org/www/drupal-7.18/sites/all/modules/i18n/i18n_translation/i18n_translation.inc        |  474 +++
 kolab.org/www/drupal-7.18/sites/all/modules/i18n/i18n_translation/i18n_translation.info       |   14 
 kolab.org/www/drupal-7.18/sites/all/modules/i18n/i18n_translation/i18n_translation.install    |   83 
 kolab.org/www/drupal-7.18/sites/all/modules/i18n/i18n_translation/i18n_translation.module     |  351 ++
 kolab.org/www/drupal-7.18/sites/all/modules/i18n/i18n_translation/i18n_translation.pages.inc  |   13 
 kolab.org/www/drupal-7.18/sites/all/modules/i18n/i18n_user/i18n_user.info                     |   12 
 kolab.org/www/drupal-7.18/sites/all/modules/i18n/i18n_user/i18n_user.install                  |   15 
 kolab.org/www/drupal-7.18/sites/all/modules/i18n/i18n_user/i18n_user.module                   |   69 
 kolab.org/www/drupal-7.18/sites/all/modules/i18n/i18n_variable/i18n_variable.class.inc        |   43 
 kolab.org/www/drupal-7.18/sites/all/modules/i18n/i18n_variable/i18n_variable.info             |   18 
 kolab.org/www/drupal-7.18/sites/all/modules/i18n/i18n_variable/i18n_variable.install          |   83 
 kolab.org/www/drupal-7.18/sites/all/modules/i18n/i18n_variable/i18n_variable.module           |  138 +
 kolab.org/www/drupal-7.18/sites/all/modules/i18n/i18n_variable/i18n_variable.test             |   91 
 kolab.org/www/drupal-7.18/sites/all/modules/i18n/i18n_variable/i18n_variable.variable.inc     |   25 
 kolab.org/www/drupal-7.18/sites/all/modules/i18n/tests/i18n_test.info                         |   15 
 kolab.org/www/drupal-7.18/sites/all/modules/i18n/tests/i18n_test.module                       |   46 
 kolab.org/www/drupal-7.18/sites/all/modules/twitter/README.txt                                |   18 
 kolab.org/www/drupal-7.18/sites/all/modules/twitter/TODO.txt                                  |    6 
 kolab.org/www/drupal-7.18/sites/all/modules/twitter/tests/twitter_mock.info                   |    6 
 kolab.org/www/drupal-7.18/sites/all/modules/twitter/tweet.tpl.php                             |   43 
 kolab.org/www/drupal-7.18/sites/all/modules/twitter/twitter.api.php                           |   13 
 kolab.org/www/drupal-7.18/sites/all/modules/twitter/twitter.css                               |   90 
 kolab.org/www/drupal-7.18/sites/all/modules/twitter/twitter.drush.inc                         |   84 
 kolab.org/www/drupal-7.18/sites/all/modules/twitter/twitter.inc                               |  180 +
 kolab.org/www/drupal-7.18/sites/all/modules/twitter/twitter.info                              |   10 
 kolab.org/www/drupal-7.18/sites/all/modules/twitter/twitter.install                           |  129 
 kolab.org/www/drupal-7.18/sites/all/modules/twitter/twitter.lib.php                           | 1377 ++++++++--
 kolab.org/www/drupal-7.18/sites/all/modules/twitter/twitter.module                            |  243 +
 kolab.org/www/drupal-7.18/sites/all/modules/twitter/twitter.pages.inc                         |  416 +--
 kolab.org/www/drupal-7.18/sites/all/modules/twitter/twitter.views.inc                         |   44 
 kolab.org/www/drupal-7.18/sites/all/modules/twitter/twitter.views_default.inc                 |  280 +-
 kolab.org/www/drupal-7.18/sites/all/modules/twitter/twitter_actions/twitter_actions.info      |    7 
 kolab.org/www/drupal-7.18/sites/all/modules/twitter/twitter_actions/twitter_actions.module    |  214 -
 kolab.org/www/drupal-7.18/sites/all/modules/twitter/twitter_actions/twitter_actions.rules.inc |   88 
 kolab.org/www/drupal-7.18/sites/all/modules/twitter/twitter_post/twitter_post.info            |    8 
 kolab.org/www/drupal-7.18/sites/all/modules/twitter/twitter_post/twitter_post.install         |   40 
 kolab.org/www/drupal-7.18/sites/all/modules/twitter/twitter_post/twitter_post.module          |   28 
 kolab.org/www/drupal-7.18/sites/all/modules/twitter/twitter_post/twitter_post.pages.inc       |   15 
 kolab.org/www/drupal-7.18/sites/all/modules/twitter/twitter_signin/twitter_signin.info        |    7 
 kolab.org/www/drupal-7.18/sites/all/modules/twitter/twitter_signin/twitter_signin.module      |   48 
 kolab.org/www/drupal-7.18/sites/all/modules/twitter/twitter_signin/twitter_signin.pages.inc   |    2 
 kolab.org/www/drupal-7.18/sites/all/modules/twitter/twitter_views_field_handlers.inc          |   96 
 138 files changed, 19643 insertions(+), 868 deletions(-)

New commits:
commit 835e925df91b505c8f4caa0a9682401464aae5bd
Author: Torsten Grote <grote at kolabsys.com>
Date:   Wed Jul 3 15:30:02 2013 +0200

    install i18n module

diff --git a/kolab.org/www/drupal-7.18/sites/all/modules/i18n/INSTALL.txt b/kolab.org/www/drupal-7.18/sites/all/modules/i18n/INSTALL.txt
new file mode 100644
index 0000000..7d64ef0
--- /dev/null
+++ b/kolab.org/www/drupal-7.18/sites/all/modules/i18n/INSTALL.txt
@@ -0,0 +1,40 @@
+********************************************************************
+                     D R U P A L    M O D U L E
+********************************************************************
+Internationalization.
+Provides multilingual features.
+
+********************************************************************
+  This is the 7.x version of i18n module, and works with Drupal 7.x
+********************************************************************
+
+********************************************************************
+Updated documentation will be kept on-line at http://drupal.org/node/133977
+********************************************************************
+
+INSTALLATION:
+============
+1. Create folder 'sites/all/modules/i18n' and copy all the modules files, keeping directory structure, to this folder.
+2. If updating, run the update.php script following the standard procedure for Drupal updates.
+
+UPGRADING FROM DRUPAL 6:
+=======================
+- Read up to date instructions at http://drupal.org/node/1113358
+
+POST-INSTALLATION/CONFIGURATION:
+================================
+- First of all review Drupal language settings and make sure you have chosen the right default language.
+- Enable the needed modules grouped under "Internationalization" package
+- Read the on-line handbook on
+
+IMPORTANT:
+==========
+- This module requires a complex set up, make sure you read the handbook and understand the different options
+- Before creating a support request, do read the handbook: http://drupal.org/node/133977
+
+Additional Support
+==================
+See README.txt
+
+====================================================================
+Jose A. Reyero, drupal at reyero dot net, http://reyero.net
diff --git a/kolab.org/www/drupal-7.18/sites/all/modules/i18n/LICENSE.txt b/kolab.org/www/drupal-7.18/sites/all/modules/i18n/LICENSE.txt
new file mode 100644
index 0000000..d159169
--- /dev/null
+++ b/kolab.org/www/drupal-7.18/sites/all/modules/i18n/LICENSE.txt
@@ -0,0 +1,339 @@
+                    GNU GENERAL PUBLIC LICENSE
+                       Version 2, June 1991
+
+ Copyright (C) 1989, 1991 Free Software Foundation, Inc.,
+ 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+                            Preamble
+
+  The licenses for most software are designed to take away your
+freedom to share and change it.  By contrast, the GNU General Public
+License is intended to guarantee your freedom to share and change free
+software--to make sure the software is free for all its users.  This
+General Public License applies to most of the Free Software
+Foundation's software and to any other program whose authors commit to
+using it.  (Some other Free Software Foundation software is covered by
+the GNU Lesser General Public License instead.)  You can apply it to
+your programs, too.
+
+  When we speak of free software, we are referring to freedom, not
+price.  Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+this service if you wish), that you receive source code or can get it
+if you want it, that you can change the software or use pieces of it
+in new free programs; and that you know you can do these things.
+
+  To protect your rights, we need to make restrictions that forbid
+anyone to deny you these rights or to ask you to surrender the rights.
+These restrictions translate to certain responsibilities for you if you
+distribute copies of the software, or if you modify it.
+
+  For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must give the recipients all the rights that
+you have.  You must make sure that they, too, receive or can get the
+source code.  And you must show them these terms so they know their
+rights.
+
+  We protect your rights with two steps: (1) copyright the software, and
+(2) offer you this license which gives you legal permission to copy,
+distribute and/or modify the software.
+
+  Also, for each author's protection and ours, we want to make certain
+that everyone understands that there is no warranty for this free
+software.  If the software is modified by someone else and passed on, we
+want its recipients to know that what they have is not the original, so
+that any problems introduced by others will not reflect on the original
+authors' reputations.
+
+  Finally, any free program is threatened constantly by software
+patents.  We wish to avoid the danger that redistributors of a free
+program will individually obtain patent licenses, in effect making the
+program proprietary.  To prevent this, we have made it clear that any
+patent must be licensed for everyone's free use or not licensed at all.
+
+  The precise terms and conditions for copying, distribution and
+modification follow.
+
+                    GNU GENERAL PUBLIC LICENSE
+   TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+  0. This License applies to any program or other work which contains
+a notice placed by the copyright holder saying it may be distributed
+under the terms of this General Public License.  The "Program", below,
+refers to any such program or work, and a "work based on the Program"
+means either the Program or any derivative work under copyright law:
+that is to say, a work containing the Program or a portion of it,
+either verbatim or with modifications and/or translated into another
+language.  (Hereinafter, translation is included without limitation in
+the term "modification".)  Each licensee is addressed as "you".
+
+Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope.  The act of
+running the Program is not restricted, and the output from the Program
+is covered only if its contents constitute a work based on the
+Program (independent of having been made by running the Program).
+Whether that is true depends on what the Program does.
+
+  1. You may copy and distribute verbatim copies of the Program's
+source code as you receive it, in any medium, provided that you
+conspicuously and appropriately publish on each copy an appropriate
+copyright notice and disclaimer of warranty; keep intact all the
+notices that refer to this License and to the absence of any warranty;
+and give any other recipients of the Program a copy of this License
+along with the Program.
+
+You may charge a fee for the physical act of transferring a copy, and
+you may at your option offer warranty protection in exchange for a fee.
+
+  2. You may modify your copy or copies of the Program or any portion
+of it, thus forming a work based on the Program, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+    a) You must cause the modified files to carry prominent notices
+    stating that you changed the files and the date of any change.
+
+    b) You must cause any work that you distribute or publish, that in
+    whole or in part contains or is derived from the Program or any
+    part thereof, to be licensed as a whole at no charge to all third
+    parties under the terms of this License.
+
+    c) If the modified program normally reads commands interactively
+    when run, you must cause it, when started running for such
+    interactive use in the most ordinary way, to print or display an
+    announcement including an appropriate copyright notice and a
+    notice that there is no warranty (or else, saying that you provide
+    a warranty) and that users may redistribute the program under
+    these conditions, and telling the user how to view a copy of this
+    License.  (Exception: if the Program itself is interactive but
+    does not normally print such an announcement, your work based on
+    the Program is not required to print an announcement.)
+
+These requirements apply to the modified work as a whole.  If
+identifiable sections of that work are not derived from the Program,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works.  But when you
+distribute the same sections as part of a whole which is a work based
+on the Program, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Program.
+
+In addition, mere aggregation of another work not based on the Program
+with the Program (or with a work based on the Program) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+  3. You may copy and distribute the Program (or a work based on it,
+under Section 2) in object code or executable form under the terms of
+Sections 1 and 2 above provided that you also do one of the following:
+
+    a) Accompany it with the complete corresponding machine-readable
+    source code, which must be distributed under the terms of Sections
+    1 and 2 above on a medium customarily used for software interchange; or,
+
+    b) Accompany it with a written offer, valid for at least three
+    years, to give any third party, for a charge no more than your
+    cost of physically performing source distribution, a complete
+    machine-readable copy of the corresponding source code, to be
+    distributed under the terms of Sections 1 and 2 above on a medium
+    customarily used for software interchange; or,
+
+    c) Accompany it with the information you received as to the offer
+    to distribute corresponding source code.  (This alternative is
+    allowed only for noncommercial distribution and only if you
+    received the program in object code or executable form with such
+    an offer, in accord with Subsection b above.)
+
+The source code for a work means the preferred form of the work for
+making modifications to it.  For an executable work, complete source
+code means all the source code for all modules it contains, plus any
+associated interface definition files, plus the scripts used to
+control compilation and installation of the executable.  However, as a
+special exception, the source code distributed need not include
+anything that is normally distributed (in either source or binary
+form) with the major components (compiler, kernel, and so on) of the
+operating system on which the executable runs, unless that component
+itself accompanies the executable.
+
+If distribution of executable or object code is made by offering
+access to copy from a designated place, then offering equivalent
+access to copy the source code from the same place counts as
+distribution of the source code, even though third parties are not
+compelled to copy the source along with the object code.
+
+  4. You may not copy, modify, sublicense, or distribute the Program
+except as expressly provided under this License.  Any attempt
+otherwise to copy, modify, sublicense or distribute the Program is
+void, and will automatically terminate your rights under this License.
+However, parties who have received copies, or rights, from you under
+this License will not have their licenses terminated so long as such
+parties remain in full compliance.
+
+  5. You are not required to accept this License, since you have not
+signed it.  However, nothing else grants you permission to modify or
+distribute the Program or its derivative works.  These actions are
+prohibited by law if you do not accept this License.  Therefore, by
+modifying or distributing the Program (or any work based on the
+Program), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Program or works based on it.
+
+  6. Each time you redistribute the Program (or any work based on the
+Program), the recipient automatically receives a license from the
+original licensor to copy, distribute or modify the Program subject to
+these terms and conditions.  You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties to
+this License.
+
+  7. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License.  If you cannot
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Program at all.  For example, if a patent
+license would not permit royalty-free redistribution of the Program by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Program.
+
+If any portion of this section is held invalid or unenforceable under
+any particular circumstance, the balance of the section is intended to
+apply and the section as a whole is intended to apply in other
+circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system, which is
+implemented by public license practices.  Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+
+  8. If the distribution and/or use of the Program is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Program under this License
+may add an explicit geographical distribution limitation excluding
+those countries, so that distribution is permitted only in or among
+countries not thus excluded.  In such case, this License incorporates
+the limitation as if written in the body of this License.
+
+  9. The Free Software Foundation may publish revised and/or new versions
+of the General Public License from time to time.  Such new versions will
+be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+Each version is given a distinguishing version number.  If the Program
+specifies a version number of this License which applies to it and "any
+later version", you have the option of following the terms and conditions
+either of that version or of any later version published by the Free
+Software Foundation.  If the Program does not specify a version number of
+this License, you may choose any version ever published by the Free Software
+Foundation.
+
+  10. If you wish to incorporate parts of the Program into other free
+programs whose distribution conditions are different, write to the author
+to ask for permission.  For software which is copyrighted by the Free
+Software Foundation, write to the Free Software Foundation; we sometimes
+make exceptions for this.  Our decision will be guided by the two goals
+of preserving the free status of all derivatives of our free software and
+of promoting the sharing and reuse of software generally.
+
+                            NO WARRANTY
+
+  11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
+FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW.  EXCEPT WHEN
+OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
+PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
+OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.  THE ENTIRE RISK AS
+TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU.  SHOULD THE
+PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
+REPAIR OR CORRECTION.
+
+  12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
+REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
+INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
+OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
+TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
+YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
+PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGES.
+
+                     END OF TERMS AND CONDITIONS
+
+            How to Apply These Terms to Your New Programs
+
+  If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
+
+  To do so, attach the following notices to the program.  It is safest
+to attach them to the start of each source file to most effectively
+convey the exclusion of warranty; and each file should have at least
+the "copyright" line and a pointer to where the full notice is found.
+
+    <one line to give the program's name and a brief idea of what it does.>
+    Copyright (C) <year>  <name of author>
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License along
+    with this program; if not, write to the Free Software Foundation, Inc.,
+    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+
+Also add information on how to contact you by electronic and paper mail.
+
+If the program is interactive, make it output a short notice like this
+when it starts in an interactive mode:
+
+    Gnomovision version 69, Copyright (C) year name of author
+    Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
+    This is free software, and you are welcome to redistribute it
+    under certain conditions; type `show c' for details.
+
+The hypothetical commands `show w' and `show c' should show the appropriate
+parts of the General Public License.  Of course, the commands you use may
+be called something other than `show w' and `show c'; they could even be
+mouse-clicks or menu items--whatever suits your program.
+
+You should also get your employer (if you work as a programmer) or your
+school, if any, to sign a "copyright disclaimer" for the program, if
+necessary.  Here is a sample; alter the names:
+
+  Yoyodyne, Inc., hereby disclaims all copyright interest in the program
+  `Gnomovision' (which makes passes at compilers) written by James Hacker.
+
+  <signature of Ty Coon>, 1 April 1989
+  Ty Coon, President of Vice
+
+This General Public License does not permit incorporating your program into
+proprietary programs.  If your program is a subroutine library, you may
+consider it more useful to permit linking proprietary applications with the
+library.  If this is what you want to do, use the GNU Lesser General
+Public License instead of this License.
diff --git a/kolab.org/www/drupal-7.18/sites/all/modules/i18n/README.txt b/kolab.org/www/drupal-7.18/sites/all/modules/i18n/README.txt
new file mode 100644
index 0000000..5a8a4f1
--- /dev/null
+++ b/kolab.org/www/drupal-7.18/sites/all/modules/i18n/README.txt
@@ -0,0 +1,27 @@
+
+README.txt
+==========
+
+********************************************************************
+This is i18n package 7.x, and will work with Drupal 7.x
+********************************************************************
+WARNING: DO READ THE INSTALL FILE AND the ON-LINE HANDBOOK
+********************************************************************
+
+This is a collection of modules providing multilingual features.
+These modules will build onto Drupal 7 core features enabling a full multilingual site
+
+Up to date documentation will be kept on-line at http://drupal.org/node/133977
+
+Additional Support
+=================
+For support, please create a support request for this module's project:
+  http://drupal.org/project/i18n
+
+Support questions by email to the module maintainer will be simply ignored. Use the issue tracker.
+
+Now if you want professional (paid) support the module maintainer may be available occassionally.
+Drop me a message to check availability and hourly rates, http://reyero.net/en/contact
+
+====================================================================
+Jose A. Reyero, drupal at reyero dot net, http://reyero.net
diff --git a/kolab.org/www/drupal-7.18/sites/all/modules/i18n/i18n.api.php b/kolab.org/www/drupal-7.18/sites/all/modules/i18n/i18n.api.php
new file mode 100644
index 0000000..cd1d7d4
--- /dev/null
+++ b/kolab.org/www/drupal-7.18/sites/all/modules/i18n/i18n.api.php
@@ -0,0 +1,117 @@
+<?php
+/**
+ * @file
+ * API documentation for Internationalization module
+ *
+ * Most i18n hooks can be placed on each module.i18n.inc file but in this case
+ * such file must be listed in the module.info file.
+ */
+
+
+/**
+ * Provide information about object types handled by i18n system.
+ *
+ * @see i18n_object_info()
+ *
+ * Other features like translation sets (i18n_translation) or string translation (i18n_string)
+ * rely on the information provided by this hook for automating string translation
+ */
+function hook_i18n_object_info() {
+  // Information for node type object, see i18n_node_i18n_object_info()
+  $info['node_type'] = array(
+    // Generic object properties, title, etc..
+    'title' => t('Node type'),
+    // Field to be used as key to index different node types
+    'key' => 'type',
+    // Mapping object fields and menu place holders
+    'placeholders' => array(
+    	'%node_type' => 'type',
+    ),
+    // Path for automatically generated translation tabs. Note placeholders above are used here.
+    'edit path' => 'admin/structure/types/manage/%node_type',
+    'translate tab' => 'admin/structure/types/manage/%node_type/translate',
+    // We can easily list all these objects because they should be limited and manageable
+    // Only in this case we provide a 'list callback'.
+    'list callback' => 'node_type_get_types',
+    // Metadata for string translation
+    // In this case we are defining fields and keys for string translation's string names
+    // String ids are of the form: [textgroup]:[type]:[key]:[property]
+    // Thus in this case we'll have string names like
+    // - node:type:story:name
+    // - node:type:story:description
+    'string translation' => array(
+      'textgroup' => 'node',
+      'type' => 'type',
+      'properties' => array(
+        'name' => t('Name'),
+        'title_label' => t('Title label'),
+        'description' => t('Description'),
+        'help' => t('Help text'),
+      ),
+      'translate path' => 'admin/structure/types/manage/%node_type/translate/%i18n_language',
+    )
+  );
+  // Example information for taxonomy term object, see i18n_taxonomy_i18n_object_info().
+  $info['taxonomy_term'] = array(
+    'title' => t('Taxonomy term'),
+    'class' => 'i18n_taxonomy_term',
+    'entity' => 'taxonomy_term',
+    'key' => 'tid',
+    'placeholders' => array(
+      '%taxonomy_term' => 'tid',
+    ),
+    // Auto generate edit path
+    'edit path' => 'taxonomy/term/%taxonomy_term/edit',
+    // Auto-generate translate tab
+    'translate tab' => 'taxonomy/term/%taxonomy_term/translate',
+    'string translation' => array(
+      'textgroup' => 'taxonomy',
+      'type' => 'term',
+      'properties' => array(
+        'name' => t('Name'),
+        'description' => array(
+          'title' => t('Description'),
+          'format' => 'format',
+        ),
+      ),
+    )
+  );
+  return $info;
+}
+
+/**
+ * Alter i18n object information provided by modules with the previous hook
+ *
+ * @see i18n_object_info()
+ */
+function hook_i18n_object_info_alter(&$info) {
+}
+
+/**
+ * Provide information about available translations for specific path.
+ *
+ * @see i18n_get_path_translations($path)
+ *
+ * @param $path
+ *   Internal path to translate.
+ * @return array
+ *   Translations indexed by language code. Each translation is an array with:
+ *   - 'path'
+ *   - 'title'
+ *   - 'options'
+ */
+function hook_i18n_translate_path($path) {
+  if ($path == 'mypath') {
+    $translations['es'] = array(
+      'path' => 'mypath/spanish',
+      'title' => t('My Spanish translation'),
+    );
+    return $translations;
+  }
+}
+
+/**
+ * Alter path translations
+ */
+function hook_i18n_translate_path_alter(&$translations, $path) {
+}
diff --git a/kolab.org/www/drupal-7.18/sites/all/modules/i18n/i18n.info b/kolab.org/www/drupal-7.18/sites/all/modules/i18n/i18n.info
new file mode 100644
index 0000000..28c5770
--- /dev/null
+++ b/kolab.org/www/drupal-7.18/sites/all/modules/i18n/i18n.info
@@ -0,0 +1,18 @@
+name = Internationalization
+description = Extends Drupal support for multilingual features.
+dependencies[] = locale
+dependencies[] = variable
+package = Multilingual - Internationalization
+core = 7.x
+
+files[] = i18n.install
+files[] = i18n_object.inc
+files[] = i18n.test
+configure = admin/config/regional/i18n
+
+; Information added by drupal.org packaging script on 2013-01-13
+version = "7.x-1.8"
+core = "7.x"
+project = "i18n"
+datestamp = "1358075001"
+
diff --git a/kolab.org/www/drupal-7.18/sites/all/modules/i18n/i18n.install b/kolab.org/www/drupal-7.18/sites/all/modules/i18n/i18n.install
new file mode 100644
index 0000000..f417b5e
--- /dev/null
+++ b/kolab.org/www/drupal-7.18/sites/all/modules/i18n/i18n.install
@@ -0,0 +1,57 @@
+<?php
+
+/**
+ * @file
+ * Installation file for Internationalization (i18n) module.
+ */
+
+
+/**
+ * Implements hook_install().
+ */
+function i18n_install() {
+  // Set module weight for it to run after core modules
+  db_query("UPDATE {system} SET weight = 10 WHERE name = 'i18n' AND type = 'module'");
+}
+
+/**
+ * Implements hook_uninstall().
+ */
+function i18n_uninstall() {
+  variable_del('i18n_drupal6_update');
+}
+
+/**
+ * Add fields to schema if they don't exist
+ */
+function i18n_install_create_fields($table, $fields) {
+  static $schema;
+  // Do not force schema refresh more than once per request.
+  $schema = drupal_get_schema($table, !isset($schema));
+  foreach ($fields as $field) {
+    if (!empty($schema['fields'][$field])) {
+      if (!db_field_exists($table, $field)) {
+        db_add_field($table, $field, $schema['fields'][$field]);
+      }
+      else {
+        // The field exists, make sure field definition is up to date.
+        db_change_field($table, $field, $field, $schema['fields'][$field]);
+      }
+    }
+  }
+}
+
+/**
+ * Mark this as updated so all (renamed) modules know they need to update from D6 version when installing
+ */
+function i18n_update_7000() {
+  variable_set('i18n_drupal6_update', TRUE);
+  variable_del('i18n_selection_mode');
+}
+
+/**
+ * Refresh caches and rebuild menus.
+ */
+function i18n_update_7001() {
+  drupal_flush_all_caches();
+}
diff --git a/kolab.org/www/drupal-7.18/sites/all/modules/i18n/i18n.js b/kolab.org/www/drupal-7.18/sites/all/modules/i18n/i18n.js
new file mode 100644
index 0000000..0c12c97
--- /dev/null
+++ b/kolab.org/www/drupal-7.18/sites/all/modules/i18n/i18n.js
@@ -0,0 +1,21 @@
+
+(function ($) {
+
+/**
+ * Rewrite autocomplete inputs to pass the language of the node currently being
+ * edited in the path.
+ *
+ * This functionality ensures node autocompletes get suggestions for the node's
+ * language rather than the current interface language.
+ */
+Drupal.behaviors.i18n = {
+  attach: function (context) {
+    if (Drupal.settings && Drupal.settings.i18n) {
+      $('form[id^=node-form]', context).find('input.autocomplete[value^=' + Drupal.settings.i18n.interface_path + ']').each(function () {
+        $(this).val($(this).val().replace(Drupal.settings.i18n.interface_path, Drupal.settings.i18n.content_path));
+      });
+    }
+  }
+};
+
+})(jQuery);
\ No newline at end of file
diff --git a/kolab.org/www/drupal-7.18/sites/all/modules/i18n/i18n.module b/kolab.org/www/drupal-7.18/sites/all/modules/i18n/i18n.module
new file mode 100644
index 0000000..ffc4ad5
--- /dev/null
+++ b/kolab.org/www/drupal-7.18/sites/all/modules/i18n/i18n.module
@@ -0,0 +1,609 @@
+<?php
+
+/**
+ * @file
+ * Internationalization (i18n) module.
+ *
+ * This module extends multilingual support being the base module for the i18n package.
+ * - Multilingual variables
+ * - Extended languages for nodes
+ * - Extended language API
+ *
+ * @author Jose A. Reyero, 2004
+ */
+
+// All multilingual options disabled
+define('I18N_LANGUAGE_DISABLED', 0);
+// Language list will include all enabled languages
+define('I18N_LANGUAGE_ENABLED', 1);
+// Language list will include also disabled languages
+define('I18N_LANGUAGE_EXTENDED', 4);
+// Disabled languages will be hidden when possible
+define('I18N_LANGUAGE_HIDDEN', 8);
+// All defined languages will be allowed but hidden when possible
+define('I18N_LANGUAGE_EXTENDED_NOT_DISPLAYED', I18N_LANGUAGE_EXTENDED | I18N_LANGUAGE_HIDDEN);
+
+// No multilingual options
+define('I18N_MODE_NONE', 0);
+// Localizable object. Run through the localization system
+define('I18N_MODE_LOCALIZE', 1);
+// Predefined language for this object and all related ones.
+define('I18N_MODE_LANGUAGE', 2);
+// Multilingual objects, translatable but not localizable.
+define('I18N_MODE_TRANSLATE', 4);
+// Objects are translatable (if they have language or localizable if not)
+define('I18N_MODE_MULTIPLE', I18N_MODE_LOCALIZE | I18N_MODE_TRANSLATE);
+
+/**
+ * Global variable for i18n context language.
+ */
+define('I18N_LANGUAGE_TYPE_CONTEXT', 'i18n_language_context');
+
+/**
+ * Implements hook_boot().
+ */
+function i18n_boot() {
+  // Just make sure the module is loaded for boot and the API is available.
+}
+
+/**
+ * Implements hook_hook_info().
+ */
+function i18n_hook_info() {
+  $hooks['i18n_object_info'] = array(
+    'group' => 'i18n',
+  );
+  return $hooks;
+}
+
+/**
+ * WARNING: Obsolete function, use other i18n_language_* instead.
+ *
+ * Get global language object, make sure it is initialized
+ *
+ * @param $language
+ *   Language code or language object to convert to valid language object
+ */
+function i18n_language($language = NULL) {
+  if ($language && ($language_object = i18n_language_object($language))) {
+    return $language_object;
+  }
+  else {
+    return i18n_language_interface();
+  }
+}
+
+/**
+ * Get language object from language code or object.
+ *
+ * @param $language
+ *   Language code or language object to convert to valid language object.
+ * @return
+ *   Language object if this is an object or a valid language code.
+ */
+function i18n_language_object($language) {
+  if (is_object($language)) {
+    return $language;
+  }
+  else {
+    $list = language_list();
+    return isset($list[$language]) ? $list[$language] : NULL;
+  }
+}
+
+/**
+ * Get interface language, make sure it is initialized.
+ */
+function i18n_language_interface() {
+  if (empty($GLOBALS[LANGUAGE_TYPE_INTERFACE])) {
+    // We don't have language yet, initialize the language system and retry
+    drupal_bootstrap(DRUPAL_BOOTSTRAP_LANGUAGE);
+  }
+  return $GLOBALS[LANGUAGE_TYPE_INTERFACE];
+}
+
+/**
+ * Get content language, make sure it is initialized.
+ */
+function i18n_language_content() {
+  if (empty($GLOBALS[LANGUAGE_TYPE_CONTENT])) {
+    // We don't have language yet, initialize the language system and retry
+    drupal_bootstrap(DRUPAL_BOOTSTRAP_LANGUAGE);
+  }
+  return $GLOBALS[LANGUAGE_TYPE_CONTENT];
+}
+
+/**
+ * Get / set language from current context / content.
+ *
+ * Depending on the page content we may need to use a different language for some operations.
+ *
+ * This should be the language of the specific page content. I.e. node language for node pages
+ * or term language for taxonomy term page.
+ *
+ * @param $language
+ *   Optional language object to set for context.
+ * @return
+ *   Context language object, which defaults to content language.
+ *
+ * @see hook_i18n_context_language().
+ */
+function i18n_language_context($language = NULL) {
+  if ($language) {
+    $GLOBALS[I18N_LANGUAGE_TYPE_CONTEXT] = $language;
+  }
+  elseif (!isset($GLOBALS[I18N_LANGUAGE_TYPE_CONTEXT])) {
+    // It will default to content language.
+    $GLOBALS[I18N_LANGUAGE_TYPE_CONTEXT] = i18n_language_content();
+    // Get language from the first module that provides it.
+    foreach (module_implements('i18n_context_language') as $module) {
+      if ($language = module_invoke($module, 'i18n_context_language')) {
+        $GLOBALS[I18N_LANGUAGE_TYPE_CONTEXT] = $language;
+        break;
+      }
+    }
+  }
+  return $GLOBALS[I18N_LANGUAGE_TYPE_CONTEXT];
+}
+
+/**
+ * Menu object loader, language
+ */
+function i18n_language_load($langcode) {
+  $list = language_list();
+  return isset($list[$langcode]) ? $list[$langcode] : FALSE;
+}
+
+/**
+ * Get language selector form element
+ */
+function i18n_element_language_select($default = LANGUAGE_NONE) {
+  if (is_object($default) || is_array($default)) {
+    $default = i18n_object_langcode($default, LANGUAGE_NONE);
+  }
+  return array(
+    '#type' => 'select',
+    '#title' => t('Language'),
+    '#default_value' => $default,
+    '#options' => array(LANGUAGE_NONE => t('Language neutral')) + i18n_language_list(),
+  );
+}
+
+/**
+ * Get language field for hook_field_extra_fields()
+ */
+function i18n_language_field_extra() {
+  return array(
+    'form' => array(
+      'language' => array(
+        'label' => t('Language'),
+        'description' => t('Language selection'),
+        'weight' => 0,
+      ),
+    ),
+    'display' => array(
+      'language' => array(
+        'label' => t('Language'),
+        'description' => t('Language'),
+        'weight' => 0,
+      ),
+    ),
+  );
+}
+
+/**
+ * Get full language list
+ *
+ * @todo See about creating a permission for seeing disabled languages
+ */
+function i18n_language_list($field = 'name', $mode = NULL) {
+  $mode = isset($mode) ? $mode : variable_get('i18n_language_list', I18N_LANGUAGE_ENABLED);
+  return locale_language_list($field, I18N_LANGUAGE_EXTENDED & $mode);
+}
+
+/**
+ * Get language name for any defined (enabled or not) language
+ *
+ * @see locale_language_list()
+ */
+function i18n_language_name($lang) {
+  $list = &drupal_static(__FUNCTION__);
+  if (!isset($list)) {
+    $list = locale_language_list('name', TRUE);
+  }
+  if (!$lang || $lang === LANGUAGE_NONE) {
+    return t('Undefined');
+  }
+  elseif (isset($list[$lang])) {
+    return check_plain($list[$lang]);
+  }
+  else {
+    return t('Unknown');
+  }
+}
+
+/**
+ * Get valid language code for current page or check whether the code is a defined language
+ */
+function i18n_langcode($langcode = NULL) {
+  return $langcode && $langcode !== LANGUAGE_NONE ? $langcode : i18n_language()->language;
+}
+
+/**
+ * Implements hook_help().
+ */
+function i18n_help($path = 'admin/help#i18n', $arg) {
+  switch ($path) {
+    case 'admin/help#i18n' :
+      $output = '<p>' . t('This module improves support for multilingual content in Drupal sites:') . '</p>';
+      $output .= '<ul>';
+      $output .= '<li>' . t('Shows content depending on page language.') . '</li>';
+      $output .= '<li>' . t('Handles multilingual variables.') . '</li>';
+      $output .= '<li>' . t('Extended language option for chosen content types. For these content types transations will be allowed for all defined languages, not only for enabled ones.') . '</li>';
+      $output .= '<li>' . t('Provides a block for language selection and two theme functions: <em>i18n_flags</em> and <em>i18n_links</em>.') . '</li>';
+      $output .= '</ul>';
+      $output .= '<p>' . t('This is the base module for several others adding different features:') . '</p>';
+      $output .= '<ul>';
+      $output .= '<li>' . t('Multilingual menu items.') . '</li>';
+      $output .= '<li>' . t('Multilingual taxonomy adds a language field for taxonomy vocabularies and terms.') . '</li>';
+      $output .= '</ul>';
+      $output .= '<p>' . t('For more information, see the online handbook entry for <a href="@i18n">Internationalization module</a>.', array('@i18n' => 'http://drupal.org/node/133977')) . '</p>';
+      return $output;
+
+    case 'admin/config/language/i18n':
+      $output = '<ul>';
+      $output .= '<li>' . t('To enable multilingual support for specific content types go to <a href="@configure_content_types">configure content types</a>.', array('@configure_content_types' => url('admin/structure/types'))) . '</li>';
+      $output .= '</ul>';
+      return $output;
+  }
+}
+
+/**
+ * Implements hook_menu().
+ */
+function i18n_menu() {
+  $items['admin/config/regional/i18n'] = array(
+    'title' => 'Multilingual settings',
+    'description' => 'Configure extended options for multilingual content and translations.',
+    'page callback' => 'drupal_get_form',
+    'page arguments' => array('variable_module_form', 'i18n'),
+    'access arguments' => array('administer site configuration'),
+    'weight' => 10,
+  );
+  $items['admin/config/regional/i18n/configure'] = array(
+    'title' => 'Multilingual system',
+    'description' => 'Configure extended options for multilingual content and translations.',
+    'type' => MENU_DEFAULT_LOCAL_TASK,
+  );
+  module_load_include('pages.inc', 'i18n');
+  $items += i18n_page_menu_items();
+  return $items;
+}
+
+/**
+ * Simple i18n API
+ */
+
+/**
+ * Switch select Mode on off if enabled
+ *
+ * Usage for disabling content selection for a while then return to previous state
+ *
+ *   // Disable selection, but store previous mode
+ *   $previous = i18n_select(FALSE);
+ *
+ *   // Other code to be run without content selection here
+ *   ..........................
+ *
+ *   // Return to previous mode
+ *   i18n_select($previous);
+ *
+ * @param $value
+ *   Optional, enable/disable selection: TRUE/FALSE
+ * @return boolean
+ *   Previous selection mode (TRUE/FALSE)
+ */
+function i18n_select($value = NULL) {
+  static $mode = FALSE;
+
+  if (isset($value)) {
+    $previous = $mode;
+    $mode = $value;
+    return $previous;
+  }
+  else {
+    return $mode;
+  }
+}
+
+/**
+ * Get language properties.
+ *
+ * @param $code
+ *   Language code.
+ * @param $property
+ *   It may be 'name', 'native', 'ltr'...
+ */
+function i18n_language_property($code, $property) {
+  $languages = language_list();
+  return isset($languages[$code]->$property) ? $languages[$code]->$property : NULL;
+}
+
+/**
+ * Implements hook_preprocess_html().
+ */
+function i18n_preprocess_html(&$variables) {
+  global $language;
+  $variables['classes_array'][] = 'i18n-' . $language->language;
+}
+
+/**
+ * Translate or update user defined string. Entry point for i18n_string API if enabled.
+ *
+ * This function is from i18n_string sub module and is subject to be moved back.
+ *
+ * @param $name
+ *   Textgroup and context glued with ':'.
+ * @param $default
+ *   String in default language. Default language may or may not be English.
+ * @param $options
+ *   An associative array of additional options, with the following keys:
+ *   - 'langcode' (defaults to the current language) The language code to translate to a language other than what is used to display the page.
+ *   - 'filter' Filtering callback to apply to the translated string only
+ *   - 'format' Input format to apply to the translated string only
+ *   - 'callback' Callback to apply to the result (both to translated or untranslated string
+ *   - 'update' (defaults to FALSE) Whether to update source table
+ *   - 'translate' (defaults to TRUE) Whether to return a translation
+ *
+ * @return $string
+ *   Translated string, $string if not found
+ */
+function i18n_string($name, $string, $options = array()) {
+  $options += array('translate' => TRUE, 'update' => FALSE);
+  if ($options['update']) {
+    $result = function_exists('i18n_string_update') ? i18n_string_update($name, $string, $options) : FALSE;
+  }
+  if ($options['translate']) {
+    $result = function_exists('i18n_string_translate') ? i18n_string_translate($name, $string, $options) : $string;
+  }
+  return $result;
+}
+
+/**
+ * Get object wrapper.
+ *
+ * Create an object wrapper or retrieve it from the static cache if
+ * a wrapper for the same object was created before.
+ *
+ * @see i18n_object_info()
+ *
+ * @param $type
+ *   The object type.
+ */
+function i18n_object($type, $object) {
+  $key = i18n_object_key($type, $object);
+  return i18n_get_object($type, $key, $object);
+}
+
+/**
+ * Get object wrapper by object key.
+ *
+ * @param $type
+ *   The object type to load e.g. node_type, menu, taxonomy_term.
+ * @param $key
+ *   The object key, can be an scalar or an array.
+ * @param $object
+ *   Optional Drupal object or array. It will be autoloaded using the key if not present.
+ *
+ * @return
+ *   A fully-populated object wrapper.
+ */
+function i18n_get_object($type, $key, $object = NULL) {
+  $cache = &drupal_static(__FUNCTION__);
+  $index = is_array($key) ? implode(':', $key) : $key;
+  if (!isset($cache[$type][$index])) {
+    $class = i18n_object_info($type, 'class', 'i18n_object_wrapper');
+    $cache[$type][$index] = new $class($type, $key, $object);
+  }
+  return $cache[$type][$index];
+}
+
+/**
+ * Get object language code
+ *
+ * @param $object
+ *   Object or array having language field or plain language field
+ * @param $default
+ *   What value to return if the object doesn't have a valid language
+ */
+function i18n_object_langcode($object, $default = FALSE, $field = 'language') {
+  $value = i18n_object_field($object, $field, $default);
+  return $value && $value != LANGUAGE_NONE ? $value : $default;
+}
+
+/**
+ * Get translation information for objects
+ */
+function i18n_object_info($type = NULL, $property = NULL, $default = NULL) {
+  $info = &drupal_static(__FUNCTION__);
+  if (!$info) {
+    $info = module_invoke_all('i18n_object_info');
+    drupal_alter('i18n_object_info', $info);
+  }
+  if ($property) {
+    return isset($info[$type][$property]) ? $info[$type][$property] : $default;
+  }
+  elseif ($type) {
+    return isset($info[$type]) ? $info[$type] : array();
+  }
+  else {
+    return $info;
+  }
+}
+
+/**
+ * Get field value from object/array
+ */
+function i18n_object_field($object, $field, $default = NULL) {
+  if (is_array($field)) {
+    // We can handle a list of fields too. This is useful for multiple keys (like blocks)
+    foreach ($field as $key => $name) {
+      $values[$key] = i18n_object_field($object, $name);
+    }
+    return $values;
+  }
+  elseif (strpos($field, '.')) {
+    // Access nested properties with the form 'name1.name2..', will map to $object->name1->name2...
+    $names = explode('.', $field);
+    $current = array_shift($names);
+    if ($nested = i18n_object_field($object, $current)) {
+      return i18n_object_field($nested, implode('.', $names), $default);
+    }
+    else {
+      return $default;
+    }
+  }
+  elseif (is_object($object)) {
+    return isset($object->$field) ? $object->$field : $default;
+  }
+  elseif (is_array($object)) {
+    return isset($object[$field]) ? $object[$field] : $default;
+  }
+  else {
+    return $default;
+  }
+}
+
+/**
+ * Get key value from object/array
+ */
+function i18n_object_key($type, $object, $default = NULL) {
+  if ($field = i18n_object_info($type, 'key')) {
+    return i18n_object_field($object, $field, $default);
+  }
+  else {
+    return $default;
+  }
+}
+
+/**
+ * Menu access callback for mixed translation tab
+ */
+function i18n_object_translate_access($type, $object) {
+  return i18n_object($type, $object)->get_translate_access();
+}
+
+/**
+ * Get translations for path.
+ *
+ * @param $path
+ *   Path to get translations for or '<front>' for front page.
+ */
+function i18n_get_path_translations($path) {
+  $translations = &drupal_static(__FUNCTION__);
+  if (!isset($translations[$path])) {
+    $translations[$path] = array();
+    foreach (module_implements('i18n_translate_path') as $module) {
+      $translated = call_user_func($module . '_i18n_translate_path', $path);
+      // Add into the array, if two modules returning a translation first takes precedence.
+      if ($translated) {
+        $translations[$path] += $translated;
+      }
+    }
+    // Chance for altering the results.
+    drupal_alter('i18n_translate_path', $translations[$path], $path);
+  }
+  return $translations[$path];
+}
+
+/**
+ * Implements hook_language_switch_links_alter().
+ *
+ * Replaces links with pointers to translated versions of the content.
+ */
+function i18n_language_switch_links_alter(array &$links, $type, $path) {
+  // For the front page we have nothing to add to Drupal core links.
+  if ($path != '<front>' && ($translations = i18n_get_path_translations($path))) {
+    foreach ($translations as $langcode => $translation) {
+      if (isset($links[$langcode])) {
+        $links[$langcode]['href'] = $translation['href'];
+        if (!empty($translation['title'])) {
+          $links[$langcode]['attributes']['title'] = $translation['title'];
+        }
+      }
+    }
+  }
+}
+
+/**
+ * Build translation link
+ */
+function i18n_translation_link($path, $langcode, $link = array()) {
+  $language = i18n_language_object($langcode);
+  $link += array(
+    'href' => $path,
+    'title' => $language->native,
+    'language' => $language,
+    'i18n_translation' => TRUE,
+  );
+  $link['attributes']['class'] = array('language-link');
+  // @todo Fix languageicons weight, but until that
+  if (function_exists('languageicons_link_add')) {
+    languageicons_link_add($link);
+  }
+  return $link;
+}
+
+/**
+ * Implements hook_form_FORM_ID_alter().
+ */
+function i18n_form_block_admin_display_form_alter(&$form, &$form_state) {
+  $form['#submit'][] = 'i18n_form_block_admin_display_form_submit';
+}
+
+/**
+ * Display a help message when enabling the language switcher block.
+ */
+function i18n_form_block_admin_display_form_submit($form, &$form_state) {
+  foreach ($form_state['values']['blocks'] as $key => $block) {
+    $previous = $form['blocks'][$key]['region']['#default_value'];
+    if (empty($previous) && $block['region'] != -1 && $block['module'] == 'locale') {
+      $message = t('The language switcher will appear only after configuring <a href="!url">language detection</a>. You need to enable at least one method that alters URLs like <em>URL</em> or <em>Session</em>.', array('!url' => url('admin/config/regional/language/configure')));
+      drupal_set_message($message, 'warning', FALSE);
+      break;
+    }
+  }
+}
+
+/**
+ * Normal path should be checked with menu item's language to avoid
+ * troubles when a node and it's translation has the same url alias.
+ */
+function i18n_prepare_normal_path($link_path, $language) {
+  $normal_path = drupal_get_normal_path($link_path, $language);
+  if ($link_path != $normal_path) {
+    drupal_set_message(t('The menu system stores system paths only, but will use the URL alias for display. %link_path has been stored as %normal_path', array('%link_path' => $link_path, '%normal_path' => $normal_path)));
+  }
+  return $normal_path;
+}
+
+/**
+ * Checks if an entity translation is enabled for the given entity type.
+ * @param $entity_type
+ */
+function i18n_entity_translation_enabled($entity_type) {
+  $cache = &drupal_static(__FUNCTION__);
+  if (!isset($cache[$entity_type])) {
+    // Check if the entity_translation module exists and if so if the given
+    // entity type is handled.
+    $cache[$entity_type] = module_exists('entity_translation') && entity_translation_enabled($entity_type);
+  }
+  return $cache[$entity_type];
+}
+
+/**
+ * Implements hook_modules_enabled().
+ */
+function i18n_modules_enabled($modules) {
+  drupal_static_reset('i18n_object_info');
+}
diff --git a/kolab.org/www/drupal-7.18/sites/all/modules/i18n/i18n.pages.inc b/kolab.org/www/drupal-7.18/sites/all/modules/i18n/i18n.pages.inc
new file mode 100644
index 0000000..56acd66
--- /dev/null
+++ b/kolab.org/www/drupal-7.18/sites/all/modules/i18n/i18n.pages.inc
@@ -0,0 +1,95 @@
+<?php
+/**
+ * @file
+ * Generic translation pages
+ * 
+ * It handles generic 'translation' tabs, redirecting to the right module depending on
+ * object type and properties.
+ */
+
+/**
+ * Create menu items for translatable objecs
+ */
+function i18n_page_menu_items() {
+  $items = array();
+  // Menus are rebuilt right after other modules are disabled, if no reset we get tabs for disabled modules.
+  drupal_static_reset('i18n_object_info');
+  foreach (i18n_object_info() as $type => $info) {
+    // These objects should have a 'translate tab' property
+    if (!empty($info['translate tab'])) {
+      $path = $info['translate tab'];
+      $localize = module_exists('i18n_string') && !empty($info['string translation']);
+      $translate = module_exists('i18n_translation') && i18n_translation_set_info($type);
+      if ($translate && $localize) {
+        $page_callback = 'i18n_page_translate_tab';
+      }
+      elseif ($translate) {
+        $page_callback = 'i18n_page_translate_translation';
+      }
+      elseif ($localize) {
+        $page_callback = 'i18n_page_translate_localize';
+      }
+      // Find the position for the object placeholder. We assume the first one.
+      $placeholder = key($info['placeholders']);
+      $parts = explode('/', $path);
+      $position = array_search($placeholder, $parts);
+      // Now create items with the right callbacks
+      if ($translate || $localize) {
+        $items[$path] = array(
+          'title' => 'Translate',
+          'page callback' => $page_callback,
+          'page arguments' => array($type, $position),
+          'access callback' => 'i18n_object_translate_access',
+          'access arguments' => array($type, $position),
+          'type' => MENU_LOCAL_TASK,
+          'file' => 'i18n.pages.inc',
+          'weight' => 10,
+        );
+      }
+      if ($localize) {
+        $items[$path . '/%i18n_language'] = array(
+          'title' => 'Translate',
+          'page callback' => $page_callback,
+          'page arguments' => array($type, $position, count($parts)),
+          'access callback' => 'i18n_object_translate_access',
+          'access arguments' => array($type, $position),
+          'type' => MENU_CALLBACK,
+          'file' => 'i18n.pages.inc',
+        );
+      }
+    }
+    
+  }
+  return $items;
+}
+
+/**
+ * Translate or localize page for object
+ */
+function i18n_page_translate_tab($type, $object, $language = NULL) {
+   // Check whether object should be part of a translation set
+   switch (i18n_object($type, $object)->get_translate_mode()) {
+     case I18N_MODE_TRANSLATE:
+       return i18n_page_translate_translation($type, $object);
+     case I18N_MODE_LOCALIZE:
+       return i18n_page_translate_localize($type, $object, $language);
+     default:
+       drupal_access_denied();
+   }
+}
+
+/**
+ * Translate object, create translation set
+ */
+function i18n_page_translate_translation($type, $object) {
+  module_load_include('pages.inc', 'i18n_translation');
+  return i18n_translation_object_translate_page($type, $object);
+}
+
+/**
+ * Translate object, string translation
+ */
+function i18n_page_translate_localize($type, $object, $language = NULL) {
+  module_load_include('pages.inc', 'i18n_string');
+  return i18n_string_object_translate_page($type, $object, $language);
+}
\ No newline at end of file
diff --git a/kolab.org/www/drupal-7.18/sites/all/modules/i18n/i18n.test b/kolab.org/www/drupal-7.18/sites/all/modules/i18n/i18n.test
new file mode 100644
index 0000000..247af33
--- /dev/null
+++ b/kolab.org/www/drupal-7.18/sites/all/modules/i18n/i18n.test
@@ -0,0 +1,460 @@
+<?php
+
+/**
+ * @file
+ * Base class for Internationalization tests
+ */
+class Drupali18nTestCase extends DrupalWebTestCase {
+  protected $current_user;
+  protected $default_language;
+  protected $secondary_language;
+
+  function setUpLanguages($admin_permissions = array()) {
+    // Setup admin user.
+    $this->admin_user = $this->drupalCreateUser(array_merge(array('bypass node access', 'administer nodes', 'administer languages', 'administer content types', 'administer blocks', 'access administration pages', 'translate interface'), $admin_permissions));
+
+    $this->drupalLogin($this->admin_user);
+
+    // Add languages.
+    $this->default_language = 'en';
+    $this->secondary_language = 'es';
+    $this->addLanguage($this->default_language);
+    $this->addLanguage($this->secondary_language);
+
+    // Enable URL language detection and selection to make the language switcher
+    // block appear.
+    $edit = array('language[enabled][locale-url]' => TRUE);
+    $this->drupalPost('admin/config/regional/language/configure', $edit, t('Save settings'));
+    $this->assertRaw(t('Language negotiation configuration saved.'), t('URL language detection enabled.'));
+    $this->drupalGet('admin/config/regional/language/configure');
+    $this->resetCaches();
+  }
+
+  /**
+   * Set up content-type (with translation).
+   */
+  function setUpContentType($settings = array()) {
+    $settings += array(
+      'type' => 'page',
+      'mode' => TRANSLATION_ENABLED,
+      'status' => 1,
+      'promote' => 0,
+    );
+
+    $type = node_type_get_type($settings['type']);
+    // Create content editor with translation permissions.
+    $this->content_editor = $this->drupalCreateUser(array(
+      'create ' . $type->type . ' content',
+      'edit own ' . $type->type . ' content',
+      'translate content',
+      'translate interface',
+    ));
+
+    $this->drupalLogin($this->admin_user);
+    // Set content type to use multilingual support with translation.
+    $this->drupalGet('admin/structure/types/manage/' . $type->type);
+    $edit = array();
+    $edit['language_content_type'] = $settings['mode'];
+    // Mark status and promoted
+    $edit['node_options[status]'] = $settings['status'];
+    $edit['node_options[promote]'] = $settings['promote'];
+    $this->drupalPost('admin/structure/types/manage/' . $type->type, $edit, t('Save content type'));
+    $this->assertRaw(t('The content type %type has been updated.', array('%type' => $type->name)), t('%type content type has been updated.', array('%type' => $type->name)));
+    $this->drupalGet('admin/structure/types/manage/' . $type->type);
+    $this->enableLanguageBlock();
+  }
+
+  /**
+   * Enable the language switcher block.
+   */
+  function enableLanguageBlock() {
+    // Enable the language switcher block.
+    $language_type = LANGUAGE_TYPE_INTERFACE;
+    $edit = array("blocks[locale_$language_type][region]" => 'sidebar_first');
+    $this->drupalPost('admin/structure/block', $edit, t('Save blocks'));
+  }
+
+  /**
+   * Set up translation for content type (default: page).
+   */
+  function setUpContentTranslation($settings = array()) {
+    $settings += array(
+      'mode' => TRANSLATION_ENABLED,
+    );
+    $this->setUpContentType($settings);
+  }
+
+  /**
+   * Install a the specified language if it has not been already. Otherwise make sure that
+   * the language is enabled.
+   *
+   * @param $language_code
+   *   The language code the check.
+   */
+  function addLanguage($language_code) {
+    // Check to make sure that language has not already been installed.
+    $this->drupalGet('admin/config/regional/language');
+
+    if (strpos($this->drupalGetContent(), 'enabled[' . $language_code . ']') === FALSE) {
+      // Doesn't have language installed so add it.
+      $edit = array();
+      $edit['langcode'] = $language_code;
+      $this->drupalPost('admin/config/regional/language/add', $edit, t('Add language'));
+
+      // Make sure we are not using a stale list.
+      drupal_static_reset('language_list');
+      $languages = language_list('language');
+      $this->assertTrue(array_key_exists($language_code, $languages), t('Language was installed successfully.'));
+
+      if (array_key_exists($language_code, $languages)) {
+        $this->assertRaw(t('The language %language has been created and can now be used. More information is available on the <a href="@locale-help">help screen</a>.', array('%language' => $languages[$language_code]->name, '@locale-help' => url('admin/help/locale'))), t('Language has been created.'));
+      }
+    }
+    elseif ($this->xpath('//input[@type="checkbox" and @name=:name and @checked="checked"]', array(':name' => 'enabled[' . $language_code . ']'))) {
+      // It's installed and enabled. No need to do anything.
+      $this->assertTrue(true, 'Language [' . $language_code . '] already installed and enabled.');
+    }
+    else {
+      // It's installed but not enabled. Enable it.
+      $this->assertTrue(true, 'Language [' . $language_code . '] already installed.');
+      $this->drupalPost(NULL, array('enabled[' . $language_code . ']' => TRUE), t('Save configuration'));
+      $this->assertRaw(t('Configuration saved.'), t('Language successfully enabled.'));
+    }
+  }
+
+  /**
+   * Create translation set from a node
+   *
+   * @param $source
+   *   Source node
+   * @param $languages
+   *   Optional list of language codes
+   */
+  function createNodeTranslationSet(&$source, $languages = NULL) {
+    if (empty($source->tnid)) {
+      $source->tnid = $source->nid;
+    }
+    $translations[$source->language] = $source;
+    foreach (language_list() as $language) {
+      if ($language->language != $source->language) {
+        $translations[$language->language] = $this->createNodeTranslation($source, $language);
+      }
+    }
+    return $translations;
+  }
+
+  /**
+   * Create a "Basic page" in the specified language.
+   *
+   * @param $title
+   *   Title of basic page in specified language.
+   * @param $body
+   *   Body of basic page in specified language.
+   * @param $langcode
+   *   Language code.
+   */
+  function createNode($type, $title, $body, $langcode, $edit = array()) {
+    $lang = LANGUAGE_NONE;
+    $edit["title"] = $title;
+    $edit["body[$lang][0][value]"] = $body;
+    $edit['language'] = $langcode;
+    $this->drupalPost('node/add/' . $type, $edit, t('Save'));
+    $this->assertRaw(t('Basic @type %title has been created.', array('@type' => $type, '%title' => $title)), t('Basic page created.'));
+
+    // Check to make sure the node was created.
+    $node = $this->drupalGetNodeByTitle($title);
+    $this->assertTrue($node, t('Node found in database.'));
+
+    return $node;
+  }
+
+  /**
+   * Create a translation for the specified basic page in the specified
+   * language.
+   *
+   * @param $node
+   *   The basic page to create translation for.
+   * @param $title
+   *   Title of basic page in specified language.
+   * @param $body
+   *   Body of basic page in specified language.
+   * @param $language
+   *   Language code.
+   */
+  function createNodeTranslation($node, $language, $title = NULL, $body = NULL) {
+    $body = $body ? $body : $this->randomName();
+    $title = $title ? $title : $this->randomName();
+    $this->drupalGet('node/add/' . $node->type, array('query' => array('translation' => $node->nid, 'target' => $language->language)));
+
+
+    $this->assertFieldByXPath('//input[@id="edit-title"]', $node->title, "Original title value correctly populated.");
+    $field_lang = field_language('node', $node, 'body');
+    $body_key = "body[und][0][value]";
+    $this->assertFieldByXPath("//textarea[@name='$body_key']", $node->body[$field_lang][0]['value'], "Original body value correctly populated.");
+
+    $edit = array();
+    $edit["title"] = $title;
+    $edit[$body_key] = $body;
+    $this->drupalPost(NULL, $edit, t('Save'));
+    $this->assertRaw(t('Basic page %title has been created.', array('%title' => $title)), t('Translation created.'));
+
+    // Check to make sure that translation was successful.
+    $translation = $this->drupalGetNodeByTitle($title);
+    $this->assertTrue($translation, t('Node found in database.'));
+    $this->assertTrue($translation->tnid == $node->nid, t('Translation set id correctly stored.'));
+
+    return $translation;
+  }
+
+  /**
+   * Retrieves a Drupal path or an absolute path with language
+   *
+   * @param $language
+   *   Language code or language object
+   */
+  protected function i18nGet($language, $path = '', array $options = array(), array $headers = array()) {
+    $options['language'] = $this->getLanguage($language);
+    return $this->drupalGet($path, $options, $headers);
+  }
+  /**
+   * Check strings for different languages
+   */
+  function i18nAssertTranslations($translations, $path = '', $message = 'Translation found for language.') {
+    foreach ($translations as $langcode => $text) {
+      $language = $this->getLanguage($langcode);
+      if ($language->enabled) {
+        $this->i18nGet($language, $path);
+        $this->assertRaw($text, $message . ' ' . $language->name . ': ' . check_plain($text));
+      }
+    }
+  }
+  /**
+   * Create node with language
+   */
+  protected function i18nCreateNode($language, $settings = array()) {
+    $language = $this->getLanguage($language);
+    $settings += array('language'  => $language->language, 'body' => array());
+    $settings['body'] += array($language->language => array(array()));
+    return $this->drupalCreateNode($settings);
+  }
+  /**
+   * Move block to region, from block.test
+   */
+  function moveBlockToRegion($block, $region = 'sidebar_first') {
+    $this->drupalLogin($this->admin_user);
+    // Set the created block to a specific region.
+    $edit = array();
+    $edit['blocks[' . $block['module'] . '_' . $block['delta'] . '][region]'] = $region;
+    $this->drupalPost('admin/structure/block', $edit, t('Save blocks'));
+
+    // Confirm that the block was moved to the proper region.
+    $this->assertText(t('The block settings have been updated.'), t('Block successfully moved to %region_name region.', array( '%region_name' => $region)));
+
+    // Confirm that the block is being displayed.
+    $this->drupalGet('node');
+    $this->assertText(check_plain($block['title']), t('Block successfully being displayed on the page.'));
+
+    // Confirm that the custom block was found at the proper region.
+    $xpath = $this->buildXPathQuery('//div[@class=:region-class]//div[@id=:block-id]/*', array(
+      ':region-class' => 'region region-' . str_replace('_', '-', $region),
+      ':block-id' => 'block-' . $block['module'] . '-' . $block['delta'],
+    ));
+    $this->assertFieldByXPath($xpath, NULL, t('Custom block found in %region_name region.', array('%region_name' => $region)));
+  }
+  /**
+   * Get language object for langcode
+   */
+  public function getLanguage($langcode) {
+    if (is_object($langcode)) {
+      return $langcode;
+    }
+    else {
+      $language_list = language_list();
+      return $language_list[$langcode];
+    }
+  }
+  /**
+   * Switch global language
+   */
+  public function switchLanguage($newlang = NULL) {
+    $newlang = $newlang ? $newlang : $this->install_locale;
+    $GLOBALS[LANGUAGE_TYPE_INTERFACE] = $this->getLanguage($newlang);
+  }
+
+  /**
+   * Get all languages that are not default
+   */
+  public function getOtherLanguages() {
+    $languages = language_list();
+    unset($languages[language_default('language')]);
+    return $languages;
+  }
+  /**
+   * Get enabled languages
+   */
+  public function getEnabledLanguages() {
+    $list = array();
+    foreach (language_list() as $langcode => $language) {
+      if (!empty($language->enabled)) {
+        $list[$langcode] = $language;
+      }
+    }
+    return $list;
+  }
+  /**
+   * Create translation for string in textgroup
+   *
+   * @param $translations
+   *   Optional array of langcode => translation. If not present, it will be generated.
+   */
+  function createStringTranslation($textgroup, $name, $translations = NULL) {
+    // Generate translations if not found, they will be the same length as source string
+    if (!$translations) {
+      $length = strlen($name);
+      foreach ($this->getOtherLanguages() as $language) {
+        $translations[$language->language] = $this->randomName($length);
+      }
+    }
+    $this->drupalLogin($this->translator);
+    // This is the language indicator on the translation search screen for
+    // untranslated strings. Copied straight from locale.inc.
+    $language_indicator = "<em class=\"locale-untranslated\">";
+    // Search for the name and translate it.
+    $search = array(
+      'string' => $name,
+      'language' => 'all',
+      'translation' => 'all',
+      'group' => $textgroup,
+    );
+    $this->drupalPost('admin/config/regional/translate/translate', $search, t('Filter'));
+    // assertText() seems to remove the input field where $name always could be
+    // found, so this is not a false assert. See how assertNoText succeeds
+    // later.
+    $this->assertText(check_plain($name), t('Search found the name.'));
+    $this->assertRaw($language_indicator, t('Name is untranslated.'));
+    // Assume this is the only result, given the random name.
+    $this->clickLink(t('edit'));
+    // We save the lid from the path.
+    $matches = array();
+    preg_match('!admin/config/regional/translate/edit/(\d+)!', $this->getUrl(), $matches);
+    $lid = $matches[1];
+    // No t() here, it's surely not translated yet.
+    $this->assertText(check_plain($name), t('name found on edit screen.'));
+    foreach ($translations as $langcode => $translation) {
+      $edit["translations[$langcode]"] = $translation;
+    }
+    $this->drupalPost(NULL, $edit, t('Save translations'));
+    $this->assertText(t('The string has been saved.'), t('The string has been saved.'));
+    $this->assertEqual($this->getUrl(), url('admin/config/regional/translate/translate', array('absolute' => TRUE)), t('Correct page redirection.'));
+    $this->drupalPost('admin/config/regional/translate/translate', $search, t('Filter'));
+    // The indicator should not be here.
+    $this->assertNoRaw($language_indicator, t('String is translated.'));
+    return $translations;
+  }
+
+  /**
+   * Reset static caches to make the test code match the client site behavior.
+   */
+  function resetCaches() {
+    drupal_static_reset('locale_url_outbound_alter');
+    drupal_static_reset('language_list');
+  }
+
+  /**
+   * Print out a variable for debugging
+   */
+  function printDebug($data, $title = 'Debug') {
+    $output = '<h2>' . $title . '<h2 />';
+    $output .= '<pre>';
+    $output .= is_array($data) || is_object($data) ? print_r($data, TRUE) : $data;
+    $output .= '<pre>';
+    //$this->assertTrue(TRUE, $output);
+    $this->verbose($output);
+  }
+  /**
+   * Debug dump object with some formatting
+   */
+  function printObject($object, $title = 'Object') {
+    $output = $this->formatTable($object);
+    $this->printDebug($output, $title);
+  }
+
+  /**
+   * Print out current HTML page
+   */
+  function printPage() {
+    $this->printDebug($this->drupalGetContent());
+  }
+
+  /**
+   * Dump table contents
+   *
+   * @params $table1, $table2..
+   *   One or more table names
+   */
+  function dumpTable() {
+    $output = '';
+    foreach (func_get_args() as $table) {
+      $header = $rows = array();
+      $result = db_query('SELECT * FROM {' . $table . '}');
+      $output .= '<h2>Table dump <i>' . $table . '</i>:</h2>';
+      while ($row = $result->fetchAssoc()) {
+        $rows[] = $row;
+        if (empty($header)) {
+          $header = array_keys($row);
+        }
+      }
+      if (!empty($rows)) {
+        $output .= theme('table', array('header' => $header, 'rows' => $rows));
+      }
+      else {
+        $output .= ' No rows';
+      }
+      $output .= '<br />';
+    }
+    $this->verbose($output);
+  }
+
+  /**
+   * Format object as table, recursive
+   */
+  function formatTable($object) {
+    foreach ($object as $key => $value) {
+      $rows[] = array(
+        $key,
+        is_array($value) || is_object($value) ? $this->formatTable($value) : $value,
+      );
+    }
+    if (!empty($rows)) {
+      return theme('table', array('rows' => $rows));
+    }
+    else {
+      return 'No properties';
+    }
+  }
+}
+
+class Drupali18nConfigTestCase extends Drupali18nTestCase {
+  public static function getInfo() {
+    return array(
+      'name' => 'Multilingual configuration',
+      'group' => 'Internationalization',
+      'description' => 'Basic configuration for the i18n module',
+    );
+  }
+
+  function setUp() {
+    parent::setUp('translation', 'i18n_node');
+    parent::setUpLanguages();
+  }
+
+  function testEnableLanguages() {
+    // A language with two letter code may help too
+    $this->addLanguage('pt-br');
+
+    // Disable Italian to test the translation behavior with disabled languages.
+    $this->addLanguage('it');
+    $edit = array('enabled[it]' => FALSE);
+    $this->drupalPost('admin/config/regional/language', $edit, t('Save configuration'));
+  }
+}
diff --git a/kolab.org/www/drupal-7.18/sites/all/modules/i18n/i18n.variable.inc b/kolab.org/www/drupal-7.18/sites/all/modules/i18n/i18n.variable.inc
new file mode 100644
index 0000000..2b81a25
--- /dev/null
+++ b/kolab.org/www/drupal-7.18/sites/all/modules/i18n/i18n.variable.inc
@@ -0,0 +1,69 @@
+<?php
+/**
+ * @file
+ * Variable information
+ */
+
+/**
+ * Implements hook_variable_group_info().
+ */
+function i18n_variable_group_info() {
+  $groups['i18n'] = array(
+    'title' => t('Multilingual settings'),
+    'description' => t('Mixed options for multilingual sites.'),
+    'access' => 'administer site configuration',
+    'path' => 'admin/config/regional/i18n',
+  );
+  return $groups;
+}
+
+/**
+ * Implements hook_variable_info().
+ */
+function i18n_variable_info($options = array()) {
+  $variables['i18n_language_list'] = array(
+    'title' => t('Languages for content', array(), $options),
+    'description' => t('Determines which languages will be allowed for content creation.', array(), $options),
+    'type' => 'select',
+    'options callback' => 'i18n_variable_option_list',
+    'default' => I18N_LANGUAGE_ENABLED,
+    'group' => 'i18n',
+  );
+  return $variables;
+}
+
+/**
+ * Implements hook_variable_type_info().
+ */
+function i18n_variable_type_info() {
+  // Multiple variable per language options
+  $types['multiple_language'] = array(
+    'title' => t('Multiple language'),
+    'element' => array('#type' => 'fieldset'),
+    'build callback' => 'variable_build_multiple',
+    'format callback' => 'variable_format_multiple',
+    'element callback' => 'variable_form_element_multiple',
+    'value callback' => 'variable_multiple_get_value',
+    'default callback' => 'variable_multiple_get_default',
+    'multiple callback' => 'i18n_variable_multiple_language_options',
+    'language list' => I18N_LANGUAGE_EXTENDED,
+  );
+  return $types;
+}
+
+/**
+ * Options for content languages
+ */
+function i18n_variable_option_list($variable, $options = array()) {
+  return array(
+    I18N_LANGUAGE_ENABLED => t('Enabled languages only.', array(), $options),
+    I18N_LANGUAGE_EXTENDED => t('All defined languages will be allowed.', array(), $options),
+  );
+}
+
+/**
+ * Callback for multiple per-language variables
+ */
+function i18n_variable_multiple_language_options($variable, $options = array()) {
+  return i18n_language_list('name', $variable['language list']);
+}
diff --git a/kolab.org/www/drupal-7.18/sites/all/modules/i18n/i18n_block/i18n_block.i18n.inc b/kolab.org/www/drupal-7.18/sites/all/modules/i18n/i18n_block/i18n_block.i18n.inc
new file mode 100644
index 0000000..a717fca
--- /dev/null
+++ b/kolab.org/www/drupal-7.18/sites/all/modules/i18n/i18n_block/i18n_block.i18n.inc
@@ -0,0 +1,65 @@
+<?php
+/**
+ * @file
+ * Internationalization (i18n) hooks
+ */
+
+/**
+ * Implements hook_i18n_object_info().
+ */
+function i18n_block_i18n_object_info() {
+  $info['block'] = array(
+    'title' => t('Block'),
+    'class' => 'i18n_block_object',
+    'load callback' => 'block_load',
+    'key' => array('module', 'delta'),
+    'placeholders' => array(
+    	'%module' => 'module',
+      '%delta' => 'delta',
+    ),
+    'edit path' => 'admin/structure/block/manage/%module/%delta/configure',
+    'string translation' => array(
+      'textgroup' => 'blocks',
+      'properties' => array(
+        'title' => array(
+          'title' => t('Title'),
+          'empty' => '<none>',
+        ),
+        'body' => array(
+          'title' => t('Body'),
+          'format' => 'format',
+        ),
+      ),
+      'translate path' => 'admin/structure/block/manage/%module/%delta/translate/%i18n_language',
+    )
+  );
+  return $info;
+}
+
+/**
+ * Implements hook_i18n_string_info().
+ */
+function i18n_block_i18n_string_info() {
+  $groups['blocks'] = array(
+    'title' => t('Blocks'),
+    'description' => t('Configurable blocks titles and content.'),
+    'format' => TRUE, // This group has strings with format (block body)
+    'list' => TRUE, // This group can list all strings
+  );
+  return $groups;
+}
+
+/**
+ * Implements hook_i18n_string_objects().
+ */
+function i18n_block_i18n_string_objects($type) {
+  if ($type == 'block') {
+    $query = db_select('block', 'b')
+      ->distinct()
+      ->fields('b', array('module', 'delta', 'title', 'i18n_mode'))
+      ->fields('bc', array('body', 'format'))
+      ->condition('i18n_mode', I18N_MODE_LOCALIZE);
+    $query->leftJoin('block_custom', 'bc', 'b.bid = bc.bid');
+    return $query->execute()->fetchAll(PDO::FETCH_OBJ);
+  }
+}
diff --git a/kolab.org/www/drupal-7.18/sites/all/modules/i18n/i18n_block/i18n_block.inc b/kolab.org/www/drupal-7.18/sites/all/modules/i18n/i18n_block/i18n_block.inc
new file mode 100644
index 0000000..0bd9e9d
--- /dev/null
+++ b/kolab.org/www/drupal-7.18/sites/all/modules/i18n/i18n_block/i18n_block.inc
@@ -0,0 +1,52 @@
+<?php
+/**
+ * Blocks textgroup handler
+ */
+
+/**
+ * Block object
+ */
+class i18n_block_object extends i18n_string_object_wrapper {
+
+  /**
+   * Load a block object.
+   *
+   * @param $object
+   *  An array with module and delta.
+   */
+  function load_object($object) {
+    $this->object = call_user_func_array($this->get_info('load callback'), $object);
+    return $this->get_object();
+  }
+
+  /**
+   * Get base keys for translating this object
+   */
+  public function get_string_context() {
+    return array($this->object->module, $this->object->delta);
+  }
+
+  /**
+   * Get object strings for translation
+   */
+  protected function build_properties() {
+    if ($this->object->module == 'block' && !isset($this->object->body)) {
+      $block = (object) block_custom_block_get($this->object->delta);
+      $this->object->body = $block->body;
+      $this->object->format = $block->format;
+    }
+    $properties = parent::build_properties();
+    // Body is available only for custom blocks.
+    if ($this->object->module != 'block') {
+      unset($properties[$this->get_textgroup()][$this->object->module][$this->object->delta]['body']);
+    }
+    return $properties;
+  }
+
+  /**
+   * Translation mode for object
+   */
+  public function get_translate_mode() {
+    return !empty($this->object->i18n_mode) ? I18N_MODE_LOCALIZE : I18N_MODE_NONE;
+  }
+}
diff --git a/kolab.org/www/drupal-7.18/sites/all/modules/i18n/i18n_block/i18n_block.info b/kolab.org/www/drupal-7.18/sites/all/modules/i18n/i18n_block/i18n_block.info
new file mode 100644
index 0000000..52de33c
--- /dev/null
+++ b/kolab.org/www/drupal-7.18/sites/all/modules/i18n/i18n_block/i18n_block.info
@@ -0,0 +1,16 @@
+name = Block languages
+description = Enables language selector for blocks and optional block translation.
+dependencies[] = block
+dependencies[] = i18n_string
+package = Multilingual - Internationalization
+core = 7.x
+files[] = i18n_block.inc
+files[] = i18n_block.test
+
+
+; Information added by drupal.org packaging script on 2013-01-13
+version = "7.x-1.8"
+core = "7.x"
+project = "i18n"
+datestamp = "1358075001"
+
diff --git a/kolab.org/www/drupal-7.18/sites/all/modules/i18n/i18n_block/i18n_block.install b/kolab.org/www/drupal-7.18/sites/all/modules/i18n/i18n_block/i18n_block.install
new file mode 100644
index 0000000..606567c
--- /dev/null
+++ b/kolab.org/www/drupal-7.18/sites/all/modules/i18n/i18n_block/i18n_block.install
@@ -0,0 +1,109 @@
+<?php
+
+/**
+ * @file
+ * Installation file for i18nblocks module.
+ */
+
+/**
+ * Implements hook_install().
+ */
+function i18n_block_install() {
+  module_load_install('i18n');
+  i18n_install_create_fields('block', array('i18n_mode'));
+  // Set module weight for it to run after all block visibility modules have run
+  db_query("UPDATE {system} SET weight = 100 WHERE name = 'i18n_block' AND type = 'module'");
+  // If updating from D6, module changed name
+  if (variable_get('i18n_drupal6_update')) {
+    i18n_block_update_7000();
+    i18n_block_update_7001();
+  }
+}
+
+/**
+ * Implements hook_uninstall().
+ */
+function i18n_block_uninstall() {
+  db_drop_field('block', 'i18n_mode');
+}
+
+/**
+ * Implements hook_schema().
+ */
+function i18n_block_schema() {
+  $schema['i18n_block_language'] = array(
+    'description' => 'Sets block visibility based on language',
+    'fields' => array(
+      'module' => array(
+        'type' => 'varchar',
+        'length' => 64,
+        'not null' => TRUE,
+        'description' => "The block's origin module, from {block}.module.",
+      ),
+      'delta' => array(
+        'type' => 'varchar',
+        'length' => 32,
+        'not null' => TRUE,
+        'description' => "The block's unique delta within module, from {block}.delta.",
+      ),
+      'language' => array(
+        'type' => 'varchar',
+        'length' => 12,
+        'not null' => TRUE,
+        'default' => '',
+        'description' => "Language code, e.g. 'de' or 'en-US'.",
+      ),
+    ),
+    'primary key' => array('module', 'delta', 'language'),
+    'indexes' => array(
+      'language' => array('language'),
+    ),
+  );
+  return $schema;
+}
+
+/**
+ * Implements hook_schema_alter().
+ *
+ * Add block table i18n_mode field
+ */
+function i18n_block_schema_alter(&$schema) {
+  $schema['block']['fields']['i18n_mode'] = array('type' => 'int', 'not null' => TRUE, 'default' => 0, 'description' => 'Block multilingual mode.');
+}
+
+/**
+ * Drupal 6 update from old i18nblocks module.
+ */
+function i18n_block_update_7000() {
+  // D6-D7 updates, to be written
+  // move block language from i18n_blocks into i18n_block_language
+  // Move block type from i18n_blocks into block table (i18n_mode)
+  if (db_table_exists('i18n_blocks')) {
+    foreach (db_query("SELECT * FROM {i18n_blocks}")->fetchAll() as $block) {
+      if ($block->language) {
+        // Set language for block
+        db_merge('i18n_block_language')
+          ->key(array('module' => $block->module, 'delta' => $block->delta))
+          ->fields(array('language' => $block->language))
+          ->execute();
+      }
+      else {
+        // Mark block as translatable
+        db_update('block')
+          ->fields(array('i18n_mode' => 1))
+          ->condition('module', $block->module)
+          ->condition('delta', $block->delta)
+          ->execute();
+      }
+    }
+  }
+}
+
+/**
+ * Drop Drupal 6 {i18n_blocks} table after migration.
+ */
+function i18n_block_update_7001() {
+  if (db_table_exists('i18n_blocks')) {
+    db_drop_table('i18n_blocks');
+  }
+}
diff --git a/kolab.org/www/drupal-7.18/sites/all/modules/i18n/i18n_block/i18n_block.js b/kolab.org/www/drupal-7.18/sites/all/modules/i18n/i18n_block/i18n_block.js
new file mode 100644
index 0000000..da17650
--- /dev/null
+++ b/kolab.org/www/drupal-7.18/sites/all/modules/i18n/i18n_block/i18n_block.js
@@ -0,0 +1,30 @@
+
+(function ($) {
+
+/**
+ * Provide the summary information for the block settings vertical tab.
+ */
+Drupal.behaviors.i18nSettingsSummary = {
+  attach: function (context) {
+
+    $('fieldset#edit-languages', context).drupalSetSummary(function (context) {
+      var summary = '';
+      if ($('.form-item-i18n-mode input[type=checkbox]:checked', context).val()) {
+        summary += Drupal.t('Translatable');
+      }
+      else {
+        summary += Drupal.t('Not translatable');
+      }
+      summary += ', ';
+      if ($('.form-item-languages input[type=checkbox]:checked', context).val()) {
+        summary += Drupal.t('Restricted to certain languages');
+      }
+      else {
+        summary += Drupal.t('Not restricted');
+      }
+      return summary;
+    });
+  }
+};
+
+})(jQuery);
diff --git a/kolab.org/www/drupal-7.18/sites/all/modules/i18n/i18n_block/i18n_block.module b/kolab.org/www/drupal-7.18/sites/all/modules/i18n/i18n_block/i18n_block.module
new file mode 100644
index 0000000..9461900
--- /dev/null
+++ b/kolab.org/www/drupal-7.18/sites/all/modules/i18n/i18n_block/i18n_block.module
@@ -0,0 +1,345 @@
+<?php
+
+/**
+ * @file
+ * Internationalization (i18n) submodule: Multilingual meta-blocks
+ *
+ * @author Jose A. Reyero, 2005
+ *
+ * @ TODO Add strings on block update.
+ */
+
+/**
+ * Implements hook_menu().
+ *
+ * Add translate tab to blocks.
+ */
+function i18n_block_menu() {
+  $items['admin/structure/block/manage/%/%/translate'] = array(
+    'title' => 'Translate',
+    'access callback' => 'i18n_block_translate_tab_access',
+    'access arguments' => array(4, 5),
+    'page callback' => 'i18n_block_translate_tab_page',
+    'page arguments' => array(4, 5),
+    'type' => MENU_LOCAL_TASK,
+    'context' => MENU_CONTEXT_PAGE | MENU_CONTEXT_INLINE,
+    'weight' => 10,
+  );
+  $items['admin/structure/block/manage/%/%/translate/%i18n_language'] = array(
+    'title' => 'Translate',
+    'access callback' => 'i18n_block_translate_tab_access',
+    'access arguments' => array(4, 5),
+    'page callback' => 'i18n_block_translate_tab_page',
+    'page arguments' => array(4, 5, 7),
+    'type' => MENU_CALLBACK,
+    'weight' => 10,
+  );
+  return $items;
+}
+
+/**
+ * Implement hook_menu_alter().
+ *
+ * Reorganize block tabs so that they make sense.
+ */
+function i18n_block_menu_alter(&$items) {
+  // Give the configure tab a short name and make it display.
+  $items['admin/structure/block/manage/%/%/configure']['weight'] = -100;
+  $items['admin/structure/block/manage/%/%/configure']['title'] = 'Configure';
+  $items['admin/structure/block/manage/%/%/configure']['context'] = MENU_CONTEXT_PAGE | MENU_CONTEXT_INLINE;
+  // Hide the delete tab. Not sure why this was even set a local task then
+  // set to not show in any context...
+  $items['admin/structure/block/manage/%/%/delete']['type'] = MENU_CALLBACK;
+}
+
+/**
+ * Menu access callback function.
+ *
+ * Only let blocks translated which are configured to be translatable.
+ */
+function i18n_block_translate_tab_access($module, $delta) {
+  $block = block_load($module, $delta);
+  return user_access('translate interface') && isset($block) && ($block->i18n_mode == I18N_MODE_LOCALIZE);
+}
+
+/**
+ * Build a translation page for the given block.
+ */
+function i18n_block_translate_tab_page($module, $delta, $language = NULL) {
+  $block = block_load($module, $delta);
+  return i18n_string_object_translate_page('block', $block, $language);
+}
+
+/**
+ * Implements hook_block_list_alter().
+ *
+ * Translate localizable blocks.
+ *
+ * To be run after all block visibility modules have run, just translate the blocks to be displayed
+ */
+function i18n_block_block_list_alter(&$blocks) {
+  global $theme_key, $language;
+
+  // Build an array of node types for each block.
+  $block_languages = array();
+  $result = db_query('SELECT module, delta, language FROM {i18n_block_language}');
+  foreach ($result as $record) {
+    $block_languages[$record->module][$record->delta][$record->language] = TRUE;
+  }
+
+  foreach ($blocks as $key => $block) {
+    if (!isset($block->theme) || !isset($block->status) || $block->theme != $theme_key || $block->status != 1) {
+      // This block was added by a contrib module, leave it in the list.
+      continue;
+    }
+    if (isset($block_languages[$block->module][$block->delta]) && !isset($block_languages[$block->module][$block->delta][$language->language])) {
+      // Block not visible for this language
+      unset($blocks[$key]);
+    }
+  }
+}
+
+/**
+ * Implements hook_block_view().
+ */
+function i18n_block_block_view_alter(&$data, $block) {
+  if (!empty($block->i18n_mode)) {
+    if (!empty($block->title) && $block->title != '<none>') {
+      // Unfiltered, as $block->subject will be created later from the title.
+      $data['title'] = i18n_string(array('blocks', $block->module, $block->delta, 'title'), $block->title, array('sanitize' => FALSE));
+    }
+    if ($block->module == 'block' && isset($data['content'])) {
+      $data['content'] = i18n_string(array('blocks', $block->module, $block->delta, 'body'), $data['content']);
+    }
+  }
+}
+
+/**
+ * Implements hook_context_block_info_alter().
+ */
+function i18n_block_context_block_info_alter(&$block_info) {
+  $theme_key = variable_get('theme_default', 'garland');
+  $result = db_select('block')
+    ->fields('block', array('module', 'delta', 'i18n_mode'))
+    ->condition('theme', $theme_key)
+    ->execute();
+  foreach ($result as $row) {
+    if (isset($block_info["{$row->module}-{$row->delta}"])) {
+      $block_info["{$row->module}-{$row->delta}"]->i18n_mode = $row->i18n_mode;
+    }
+  }
+}
+
+/**
+ * Implements hook_help().
+ */
+function i18n_block_help($path, $arg) {
+  switch ($path) {
+    case 'admin/help#i18n_block':
+      $output = '<p>' . t('This module provides support for multilingual blocks.') . '</p>';
+      $output .= '<p>' . t('You can set up a language for a block or define it as translatable:') . '</p>';
+      $output .= '<ul>';
+      $output .= '<li>' . t('Blocks with a language will be displayed only in pages with that language.') . '</li>';
+      $output .= '<li>' . t('Translatable blocks can be translated using the localization interface.') . '</li>';
+      $output .= '</ul>';
+      $output .= '<p>' . t('To search and translate strings, use the <a href="@translate-interface">translation interface</a> pages.', array('@translate-interface' => url('admin/config/regional/translate'))) . '</p>';
+      return $output;
+    case 'admin/config/regional/i18n':
+      $output = '<p>'. t('To set up multilingual options for blocks go to the <a href="@configure_blocks">Blocks administration page</a>.', array('@configure_blocks' => url('admin/structure/block'))) .'</p>';
+      return $output;
+  }
+}
+
+/**
+ * Remove strings for deleted custom blocks.
+ */
+function i18n_block_block_delete_submit(&$form, $form_state) {
+  $delta = $form_state['values']['delta'];
+  // Delete stored strings for the title and content fields.
+  i18n_string_remove("blocks:block:$delta:title");
+  i18n_string_remove("blocks:block:$delta:body");
+}
+
+/**
+ * Implements block hook_form_FORM_ID_alter().
+ *
+ * Remove block title for multilingual blocks.
+ */
+function i18n_block_form_block_add_block_form_alter(&$form, &$form_state, $form_id) {
+  //i18n_block_alter_forms($form, $form_state, $form_id);
+  i18n_block_form_block_admin_configure_alter($form, $form_state, $form_id);
+}
+
+/**
+ * Implements block hook_form_FORM_ID_alter().
+ *
+ * Remove block title for multilingual blocks.
+ */
+function i18n_block_form_block_admin_configure_alter(&$form, &$form_state, $form_id) {
+  $form['i18n_block']['languages'] = array(
+    '#type' => 'fieldset',
+    '#title' => t('Languages'),
+    '#collapsible' => TRUE,
+    '#collapsed' => TRUE,
+    '#group' => 'visibility',
+    '#weight' => 5,
+    '#attached' => array(
+      'js' => array(drupal_get_path('module', 'i18n_block') . '/i18n_block.js'),
+    ),
+  );
+
+  // Add translatable option, just title for module blocks, title and content
+  // for custom blocks.
+  $description = '';
+  $module = $form['module']['#value'];
+  $delta = $form['delta']['#value'];
+
+  // User created menus are exposed by the menu module, others by system.module.
+  if ($module == 'menu' || ($module == 'system' && !in_array($delta, array('mail', 'help', 'powered-by')))) {
+    $description = t('To translate the block content itself, <a href="@menu_translate_url">translate the menu</a> that is being shown.', array('@menu_translate_url' => url('admin/structure/menu/manage/' . $form['delta']['#value'])));
+  }
+  elseif ($module == 'views' && module_exists('i18nviews')) {
+    $name = substr($delta, 0, strpos($delta, '-'));
+    $description = t('To translate the block content itself, <a href="@views_translate_url">translate the view</a> that is being shown.', array('@views_translate_url' => url('admin/structure/views/view/' . $name . '/translate')));
+  }
+  elseif ($module != 'block') {
+    $description = t('This block has generated content, only the title can be translated here.');
+  }
+
+  $block = block_load($form['module']['#value'], $form['delta']['#value']);
+  $form['i18n_block']['languages']['i18n_mode'] = array(
+    '#type' => 'checkbox',
+    '#title' => t('Make this block translatable'),
+    '#default_value' => isset($block->i18n_mode) ? $block->i18n_mode : I18N_MODE_NONE,
+    '#description' => $description,
+  );
+
+  // Add option to select which language pages to show on.
+  $default_options = db_query("SELECT language FROM {i18n_block_language} WHERE module = :module AND delta = :delta", array(
+    ':module' => $form['module']['#value'],
+    ':delta' => $form['delta']['#value'],
+  ))->fetchCol();
+  $form['i18n_block']['languages']['languages'] = array(
+    '#type' => 'checkboxes',
+    '#title' => t('Show this block for these languages'),
+    '#default_value' => $default_options,
+    '#options' => i18n_language_list(),
+    '#description' => t('If no language is selected, block will show regardless of language.'),
+  );
+  if (user_access('translate interface')) {
+    $form['actions']['translate'] = array(
+      '#type' => 'submit',
+      '#name'   => 'save_translate',
+      '#value' => t('Save and translate'),
+      '#states' => array(
+        'visible' => array(
+          // The value must be a string so that the javascript comparison works.
+          ":input[name=i18n_mode]" => array('checked' => TRUE),
+        ),
+      ),
+    );
+  }
+  $form['#submit'][] = 'i18n_block_form_block_admin_configure_submit';
+}
+
+/**
+ * Form submit handler for block configuration form.
+ *
+ * @see i18n_block_form_block_admin_configure_alter()
+ */
+function i18n_block_form_block_admin_configure_submit(&$form, &$form_state) {
+  $module = $form_state['values']['module'];
+  $delta = $form_state['values']['delta'];
+
+  // Update block languages
+  db_delete('i18n_block_language')
+    ->condition('module', $module)
+    ->condition('delta', $delta)
+    ->execute();
+  $query = db_insert('i18n_block_language')->fields(array('language', 'module', 'delta'));
+  foreach (array_filter($form_state['values']['languages']) as $language) {
+    $query->values(array(
+      'language' => $language,
+      'module' => $module,
+      'delta' => $delta,
+    ));
+  }
+  $query->execute();
+  // Update block translation options and strings
+  if (isset($form_state['values']['i18n_mode'])) {
+    db_update('block')
+      ->fields(array('i18n_mode' => $form_state['values']['i18n_mode']))
+      ->condition('module', $module)
+      ->condition('delta', $delta)
+      ->execute();
+    i18n_block_update_strings($form_state['values'], $form_state['values']['i18n_mode']);
+
+    // If the save and translate button was clicked, redirect to the translate
+    // tab instead of the block overview.
+    if ($form_state['triggering_element']['#name'] == 'save_translate') {
+      $form_state['redirect'] = 'admin/structure/block/manage/' . $module . '/' . $delta . '/translate';
+    }
+  }
+}
+
+/**
+ * Update block strings
+ */
+function i18n_block_update_strings($block, $i18n_mode = TRUE) {
+  $title = $i18n_mode && $block['title'] !== '<none>' ? $block['title'] : '';
+  i18n_string_update(array('blocks', $block['module'], $block['delta'], 'title'), $title);
+  if (isset($block['body'])) {
+    if ($i18n_mode) {
+      i18n_string_update(array('blocks', $block['module'], $block['delta'], 'body'), $block['body']['value'], array('format' => $block['body']['format']));
+    }
+    else {
+      i18n_string_remove(array('blocks', $block['module'], $block['delta'], 'body'));
+    }
+  }
+}
+
+/**
+ * Implements hook_form_FORMID_alter().
+ *
+ * Adds node specific submit handler to delete custom block form.
+ *
+ * @see block_custom_block_delete()
+ */
+function i18n_block_form_block_custom_block_delete_alter(&$form, &$form_state) {
+  $form['#submit'][] = 'i18n_block_form_block_custom_block_delete_submit';
+}
+
+/**
+ * Form submit handler for custom block delete form.
+ *
+ * @see node_form_block_custom_block_delete_alter()
+ */
+function i18n_block_form_block_custom_block_delete_submit($form, &$form_state) {
+  db_delete('i18n_block_language')
+    ->condition('module', 'block')
+    ->condition('delta', $form_state['values']['bid'])
+    ->execute();
+  // Remove related strings
+  module_invoke('i18n_strings', 'remove',
+    array('blocks', 'block', $form_state['values']['bid']),
+    array('title', 'body')
+  );
+}
+
+/**
+ * Translate block.
+ *
+ * @param $block
+ *   Core block object
+ */
+function i18n_block_translate_block($block) {
+  if (!empty($block->content) && $localizable) {
+    $block->content = i18n_string_text("blocks:$block->module:$block->delta:body", $block->content);
+  }
+  // If it has a custom title, localize it
+  if (!empty($block->title) && $block->title != '<none>') {
+    // Check plain here to allow module generated titles to keep any markup.
+    $block->subject = i18n_string_plain("blocks:$block->module:$block->delta:title", $block->subject);
+  }
+  return $block;
+}
diff --git a/kolab.org/www/drupal-7.18/sites/all/modules/i18n/i18n_block/i18n_block.test b/kolab.org/www/drupal-7.18/sites/all/modules/i18n/i18n_block/i18n_block.test
new file mode 100644
index 0000000..b1e4680
--- /dev/null
+++ b/kolab.org/www/drupal-7.18/sites/all/modules/i18n/i18n_block/i18n_block.test
@@ -0,0 +1,197 @@
+<?php
+/**
+ * @file
+ * Test case for multilingual blocks
+ */
+
+class i18nBlocksTestCase extends Drupali18nTestCase {
+
+  public static function getInfo() {
+    return array(
+      'name' => 'Block translation',
+      'group' => 'Internationalization',
+      'description' => 'Block translation functions'
+    );
+  }
+
+  function setUp() {
+    parent::setUp('i18n_block');
+    parent::setUpLanguages();
+    $this->translator = $this->drupalCreateUser(array('translate interface', 'translate user-defined strings'));
+
+    $format = filter_default_format();
+    variable_set('i18n_string_allowed_formats', array($format => $format));
+    $this->drupalLogin($this->admin_user);
+  }
+
+  function testBlockTranslation() {
+
+    $block_translater = $this->drupalCreateUser(array('administer blocks', 'translate interface', 'translate user-defined strings'));
+
+    // Display Language switcher block
+    $switcher = array('module' => 'locale', 'delta' => 'language', 'title' => t('Languages'));
+    $this->moveBlockToRegion($switcher);
+    // Add a custom title to language switcher block and check it displays translated
+    $title = $this->randomName(10);
+    $this->updateBlock($switcher, array('title' => $title, 'i18n_mode' => I18N_MODE_LOCALIZE));
+    $this->assertText($title, "The new custom title is displayed on the home page.");
+    $translations = $this->createStringTranslation('blocks', $title);
+    $this->i18nAssertTranslations($translations);
+
+    // Create a translatable block and test block visibility per language.
+    $block = $this->i18nCreateBlock();
+    // Now set a language for the block and confirm it shows just for that one (without translation)
+    $languages = $this->getEnabledLanguages();
+    $setlanguage = array_shift($languages);
+    $otherlanguage = array_shift($languages);
+    $this->setBlockLanguages($block, array($setlanguage->language));
+    // Show in block's language but not translated
+    $this->i18nGet($setlanguage);
+    $this->assertText($block['title']);
+    // Do not show in the other language
+    $this->i18nGet($otherlanguage);
+    $this->assertNoText($block['title']);
+
+    // Create a new block, translate it and check the right translations are displayed for title and body
+    $box2 = $this->i18nCreateBlock();
+    // Create translations for title and body, source strings should be already there
+    $translations = $this->i18nTranslateBlock($box2);
+    $this->i18nAssertTranslations($translations['title'], '', 'Custom block title translation displayed.');
+    $this->i18nAssertTranslations($translations['body'], '', 'Custom block body translation displayed.');
+
+    // Test the translate tab.
+    $this->drupalLogin($this->admin_user);
+    $this->drupalGet('admin/structure/block/manage/' . $box2['module'] . '/' . $box2['delta'] . '/configure');
+    $this->assertNoFieldByName('save_and_translate');
+
+    $this->drupalLogin($block_translater);
+    $this->drupalPost('admin/structure/block/manage/' . $box2['module'] . '/' . $box2['delta'] . '/configure', array(), t('Save and translate'));
+    // @todo Improve these checks.
+    $this->assertText(t('Spanish'));
+    $this->assertText(t('translated'));
+
+    $this->clickLink(t('translate'));
+
+    $this->assertFieldByName('strings[blocks:block:' . $box2['delta'] . ':title]', $translations['title']['es']);
+    $this->assertFieldByName('strings[blocks:block:' . $box2['delta'] . ':body]', $translations['body']['es']);
+
+    // Update the translation.
+    $translations['title']['es'] = $this->randomName(10);
+    $translations['body']['es'] = $this->randomName(20);
+    $edit = array(
+      'strings[blocks:block:' . $box2['delta'] . ':title]' => $translations['title']['es'],
+      'strings[blocks:block:' . $box2['delta'] . ':body]' => $translations['body']['es'],
+    );
+    $this->drupalPost(NULL, $edit, t('Save translation'));
+    $this->i18nAssertTranslations($translations['title'], '', 'Updated block title translation displayed.');
+    $this->i18nAssertTranslations($translations['body'], '', 'Updated block body translation displayed.');
+
+    // Test a block translation with filtering and text formats
+    $box3 = $this->i18nCreateBlock(array(
+      'title' => '<div><script>alert(0)</script>Title</script>',
+      'body' => "Dangerous text\nOne line\nTwo lines<script>alert(1)</script>",
+    ));
+    // This should be the actual HTML displayed
+    $title = check_plain($box3['title']);
+    $body = check_markup($box3['body'], $box3['format']);
+    $this->drupalGet('');
+    $this->assertRaw($title, "Title being displayed for default language: " . $title);
+    $this->assertRaw($body, "Body being displayed for default language: " . $body);
+
+    // We add language name to the body just to make sure we get the right translation later
+    // This won't work for block titles as they don't have input format thus scripts will be blocked by locale
+    $translations = array();
+    foreach ($this->getOtherLanguages() as $langcode => $language) {
+      $translations[$langcode] = $box3['body'] . "\n" . $language->name;
+      $filtered[$langcode] = check_markup($translations[$langcode], $box3['format']);
+    }
+    // We need to find the string by this part alone, the rest will be filtered
+    $this->createStringTranslation('blocks', 'Dangerous text', $translations);
+    // Check the right filtered strings are displayed
+    $this->i18nAssertTranslations($filtered);
+
+    // Assert translatable descriptions.
+    $this->drupalLogin($this->admin_user);
+    $this->drupalGet('admin/structure/block/manage/system/powered-by/configure');
+    $this->assertText(t('This block has generated content, only the title can be translated here.'));
+
+    $this->drupalGet('admin/structure/block/manage/system/navigation/configure');
+    $this->assertText(t('To translate the block content itself, translate the menu that is being shown.'));
+  }
+
+  /**
+   * Translate block fields to all languages
+   */
+  function i18nTranslateBlock($block) {
+    $translations['title'] = $this->createStringTranslation('blocks', $block['title']);
+    $translations['body'] = $this->createStringTranslation('blocks', $block['body']);
+    return $translations;
+  }
+  /**
+   * Test creating custom block (i.e. box), moving it to a specific region and then deleting it.
+   */
+  function i18nCreateBlock($block = array(), $region = 'sidebar_first', $check_display = TRUE) {
+    $this->drupalLogin($this->admin_user);
+    // Add a new custom block by filling out the input form on the admin/structure/block/add page.
+    $block += array(
+      'info' => $this->randomName(8),
+      'title' => $this->randomName(8),
+      'i18n_mode' => I18N_MODE_LOCALIZE,
+      'body' => $this->randomName(16),
+    );
+    $custom_block = array(
+      'info' => $block['info'],
+      'title' => $block['title'],
+      'i18n_mode' => $block['i18n_mode'],
+      'body[value]' => $block['body'],
+    );
+    $this->drupalPost('admin/structure/block/add', $custom_block, t('Save block'));
+
+    // Confirm that the custom block has been created, and then query the created bid.
+    $this->assertText(t('The block has been created.'), t('Custom block successfully created.'));
+    $bid = db_query("SELECT bid FROM {block_custom} WHERE info = :info", array(':info' => $block['info']))->fetchField();
+
+    // Check to see if the custom block was created by checking that it's in the database.
+    $this->assertNotNull($bid, t('Custom block found in database'));
+
+    // Check that block_block_view() returns the correct title and content.
+    $data = block_block_view($bid);
+    $format = db_query("SELECT format FROM {block_custom} WHERE bid = :bid", array(':bid' => $bid))->fetchField();
+    $this->assertTrue(array_key_exists('subject', $data) && empty($data['subject']), t('block_block_view() provides an empty block subject, since custom blocks do not have default titles.'));
+    $this->assertEqual(check_markup($block['body'], $format), $data['content'], t('block_block_view() provides correct block content.'));
+
+    // Check if the block can be moved to all available regions.
+    $block['module'] = 'block';
+    $block['delta'] = $bid;
+    $block['format'] = $format;
+    $this->moveBlockToRegion($block, $region);
+
+    return $block;
+  }
+
+  /**
+   * Update block i18n mode
+   */
+  function setBlockMode($block, $mode = I18N_MODE_LOCALIZE) {
+    $edit['i18n_mode'] = $mode;
+    $this->updateBlock($block, $edit);
+  }
+  /**
+   * Update block visibility for languages
+   */
+  function setBlockLanguages($block, $languages = array()) {
+    $edit = array();
+    foreach ($this->getEnabledLanguages() as $langcode => $language) {
+      $edit["languages[$langcode]"] = in_array($langcode, $languages) ? TRUE : FALSE;
+    }
+    $this->updateBlock($block, $edit);
+  }
+  /**
+   * Update block
+   */
+  function updateBlock($block, $edit) {
+    $this->drupalLogin($this->admin_user);
+    $this->drupalPost('admin/structure/block/manage/' . $block['module'] . '/' . $block['delta'] . '/configure', $edit, t('Save block'));
+  }
+
+}
diff --git a/kolab.org/www/drupal-7.18/sites/all/modules/i18n/i18n_contact/i18n_contact.i18n.inc b/kolab.org/www/drupal-7.18/sites/all/modules/i18n/i18n_contact/i18n_contact.i18n.inc
new file mode 100644
index 0000000..26a0db4
--- /dev/null
+++ b/kolab.org/www/drupal-7.18/sites/all/modules/i18n/i18n_contact/i18n_contact.i18n.inc
@@ -0,0 +1,57 @@
+<?php
+/**
+ * @file
+ * Internationalization (i18n) hooks
+ */
+
+/**
+ * Implements hook_i18n_string_info().
+ */
+function i18n_contact_i18n_string_info() {
+  $groups['contact'] = array(
+    'title' => t('Contact forms'),
+    'description' => t('Configurable contact form categories.'),
+    'format' => FALSE,
+    'list' => TRUE,
+  );
+  return $groups;
+}
+
+/**
+ * Implements hook_i18n_object_info().
+ */
+function i18n_contact_i18n_object_info() {
+  $info['contact_category'] = array(
+    // Generic object title.
+    'title' => t('Contact category'),
+    // The object key field.
+    'key' => 'cid',
+    // The object load callback.
+    'load callback' => 'contact_load',
+    // Placeholders for automatic paths.
+    'placeholders' => array(
+      '%contact' => 'cid',
+    ),
+    // To produce edit links automatically.
+    'edit path' => 'admin/structure/contact/edit/%contact',
+    // Auto-generate translate tab.
+    'translate tab' => 'admin/structure/contact/edit/%contact/translate',
+    // Properties for string translation.
+    'string translation' => array(
+      // Text group that will handle this object's strings.
+      'textgroup' => 'contact',
+      // Object type property for string translation.
+      'type' => 'category',
+      // Table where the object is stored, to automate string lists
+      'table' => 'contact',
+      // Translatable properties of these objects.
+      'properties' => array(
+        'category' => t('Category'),
+        'reply' => t('Auto-reply'),
+      ),
+      // Path to translate strings to every language.
+      'translate path' => 'admin/structure/contact/edit/%contact/translate/%i18n_language',
+    )
+  );
+  return $info;
+}
diff --git a/kolab.org/www/drupal-7.18/sites/all/modules/i18n/i18n_contact/i18n_contact.info b/kolab.org/www/drupal-7.18/sites/all/modules/i18n/i18n_contact/i18n_contact.info
new file mode 100644
index 0000000..9ca0957
--- /dev/null
+++ b/kolab.org/www/drupal-7.18/sites/all/modules/i18n/i18n_contact/i18n_contact.info
@@ -0,0 +1,13 @@
+name = Contact translation
+description = Makes contact categories and replies available for translation.
+dependencies[] = contact
+dependencies[] = i18n_string
+package = Multilingual - Internationalization
+core = 7.x
+
+; Information added by drupal.org packaging script on 2013-01-13
+version = "7.x-1.8"
+core = "7.x"
+project = "i18n"
+datestamp = "1358075001"
+
diff --git a/kolab.org/www/drupal-7.18/sites/all/modules/i18n/i18n_contact/i18n_contact.module b/kolab.org/www/drupal-7.18/sites/all/modules/i18n/i18n_contact/i18n_contact.module
new file mode 100644
index 0000000..620b9ff
--- /dev/null
+++ b/kolab.org/www/drupal-7.18/sites/all/modules/i18n/i18n_contact/i18n_contact.module
@@ -0,0 +1,94 @@
+<?php
+/**
+ * @file
+ * Internationalization (i18n) submodule: Multilingual contact forms
+ *
+ * @author Jose A. Reyero, 2005
+ */
+
+/**
+ * Implements hook_menu().
+ *
+ * Add translate tab to contact config.
+ */
+function i18n_contact_menu() {
+  $items['admin/structure/contact/edit/%contact/edit'] = array(
+    'title' => 'Edit',
+    'type' => MENU_DEFAULT_LOCAL_TASK,
+    'weight' => -100,
+  );
+  return $items;
+}
+
+/**
+ * Implements hook_form_FORM_ID_alter().
+ */
+function i18n_contact_form_contact_category_delete_form_alter(&$form, &$form_state) {
+  $form['#submit'][] = 'i18n_contact_form_contact_category_delete_form_submit';
+}
+
+/**
+ * Remove strings for deleted categories.
+ */
+function i18n_contact_form_contact_category_delete_form_submit(&$form, $form_state) {
+  i18n_string_object_remove('contact_category', $form['contact']['#value']);
+}
+
+/**
+ * Implements hook_form_FORM_ID_alter().
+ */
+function i18n_contact_form_contact_category_edit_form_alter(&$form, &$form_state) {
+    $form['actions']['translate'] = array(
+    '#type' => 'submit',
+    '#name'   => 'save_translate',
+    '#value' => t('Save and translate'),
+  );
+  $form['#submit'][] = 'i18n_contact_form_contact_category_edit_form_submit';
+}
+
+/**
+ * Remove strings for edited/added categories.
+ */
+function i18n_contact_form_contact_category_edit_form_submit($form, &$form_state) {
+  $contact = $form_state['values'];
+  i18n_string_object_update('contact_category', $contact);
+  // If the save and translate button was clicked, redirect to the translate
+  // tab instead of the block overview.
+  if ($form_state['triggering_element']['#name'] == 'save_translate') {
+    $form_state['redirect'] = 'admin/structure/contact/edit/' . $contact['cid'] . '/translate';
+  }
+}
+
+/**
+ * Implements hook_form_FORM_ID_alter().
+ */
+function i18n_contact_form_contact_site_form_alter(&$form, &$form_state) {
+  // Example of array translation. The placeholder '*' indicates the name part to be replace by the array keys
+  if (isset($form['cid']['#options'])) {
+    $form['cid']['#options'] = i18n_string_translate("contact:category:*:category", $form['cid']['#options'], array('sanitize' => FALSE));
+  }
+}
+
+/**
+ * Implements hook_mail_alter().
+ */
+function i18n_contact_mail_alter(&$message) {
+  if (in_array($message['id'], array('contact_page_mail', 'contact_page_copy', 'contact_page_autoreply'))) {
+    // Alter the first part of the subject of emails going out if they need
+    // translation.
+    $contact = i18n_string_object_translate('contact_category', $message['params']['category'], array('langcode' => $message['language']->language));
+    $message['subject'] = t(
+      '[!category] !subject',
+      array('!category' => $contact['category'], '!subject' => $message['params']['subject']),
+      array('langcode' => $message['language']->language)
+    );
+
+    if ($message['id'] == 'contact_page_autoreply') {
+      // Overwrite the whole message body. Maybe this is not entirely responsible
+      // (it might overwrite other existing items altered in by others),
+      // but unfortunately Drupal core cotact module does not make its item
+      // identifiable easily.
+      $message['body'] = array($contact['reply']);
+    }
+  }
+}
diff --git a/kolab.org/www/drupal-7.18/sites/all/modules/i18n/i18n_field/i18n_field.i18n.inc b/kolab.org/www/drupal-7.18/sites/all/modules/i18n/i18n_field/i18n_field.i18n.inc
new file mode 100644
index 0000000..d07c8a4
--- /dev/null
+++ b/kolab.org/www/drupal-7.18/sites/all/modules/i18n/i18n_field/i18n_field.i18n.inc
@@ -0,0 +1,90 @@
+<?php
+/**
+ * @file
+ * Internationalization (i18n) hooks
+ */
+
+/**
+ * Implements hook_i18n_object_info().
+ */
+function i18n_field_i18n_object_info() {
+  $info['field'] = array(
+    'title' => t('Field'),
+    'class' => 'i18n_field',
+    'key' => 'field_name',
+    'load callback' => 'field_info_field',
+    'placeholders' => array(
+      '%field_ui_menu' => 'field_name',
+      '%field_type' => 'type',
+    ),
+    'edit path' => 'admin/structure/types/manage/%bundle/fields/%field_ui_menu/field-settings',
+    // We can easily list all these objects
+    'list callback' => 'field_read_fields',
+    'string translation' => array(
+      'textgroup' => 'field',
+      'properties' => array(
+        'label' => array(
+          'title' => t('Label'),
+        ),
+      ),
+      //'translate path' => 'admin/structure/block/manage/%module/%delta/translate/%i18n_language',
+    )
+  );
+  $info['field_instance'] = array(
+    'title' => t('Field instance'),
+    'class' => 'i18n_field_instance',
+    'key' => array('field_name', 'bundle'),
+    'placeholders' => array(
+      '%bundle' => 'bundle',
+      '%field_ui_menu' => 'field_name',
+    ),
+    'edit path' => 'admin/structure/types/manage/%bundle/fields/%field_ui_menu',
+    // We can easily list all these objects.
+    'list callback' => 'field_read_instances',
+    // Metadata for string translation.
+    'string translation' => array(
+      'textgroup' => 'field',
+      'properties' => array(
+        'label' => array(
+          'title' => t('Label'),
+        ),
+        'description' => array(
+          'title' => t('Description'),
+          'format' => 'format',
+        ),
+        'default_value' => array(
+          'title' => t('Default value'),
+          'format' => 'format',
+        ),
+      ),
+      //'translate path' => 'admin/structure/types/manage/%bundle/fields/%field_ui_menu/translate/%i18n_language',
+    )
+  );
+  return $info;
+}
+
+/**
+ * Implements hook_i18n_string_info().
+ */
+function i18n_field_i18n_string_info() {
+  $groups['field'] = array(
+    'title' => t('Fields'),
+    'description' => t('Configurable fields descriptions, defaults, options, etc.'),
+    'format' => FALSE, // This group doesn't have formatted strings
+    'list' => TRUE, // This group can list all strings
+  );
+  return $groups;
+}
+
+/**
+ * Implements hook_i18n_field_info().
+ */
+function i18n_field_i18n_field_info() {
+  $info['text'] = $info['text_long'] = $info['text_with_summary'] = array(
+    'translate_default' => 'i18n_field_translate_default',
+  );
+  $info['list_text'] = $info['list_boolean'] = $info['list_integer'] = array(
+    'translate_options' => 'i18n_field_translate_allowed_values',
+  );
+  return $info;
+}
diff --git a/kolab.org/www/drupal-7.18/sites/all/modules/i18n/i18n_field/i18n_field.inc b/kolab.org/www/drupal-7.18/sites/all/modules/i18n/i18n_field/i18n_field.inc
new file mode 100644
index 0000000..8133f1f
--- /dev/null
+++ b/kolab.org/www/drupal-7.18/sites/all/modules/i18n/i18n_field/i18n_field.inc
@@ -0,0 +1,182 @@
+<?php
+/**
+ * @file
+ * Field and field instance object handlers
+ */
+
+/**
+ * Base object for field and field instance
+ */
+class i18n_field_base extends i18n_string_object_wrapper {
+  /**
+   * Get base path for object
+   */
+  protected function get_base_path() {
+    $info = entity_get_info($this->object['entity_type']);
+    if (isset($info['bundles'][$this->object['bundle']]['admin'])) {
+      $admin = $info['bundles'][$this->object['bundle']]['admin'];
+      // Extract path information from the bundle.
+      if (isset($admin['real path'])) {
+        return $admin['real path'] . '/fields/' . $this->object['field_name'];
+      }
+      else {
+        // We don't have real path, use path instead, may work or not.
+        return $admin['path'] . '/fields/' . $this->object['field_name'];
+      }
+    }
+  }
+}
+
+/**
+ * Field object
+ */
+class i18n_field extends i18n_field_base {
+  /**
+   * Class constructor
+   *
+   * For convenience field objects can be built from field info and from field instance.
+   */
+  public function __construct($type, $key, $object) {
+    parent::__construct($type, $key, $object);
+
+    // If this is a field instance, get field info but add instance data too.
+    // This instance data will be used to get the paths to get the edit/translate path.
+    if (isset($this->object['bundle']) && isset($this->object['entity_type'])) {
+      $this->object = field_info_field($this->object['field_name']) + array('bundle' => $this->object['bundle'], 'entity_type' => $object['entity_type']);
+    }
+  }
+
+  /**
+   * Get edit path for object
+   */
+  public function get_edit_path() {
+    return $this->get_base_path() . '/field-settings';
+  }
+
+  /**
+   * Get translate path for object
+   */
+  public function get_translate_path($langcode = NULL) {
+    return $this->get_base_path() . '/translate/field' . ($langcode ? '/' . $langcode : '');
+  }
+  /**
+   * Get string context
+   */
+  public function get_string_context() {
+    return array($this->object['field_name'], '#field');
+  }
+  /**
+   * Get translatable properties
+   */
+  protected function build_properties() {
+    $properties = parent::build_properties();
+    $object = $this->object;
+    // For select fields field:field_name
+    if (!empty($object['settings']['allowed_values']) && i18n_field_type_info($object['type'], 'translate_options')) {
+      //return array('field', $field['field_name'], '#allowed_values');
+      foreach ($object['settings']['allowed_values'] as $key => $value) {
+        $properties[$this->get_textgroup()][$object['field_name']]['#allowed_values'][$key] = array(
+          'title' => t('Option %name', array('%name' => $value)),
+          'string' => $value,
+        );
+      }
+    }
+    return $properties;
+  }
+
+  /**
+   * Context to be pre-loaded before translation.
+   */
+  protected function get_translate_context($langcode, $options) {
+    return array(
+      $this->object['field_name'],
+      array('#field', '#allowed_values'),
+      '*'
+    );
+  }
+
+  /**
+   * Set field translation for object.
+   *
+   * Mot often, this is a direct field set, but sometimes fields may have different formats.
+   *
+   * @param $object
+   *   A clone of the object or array. Field instance.
+   */
+  protected function translate_field(&$object, $i18nstring, $langcode, $options) {
+    if ($i18nstring->objectid == '#allowed_values') {
+      $object['settings']['#allowed_values'][$i18nstring->key] = $i18nstring->format_translation($langcode, $options);
+    }
+    else {
+      parent::translate_field($object, $i18nstring, $langcode, $options);
+    }
+  }
+}
+
+/**
+ * Field instance object
+ */
+class i18n_field_instance extends i18n_field_base {
+  /**
+   * Get edit path for object
+   */
+  public function get_edit_path() {
+    return $this->get_base_path();
+  }
+  /**
+   * Get translate path for object
+   */
+  public function get_translate_path($langcode = NULL) {
+    return $this->get_base_path() . '/translate' . ($langcode ? '/' . $langcode : '');
+  }
+  /**
+   * Get string context
+   */
+  public function get_string_context() {
+    return array($this->object['field_name'], $this->object['bundle']);
+  }
+  /**
+   * Get translatable properties
+   */
+  protected function build_properties() {
+    $properties = parent::build_properties();
+    $object = $this->object;
+    $field = field_info_field($object['field_name']);
+    // Only for text field types
+    if (!empty($object['default_value']) && i18n_field_type_info($field['type'], 'translate_default')) {
+      $format = isset($object['default_value'][0]['format']) ? $object['default_value'][0]['format'] : NULL;
+      $properties[$this->get_textgroup()][$object['field_name']][$object['bundle']]['default_value']['string'] = $object['default_value'][0]['value'];
+      $properties[$this->get_textgroup()][$object['field_name']][$object['bundle']]['default_value']['format'] = $format;
+    }
+    return $properties;
+  }
+
+  /**
+   * Set field translation for object.
+   *
+   * Mot often, this is a direct field set, but sometimes fields may have different formats.
+   *
+   * @param $object
+   *   A clone of the object or array. Field instance.
+   */
+  protected function translate_field(&$object, $i18nstring, $langcode, $options) {
+    if ($i18nstring->property == 'default_value') {
+      // Render string without applying format
+      $object['default_value'][0]['value'] = $i18nstring->format_translation($langcode, array('sanitize' => FALSE) + $options);
+    }
+    else {
+      parent::translate_field($object, $i18nstring, $langcode, $options);
+    }
+  }
+
+  /**
+   * Context to be pre-loaded before translation.
+   */
+  protected function get_translate_context($langcode, $options) {
+    return array(
+      $this->object['field_name'],
+      array('#field', '#allowed_values', $this->object['bundle']),
+      '*'
+    );
+  }
+}
diff --git a/kolab.org/www/drupal-7.18/sites/all/modules/i18n/i18n_field/i18n_field.info b/kolab.org/www/drupal-7.18/sites/all/modules/i18n/i18n_field/i18n_field.info
new file mode 100644
index 0000000..c3a40a3
--- /dev/null
+++ b/kolab.org/www/drupal-7.18/sites/all/modules/i18n/i18n_field/i18n_field.info
@@ -0,0 +1,14 @@
+name = Field translation
+description = Translate field properties
+dependencies[] = field
+dependencies[] = i18n_string
+package = Multilingual - Internationalization
+core = 7.x
+files[] = i18n_field.inc
+files[] = i18n_field.test
+; Information added by drupal.org packaging script on 2013-01-13
+version = "7.x-1.8"
+core = "7.x"
+project = "i18n"
+datestamp = "1358075001"
+
diff --git a/kolab.org/www/drupal-7.18/sites/all/modules/i18n/i18n_field/i18n_field.install b/kolab.org/www/drupal-7.18/sites/all/modules/i18n/i18n_field/i18n_field.install
new file mode 100644
index 0000000..008c043
--- /dev/null
+++ b/kolab.org/www/drupal-7.18/sites/all/modules/i18n/i18n_field/i18n_field.install
@@ -0,0 +1,85 @@
+<?php
+
+/**
+ * @file
+ * Install, update and uninstall functions for the i18n_field module.
+ */
+
+/**
+ * Implements hook_install().
+ */
+function i18n_field_install() {
+  // If updating from D6, module changed name
+  if (variable_get('i18n_drupal6_update')) {
+    i18n_field_update_7000();
+  }
+}
+
+
+/**
+ * Implements hook_update_dependencies()
+ */
+function i18n_field_update_dependencies() {
+  $dependencies['i18n_field'][7000] = array(
+    'i18n_string' => 7001,
+  );
+  return $dependencies;
+}
+
+/**
+ * Implements hook_i18n_drupal6_update().
+ *
+ * Update old string names
+ */
+function i18n_field_update_7000() {
+  // @todo
+  module_load_install('i18n_string');
+  // Old CCK label and description
+  $query = db_select('i18n_string', 's')
+    ->fields('s')
+    ->condition('textgroup', 'cck')
+    ->condition('type', 'field');
+  foreach ($query->execute() as $string) {
+    $string->textgroup = 'field';
+    list($bundle, $field) = explode('-', $string->objectid);
+    $string->type = $field;
+    $string->objectid = $bundle;
+    $string->property = str_replace('widget_', '', $string->property);
+    i18n_string_install_update_string($string);
+  }
+  // @todo Field groups ??
+  // Old Profile fields
+  $query = db_select('i18n_string', 's')
+    ->fields('s')
+    ->condition('textgroup', 'profile')
+    ->condition('type', 'field');
+  foreach ($query->execute() as $string) {
+    $string->textgroup = 'field';
+    $string->type = $string->property;
+    if ($string->objectid == 'options') {
+      // @todo Handle field options
+      $string->objectid = '#allowed_values';
+    }
+    else {
+      $string->objectid = 'user'; // Bundle for profile fields
+      i18n_string_install_update_string($string);
+    }
+  }
+  // @todo Profile categories ??
+}
+
+/**
+ * Old strings to update. All these will be handled by i18n_field module
+ *
+ * 'cck:field:'. $content_type .'-'. $field_name .':widget_label'
+ *  --> 'field:$field_name:$bundle:label' (though not used atm)
+ * 'cck:field:'. $content_type .'-'. $field_name .':widget_description'
+ *  --> 'field:$field_name:$bundle:description'
+ * 'cck:fieldgroup:'. $content_type .'-'. $group_name .':display_description'
+ * 'cck:fieldgroup:'. $content_type .'-'. $group_name .':form_description', $group['settings']['form']['description']);
+ *
+ * Profile:
+ * profile:field:$field_name:title|explanation|options
+ * "profile:category", $field->category
+ *
+ */
diff --git a/kolab.org/www/drupal-7.18/sites/all/modules/i18n/i18n_field/i18n_field.module b/kolab.org/www/drupal-7.18/sites/all/modules/i18n/i18n_field/i18n_field.module
new file mode 100644
index 0000000..f39a6a7
--- /dev/null
+++ b/kolab.org/www/drupal-7.18/sites/all/modules/i18n/i18n_field/i18n_field.module
@@ -0,0 +1,380 @@
+<?php
+
+/**
+ * @file
+ * Internationalization (i18n) module - Field handling
+ *
+ * For string keys we use:
+ * - field:[field_name]:[bundle]:property, when it is an instance property (linked to bundle)
+ * - field:[field_name]:#property..., when it is a field property (that may have multiple values)
+ */
+
+/**
+ * Implements hook_menu().
+ */
+function i18n_field_menu() {
+  $items = array();
+
+  // Ensure the following is not executed until field_bundles is working and
+  // tables are updated. Needed to avoid errors on initial installation.
+  if (!module_exists('field_ui') || defined('MAINTENANCE_MODE')) {
+    return $items;
+  }
+
+  // Create tabs for all possible bundles. From field_ui_menu().
+  foreach (entity_get_info() as $entity_type => $entity_info) {
+    if ($entity_info['fieldable']) {
+      foreach ($entity_info['bundles'] as $bundle_name => $bundle_info) {
+        if (isset($bundle_info['admin'])) {
+          // Extract path information from the bundle.
+          $path = $bundle_info['admin']['path'];
+          // Different bundles can appear on the same path (e.g. %node_type and
+          // %comment_node_type). To allow field_ui_menu_load() to extract the
+          // actual bundle object from the translated menu router path
+          // arguments, we need to identify the argument position of the bundle
+          // name string ('bundle argument') and pass that position to the menu
+          // loader. The position needs to be casted into a string; otherwise it
+          // would be replaced with the bundle name string.
+          if (isset($bundle_info['admin']['bundle argument'])) {
+            $bundle_arg = $bundle_info['admin']['bundle argument'];
+            $bundle_pos = (string) $bundle_arg;
+          }
+          else {
+            $bundle_arg = $bundle_name;
+            $bundle_pos = '0';
+          }
+          // This is the position of the %field_ui_menu placeholder in the
+          // items below.
+          $field_position = count(explode('/', $path)) + 1;
+
+          // Extract access information, providing defaults.
+          $access = array_intersect_key($bundle_info['admin'], drupal_map_assoc(array('access callback', 'access arguments')));
+          $access += array(
+            'access callback' => 'user_access',
+            'access arguments' => array('administer site configuration'),
+          );
+          $items["$path/fields/%field_ui_menu/translate"] = array(
+            'load arguments' => array($entity_type, $bundle_arg, $bundle_pos, '%map'),
+            'title' => 'Translate',
+            'page callback' => 'i18n_field_page_translate',
+            'page arguments' => array($field_position),
+            'file' => 'i18n_field.pages.inc',
+            'type' => MENU_LOCAL_TASK,
+          ) + $access;
+
+          $items["$path/fields/%field_ui_menu/translate/%i18n_language"] = array(
+            'load arguments' => array($entity_type, $bundle_arg, $bundle_pos, '%map'),
+            'title' => 'Instance',
+            'page callback' => 'i18n_field_page_translate',
+            'page arguments' => array($field_position, $field_position + 2),
+            'file' => 'i18n_field.pages.inc',
+            'type' => MENU_CALLBACK,
+          ) + $access;
+        }
+      }
+    }
+  }
+  return $items;
+}
+
+/**
+ * Implements hook_hook_info().
+ */
+function i18n_field_hook_info() {
+  $hooks['i18n_field_info'] = array(
+    'group' => 'i18n',
+  );
+  return $hooks;
+}
+
+/**
+ * Implements hook_field_attach_form().
+ *
+ * After the form fields are built. Translate title and description for fields with multiple values.
+ */
+function i18n_field_field_attach_form($entity_type, $entity, &$form, &$form_state, $langcode) {
+  // Determine the list of instances to iterate on.
+  list(, , $bundle) = entity_extract_ids($entity_type, $entity);
+  $instances = field_info_instances($entity_type, $bundle);
+  foreach ($instances as $field_name => $instance) {
+    if (isset($form[$field_name])) {
+      $langcode = $form[$field_name]['#language'];
+      $field = &$form[$field_name];
+      // Note: cardinality for unlimited fields is -1
+      if (isset($field[$langcode]['#cardinality']) && $field[$langcode]['#cardinality'] != 1) {
+        $translated = i18n_string_object_translate('field_instance', $instance);
+        if (!empty($field[$langcode]['#title'])) {
+          $field[$langcode]['#title'] = $translated['label'];
+        }
+        if (!empty($field[$langcode]['#description'])) {
+          $field[$langcode]['#description'] = $translated['description'];
+        }
+      }
+    }
+  }
+}
+
+/**
+ * Implements hook_field_formatter_info().
+ */
+function i18n_field_field_formatter_info() {
+  $types = array();
+  foreach (i18n_field_type_info() as $type => $info) {
+    if (!empty($info['translate_options'])) {
+      $types[] = $type;
+    }
+  }
+  return array(
+    'i18n_list_default' => array(
+      'label' => t('Default translated'),
+      'field types' => $types,
+    ),
+  );
+}
+
+/**
+ * Implements hook_field_formatter_view().
+ */
+function i18n_field_field_formatter_view($entity_type, $entity, $field, $instance, $langcode, $items, $display) {
+  $element = array();
+
+  switch ($display['type']) {
+    case 'i18n_list_default':
+      if (($translate = i18n_field_type_info($field['type'], 'translate_options'))) {
+        $allowed_values = $translate($field);
+      }
+      else {
+        // Defaults to list_default behavior
+        $allowed_values = list_allowed_values($field);
+      }
+      foreach ($items as $delta => $item) {
+        if (isset($allowed_values[$item['value']])) {
+          $output = field_filter_xss($allowed_values[$item['value']]);
+        }
+        else {
+          // If no match was found in allowed values, fall back to the key.
+          $output = field_filter_xss($item['value']);
+        }
+        $element[$delta] = array('#markup' => $output);
+      }
+      break;
+  }
+
+  return $element;
+}
+
+
+/**
+ * Implements hook_field_widget_form_alter().
+ *
+ * Translate:
+ * - Title (label)
+ * - Description (help)
+ * - Default value
+ * - List options
+ */
+function i18n_field_field_widget_form_alter(&$element, &$form_state, $context) {
+  global $language;
+
+  // Skip the node type edit fields by checking for existing entity
+  if (empty($element['#entity'])) {
+    return;
+  }
+
+  // Skip if we are missing any of the parameters
+  if (empty($context['field']) || empty($context['instance']) || empty($context['langcode'])) {
+    return;
+  }
+  $field = $context['field'];
+  $instance = $context['instance'];
+  $langcode = $context['langcode'];
+
+  // The field language may affect some variables (default) but not others (description will be in current page language)
+  $i18n_langcode = empty($element['#language']) || $element['#language'] == LANGUAGE_NONE ? $language->language : $element['#language'];
+
+  // Translate instance to current page language and set to form_state
+  // so it will be used for validation messages later.
+  $instance_current = i18n_string_object_translate('field_instance', $instance);
+  if (isset($form_state['field'][$instance['field_name']][$langcode]['instance'])) {
+    $form_state['field'][$instance['field_name']][$langcode]['instance'] = $instance_current;
+  }
+
+  // Translate field title if set and it is the default one.
+  // When cardinality is 1, $element['value'] is used instead.
+  if (!empty($instance_current['label']) && $instance_current['label'] != $instance['label']) {
+    if (!empty($element['#title']) && $element['#title'] == $instance['label']) {
+      $element['#title'] = $instance_current['label'];
+    }
+    if (isset($element['value']) && !empty($element['value']['#title']) && $element['value']['#title'] == $instance['label']) {
+      $element['value']['#title'] = $instance_current['label'];
+    }
+  }
+
+  // Translate field description if set and it is the default one.
+  // When cardinality is 1, $element['value'] is used instead.
+  if (!empty($instance_current['description']) && $instance_current['description'] != $instance['description']) {
+    if (!empty($element['#description']) && $element['#description'] == $instance['description']) {
+      $element['#description'] = $instance_current['description'];
+    }
+    if (isset($element['value']) && !empty($element['value']['#description']) && $element['value']['#description'] == $instance['description']) {
+      $element['value']['#description'] = $instance_current['description'];
+    }
+  }
+
+  // Translate list options
+  if (!empty($element['#options']) && ($translate = i18n_field_type_info($field['type'], 'translate_options')) && !empty($field['settings']['allowed_values'])) {
+    $element['#options'] = $translate($field, $i18n_langcode);
+    if (isset($element['#properties']) && !empty($element['#properties']['empty_option'])) {
+      $label = theme('options_none', array('instance' => $instance, 'option' => $element['#properties']['empty_option']));
+      $element['#options'] = array('_none' => $label) + $element['#options'];
+      // For some elements, change title to new translated option
+      if (!empty($element['#title']) && $field['type'] == 'list_boolean' && !empty($element['#on_value'])) {
+        $on_value = $element['#on_value'];
+        $element['#title'] = $element['#options'][$on_value];
+      }
+    }
+  }
+
+  // Check for more parameters, skip this part if missing.
+  if (!isset($context['delta']) || !isset($context['items'])) {
+    return;
+  }
+  $delta = $context['delta'];
+  $items = $context['items'];
+
+  // Translate default value if exists and the current value is the default
+  if (isset($element['value']['#default_value']) && ($translate = i18n_field_type_info($field['type'], 'translate_default')) &&
+      !empty($instance['default_value'][$delta]['value']) && !empty($items[$delta]['value']) &&
+      $instance['default_value'][$delta]['value'] === $items[$delta]['value']) {
+    $element['value']['#default_value'] = $translate($instance, $items[$delta]['value'], $i18n_langcode);
+  }
+}
+
+/**
+ * Implements hook_field_attach_view_alter().
+ */
+function i18n_field_field_attach_view_alter(&$output, $context) {
+  foreach (element_children($output) as $field_name) {
+    $element = &$output[$field_name];
+    if (!empty($element['#entity_type']) && !empty($element['#field_name']) && !empty($element['#bundle'])) {
+      $instance = field_info_instance($element['#entity_type'], $element['#field_name'], $element['#bundle']);
+
+      // Translate field title if set
+      if (!empty($instance['label'])) {
+        $element['#title'] = i18n_field_translate_property($instance, 'label');
+      }
+
+      // Translate field description if set
+      if (!empty($instance['description'])) {
+        $element['#description'] = i18n_field_translate_property($instance, 'description');
+      }
+    }
+  }
+}
+
+/**
+ * Implements hook_field_create_field().
+ */
+function i18n_field_field_create_field($field) {
+  i18n_field_field_update_strings($field);
+}
+
+/**
+ * Implements hook_field_create_instance().
+ */
+function i18n_field_field_create_instance($instance) {
+  i18n_field_instance_update_strings($instance);
+}
+
+/**
+ * Implements hook_field_delete_instance().
+ */
+function i18n_field_field_delete_instance($instance) {
+  i18n_string_object_remove('field_instance', $instance);
+}
+
+/**
+ * Implements hook_field_update_instance().
+ */
+function i18n_field_field_update_instance($instance, $prior_instance) {
+  i18n_field_instance_update_strings($instance);
+}
+
+/**
+ * Implements hook_field_update_field().
+ */
+function i18n_field_field_update_field($field) {
+  i18n_field_field_update_strings($field);
+}
+
+/**
+ * Update field strings
+ */
+function i18n_field_field_update_strings($field) {
+  i18n_string_object_update('field', $field);
+}
+
+/**
+ * Update field instance strings
+ */
+function i18n_field_instance_update_strings($instance) {
+  i18n_string_object_update('field_instance', $instance);
+}
+
+/**
+ * Returns the array of translated allowed values for a list field.
+ *
+ * The strings are not safe for output. Keys and values of the array should be
+ * sanitized through field_filter_xss() before being displayed.
+ *
+ * @param $field
+ *   The field definition.
+ *
+ * @return
+ *   The array of allowed values. Keys of the array are the raw stored values
+ *   (number or text), values of the array are the display labels.
+ */
+function i18n_field_translate_allowed_values($field, $langcode = NULL) {
+  if (!empty($field['settings']['allowed_values'])) {
+    return i18n_string_translate(array('field', $field['field_name'], '#allowed_values'), $field['settings']['allowed_values'], array('langcode' => $langcode, 'sanitize' => FALSE));
+  }
+  else {
+    return array();
+  }
+}
+
+/**
+ * Translate field default
+ */
+function i18n_field_translate_default($instance, $value, $langcode = NULL) {
+  return i18n_string_translate(array('field', $instance['field_name'], $instance['bundle'], 'default_value'), $value, array('langcode' => $langcode));
+}
+
+/**
+ * Translate field property
+ */
+function i18n_field_translate_property($instance, $property, $langcode = NULL) {
+  // For performance reasons, we translate the whole instance once, which is cached.
+  $instance = i18n_string_object_translate('field_instance', $instance, array('langcode' => $langcode));
+  return $instance[$property];
+}
+
+/**
+ * Get i18n information for fields
+ */
+function i18n_field_type_info($type = NULL, $property = NULL) {
+  $info = &drupal_static(__FUNCTION__);
+  if (!isset($info)) {
+    $info = module_invoke_all('i18n_field_info');
+    drupal_alter('i18n_field_info', $info);
+  }
+  if ($property) {
+    return isset($info[$type]) && isset($info[$type][$property]) ? $info[$type][$property] : NULL;
+  }
+  elseif ($type) {
+    return isset($info[$type]) ? $info[$type] : array();
+  }
+  else {
+    return $info;
+  }
+}
diff --git a/kolab.org/www/drupal-7.18/sites/all/modules/i18n/i18n_field/i18n_field.pages.inc b/kolab.org/www/drupal-7.18/sites/all/modules/i18n/i18n_field/i18n_field.pages.inc
new file mode 100644
index 0000000..8e4b9da
--- /dev/null
+++ b/kolab.org/www/drupal-7.18/sites/all/modules/i18n/i18n_field/i18n_field.pages.inc
@@ -0,0 +1,36 @@
+<?php
+/**
+ * @file
+ * Translation page for fields.
+ */
+
+/**
+ * Field translation page
+ *
+ * We need to translate field and field instance.
+ */
+function i18n_field_page_translate($instance, $language = NULL) {
+  module_load_include('inc', 'i18n_string', 'i18n_string.pages');
+  if (!$language) {
+    // Overview page will be the regular one
+    return i18n_string_translate_page_object('field_instance', $instance);
+  }
+  else {
+    // Because of some weird menu mapping for comment fields language object loader is not working.
+    $language = i18n_language_object($language);
+    drupal_set_title(t('Translate to !language', array('!language' => i18n_language_name($language->language))));
+    //return drupal_get_form('i18n_field_page_translate_form', $instance, $language->language);
+    // Create form with two tabs, one for instance, once for field.
+    $groups = array(
+      'instance' => t('Field instance'),
+      'field' => t('Field settings'),
+    );
+    // Field instance
+    $instance_object = i18n_object('field_instance', $instance);
+    $strings['instance'] = $instance_object->get_strings(array('empty' => TRUE));
+    // Field settings
+    $field_object = i18n_object('field', $instance);
+    $strings['field'] = $field_object->get_strings(array('empty' => TRUE));
+    return drupal_get_form('i18n_string_translate_page_form', $strings, $language->language, $groups);
+  }
+}
diff --git a/kolab.org/www/drupal-7.18/sites/all/modules/i18n/i18n_field/i18n_field.test b/kolab.org/www/drupal-7.18/sites/all/modules/i18n/i18n_field/i18n_field.test
new file mode 100644
index 0000000..6fea159
--- /dev/null
+++ b/kolab.org/www/drupal-7.18/sites/all/modules/i18n/i18n_field/i18n_field.test
@@ -0,0 +1,129 @@
+<?php
+/**
+ * @file
+ * Test case for multilingual fields.
+ */
+
+
+class i18nFieldTestCase extends Drupali18nTestCase {
+
+  public static function getInfo() {
+    return array(
+      'name' => 'Field translation',
+      'group' => 'Internationalization',
+      'description' => 'Field translation functions'
+    );
+  }
+
+  function setUp() {
+    parent::setUp('i18n_field', 'field_test');
+    parent::setUpLanguages(array('access field_test content', 'administer field_test content'));
+    $this->translator = $this->drupalCreateUser(array('translate interface', 'translate user-defined strings'));
+  }
+
+  /**
+   * Test the translation of list fields, including allowed values.
+   */
+  function testListFieldTranslation() {
+    $field_name = drupal_strtolower($this->randomName());
+    $label = $this->randomName();
+    $description = $this->randomName();
+    $value = $this->randomName();
+
+    $field = array(
+      'field_name' => $field_name,
+      'type' => 'list_integer',
+      'cardinality' => 1,
+      'settings' => array(
+        'allowed_values' => array(1 => $value),
+      ),
+    );
+    $field = field_create_field($field);
+
+    $instance = array(
+      'field_name' => $field_name,
+      'entity_type' => 'test_entity',
+      'bundle' => 'test_bundle',
+      'label' => $label,
+      'description' => $description,
+      'widget' => array(
+        'type' => 'options_buttons',
+      ),
+    );
+    $instance = field_create_instance($instance);
+
+    // Refresh i18n_strings.
+    $edit = array('groups[field]' => TRUE);
+    $this->drupalPost('admin/config/regional/translate/i18n_string', $edit, t('Refresh strings'));
+
+    // Save translations for each attribute.
+    $label_translation = $this->createStringTranslation('field', $label);
+    $description_translation = $this->createStringTranslation('field', $description);
+    $value_translation = $this->createStringTranslation('field', $value);
+    $this->drupalLogin($this->admin_user);
+
+    // Test untranslated values in default language.
+    $this->drupalGet('test-entity/add/test-bundle');
+    $this->assertText($label, 'Field label is not translated');
+    $this->assertText($description, 'Field description is not translated');
+    $this->assertText($value, 'Field allowed values are not translated');
+
+    // Test translated values in secondary language.
+    $this->drupalGet($this->secondary_language . '/test-entity/add/test-bundle');
+    $this->assertText($label_translation[$this->secondary_language], 'Field label is translated');
+    $this->assertText($description_translation[$this->secondary_language], 'Field description is translated');
+    $this->assertText($value_translation[$this->secondary_language], 'Field allowed values are translated');
+  }
+
+  /**
+   * Test the translation of text fields, including default values.
+   */
+  function testTextFieldTranslation() {
+    $field_name = drupal_strtolower($this->randomName());
+    $label = $this->randomName();
+    $description = $this->randomName();
+    $default_value = $this->randomName();
+
+    $field = array(
+      'field_name' => $field_name,
+      'type' => 'text',
+      'cardinality' => 1,
+    );
+    $field = field_create_field($field);
+
+    $instance = array(
+      'field_name' => $field_name,
+      'entity_type' => 'test_entity',
+      'bundle' => 'test_bundle',
+      'label' => $label,
+      'description' => $description,
+      'default_value' => array(0 => array('value' => $default_value)),
+      'widget' => array(
+        'type' => 'text_textfield',
+      ),
+    );
+    $instance = field_create_instance($instance);
+
+    // Refresh i18n_strings.
+    $edit = array('groups[field]' => TRUE);
+    $this->drupalPost('admin/config/regional/translate/i18n_string', $edit, t('Refresh strings'));
+
+    // Save translations for each attribute.
+    $label_translation = $this->createStringTranslation('field', $label);
+    $description_translation = $this->createStringTranslation('field', $description);
+    $default_value_translation = $this->createStringTranslation('field', $default_value);
+    $this->drupalLogin($this->admin_user);
+
+    // Test untranslated values in default language.
+    $this->drupalGet('test-entity/add/test-bundle');
+    $this->assertText($label, 'Field label is not translated');
+    $this->assertText($description, 'Field description is not translated');
+    $this->assertRaw($default_value, 'Default value is not translated');
+
+    // Test translated values in secondary language.
+    $this->drupalGet($this->secondary_language . '/test-entity/add/test-bundle');
+    $this->assertText($label_translation[$this->secondary_language], 'Field label is translated');
+    $this->assertText($description_translation[$this->secondary_language], 'Field description is translated');
+    $this->assertRaw($default_value_translation[$this->secondary_language], 'Default value translated');
+  }
+}
diff --git a/kolab.org/www/drupal-7.18/sites/all/modules/i18n/i18n_forum/i18n_forum.info b/kolab.org/www/drupal-7.18/sites/all/modules/i18n/i18n_forum/i18n_forum.info
new file mode 100644
index 0000000..d645355
--- /dev/null
+++ b/kolab.org/www/drupal-7.18/sites/all/modules/i18n/i18n_forum/i18n_forum.info
@@ -0,0 +1,15 @@
+name = Multilingual forum
+description = Enables multilingual forum, translates names and containers.
+dependencies[] = forum
+dependencies[] = i18n_taxonomy
+dependencies[] = i18n_node
+package = Multilingual - Internationalization
+core = 7.x
+files[] = i18n_forum.test
+
+; Information added by drupal.org packaging script on 2013-01-13
+version = "7.x-1.8"
+core = "7.x"
+project = "i18n"
+datestamp = "1358075001"
+
diff --git a/kolab.org/www/drupal-7.18/sites/all/modules/i18n/i18n_forum/i18n_forum.install b/kolab.org/www/drupal-7.18/sites/all/modules/i18n/i18n_forum/i18n_forum.install
new file mode 100644
index 0000000..3055810
--- /dev/null
+++ b/kolab.org/www/drupal-7.18/sites/all/modules/i18n/i18n_forum/i18n_forum.install
@@ -0,0 +1,19 @@
+<?php
+/**
+ * @file
+ * Multilingual forum install file.
+ */
+
+/**
+ * Implements hook_install().
+ */
+function i18n_forum_install() {
+  // Set module weight for it to run after core modules and i18n_taxonomy.
+  db_query("UPDATE {system} SET weight = 10 WHERE name = 'i18n_forum' AND type = 'module'");
+  // Make forum vocabulary translatable.
+  if (($vid = variable_get('forum_nav_vocabulary', 0)) && !i18n_taxonomy_vocabulary_mode($vid)) {
+    $vocabulary = taxonomy_vocabulary_load($vid);
+    $vocabulary->i18n_mode = I18N_MODE_LOCALIZE;
+    taxonomy_vocabulary_save($vocabulary);
+  }
+}
\ No newline at end of file
diff --git a/kolab.org/www/drupal-7.18/sites/all/modules/i18n/i18n_forum/i18n_forum.module b/kolab.org/www/drupal-7.18/sites/all/modules/i18n/i18n_forum/i18n_forum.module
new file mode 100644
index 0000000..3777a32
--- /dev/null
+++ b/kolab.org/www/drupal-7.18/sites/all/modules/i18n/i18n_forum/i18n_forum.module
@@ -0,0 +1,164 @@
+<?php
+
+/**
+ * @file
+ * i18n forum module
+ *
+ * Internationalization (i18n) package.
+ */
+
+/**
+ * Implements hook_help().
+ */
+function i18n_forum_help($path, $arg) {
+  if ($path == 'admin/structure/forum' && ($vocabulary = i18n_forum_vocabulary())) {
+    $base_path = 'admin/structure/taxonomy/' . $vocabulary->machine_name;
+    return t('To translate the forum, <a href="@edit">edit and make it translatable</a>, then <a href="@translate">translate the forum</a> and <a href="@list">its containers and sub-forums</a> on the taxonomy administration page.', array(
+      '@edit' => url($base_path . '/edit'),
+      '@translate' => url($base_path . '/translate'),
+      '@list' => url($base_path . '/list'))
+    );
+  }
+}
+
+/**
+ * Implements hook_menu_local_tasks_alter().
+ */
+function i18n_forum_menu_local_tasks_alter(&$data, $router_item, $root_path) {
+  // Translate link to 'node/add/forum' on 'forum' sub-pages.
+  if ($root_path == 'forum' || $root_path == 'forum/%') {
+    $tid = (isset($router_item['page_arguments'][0]) ? $router_item['page_arguments'][0]->tid : 0);
+    $forum_term = forum_forum_load($tid);
+    if ($forum_term) {
+      // Loop through all bundles for forum taxonomy vocabulary field.
+      $vid = variable_get('forum_nav_vocabulary', 0);
+      if ($vid && ($vocabulary = taxonomy_vocabulary_load($vid)) && ($field = field_info_field('taxonomy_' . $vocabulary->machine_name))) {
+        foreach ($field['bundles']['node'] as $type) {
+          if (isset($data['actions']['output'][$type])) {
+            $data['actions']['output'][$type]['#link']['title'] = t('Add new @node_type', array('@node_type' => i18n_node_type_name($type, node_type_get_name($type))));
+          }
+        }
+      }
+    }
+  }
+}
+
+/**
+ * Implements hook_form_FORM_ID_alter()
+ */
+function i18n_forum_form_taxonomy_form_vocabulary_alter(&$form, &$form_state) {
+  $vid = variable_get('forum_nav_vocabulary', 0);
+  if ($vid && !isset($form_state['confirm_delete']) && isset($form['vid']) && $form['vid']['#value'] == $vid) {
+    // Only two options for this vocabulary
+    $replacements = array(
+      '@item_name_multiple' => t('forum containers'),
+      '@item_name_multiple_capitalized' => t('Forum containers'),
+    );
+    $form['i18n_translation']['i18n_mode']['#options'] = i18n_translation_options_list($replacements, array(I18N_MODE_LOCALIZE, I18N_MODE_TRANSLATE));
+  }
+}
+
+/**
+ * Implements hook_node_view().
+ *
+ * Localize breadcrumb for forum nodes.
+ */
+function i18n_forum_node_view($node, $view_mode, $langcode) {
+  if (_forum_node_check_node_type($node)) {
+    if ($view_mode == 'full' && node_is_page($node)) {
+      $vid = variable_get('forum_nav_vocabulary', 0);
+      $vocabulary = taxonomy_vocabulary_load($vid);
+      // Breadcrumb navigation
+      $breadcrumb[] = l(t('Home'), NULL);
+      $breadcrumb[] = l(i18n_taxonomy_vocabulary_name($vocabulary), 'forum');
+      if ($parents = taxonomy_get_parents_all($node->forum_tid)) {
+        $parents = array_reverse($parents);
+        foreach ($parents as $parent) {
+          $breadcrumb[] = l(i18n_taxonomy_term_name($parent), 'forum/' . $parent->tid);
+        }
+      }
+      drupal_set_breadcrumb($breadcrumb);
+    }
+  }
+}
+
+/**
+ * Implements hook_i18n_translate_path()
+ */
+function i18n_forum_i18n_translate_path($path) {
+  if (strpos($path, 'forum/') === 0 && i18n_forum_mode() & I18N_MODE_TRANSLATE) {
+    return i18n_taxonomy_translate_path($path, 'forum/');
+  }
+}
+
+/**
+ * Translate forums list.
+ */
+function i18n_forum_preprocess_forum_list(&$variables) {
+  if (i18n_forum_mode() & I18N_MODE_LOCALIZE) {
+    foreach ($variables['forums'] as $id => $forum) {
+      $variables['forums'][$id]->description = i18n_string('taxonomy:term:' . $forum->tid . ':description', $forum->description);
+      $variables['forums'][$id]->name = i18n_string('taxonomy:term:' . $forum->tid . ':name', $forum->name);
+    }
+  }
+}
+
+
+/**
+ * Translate forum page.
+ */
+function i18n_forum_preprocess_forums(&$variables) {
+  if (i18n_forum_mode()) {
+    $vocabulary = i18n_forum_vocabulary();
+    if (isset($variables['links']['forum'])) {
+      $variables['links']['forum']['title'] = i18n_string('nodetype:type:forum:post_button', 'Post new Forum topic');
+    }
+    // This one is from advanced forum, http://drupal.org/project/advanced_forum
+    if (!empty($variables['forum_description'])) {
+      $variables['forum_description'] = i18n_string('taxonomy:term:' . $variables['tid'] . ':description', $variables['forum_description']);
+    }
+    // Translate breadrumb and page title.
+    $title = $vocabulary_name = !empty($vocabulary->name) ? i18n_taxonomy_vocabulary_name($vocabulary) : '';
+    $breadcrumb[] = l(t('Home'), NULL);
+    if ($variables['tid']) {
+      $breadcrumb[] = l($vocabulary_name, 'forum');
+    }
+    if ($variables['parents']) {
+      $variables['parents'] = array_reverse($variables['parents']);
+      foreach ($variables['parents'] as $p) {
+        if ($p->tid == $variables['tid']) {
+          $title = i18n_taxonomy_term_name($p);
+        }
+        else {
+          $breadcrumb[] = l(i18n_taxonomy_term_name($p), 'forum/' . $p->tid);
+        }
+      }
+    }
+    drupal_set_breadcrumb($breadcrumb);
+    drupal_set_title($title);
+  }
+}
+
+/**
+ * Get forum vocabulary.
+ */
+function i18n_forum_vocabulary() {
+  if ($vid = variable_get('forum_nav_vocabulary', 0)) {
+    return taxonomy_vocabulary_load($vid);
+  }
+  else {
+    return NULL;
+  }
+}
+
+/**
+ * Get forum vocabulary translation mode.
+ */
+function i18n_forum_mode($mode = NULL) {
+  if ($vocabulary = i18n_forum_vocabulary()) {
+    return i18n_taxonomy_vocabulary_mode($vocabulary);
+  }
+  else {
+    return I18N_MODE_NONE;
+  }
+}
diff --git a/kolab.org/www/drupal-7.18/sites/all/modules/i18n/i18n_forum/i18n_forum.test b/kolab.org/www/drupal-7.18/sites/all/modules/i18n/i18n_forum/i18n_forum.test
new file mode 100644
index 0000000..78cedd4
--- /dev/null
+++ b/kolab.org/www/drupal-7.18/sites/all/modules/i18n/i18n_forum/i18n_forum.test
@@ -0,0 +1,30 @@
+<?php
+
+/**
+ * @file
+ * Test case for multilingual forums.
+ */
+class i18nForumTestCase extends Drupali18nTestCase {
+  public static function getInfo() {
+    return array(
+      'name' => 'Multilingual forum',
+      'group' => 'Internationalization',
+      'description' => 'Tests multilingual forum',
+    );
+  }
+
+  public function setUp() {
+    parent::setUp('translation', 'i18n_select', 'i18n_forum');
+    parent::setUpLanguages();
+    parent::setUpContentTranslation();
+  }
+
+  /**
+   * Tests i18n_select integration.
+   */
+  public function testI18nSelectTest() {
+    // @TODO: improve test. its just a quick test against the PDO exception
+    // @see http://drupal.org/node/1437932
+    $this->i18nGet($this->default_language, 'forum');
+  }
+}
\ No newline at end of file
diff --git a/kolab.org/www/drupal-7.18/sites/all/modules/i18n/i18n_menu/i18n_menu.admin.inc b/kolab.org/www/drupal-7.18/sites/all/modules/i18n/i18n_menu/i18n_menu.admin.inc
new file mode 100644
index 0000000..f76c06d
--- /dev/null
+++ b/kolab.org/www/drupal-7.18/sites/all/modules/i18n/i18n_menu/i18n_menu.admin.inc
@@ -0,0 +1,259 @@
+<?php
+
+/**
+ * @file
+ * Helper functions for menu administration.
+ */
+
+
+/**
+ * Produces a menu translation form.
+ */
+function i18n_menu_translation_form($form, $form_state, $translation_set = NULL, $item = NULL) {
+  $translation_set = $translation_set ? $translation_set : i18n_translation_set_create('menu_link');
+  $form['translation_set'] = array('#type' => 'value', '#value' => $translation_set);
+  $translations = $translation_set->get_translations();
+  // What to do with title? Let's make it a hidden field for now, some tests relay on it
+  $form['title'] = array('#type' => 'hidden', '#default_value' => $translation_set->title);
+  if ($item && ($lang = i18n_object_langcode($item))) {
+    $translations[$lang] = $item;
+  }
+  $item = $item ? $item : array('mlid' => 0, 'menu_name' => '', 'plid' => 0);
+  $item_lang = i18n_object_langcode($item);
+  $form['translations'] = array(
+    '#type' => 'fieldset',
+    '#title' => t('Translations'),
+    '#tree' => TRUE,
+    '#description' => t('Enter items that will be considered as translations of each other.'),
+  );
+  foreach (i18n_language_list() as $langcode => $language_name) {
+    if ($langcode == $item_lang) {
+      // We've got a predefined item for this language
+      $form['translations'][$langcode] = array('#type' => 'value', '#value' => $item['menu_name'] . ':' . $item['mlid']);
+      $form['translations']['display'] = array(
+        '#type' => 'item',
+        '#title' => $language_name,
+        '#markup' => check_plain($item['link_title']),
+      );
+    }
+    else {
+      // Generate a list of possible parents (not including this link or descendants).
+      $options = i18n_menu_parent_options(menu_get_menus(), $item, $langcode);
+      $default = isset($translations[$langcode]) ? $translations[$langcode]['menu_name'] . ':' . $translations[$langcode]['mlid'] : 'navigation:0';
+      if (!isset($options[$default])) {
+        $default = 'navigation:0';
+      }
+      $form['translations'][$langcode] = array(
+        '#type' => 'select',
+        '#title' => $language_name,
+        '#default_value' => $default,
+        '#options' => $options,
+        '#description' => t('The maximum depth for a link and all its children is fixed at !maxdepth. Some menu links may not be available as parents if selecting them would exceed this limit.', array('!maxdepth' => MENU_MAX_DEPTH)),
+        '#attributes' => array('class' => array('menu-title-select')),
+      );
+    }
+  }
+  $form['actions'] = array('#type' => 'actions');
+  $form['actions']['update'] = array('#type' => 'submit', '#value' => t('Save'));
+  if ($translation_set) {
+    $form['actions']['delete'] = array('#type' => 'submit', '#value' => t('Delete'));
+  }
+  return $form;
+}
+
+/**
+ * Process form validation
+ */
+function i18n_menu_translation_form_validate($form, &$form_state)  {
+  if ($form_state['values']['op'] == t('Save')) {
+    $selected = 0;
+    // example array('en' => 'navigation:0')
+    $mlids = array_filter($form_state['values']['translations']);
+    foreach ($mlids as $lang => $item_name) {
+      list($menu_name, $mlid) = explode(':', $item_name);
+      if ($mlid && ($item = menu_link_load($mlid)) && i18n_object_langcode($item)) {
+        $selected++;
+      }
+      else {
+        unset($form_state['values']['translations'][$lang]);
+      }
+    }
+    if ($selected < 1) {
+      form_set_error('translations', t('There are no translations to save.'));
+    }
+  }
+}
+
+/**
+ * Menu item translation form submission
+ */
+function i18n_menu_translation_form_submit($form, &$form_state) {
+  $translation_set = $form_state['values']['translation_set'];
+
+  switch ($form_state['values']['op']) {
+    case t('Save'):
+      $mlids = array_filter($form_state['values']['translations']);
+      $translation_set->reset_translations();
+      foreach ($mlids as $lang => $item_name) {
+        list($menu_name, $mlid) = explode(':', $item_name);
+        $item = menu_link_load($mlid);
+        $translation_set->add_item($item, $lang);
+      }
+      $translation_set->title = !empty($form_state['values']['title']) ? $form_state['values']['title'] : '';
+      $translation_set->save(TRUE);
+      drupal_set_message(t('The item translation has been saved.'));
+      break;
+    case t('Delete'):
+      $translation_set->delete(TRUE);
+      drupal_set_message(t('The item translation has been deleted.'));
+      break;
+  }
+
+  $form_state['redirect'] = 'admin/structure/menu';
+}
+
+/**
+ * Return a list of menu items that are valid possible parents for the given menu item.
+ *
+ * @param $menus
+ *   An array of menu names and titles, such as from menu_get_menus().
+ * @param $item
+ *   The menu item or the node type for which to generate a list of parents.
+ *   If $item['mlid'] == 0 then the complete tree is returned.
+ * @return
+ *   An array of menu link titles keyed on the a string containing the menu name
+ *   and mlid. The list excludes the given item and its children.
+ *
+ * @todo This has to be turned into a #process form element callback. The
+ *   'menu_override_parent_selector' variable is entirely superfluous.
+ */
+function i18n_menu_parent_options($menus, $item, $langcode) {
+  // The menu_links table can be practically any size and we need a way to
+  // allow contrib modules to provide more scalable pattern choosers.
+  // hook_form_alter is too late in itself because all the possible parents are
+  // retrieved here, unless menu_override_parent_selector is set to TRUE.
+  if (variable_get('i18n_menu_override_parent_selector', FALSE)) {
+    return array();
+  }
+  // If no menu item, create a dummy one
+  $item = $item ? $item : array('mlid' => 0);
+  // Get menus that have a language or have language for terms
+  $available_menus = array();
+  foreach (menu_load_all() as $name => $menu) {
+    if ($menu['i18n_mode'] & I18N_MODE_TRANSLATE) {
+      $available_menus[$name] = $menu;
+    }
+    elseif ($menu['i18n_mode'] & I18N_MODE_LANGUAGE && $menu['language'] == $langcode) {
+      $available_menus[$name] = $menu;
+    }
+  }
+  // Disable i18n selection, enable after the query.
+  $previous = i18n_select(FALSE);
+  $options = _i18n_menu_get_options($menus, $available_menus, $item, $langcode);
+  i18n_select($previous);
+  return $options;
+}
+
+/**
+ * Helper function to get the items of the given menu.
+ */
+function _i18n_menu_get_options($menus, $available_menus, $item, $langcode) {
+  // If the item has children, there is an added limit to the depth of valid parents.
+  if (isset($item['parent_depth_limit'])) {
+    $limit = $item['parent_depth_limit'];
+  }
+  else {
+    $limit = _menu_parent_depth_limit($item);
+  }
+
+  $options = array();
+  foreach ($menus as $menu_name => $title) {
+    if (isset($available_menus[$menu_name])) {
+      if ($tree = i18n_menu_tree_all_data($menu_name, $langcode, NULL)) {
+        $options[$menu_name . ':0'] = '<' . $title . '>';
+        _menu_parents_recurse($tree, $menu_name, '--', $options, $item['mlid'], $limit);
+      }
+    }
+  }
+  return $options;
+}
+
+/**
+ * Filter out menu items that have a different language
+ */
+function i18n_menu_tree_all_data($menu_name, $langcode, $link = NULL, $max_depth = NULL) {
+  $tree = menu_tree_all_data($menu_name, $link, $max_depth);
+  return _i18n_menu_tree_filter_items($tree, $langcode);
+}
+
+/**
+ * Filter out menu items that have a different language
+ */
+function _i18n_menu_tree_filter_items($tree, $langcode) {
+  $result = array();
+  foreach ($tree as $key => $item) {
+    $lang = i18n_object_langcode($item['link']);
+    if (!empty($item['below'])) {
+      $item['below'] = _i18n_menu_tree_filter_items($item['below'], $langcode);
+    }
+    if (!empty($item['link']['customized']) && $lang == $langcode) {
+      $result[$key] = $item;
+    }
+    elseif (!empty($item['below'])) {
+      // Keep for the tree but mark as unselectable.
+      $item['link']['title'] = '(' . $item['link']['title'] . ')';
+      $result[$key] = $item;
+    }
+  }
+  return $result;
+}
+
+/**
+ * Callback for menu translation tab.
+ */
+function i18n_menu_translation_item_overview($item, $translation_set = NULL) {
+  if ($item['i18n_tsid']) {
+    // Already part of a set, grab that set.
+    $translation_set = i18n_translation_set_load($item['i18n_tsid']);
+    $translations = $translation_set->get_translations();
+  }
+  else {
+    // We have no translation source mlid, this could be a new set, emulate that.
+    $translations = array($item['language'] => $item);
+  }
+
+  $type = variable_get('translation_language_type', LANGUAGE_TYPE_INTERFACE);
+  $header = array(t('Language'), t('Title'), t('Operations'));
+  $rows = array();
+
+  foreach (i18n_language_list() as $langcode => $language_name) {
+    $options = array();
+    if (isset($translations[$langcode])) {
+      // Existing translation in the translation set: display status.
+      $translation_item = menu_link_load($translations[$langcode]['mlid']);
+      $title = l($translation_item['link_title'], $translation_item['link_path']);
+      $path = 'admin/structure/menu/item/' . $translation_item['mlid'];
+      $options[] = l(t('edit'), $path);
+
+      if ($translation_item['mlid'] == $item['mlid']) {
+        $language_name = t('<strong>@language_name</strong> (source)', array('@language_name' => $language_name));
+      }
+    }
+    else {
+      // No such translation in the set yet: help user to create it.
+      $title = t('n/a');
+      $options[] = l(t('add translation'), 'admin/structure/menu/manage/' . $item['menu_name'] . '/add', array('query' => array('translation' => $item['mlid'], 'target' => $langcode) + drupal_get_destination()));
+    }
+    $rows[] = array($language_name, $title, implode(" | ", $options));
+  }
+
+  drupal_set_title(t('Translations of menu item %title', array('%title' => $item['link_title'])), PASS_THROUGH);
+
+  $build['translation_item_overview'] = array(
+    '#theme' => 'table',
+    '#header' => $header,
+    '#rows' => $rows,
+  );
+
+  return $build;
+}
diff --git a/kolab.org/www/drupal-7.18/sites/all/modules/i18n/i18n_menu/i18n_menu.i18n.inc b/kolab.org/www/drupal-7.18/sites/all/modules/i18n/i18n_menu/i18n_menu.i18n.inc
new file mode 100644
index 0000000..cad3b43
--- /dev/null
+++ b/kolab.org/www/drupal-7.18/sites/all/modules/i18n/i18n_menu/i18n_menu.i18n.inc
@@ -0,0 +1,130 @@
+<?php
+/**
+ * @file
+ * Internationalization (i18n) hooks
+ */
+
+/**
+ * Implements hook_i18n_object_info().
+ */
+function i18n_menu_i18n_object_info() {
+  $info['menu'] = array(
+    'title' => t('Menu'),
+    'key' => 'menu_name',
+    'load callback' => 'menu_load',
+    'base path' => 'admin/structure/menu/manage',
+    'placeholders' => array(
+      '%menu' => 'menu_name',
+    ),
+    'edit path' => 'admin/structure/menu/manage/%menu/edit',
+    // Auto-generate translate tab.
+    'translate tab' => 'admin/structure/menu/manage/%menu/translate',
+    // We can easily list all these objects
+    'list callback' => 'menu_load_all',
+    // Metadata for string translation
+    'string translation' => array(
+      'textgroup' => 'menu',
+      'type' => 'menu',
+      'properties' => array(
+        'title' => t('Title'),
+        'description' => t('Description'),
+      ),
+    ),
+    'translation container' => array(
+      'name' => t('menu'),
+      'item type' => 'menu_link',
+      'item name' => t('menu items'),
+      'options' => array(I18N_MODE_NONE, I18N_MODE_MULTIPLE, I18N_MODE_LANGUAGE),
+    ),
+  );
+  $info['menu_link'] = array(
+    'title' => t('Menu link'),
+    'class' => 'i18n_menu_link',
+    'key' => 'mlid',
+    'load callback' => 'menu_link_load',
+    'base path' => 'admin/structure/menu/item',
+    'edit path' => 'admin/structure/menu/item/%menu_link/edit',
+    // Auto-generate translate tab
+    'translate tab' => 'admin/structure/menu/item/%menu_link/translate',
+    'placeholders' => array(
+      '%menu_link' => 'mlid',
+      '%menu' => 'menu_name',
+    ),
+    'string translation' => array(
+      'textgroup' => 'menu',
+      'type' => 'item',
+      'properties' => array(
+        'title' => array(
+          'title' => t('Title'),
+          'field' => 'link_title',
+        ),
+        'description' => array(
+          'title' => t('Description'),
+          'field' => 'options.attributes.title',
+        ),
+      ),
+    ),
+    'translation set' => TRUE,
+  );
+  return $info;
+}
+
+/**
+ * Implements hook_i18n_translation_set_info()
+ */
+function i18n_menu_i18n_translation_set_info() {
+  $info['menu_link'] = array(
+    'title' => t('Menu link'),
+    'class' => 'i18n_menu_link_translation_set',
+    'table' => 'menu_links',
+    'field' => 'i18n_tsid',
+    'parent' => 'menu',
+    'placeholder' => '%i18n_menu_translation',
+    'list path' => 'admin/structure/menu/manage/translation',
+    'edit path' => 'admin/structure/menu/manage/translation/edit/%i18n_menu_translation',
+    'delete path' => 'admin/structure/menu/manage/translation/delete/%i18n_menu_translation',
+    'page callback' => 'i18n_menu_item_translation_page',
+  );
+  return $info;
+}
+
+/**
+ * Implements hook_i18n_string_info()
+ */
+function i18n_menu_i18n_string_info() {
+  $groups['menu'] = array(
+    'title' => t('Menu'),
+    'description' => t('Translatable menu items: title and description.'),
+    'format' => FALSE, // This group doesn't have strings with format
+    'list' => TRUE, // This group can list all strings
+  );
+  return $groups;
+}
+
+/**
+ * Implements hook_i18n_string_objects()
+ */
+function i18n_menu_i18n_string_objects($type) {
+  if ($type == 'menu_link') {
+    // All menu items that have no language and are customized.
+    return db_select('menu_links', 'm')
+      ->fields('m')
+      ->condition('language', LANGUAGE_NONE)
+      ->condition('customized', 1)
+      ->execute()
+      ->fetchAllAssoc('mlid', PDO::FETCH_ASSOC);
+  }
+}
+
+/**
+ * Callback for menu item translation tab.
+ */
+function i18n_menu_item_translation_page($type, $item) {
+  module_load_include('admin.inc', 'i18n_menu');
+  // If the item has a language code, we can only support translation sets.
+  $translation_set = !empty($item['i18n_tsid']) ? i18n_translation_set_load($item['i18n_tsid']) : NULL;
+  $build['overview'] = i18n_menu_translation_item_overview($item, $translation_set);
+  $build['translation_form'] = drupal_get_form('i18n_menu_translation_form', $translation_set, $item);
+  return $build;
+}
+
diff --git a/kolab.org/www/drupal-7.18/sites/all/modules/i18n/i18n_menu/i18n_menu.inc b/kolab.org/www/drupal-7.18/sites/all/modules/i18n/i18n_menu/i18n_menu.inc
new file mode 100644
index 0000000..570bf9e
--- /dev/null
+++ b/kolab.org/www/drupal-7.18/sites/all/modules/i18n/i18n_menu/i18n_menu.inc
@@ -0,0 +1,98 @@
+<?php
+/**
+ * @file
+ * Internationalization (i18n) module - Translation set
+ */
+class i18n_menu_link_translation_set extends i18n_translation_set {
+  /**
+   * Load all path translations
+   */
+  public function load_translations() {
+    $translations = array();
+    $query = db_select('menu_links', 'ml');
+    $query->leftJoin('menu_router', 'm', 'm.path = ml.router_path');
+    $query->fields('ml');
+    // Weight should be taken from {menu_links}, not {menu_router}.
+    $query->addField('ml', 'weight', 'link_weight');
+    $query->fields('m');
+    $query->condition('ml.i18n_tsid', $this->tsid);
+    foreach ($query->execute()->fetchAll(PDO::FETCH_ASSOC) as $item) {
+      $item['weight'] = $item['link_weight'];
+      _menu_link_translate($item);
+      $translations[$item['language']] = $item;
+    }
+    return $translations;
+  }
+}
+
+/**
+ * Menu link object
+ */
+class i18n_menu_link extends i18n_string_object_wrapper {
+  /**
+   * Class constructor
+   */
+  public function __construct($type, $key, $object) {
+    // Unserialize options if not done
+    if (isset($object['options']) && !is_array($object['options'])) {
+      $object['options'] = unserialize($object['options']);
+    }
+    parent::__construct($type, $key, $object);
+  }
+
+  /**
+   * Get path for item
+   */
+  public function get_path() {
+    return $this->object['link_path'];
+  }
+
+  /**
+   * Get title from item
+   */
+  public function get_title() {
+    return $this->object['title'];
+  }
+
+  /**
+   * Translation mode for object
+   */
+  public function get_translate_mode() {
+    $mode = i18n_menu_mode($this->object['menu_name']);
+    if ($this->get_langcode()) {
+      return $mode & I18N_MODE_TRANSLATE;
+    }
+    elseif (!empty($this->object['customized'])) {
+      return $mode & I18N_MODE_LOCALIZE;
+    }
+    else {
+      return I18N_MODE_NONE;
+    }
+  }
+  /**
+   * Access to object translation. This should check object properties and permissions
+   */
+  protected function translate_access() {
+    return user_access('administer menu') && user_access('translate interface');
+  }
+
+  /**
+   * Get translatable properties.
+   *
+   * Check whether title or description are to be translated by default menu
+   * system.
+   */
+  protected function build_properties() {
+    $properties = parent::build_properties();
+    if ($properties) {
+      $strings = &$properties['menu']['item'][$this->get_key()];
+      $localizable = _i18n_menu_link_localizable_properties($this->object);
+      foreach ($strings as $key => $data) {
+        if (!in_array($key, $localizable)) {
+          unset($strings[$key]);
+        }
+      }
+    }
+    return $properties;
+  }
+}
diff --git a/kolab.org/www/drupal-7.18/sites/all/modules/i18n/i18n_menu/i18n_menu.info b/kolab.org/www/drupal-7.18/sites/all/modules/i18n/i18n_menu/i18n_menu.info
new file mode 100644
index 0000000..0c65b83
--- /dev/null
+++ b/kolab.org/www/drupal-7.18/sites/all/modules/i18n/i18n_menu/i18n_menu.info
@@ -0,0 +1,18 @@
+name = Menu translation
+description = Supports translatable custom menu items.
+dependencies[] = i18n
+dependencies[] = menu
+dependencies[] = i18n_string
+dependencies[] = i18n_translation
+package = Multilingual - Internationalization
+core = 7.x
+
+files[] = i18n_menu.inc
+files[] = i18n_menu.test
+
+; Information added by drupal.org packaging script on 2013-01-13
+version = "7.x-1.8"
+core = "7.x"
+project = "i18n"
+datestamp = "1358075001"
+
diff --git a/kolab.org/www/drupal-7.18/sites/all/modules/i18n/i18n_menu/i18n_menu.install b/kolab.org/www/drupal-7.18/sites/all/modules/i18n/i18n_menu/i18n_menu.install
new file mode 100644
index 0000000..0f49e1e
--- /dev/null
+++ b/kolab.org/www/drupal-7.18/sites/all/modules/i18n/i18n_menu/i18n_menu.install
@@ -0,0 +1,71 @@
+<?php
+
+/**
+ * @file
+ * Installation file for i18nmenu module.
+ */
+
+/**
+ * Implements hook_install().
+ */
+function i18n_menu_install() {
+  // Set module weight for it to run after core modules, but before views.
+  db_query("UPDATE {system} SET weight = 5 WHERE name = 'i18n_menu' AND type = 'module'");
+  module_load_install('i18n');
+  i18n_install_create_fields('menu_links', array('language', 'i18n_tsid'));
+  i18n_install_create_fields('menu_custom', array('language', 'i18n_mode'));
+  // If updating from D6, module changed name
+  if (variable_get('i18n_drupal6_update')) {
+    i18n_menu_update_7000();
+  }
+}
+
+/**
+ * Implements hook_uninstall().
+ */
+function i18n_menu_uninstall() {
+  db_drop_field('menu_links', 'language');
+  db_drop_field('menu_links', 'i18n_tsid');
+  db_drop_field('menu_custom', 'language');
+  db_drop_field('menu_custom', 'i18n_mode');
+}
+
+/**
+ * Implements hook_schema_alter().
+ */
+function i18n_menu_schema_alter(&$schema) {
+  $schema['menu_links']['fields']['language'] = array('type' => 'varchar', 'length' => 12, 'not null' => TRUE, 'default' => LANGUAGE_NONE);
+  $schema['menu_links']['fields']['i18n_tsid'] = array('type' => 'int', 'unsigned' => TRUE, 'not null' => TRUE, 'default' => 0);
+  $schema['menu_custom']['fields']['language'] = array('type' => 'varchar', 'length' => 12, 'not null' => TRUE, 'default' => LANGUAGE_NONE);
+  $schema['menu_custom']['fields']['i18n_mode'] = array('type' => 'int', 'unsigned' => TRUE, 'not null' => TRUE, 'default' => 0);
+}
+
+/**
+ * Update menu items language field from Drupal 6
+ */
+function i18n_menu_update_7000() {
+  // @todo
+}
+
+/**
+ * Set alter property for menu items with language.
+ */
+function i18n_menu_update_7001() {
+  // Compile a list of menus with i18n options.
+  $i18n_menus = array_filter(menu_get_names(), 'i18n_menu_mode');
+  if ($i18n_menus) {
+    $query = db_select('menu_links', 'm')
+      ->fields('m')
+      ->condition('menu_name', $i18n_menus);
+    foreach ($query->execute()->fetchAllAssoc('mlid', PDO::FETCH_ASSOC) as $mlid => $item) {
+      $options = unserialize($item['options']);
+      if (_i18n_menu_link_check_alter($item) && empty($options['alter'])) {
+        $options['alter'] = TRUE;
+        db_update('menu_links')
+          ->condition('mlid', $mlid)
+          ->fields(array('options' => serialize($options)))
+          ->execute();
+      }
+    }
+  }
+}
diff --git a/kolab.org/www/drupal-7.18/sites/all/modules/i18n/i18n_menu/i18n_menu.module b/kolab.org/www/drupal-7.18/sites/all/modules/i18n/i18n_menu/i18n_menu.module
new file mode 100644
index 0000000..df670e5
--- /dev/null
+++ b/kolab.org/www/drupal-7.18/sites/all/modules/i18n/i18n_menu/i18n_menu.module
@@ -0,0 +1,952 @@
+<?php
+
+/**
+ * @file
+ * Internationalization (i18n) submodule: Menu translation.
+ *
+ * @author Jose A. Reyero, 2005
+ *
+ */
+
+/**
+ * Implements hook_menu()
+ */
+function i18n_menu_menu() {
+  $items['admin/structure/menu/manage/translation'] = array(
+    'title' => 'Translation sets',
+    'page callback' => 'i18n_translation_set_list_manage',
+    'page arguments' => array('menu_link'),
+    'access arguments' => array('administer menu'),
+    'type' => MENU_LOCAL_TASK,
+    'weight' => 10,
+  );
+  $items['admin/structure/menu/manage/translation/add'] = array(
+    'title' => 'Add translation',
+    'page callback' => 'drupal_get_form',
+    'page arguments' => array('i18n_menu_translation_form'),
+    'access arguments' => array('administer menu'),
+    'type' => MENU_LOCAL_ACTION,
+    'file' => 'i18n_menu.admin.inc',
+  );
+  $items['admin/structure/menu/manage/translation/edit/%i18n_menu_translation'] = array(
+    'title' => 'Edit translation',
+    'page callback' => 'drupal_get_form',
+    'page arguments' => array('i18n_menu_translation_form', 6),
+    'access arguments' => array('administer menu'),
+    'type' => MENU_CALLBACK,
+    'file' => 'i18n_menu.admin.inc',
+  );
+  $items['admin/structure/menu/manage/translation/delete/%i18n_menu_translation'] = array(
+    'title' => 'Delete translation',
+    'page callback' => 'drupal_get_form',
+    'page arguments' => array('i18n_translation_set_delete_confirm', 6),
+    'access arguments' => array('administer menu'),
+    'type' => MENU_CALLBACK,
+  );
+  return $items;
+}
+
+/**
+ * Implements hook_menu_alter()
+ */
+function i18n_menu_menu_alter(&$items) {
+  $items['admin/structure/menu/item/%menu_link'] = $items['admin/structure/menu/item/%menu_link/edit'];
+  $items['admin/structure/menu/item/%menu_link']['type'] = MENU_CALLBACK;
+  $items['admin/structure/menu/item/%menu_link/edit']['type'] = MENU_DEFAULT_LOCAL_TASK;
+}
+
+/**
+ * Implements hook_block_view().
+ */
+function i18n_menu_block_view_alter(&$data, $block) {
+  if (($block->module == 'menu' || $block->module == 'system') && (i18n_menu_mode($block->delta) & I18N_MODE_MULTIPLE)) {
+    $menus = menu_get_menus();
+    if (isset($menus[$block->delta])) {
+      if (empty($block->title)) {
+        $data['subject'] = i18n_string_plain(
+          array('menu', 'menu', $block->delta, 'title'),
+          $menus[$block->delta]
+        );
+      }
+      // Add contextual links for this block.
+      if (!empty($data['content'])) {
+        $data['content']['#contextual_links']['menu'] = array('admin/structure/menu/manage', array($block->delta));
+      }
+    }
+  }
+}
+
+/**
+ * Implements hook_i18n_translate_path()
+ */
+function i18n_menu_i18n_translate_path($path) {
+  $item = i18n_menu_link_load($path, i18n_langcode());
+  if ($item && ($set = i18n_translation_object('menu_link', $item))) {
+    $links = array();
+    foreach ($set->get_translations() as $lang => $link) {
+      $links[$lang] = array(
+        'href' => $link['link_path'],
+        'title' => $link['link_title'],
+        'i18n_type' => 'menu_link',
+        'i18n_object' => $link,
+      );
+    }
+    return $links;
+  }
+}
+
+/**
+ * Implements hook_menu_insert()
+ */
+function i18n_menu_menu_insert($menu) {
+  i18n_menu_menu_update($menu);
+}
+
+/**
+ * Implements hook_menu_update()
+ */
+function i18n_menu_menu_update($menu) {
+  // Stores the fields of menu links which need an update.
+  $update = array();
+
+  if (!isset($menu['i18n_mode'])) {
+    $menu['i18n_mode'] = I18N_MODE_NONE;
+  }
+  if (!($menu['i18n_mode'] & I18N_MODE_LANGUAGE)) {
+    $menu['language'] = LANGUAGE_NONE;
+  }
+  db_update('menu_custom')
+    ->fields(array('language' => $menu['language'], 'i18n_mode' => $menu['i18n_mode']))
+    ->condition('menu_name', $menu['menu_name'])
+    ->execute();
+  if (!$menu['i18n_mode']) {
+    $update['language'] = LANGUAGE_NONE;
+  }
+  elseif ($menu['i18n_mode'] & I18N_MODE_LANGUAGE) {
+    $update['language'] = $menu['language'];
+  }
+
+  // Non translatable menu.
+  if (!($menu['i18n_mode'] & I18N_MODE_TRANSLATE)) {
+    $tsids = db_select('menu_links')
+      ->fields('menu_links', array('i18n_tsid'))
+      ->groupBy('i18n_tsid')
+      ->condition('menu_name', $menu['menu_name'])
+      ->condition('customized', 1)
+      ->condition('i18n_tsid', 0, '<>')
+      ->execute()
+      ->fetchCol(0);
+    if (!empty($tsids)) {
+      foreach ($tsids as $tsid) {
+        if ($translation_set = i18n_translation_set_load($tsid)) {
+          $translation_set->delete();
+        }
+      }
+    }
+    $update['i18n_tsid'] = 0;
+  }
+
+  if (!empty($update)) {
+    db_update('menu_links')
+      ->fields($update)
+      ->condition('menu_name', $menu['menu_name'])
+      ->condition('customized', 1)
+      ->execute();
+  }
+
+  // Update strings, always add translation if no language
+  if (!i18n_object_langcode($menu)) {
+    i18n_string_object_update('menu', $menu);
+  }
+
+  // Clear all menu caches.
+  menu_cache_clear_all();
+}
+
+/**
+ * Implements hook_menu_delete()
+ */
+function i18n_menu_menu_delete($menu) {
+  i18n_string_object_remove('menu', $menu);
+}
+
+/**
+ * Implements hook_menu_link_alter().
+ *
+ * This function is invoked from menu_link_save() before default
+ * menu link options (menu_name, module, etc.. have been set)
+ */
+function i18n_menu_menu_link_alter(&$item) {
+  // We just make sure every link has a valid language property.
+  if (!i18n_object_langcode($item)) {
+    $item['language'] = LANGUAGE_NONE;
+  }
+}
+
+/**
+ * Implements hook_menu_link_insert()
+ */
+function i18n_menu_menu_link_insert($link) {
+  i18n_menu_menu_link_update($link);
+}
+
+/**
+ * Implements hook_menu_link_update().
+ */
+function i18n_menu_menu_link_update($link) {
+  // Stores the fields to update.
+  $fields = array();
+  $menu_mode = i18n_menu_mode($link['menu_name']);
+
+  if ($menu_mode & I18N_MODE_TRANSLATE && isset($link['language'])) {
+    // Multilingual menu links, translatable, it may be part of a
+    // translation set.
+    if (i18n_object_langcode($link)) {
+      if (!empty($link['translation_set'])) {
+        // Translation set comes as parameter, we may be creating a translation,
+        // add link to the set.
+        $translation_set = $link['translation_set'];
+        $translation_set
+          ->add_item($link)
+          ->save(TRUE);
+      }
+    }
+    elseif ($link['language'] === LANGUAGE_NONE && !empty($link['original_item']['i18n_tsid'])) {
+      if ($translation_set = i18n_translation_set_load($link['original_item']['i18n_tsid'])) {
+        $translation_set->remove_language($link['original_item']['language']);
+        // If there are no links left in this translation set, delete the set.
+        // Otherwise update the set.
+        $translation_set->update_delete();
+      }
+      $fields['i18n_tsid'] = 0;
+    }
+  }
+  // For multilingual menu items, always set a language and mark them for
+  // 'alter' so they can be processed later by
+  // hook_translated_link_menu_alter().
+  if ($menu_mode) {
+    if (!isset($link['language'])) {
+      $link['language'] = LANGUAGE_NONE;
+    }
+    if (_i18n_menu_link_check_alter($link) && empty($link['options']['alter'])) {
+      $fields['options'] = $link['options'];
+      $fields['options']['alter'] = TRUE;
+    }
+    // We cannot unmark links for altering because we don't know what other
+    // modules use it for.
+  }
+  // Update language field if the link has a language value.
+  if (isset($link['language'])) {
+    $fields['language'] = $link['language'];
+  }
+
+  if (!empty($fields)) {
+    // If link options are to be updated, they need to be serialized.
+    if (isset($fields['options'])) {
+      $fields['options'] = serialize($fields['options']);
+    }
+    db_update('menu_links')
+      ->fields($fields)
+      ->condition('mlid', $link['mlid'])
+      ->execute();
+  }
+  // Update translatable strings if any for customized links that belong to a
+  // localizable menu.
+  if (_i18n_menu_link_is_localizable($link)) {
+    i18n_string_object_update('menu_link', $link);
+  }
+  else {
+    i18n_string_object_remove('menu_link', $link);
+  }
+}
+
+/**
+ * Implements hook_menu_delete()
+ */
+function i18n_menu_menu_link_delete($link) {
+  // If a translation set exists for this link, remove this link from the set.
+  if (!empty($link['i18n_tsid'])) {
+    if ($translation_set = i18n_translation_set_load($link['i18n_tsid'])) {
+      $translation_set->get_translations();
+
+      $translation_set->remove_language($link['language']);
+
+      // If there are no links left in this translation set, delete the set.
+      // Otherwise update the set.
+      $translation_set->update_delete();
+    }
+  }
+
+  i18n_string_object_remove('menu_link', $link);
+}
+
+/**
+ * Get menu mode or compare with given one
+ */
+function i18n_menu_mode($name, $mode = NULL) {
+  $menu = menu_load($name);
+  if (!$menu || !isset($menu['i18n_mode'])) {
+    return isset($mode) ? FALSE : I18N_MODE_NONE;
+  }
+  else {
+    return isset($mode) ? $menu['i18n_mode'] & $mode : $menu['i18n_mode'];
+  }
+}
+
+/**
+ * Implements hook_translated_menu_link_alter().
+ *
+ * Translate localizable menu links on the fly.
+ * Filter out items that have a different language from current interface.
+ *
+ * @see i18n_menu_menu_link_alter()
+ */
+function i18n_menu_translated_menu_link_alter(&$item) {
+  // Only process links to be displayed not processed before by i18n_menu.
+  if (_i18n_menu_link_process($item)) {
+    if (!_i18n_menu_link_is_visible($item)) {
+      $item['hidden'] = TRUE;
+    }
+    elseif (_i18n_menu_link_is_localizable($item)) {
+      // Item has undefined language, it is a candidate for localization.
+      _i18n_menu_link_localize($item);
+    }
+  }
+}
+
+/**
+ * Implements hook_help().
+ */
+function i18n_menu_help($path, $arg) {
+  switch ($path) {
+    case 'admin/help#i18n_menu' :
+      $output = '<p>' . t('This module adds support for multilingual menus. You can setup multilingual options for each menu:') . '</p>';
+      $output .= '<ul>';
+      $output .= '<li>' . t('Menus can be fully multilingual with translatable (or localized) menu items.') . '</li>';
+      $output .= '<li>' . t('Menus can be configured to have a fixed language. All menu items share this language setting and the menu will be visible in that language only.') . '</li>';
+      $output .= '<li>' . t('Menus can also be configured to have no translations.') . '</li>';
+      $output .= '</ul>';
+      $output .= '<p>' . t('The multilingual options of a menu must be configured before individual menu items can be translated. Go to the <a href="@menu-admin">Menus administration page</a> and follow the "edit menu" link to the menu in question.', array('@menu-admin' => url('admin/structure/menu') ) ) . '</p>';
+      $output .= '<p>' . t('To search and translate strings, use the <a href="@translate-interface">translation interface</a> pages.', array('@translate-interface' => url('admin/config/regional/translate'))) . '</p>';
+      return $output;
+
+    case 'admin/config/regional/i18n':
+      $output = '<p>' . t('Menus and menu items can be translated on the <a href="@configure_menus">Menu administration page</a>.', array('@configure_menus' => url('admin/structure/menu'))) . '</p>';
+      return $output;
+  }
+}
+
+/**
+ * Implements hook_variable_info_alter()
+ */
+function i18n_menu_variable_info_alter(&$variables, $options) {
+  // Make menu variables translatable
+  $variables['menu_main_links_source']['localize'] = TRUE;
+  $variables['menu_secondary_links_source']['localize'] = TRUE;
+}
+
+/**
+ * Get localized menu tree.
+ *
+ * @param string $menu_name
+ *   The menu the translated tree has to be fetched from.
+ * @param string $langcode
+ *   Optional language code to get the menu in, defaults to request language.
+ * @param bool $reset
+ *   Whether to reset the internal i18n_menu_translated_tree cache.
+ */
+function i18n_menu_translated_tree($menu_name, $langcode = NULL, $reset = FALSE) {
+  $menu_output = &drupal_static(__FUNCTION__);
+  $langcode = $langcode ? $langcode : i18n_language_interface()->language;
+  if (!isset($menu_output[$langcode][$menu_name]) || $reset) {
+    $tree = menu_tree_page_data($menu_name);
+    $tree = i18n_menu_localize_tree($tree, $langcode);
+    $menu_output[$langcode][$menu_name] = menu_tree_output($tree);
+  }
+  return $menu_output[$langcode][$menu_name];
+}
+
+/**
+ * Localize menu tree.
+ */
+function i18n_menu_localize_tree($tree, $langcode = NULL) {
+  $langcode = $langcode ? $langcode : i18n_language_interface()->language;
+  foreach ($tree as $index => &$item) {
+    $link = $item['link'];
+    // We only process links that are visible and not processed before.
+    if (_i18n_menu_link_process($item['link'])) {
+      if (!_i18n_menu_link_is_visible($item['link'], $langcode)) {
+        // Remove links for other languages than current.
+        // Links with language wont be localized.
+        unset($tree[$index]);
+        // @todo Research whether the above has any advantage over:
+        // $item['hidden'] = TRUE;
+      }
+      else {
+        if (_i18n_menu_link_is_localizable($item['link'])) {
+          // Item has undefined language, it is a candidate for localization.
+          _i18n_menu_link_localize($item['link'], $langcode);
+        }
+        // Localize subtree.
+        if (!empty($item['below'])) {
+          $item['below'] = i18n_menu_localize_tree($item['below'], $langcode);
+        }
+      }
+    }
+  }
+  return $tree;
+}
+
+/**
+ * Localize menu renderable array
+ */
+function i18n_menu_localize_elements(&$elements) {
+  foreach (element_children($elements) as $mlid) {
+    $elements[$mlid]['#title'] = i18n_string(array('menu', 'item', $mlid, 'title'), $elements[$mlid]['#title']);
+    if (!empty($tree[$mlid]['#localized_options']['attributes']['title'])) {
+      $elements[$mlid]['#localized_options']['attributes']['title'] = i18n_string(array('menu', 'item', $mlid, 'description'), $tree[$mlid]['#localized_options']['attributes']['title']);
+    }
+    i18n_menu_localize_elements($elements[$mlid]);
+  }
+}
+
+/**
+ * Return an array of localized links for a navigation menu.
+ *
+ * Localized version of menu_navigation_links()
+ */
+function i18n_menu_navigation_links($menu_name, $level = 0) {
+  // Don't even bother querying the menu table if no menu is specified.
+  if (empty($menu_name)) {
+    return array();
+  }
+
+  // Get the menu hierarchy for the current page.
+  $tree = menu_tree_page_data($menu_name, $level + 1);
+  $tree = i18n_menu_localize_tree($tree);
+
+  // Go down the active trail until the right level is reached.
+  while ($level-- > 0 && $tree) {
+    // Loop through the current level's items until we find one that is in trail.
+    while ($item = array_shift($tree)) {
+      if ($item['link']['in_active_trail']) {
+        // If the item is in the active trail, we continue in the subtree.
+        $tree = empty($item['below']) ? array() : $item['below'];
+        break;
+      }
+    }
+  }
+
+  // Create a single level of links.
+  $router_item = menu_get_item();
+  $links = array();
+  foreach ($tree as $item) {
+    if (!$item['link']['hidden']) {
+      $class = '';
+      $l = $item['link']['localized_options'];
+      $l['href'] = $item['link']['href'];
+      $l['title'] = $item['link']['title'];
+      if ($item['link']['in_active_trail']) {
+        $class = ' active-trail';
+        $l['attributes']['class'][] = 'active-trail';
+      }
+      // Normally, l() compares the href of every link with $_GET['q'] and sets
+      // the active class accordingly. But local tasks do not appear in menu
+      // trees, so if the current path is a local task, and this link is its
+      // tab root, then we have to set the class manually.
+      if ($item['link']['href'] == $router_item['tab_root_href'] && $item['link']['href'] != $_GET['q']) {
+        $l['attributes']['class'][] = 'active';
+      }
+      // Keyed with the unique mlid to generate classes in theme_links().
+      $links['menu-' . $item['link']['mlid'] . $class] = $l;
+    }
+  }
+  return $links;
+}
+
+/**
+ * Get localized menu title
+ */
+function _i18n_menu_link_title($link, $langcode = NULL) {
+  return i18n_string_translate(array('menu', 'item', $link['mlid'], 'title'), $link['link_title'], array('langcode' => $langcode, 'sanitize' => FALSE));
+}
+
+/**
+ * Localize menu item title and description.
+ *
+ * This will be invoked always after _menu_item_localize()
+ *
+ * Link properties to manage:
+ * - title, menu router title
+ * - link_title, menu link title
+ * - options.attributes.title, menu link description.
+ * - localized_options.attributes.title,
+ *
+ * @see _menu_item_localize()
+ * @see _menu_link_translate()
+ */
+function _i18n_menu_link_localize(&$link, $langcode = NULL) {
+  // Only translate title if it has no special callback.
+  if (empty($link['title callback']) || $link['title callback'] === 't') {
+    $link['title'] = _i18n_menu_link_title($link, $langcode);
+  }
+  if ($description = _i18n_menu_link_description($link, $langcode)) {
+    $link['localized_options']['attributes']['title'] = $description;
+  }
+}
+
+/**
+ * Get localized menu description
+ */
+function _i18n_menu_link_description($link, $langcode = NULL) {
+  if (!empty($link['options']['attributes']['title'])) {
+    return i18n_string_translate(array('menu', 'item', $link['mlid'], 'description'), $link['options']['attributes']['title'], array('langcode' => $langcode));
+  }
+  else {
+    return NULL;
+  }
+}
+
+/**
+ * Check whether this link is to be processed by i18n_menu and start processing.
+ */
+function _i18n_menu_link_process(&$link) {
+  // Only visible links that have a language property and haven't been processed
+  // before. We also check that they belong to a menu with language options.
+  if (empty($link['i18n_menu']) && !empty($link['language']) && !empty($link['access']) && empty($link['hidden']) && i18n_menu_mode($link['menu_name'])) {
+    // Mark so it won't be processed twice.
+    $link['i18n_menu'] = TRUE;
+    // Skip if administering this menu or this menu item.
+    if (arg(0) == 'admin' && arg(1) == 'structure' && arg(2) == 'menu') {
+      if (arg(3) == 'manage' && $link['menu_name'] == arg(4)) {
+        return FALSE;
+      }
+      elseif (arg(3) == 'item' && arg(4) == $link['mlid']) {
+        return FALSE;
+      }
+    }
+    return TRUE;
+  }
+  else {
+    return FALSE;
+  }
+}
+
+/**
+ * Check whether this menu item should be marked for altering.
+ *
+ * Menu items that have a language or that have any localizable strings
+ * will be marked to be run through hook_translated_menu_link_alter().
+ *
+ * @see i18n_menu_translated_menu_link_alter()
+ */
+function _i18n_menu_link_check_alter($link) {
+  return i18n_menu_mode($link['menu_name']) && (i18n_object_langcode($link) || _i18n_menu_link_is_localizable($link, TRUE));
+}
+
+/**
+ * Check whether this link should be localized by i18n_menu.
+ *
+ * @param array $link
+ *   Menu link array.
+ * @param bool $check_strings
+ *   Whether to check if the link has actually localizable strings. Since this
+ *   is a more expensive operation, it will be just checked when editing menu
+ *   items.
+ *
+ * @return boolean
+ *   Returns TRUE if link is localizable.
+ */
+function _i18n_menu_link_is_localizable($link, $check_strings = FALSE) {
+  return !empty($link['customized']) && !i18n_object_langcode($link) && i18n_menu_mode($link['menu_name'], I18N_MODE_LOCALIZE) &&
+  (!$check_strings || _i18n_menu_link_localizable_properties($link));
+}
+
+/**
+ * Check whether this menu link is visible for current/given language.
+ */
+function _i18n_menu_link_is_visible($link, $langcode = NULL) {
+  $langcode = $langcode ? $langcode : i18n_language_interface()->language;
+  return $link['language'] == LANGUAGE_NONE || $link['language'] == $langcode;
+}
+
+/**
+ * Get localizable properties for menu link checking agains the router item.
+ */
+function _i18n_menu_link_localizable_properties($link) {
+  $props = array();
+  $router = !empty($link['router_path']) ? _i18n_menu_get_router($link['router_path']) : NULL;
+  if (!empty($link['link_title'])) {
+    // If the title callback is 't' and the link title matches the router title
+    // it will be localized by core, not by i18n_menu.
+    if (!$router ||
+        (empty($router['title_callback']) || $router['title_callback'] != 't') ||
+        (empty($router['title']) || $router['title'] != $link['link_title'])
+    ) {
+      $props[] = 'title';
+    }
+  }
+  if (!empty($link['options']['attributes']['title'])) {
+    // If the description matches the router description, it will be localized
+    // by core.
+    if (!$router || empty($router['description']) || $router['description'] != $link['options']['attributes']['title']) {
+      $props[] = 'description';
+    }
+  }
+  return $props;
+}
+
+/**
+ * Get the menu router for this router path.
+ *
+ * We need the untranslated title to compare, and this will be fast.
+ * There's no api function to do this?
+ *
+ * @param string $path
+ *   The path to fetch from the router.
+ */
+function _i18n_menu_get_router($path) {
+  $cache = &drupal_static(__FUNCTION__, array());
+  if (!array_key_exists($path, $cache)) {
+    $cache[$path] = db_select('menu_router', 'mr')
+      ->fields('mr', array('title', 'title_callback', 'description'))
+      ->condition('path', $path)
+      ->execute()
+      ->fetchAssoc();
+  }
+  return $cache[$path];
+}
+
+/**
+ * Implements hook_form_FORM_ID_alter().
+ */
+function i18n_menu_form_menu_edit_menu_alter(&$form, &$form_state) {
+  $menu = menu_load($form['old_name']['#value']);
+  $i18n_mode = $menu && isset($menu['i18n_mode']) ? $menu['i18n_mode'] : I18N_MODE_NONE;
+  $langcode = $menu && isset($menu['language']) ? $menu['language'] : LANGUAGE_NONE;
+
+  $form += i18n_translation_mode_element('menu', $i18n_mode, $langcode);
+}
+
+/**
+ * Implements hook_form_FORM_ID_alter().
+ *
+ * Add a language selector to the menu_edit_item form and register a submit
+ * callback to process items.
+ */
+function i18n_menu_form_menu_edit_item_alter(&$form, &$form_state) {
+  $item = &$form['original_item']['#value'];
+  $item['language'] = i18n_menu_item_get_language($item);
+  // Check whether this item belongs to a node object and it is a supported type.
+  $node_item = ($node = i18n_menu_item_get_node($item)) && i18n_menu_node_supported_type($node->type);
+  if (!$node_item && i18n_menu_mode($item['menu_name'], I18N_MODE_TRANSLATE)) {
+    //$form['i18n'] = array('#type' => 'fieldset');
+    $form['i18n']['language'] = array(
+      '#description' => t('This item belongs to a multilingual menu. You can set a language for it.'),
+    ) + i18n_element_language_select($item);
+
+    // If the term to be added will be a translation of a source term,
+    // set the default value of the option list to the target language and
+    // create a form element for storing the translation set of the source term.
+    if (isset($_GET['translation']) && isset($_GET['target']) && ($source_item = menu_link_load($_GET['translation']))) {
+      if (!empty($source_item['i18n_tsid'])) {
+        $translation_set = i18n_translation_set_load($source_item['i18n_tsid']);
+      }
+      else {
+        // Create object and stick the source information in the translation set.
+        $translation_set = i18n_translation_set_build('menu_link')
+          ->add_item($source_item);
+      }
+      $form['link_path']['#default_value'] = $source_item['link_path'];
+
+      // Maybe we should disable the 'link_path' and 'parent' form elements?
+      // $form['link_path']['#disabled'] = TRUE;
+      // $form['parent']['#disabled'] = TRUE;
+
+      $form['i18n']['language']['#default_value'] = $_GET['target'];
+      $form['i18n']['language']['#disabled'] = TRUE;
+
+      drupal_set_title(t('%language translation of menu item %title', array('%language' => locale_language_name($_GET['target']), '%title' => $source_item['link_title'])), PASS_THROUGH);
+    }
+    elseif (!empty($item['i18n_tsid'])) {
+      $translation_set = i18n_translation_set_load($item['i18n_tsid']);
+    }
+
+    // Add the translation set to the form so we know the new menu item
+    // needs to be added to that set.
+    if (!empty($translation_set)) {
+      $form['translation_set'] = array(
+        '#type' => 'value',
+        '#value' => $translation_set,
+      );
+
+      // If the current term is part of a translation set,
+      // remove all other languages of the option list.
+      if ($translations = $translation_set->get_translations()) {
+        unset($form['i18n']['language']['#options'][LANGUAGE_NONE]);
+        foreach ($translations as $langcode => $translation) {
+          if ($translation['mlid'] !== $item['mlid']) {
+            unset($form['i18n']['language']['#options'][$langcode]);
+          }
+        }
+      }
+    }
+  }
+  else {
+    $form['language'] = array(
+      '#type' => 'value',
+      '#value' => $item['language'],
+    );
+  }
+  if ($node_item && i18n_langcode($item['language'])) {
+    $form['i18n']['message'] = array(
+      '#type' => 'item',
+      '#title' => t('Language'),
+      '#markup' => i18n_language_name($item['language']),
+      '#description' => t('This menu item belongs to a node, so it will have the same language as the node and cannot be localized.'),
+    );
+  }
+  array_unshift($form['#validate'], 'i18n_menu_menu_item_prepare_normal_path');
+}
+
+/**
+ * Normal path should be checked with menu item's language to avoid
+ * troubles when a node and it's translation has the same url alias.
+ */
+function i18n_menu_menu_item_prepare_normal_path($form, &$form_state) {
+  $item = &$form_state['values'];
+  $item['link_path'] = i18n_prepare_normal_path($item['link_path'], $item['language']);
+}
+
+/**
+ * Get language for menu item
+ */
+function i18n_menu_item_get_language($item) {
+  if (isset($item['language'])) {
+    return $item['language'];
+  }
+  else {
+    $menu = menu_load($item['menu_name']);
+    switch ($menu['i18n_mode']) {
+      case I18N_MODE_LANGUAGE:
+        return $menu['language'];
+      case I18N_MODE_NONE:
+      case I18N_MODE_LOCALIZE:
+        return LANGUAGE_NONE;
+      default:
+        if (!empty($item['mlid'])) {
+          return db_select('menu_links', 'm')
+            ->fields('m', array('language'))
+            ->condition('mlid', $item['mlid'])
+            ->execute()
+            ->fetchField();
+        }
+        else {
+          return LANGUAGE_NONE;
+        }
+    }
+  }
+}
+
+/**
+ * Implements hook_form_node_form_alter().
+ *
+ * Add language to menu settings of the node form, as well as setting defaults
+ * to match the translated item's menu settings.
+ */
+function i18n_menu_form_node_form_alter(&$form, &$form_state, $form_id) {
+  if (isset($form['menu'])) {
+    $node = $form['#node'];
+    $link = $node->menu;
+    if (!empty($link['mlid'])) {
+      // Preserve the menu item language whatever it is.
+      $form['menu']['link']['language'] = array('#type' => 'value', '#value' => $link['language']);
+    }
+    elseif (i18n_menu_node_supported_type($node->type)) {
+      // Set menu language to node language but only if it is a supported node type.
+      $form['menu']['link']['language'] = array('#type' => 'value', '#value' => $node->language);
+    }
+    else {
+      $form['menu']['link']['language'] = array('#type' => 'value', '#value' => LANGUAGE_NONE);
+    }
+    // Customized must be set to 1 to save language.
+    $form['menu']['link']['customized'] = array('#type' => 'value', '#value' => 1);
+  }
+}
+
+/**
+ * Check whether a node type has multilingual support (but not entity translation).
+ */
+function i18n_menu_node_supported_type($type) {
+  $supported = &drupal_static(__FUNCTION__);
+  if (!isset($supported[$type])) {
+    $mode = variable_get('language_content_type_' . $type, 0);
+    $supported[$type] = $mode == 1 || $mode == 2; // 2 == TRANSLATION_ENABLED
+  }
+  return $supported[$type];
+}
+
+/**
+ * Get the node object for a menu item.
+ */
+function i18n_menu_item_get_node($item) {
+  return isset($item['router_path']) && $item['router_path'] == 'node/%' ? node_load(arg(1, $item['link_path'])) : NULL;
+}
+
+/**
+ * Implements hook_node_presave()
+ *
+ * Set menu link language to node language
+ */
+function i18n_menu_node_presave($node) {
+  if (!empty($node->menu) && isset($node->language) && i18n_menu_node_supported_type($node->type)) {
+    $node->menu['language'] = i18n_object_langcode($node, LANGUAGE_NONE);
+    // Store node type with menu item so we can quickly access it later.
+    $node->menu['options']['node_type'] = $node->type;
+  }
+}
+
+/**
+ * Implements hook_node_prepare_translation().
+ */
+function i18n_menu_node_prepare_translation($node) {
+  if (empty($node->menu['mlid']) && !empty($node->translation_source)) {
+    $tnode = $node->translation_source;
+    // Prepare the tnode so the menu item will be available.
+    node_object_prepare($tnode);
+    $node->menu['link_title'] = $tnode->menu['link_title'];
+    $node->menu['weight'] = $tnode->menu['weight'];
+  }
+}
+
+/**
+ * Process menu and menu item add/edit form submissions.
+ *
+ * @todo See where this fits
+ */
+/*
+function i18n_menu_edit_item_form_submit($form, &$form_state) {
+  $mid = menu_edit_item_save($form_state['values']);
+  db_query("UPDATE {menu} SET language = '%s' WHERE mid = %d", array($form_state['values']['language'], $mid));
+  return 'admin/build/menu';
+}
+*/
+
+/**
+ * Load translation set. Menu loading callback.
+ */
+function i18n_menu_translation_load($tsid) {
+  return i18n_translation_set_load($tsid, 'menu_link');
+}
+
+/**
+ * Load menu item by path, language
+ */
+function i18n_menu_link_load($path, $langcode) {
+  $query = db_select('menu_links', 'ml');
+  $query->leftJoin('menu_router', 'm', 'm.path = ml.router_path');
+  $query->fields('ml');
+  // Weight should be taken from {menu_links}, not {menu_router}.
+  $query->addField('ml', 'weight', 'link_weight');
+  $query->fields('m');
+  $query->condition('ml.link_path', $path);
+  $query->condition('ml.language', $langcode);
+  if ($item = $query->execute()->fetchAssoc()) {
+    $item['weight'] = $item['link_weight'];
+    _menu_link_translate($item);
+    return $item;
+  }
+}
+
+/**
+ * Implements hook_init().
+ */
+function i18n_menu_init() {
+
+  // The only way to override the default preferred menu link for a path is to
+  // inject it into the static cache of the function menu_link_get_preferred().
+
+  // The problem with the default implementation is that it does not take the
+  // language of a menu link into account. Whe having different menu trees for
+  // different menus, this means that the active trail will not work for all but
+  // one language.
+
+  // The code below is identical to the mentioned function except the added
+  // language condition on the query.
+
+  // TODO: Adding an alter tag to the query would allow to do this with a simple
+  // hook_query_alter() implementation.
+
+  $preferred_links = &drupal_static('menu_link_get_preferred');
+
+  $path = $_GET['q'];
+
+  // Look for the correct menu link by building a list of candidate paths,
+  // which are ordered by priority (translated hrefs are preferred over
+  // untranslated paths). Afterwards, the most relevant path is picked from
+  // the menus, ordered by menu preference.
+  $item = menu_get_item($path);
+  $path_candidates = array();
+  // 1. The current item href.
+  $path_candidates[$item['href']] = $item['href'];
+  // 2. The tab root href of the current item (if any).
+  if ($item['tab_parent'] && ($tab_root = menu_get_item($item['tab_root_href']))) {
+    $path_candidates[$tab_root['href']] = $tab_root['href'];
+  }
+  // 3. The current item path (with wildcards).
+  $path_candidates[$item['path']] = $item['path'];
+  // 4. The tab root path of the current item (if any).
+  if (!empty($tab_root)) {
+    $path_candidates[$tab_root['path']] = $tab_root['path'];
+  }
+  // Retrieve a list of menu names, ordered by preference.
+  $menu_names = menu_get_active_menu_names();
+  // Use an illegal menu name as the key for the preferred menu link.
+  $selected_menu = MENU_PREFERRED_LINK;
+  // Put the selected menu at the front of the list.
+  array_unshift($menu_names, $selected_menu);
+
+  $query = db_select('menu_links', 'ml', array('fetch' => PDO::FETCH_ASSOC));
+  $query->leftJoin('menu_router', 'm', 'm.path = ml.router_path');
+  $query->fields('ml');
+  // Weight must be taken from {menu_links}, not {menu_router}.
+  $query->addField('ml', 'weight', 'link_weight');
+  $query->fields('m');
+  $query->condition('ml.link_path', $path_candidates, 'IN');
+
+  // Only look menu links with none or the current language.
+  $query->condition('ml.language', array(LANGUAGE_NONE, i18n_language_interface()->language), 'IN');
+
+  // Sort candidates by link path and menu name.
+  $candidates = array();
+  foreach ($query->execute() as $candidate) {
+    $candidate['weight'] = $candidate['link_weight'];
+    $candidates[$candidate['link_path']][$candidate['menu_name']] = $candidate;
+    // Add any menus not already in the menu name search list.
+    if (!in_array($candidate['menu_name'], $menu_names)) {
+      $menu_names[] = $candidate['menu_name'];
+    }
+  }
+
+  // Store the most specific link for each menu. Also save the most specific
+  // link of the most preferred menu in $preferred_link.
+  foreach ($path_candidates as $link_path) {
+    if (isset($candidates[$link_path])) {
+      foreach ($menu_names as $menu_name) {
+        if (empty($preferred_links[$path][$menu_name]) && isset($candidates[$link_path][$menu_name])) {
+          $candidate_item = $candidates[$link_path][$menu_name];
+          $map = explode('/', $path);
+          _menu_translate($candidate_item, $map);
+          if ($candidate_item['access']) {
+            $preferred_links[$path][$menu_name] = $candidate_item;
+            if (empty($preferred_links[$path][MENU_PREFERRED_LINK])) {
+              // Store the most specific link.
+              $preferred_links[$path][MENU_PREFERRED_LINK] = $candidate_item;
+            }
+          }
+        }
+      }
+    }
+  }
+}
+
diff --git a/kolab.org/www/drupal-7.18/sites/all/modules/i18n/i18n_menu/i18n_menu.test b/kolab.org/www/drupal-7.18/sites/all/modules/i18n/i18n_menu/i18n_menu.test
new file mode 100644
index 0000000..b19e9eb
--- /dev/null
+++ b/kolab.org/www/drupal-7.18/sites/all/modules/i18n/i18n_menu/i18n_menu.test
@@ -0,0 +1,292 @@
+<?php
+
+/**
+ * @file
+ * Test case for multilingual menus.
+ */
+class i18nMenuTestCase extends Drupali18nTestCase {
+  public static function getInfo() {
+    return array(
+      'name' => 'Menu translation',
+      'group' => 'Internationalization',
+      'description' => 'Menu translation functions',
+    );
+  }
+
+  function setUp() {
+    parent::setUp(array('i18n_menu', 'translation'));
+    parent::setUpLanguages(array('administer menu'));
+    $this->translator = $this->drupalCreateUser(array('translate interface', 'translate user-defined strings'));
+  }
+
+  function testMenuTranslateLocalize() {
+    // Test filtering for menu blocks.
+    $menu = $this->createMenu(array('i18n_mode' => I18N_MODE_MULTIPLE));
+
+    $neutral_item = $this->createMenuLink(array('menu_name' => $menu['menu_name']));
+    $default_item = $this->createMenuLink(array('menu_name' => $menu['menu_name'], 'language' => $this->default_language));
+    $secondary_item = $this->createMenuLink(array('menu_name' => $menu['menu_name'], 'language' => $this->secondary_language));
+
+    $block['title'] = $menu['title'];
+    $block['module'] = 'menu';
+    $block['delta'] = $menu['menu_name'];
+    $this->moveBlockToRegion($block, 'sidebar_first');
+
+    $this->drupalGet('<front>');
+    $this->assertText($neutral_item['link_title']);
+    $this->assertText($default_item['link_title']);
+    $this->assertNoText($secondary_item['link_title']);
+
+    $this->i18nGet($this->secondary_language, '<front>');
+    $this->assertText($neutral_item['link_title']);
+    $this->assertNoText($default_item['link_title']);
+    $this->assertText($secondary_item['link_title']);
+
+    // Test filtering for built-in menus.
+    $edit = array(
+      'i18n_mode' => I18N_MODE_MULTIPLE,
+    );
+    $this->drupalPost('admin/structure/menu/manage/main-menu/edit', $edit, t('Save'));
+
+    $neutral_item = $this->createMenuLink(array('menu_name' => 'main-menu'));
+    $default_item = $this->createMenuLink(array('menu_name' => 'main-menu', 'language' => $this->default_language));
+    $secondary_item = $this->createMenuLink(array('menu_name' => 'main-menu', 'language' => $this->secondary_language));
+
+    $this->drupalGet('<front>');
+    $this->assertText($neutral_item['link_title']);
+    $this->assertText($default_item['link_title']);
+    $this->assertNoText($secondary_item['link_title']);
+
+    $this->i18nGet($this->secondary_language, '<front>');
+    $this->assertText($neutral_item['link_title']);
+    $this->assertNoText($default_item['link_title']);
+    $this->assertText($secondary_item['link_title']);
+
+    // Test the same thing with a system menu used as a block.
+    $block['title'] = $menu['title'];
+    $block['module'] = 'system';
+    $block['delta'] = 'main-menu';
+    $this->moveBlockToRegion($block, 'sidebar_first');
+
+    $this->drupalGet('<front>');
+    $this->assertText($neutral_item['link_title']);
+    $this->assertText($default_item['link_title']);
+    $this->assertNoText($secondary_item['link_title']);
+
+    $this->i18nGet($this->secondary_language, '<front>');
+    $this->assertText($neutral_item['link_title']);
+    $this->assertNoText($default_item['link_title']);
+    $this->assertText($secondary_item['link_title']);
+  }
+
+  /**
+   * Test menu items for nodes.
+   */
+  function testNodeMenuItems() {
+    // Create menu and display it in a block.
+    $menu = $this->createMenu(array(
+      'i18n_mode' => I18N_MODE_MULTIPLE,
+      'language' => LANGUAGE_NONE,
+      'menu_name' => 'test',
+    ));
+
+    $neutral_item = $this->createMenuLink(array('menu_name' => $menu['menu_name']));
+    $block['title'] = $menu['title'];
+    $block['module'] = 'menu';
+    $block['delta'] = $menu['menu_name'];
+    $this->moveBlockToRegion($block, 'sidebar_first');
+    $menu_parent = $menu['menu_name'] . ':0';
+    // Create content type 'page', translation enabled, login as translator
+    parent::setUpContentTranslation();
+    $settings = array(
+      'menu_options[' . $menu['menu_name'] . ']' => TRUE,
+      'menu_parent' => $menu_parent,
+      'node_options[promote]' => FALSE,
+    );
+    $this->drupalPost('admin/structure/types/manage/page/edit', $settings, t('Save content type'));
+
+    // Create nodes with language and menu item: es, en, und
+    $edit = array(
+      'menu[enabled]' => TRUE,
+      'menu[parent]' => $menu_parent,
+      'promote' => FALSE,
+    );
+    // English Page => English menu item
+    $en_title = $this->randomName(10);
+    $en_body = $this->randomString(50);
+    $nodes[] = $en_node = $this->createNode('page', $en_title, $en_body, 'en', $edit + array('menu[link_title]' => $en_title));
+    // Spanish page => Spanish menu item
+    $es_title = $this->randomName(10);
+    $es_body = $this->randomString(50);
+    $nodes[] = $es_node = $this->createNode('page', $es_title, $es_body, 'es', $edit + array('menu[link_title]' => $es_title));
+    // Language neutral page, localicable menu item
+    $und_title = $this->randomName(10);
+    $und_body = $this->randomString(50);
+    $nodes[] = $und_node = $this->createNode('page', $und_title, $und_body, LANGUAGE_NONE, $edit + array('menu[link_title]' => $und_title));
+    // Check menu items have right language and we cannot edit them.
+    foreach ($nodes as $node) {
+      menu_node_prepare($node);
+      $this->assertEqual($node->menu['language'], $node->language, 'Menu item has the same language that the node it belongs to.');
+      $this->drupalGet('admin/structure/menu/item/' . $node->menu['mlid'] . '/edit');
+      $this->assertText(t('This menu item belongs to a node, so it will have the same language as the node and cannot be localized.'));
+      $this->assertNoField('language', 'We cannot edit language for menu items that belong to nodes.');
+    }
+    // Check menu items show up for the right language.
+    $this->drupalGet('<front>');
+    $this->assertText($en_title);
+    $this->assertNoText($es_title);
+    $this->assertText($und_title);
+    $this->i18nGet('es', '<front>');
+    $this->assertText($es_title);
+    $this->assertNoText($en_title);
+    $this->assertText($und_title);
+    // Create string translation for neutral menu item and check it shows up
+    $translation = $this->randomName(10);
+    $this->createStringTranslation('menu', $und_title, array('es' => $translation));
+    $this->drupalGet('<front>');
+    $this->assertText($und_title);
+    $this->i18nGet('es', '<front>');
+    $this->assertText($translation);
+  }
+
+  /**
+   * Tests if the translation set management works.
+   */
+  function testMenuTranslationSets() {
+    $menu = $this->createMenu(array('i18n_mode' => I18N_MODE_MULTIPLE));
+
+    $neutral_item = $this->createMenuLink(array('menu_name' => $menu['menu_name']));
+    $default_item = $this->createMenuLink(array('menu_name' => $menu['menu_name'], 'language' => $this->default_language));
+    $secondary_item = $this->createMenuLink(array('menu_name' => $menu['menu_name'], 'language' => $this->secondary_language));
+
+    $translationset_edit = array(
+      'translations[' . $this->default_language . ']' => $default_item['menu_name'] . ':' . $default_item['mlid'],
+      'translations[' . $this->secondary_language . ']' => $secondary_item['menu_name'] . ':' . $secondary_item['mlid'],
+    );
+    $translation_set = $this->createMenuLinkTranslationSet($translationset_edit);
+
+    // Check if the overview works
+    $this->drupalGet('admin/structure/menu/manage/translation');
+    $link = $this->xpath('//*/a[contains(@href,"admin/structure/menu/manage/translation/edit/' . $translation_set->tsid . '")]');
+    $this->assertTrue(!empty($link), 'Created translation-set found.');
+
+    // Check if the edit mode works
+    $this->drupalGet('admin/structure/menu/manage/translation/edit/' . $translation_set->tsid);
+    $this->assertFieldByXPath(
+      "//*[@id='edit-translations-" . $this->default_language . "']/option[@selected]/@value",
+      $menu['menu_name'] . ':' . $default_item['mlid'],
+      'Expected option selection for language ' . $this->default_language . ' found.'
+    );
+    $this->assertFieldByXPath(
+      "//*[@id='edit-translations-" . $this->secondary_language . "']/option[@selected]/@value",
+      $menu['menu_name'] . ':' . $secondary_item['mlid'],
+      'Expected option selection for language ' . $this->secondary_language . ' found.'
+    );
+  }
+
+  /**
+   * Tests if on a switch from translatable to non translatable the translation
+   * sets and links are cleaned up.
+   */
+  function testMenuTranslateLocalizeSwitchToNonTranslatable() {
+    // Test filtering for menu blocks.
+    $menu = $this->createMenu(array('i18n_mode' => I18N_MODE_MULTIPLE));
+
+    // Check current menu mode
+    $this->drupalGet('admin/structure/menu/manage/' . $menu['menu_name'] . '/edit');
+    $this->assertFieldByName('i18n_mode', I18N_MODE_MULTIPLE, 'Menu i18n mode set to I18N_MODE_MULTIPLE');
+
+    // Setup menu links for testing.
+    $neutral_item = $this->createMenuLink(array('menu_name' => $menu['menu_name']));
+    $default_item = $this->createMenuLink(array('menu_name' => $menu['menu_name'], 'language' => $this->default_language));
+    $secondary_item = $this->createMenuLink(array('menu_name' => $menu['menu_name'], 'language' => $this->secondary_language));
+
+    $translationset_edit = array(
+      'translations[' . $this->default_language . ']' => $default_item['menu_name'] . ':' . $default_item['mlid'],
+      'translations[' . $this->secondary_language . ']' => $secondary_item['menu_name'] . ':' . $secondary_item['mlid'],
+    );
+    $translation_set = $this->createMenuLinkTranslationSet($translationset_edit);
+    $tsid = $translation_set->tsid;
+
+    // Test language mode switch
+    $edit = array(
+      'i18n_mode' => I18N_MODE_LANGUAGE,
+      'language' => $this->secondary_language,
+    );
+    $this->drupalPost('admin/structure/menu/manage/' . $menu['menu_name'] . '/edit', $edit, t('Save'));
+    $this->drupalGet('admin/structure/menu/manage/' . $menu['menu_name'] . '/edit');
+    $this->assertFieldByName('i18n_mode', I18N_MODE_LANGUAGE, 'Menu i18n mode changed to I18N_MODE_LANGUAGE');
+
+    $this->assertTrue(!empty(i18n_translation_set_load($tsid)->tsid), 'Translation set kept.');
+
+    $menu_link_languages = db_select('menu_links')
+      ->fields('menu_links', array('language'))
+      ->condition('menu_name', $menu['menu_name'])
+      ->groupBy('language')
+      ->execute()
+      ->fetchCol();
+    $this->assertTrue((count($menu_link_languages) == 1 && reset($menu_link_languages) === $this->secondary_language), 'Menu link language changed to menu language.');
+
+    // Test cleanup.
+    $edit = array(
+      'i18n_mode' => I18N_MODE_NONE,
+    );
+    $this->drupalPost('admin/structure/menu/manage/' . $menu['menu_name'] . '/edit', $edit, t('Save'));
+    $this->drupalGet('admin/structure/menu/manage/' . $menu['menu_name'] . '/edit');
+    $this->assertFieldByName('i18n_mode', I18N_MODE_NONE, 'Menu i18n mode changed to I18N_MODE_NONE');
+    $translation_sets = entity_load('i18n_translation', FALSE, array('tsid' => $tsid), TRUE);
+    $this->assertTrue(empty($translation_sets), 'Translation set deleted.');
+
+    $menu_link_languages = db_select('menu_links')
+      ->fields('menu_links', array('language'))
+      ->condition('menu_name', $menu['menu_name'])
+      ->groupBy('language')
+      ->execute()
+      ->fetchCol();
+    $this->assertTrue(((count($menu_link_languages) == 1) && reset($menu_link_languages) === LANGUAGE_NONE), 'Menu link language switched to LANGUAGE_NONE.');
+  }
+
+  /**
+   * Helper function to create a menu.
+   */
+  function createMenu($edit = array()) {
+    $edit += array(
+      'title' => $this->randomName(),
+      'menu_name' => substr(hash('sha256', $this->randomName(16)), 0, MENU_MAX_MENU_NAME_LENGTH_UI),
+      'language' => $this->secondary_language,
+    );
+    $this->drupalPost('admin/structure/menu/add', $edit, t('Save'));
+    return menu_load('menu-' . $edit['menu_name']);
+  }
+
+  /**
+   * Helper function to create a menu link.
+   */
+  function createMenuLink($item = array()) {
+    $item += array(
+      'link_title' => $this->randomName(),
+      'link_path' => '<front>',
+      'customized' => TRUE,
+    );
+
+    return menu_link_load(menu_link_save($item));
+  }
+
+  /**
+   * Helper function to create a translation set.
+   */
+  function createMenuLinkTranslationSet($edit = array()) {
+    $edit += array(
+      'title' => $this->randomName(16),
+    );
+    $this->drupalPost('admin/structure/menu/manage/translation/add', $edit, t('Save'));
+
+    // Load translation set entity.
+    $entity = entity_load('i18n_translation', FALSE, array('title' => $edit['title']), TRUE);
+    if (empty($entity)) {
+      $this->fail('Could not create a translation set.', 'i18n_translation');
+      return FALSE;
+    }
+    return reset($entity);
+  }
+}
diff --git a/kolab.org/www/drupal-7.18/sites/all/modules/i18n/i18n_node/i18n_node.features.inc b/kolab.org/www/drupal-7.18/sites/all/modules/i18n/i18n_node/i18n_node.features.inc
new file mode 100644
index 0000000..a04cae8
--- /dev/null
+++ b/kolab.org/www/drupal-7.18/sites/all/modules/i18n/i18n_node/i18n_node.features.inc
@@ -0,0 +1,26 @@
+<?php
+/**
+ * @file
+ * Feature integration
+ */
+
+/**
+ * Implements hook_features_pipe_node_alter().
+ */
+function i18n_node_features_pipe_node_alter(&$pipe, $data, $export) {
+  if (!empty($data) && module_exists('variable')) {
+    variable_include();
+    foreach (variable_list_module('i18n_node') as $variable) {
+      if (isset($variable['multiple']) && $variable['multiple'] === 'node_type') {
+        $children = variable_build($variable['name']);
+        if (!empty($children['children'])) {
+          foreach ($children['children'] as $child_variable) {
+            if (in_array($child_variable['index'], $data)) {
+              $pipe['variable'][] = $child_variable['name'];
+            }
+          }
+        }
+      }
+    }
+  }
+}
\ No newline at end of file
diff --git a/kolab.org/www/drupal-7.18/sites/all/modules/i18n/i18n_node/i18n_node.i18n.inc b/kolab.org/www/drupal-7.18/sites/all/modules/i18n/i18n_node/i18n_node.i18n.inc
new file mode 100644
index 0000000..a9eb71a
--- /dev/null
+++ b/kolab.org/www/drupal-7.18/sites/all/modules/i18n/i18n_node/i18n_node.i18n.inc
@@ -0,0 +1,50 @@
+<?php
+/**
+ * @file
+ * Internationalization (i18n) hooks
+ */
+
+
+/**
+ * Implements hook_i18n_object_info().
+ */
+function i18n_node_i18n_object_info() {
+  $info['node_type'] = array(
+    'title' => t('Node type'),
+    'key' => 'type',
+    'load callback' => 'node_type_get_type',
+    'placeholders' => array(
+      '%node_type' => 'type',
+    ),
+    'edit path' => 'admin/structure/types/manage/%node_type',
+    'translate tab' => 'admin/structure/types/manage/%node_type/translate',
+    // We can easily list all these objects
+    'list callback' => 'node_type_get_types',
+    // Metadata for string translation
+    'string translation' => array(
+      'textgroup' => 'node',
+      'type' => 'type',
+      'properties' => array(
+        'name' => t('Name'),
+        'title_label' => t('Title label'),
+        'description' => t('Description'),
+        'help' => t('Help text'),
+      ),
+      'translate path' => 'admin/structure/types/manage/%node_type/translate/%i18n_language',
+    )
+  );
+  return $info;
+}
+
+/**
+ * Implements hook_i18n_string_info()
+ */
+function i18n_node_i18n_string_info() {
+  $groups['node'] = array(
+    'title' => t('Node types'),
+    'description' => t('Content type names, descriptions, help texts.'),
+    //'format' => TRUE, // This group has strings with format (block body)
+    'list' => TRUE, // This group can list all strings
+  );
+  return $groups;
+}
diff --git a/kolab.org/www/drupal-7.18/sites/all/modules/i18n/i18n_node/i18n_node.info b/kolab.org/www/drupal-7.18/sites/all/modules/i18n/i18n_node/i18n_node.info
new file mode 100644
index 0000000..56670fa
--- /dev/null
+++ b/kolab.org/www/drupal-7.18/sites/all/modules/i18n/i18n_node/i18n_node.info
@@ -0,0 +1,17 @@
+name = Multilingual content
+description = Extended node options for multilingual content
+dependencies[] = translation
+dependencies[] = i18n
+dependencies[] = i18n_string
+package = Multilingual - Internationalization
+core = 7.x
+configure = admin/config/regional/i18n/node
+files[]=i18n_node.test
+files[]=i18n_node.variable.inc
+
+; Information added by drupal.org packaging script on 2013-01-13
+version = "7.x-1.8"
+core = "7.x"
+project = "i18n"
+datestamp = "1358075001"
+
diff --git a/kolab.org/www/drupal-7.18/sites/all/modules/i18n/i18n_node/i18n_node.install b/kolab.org/www/drupal-7.18/sites/all/modules/i18n/i18n_node/i18n_node.install
new file mode 100644
index 0000000..7622bcf
--- /dev/null
+++ b/kolab.org/www/drupal-7.18/sites/all/modules/i18n/i18n_node/i18n_node.install
@@ -0,0 +1,64 @@
+<?php
+
+/**
+ * @file
+ * Installation file for Internationalization (i18n) module.
+ */
+
+/**
+ * Implements hook_install().
+ */
+function i18n_node_install() {
+  // If updating from D6, module changed name
+  if (variable_get('i18n_drupal6_update')) {
+    i18n_node_update_7000();
+  }
+}
+
+/**
+ * Implements hook_uninstall().
+ */
+function i18n_node_uninstall() {
+  variable_del('i18n_hide_translation_links');
+  variable_del('i18n_selection_mode');
+  foreach (array_keys(node_type_get_types()) as $type) {
+    variable_del('i18n_node_' . $type);
+  }
+}
+
+/**
+ * Implements hook_update_dependencies()
+ */
+function i18n_node_update_dependencies() {
+  $dependencies['i18n_node'][7000] = array(
+    'i18n_string' => 7001,
+  );
+  return $dependencies;
+}
+
+/**
+ * Implements hook_i18n_update_drupal6().
+ *
+ * Update old string names
+ */
+function i18n_node_update_7000() {
+  // @todo Update from D6 i18n
+  // Variables:
+  // i18n_newnode_current, i18n_required_node, i18n_lock_node => i18n_node_options_[node_type]
+  // i18n_node => 'i18n_node_extended_[node_type]'
+  // Update string names
+  // - nodetype:type:[type]:[property] -> node:type:[type]:[property]
+  // - Property names: title -> title_label
+  module_load_install('i18n_string');
+  i18n_string_install_update_context('nodetype:type:*:*', 'node:type:*:*');
+  i18n_string_install_update_context('node:type:*:title', 'node:type:*:title_label');
+}
+
+/**
+ * Delete obsoleted variable for switch interface for translating.
+ */
+/*
+function i18n_node_update_7001() {
+  variable_del('i18n_node_translation_switch');
+}
+*/
diff --git a/kolab.org/www/drupal-7.18/sites/all/modules/i18n/i18n_node/i18n_node.js b/kolab.org/www/drupal-7.18/sites/all/modules/i18n/i18n_node/i18n_node.js
new file mode 100644
index 0000000..ab4cf85
--- /dev/null
+++ b/kolab.org/www/drupal-7.18/sites/all/modules/i18n/i18n_node/i18n_node.js
@@ -0,0 +1,20 @@
+(function ($) {
+
+/**
+ * Rewrite autocomplete inputs to pass the language of the node currently being
+ * edited in the path.
+ *
+ * This functionality ensures node autocompletes get suggestions for the node's
+ * language rather than the current interface language.
+ */
+Drupal.behaviors.i18n = {
+  attach : function (context) {
+    if (Drupal.settings && Drupal.settings.i18n) {
+      $('form[id^=node-form]', context).find('input.autocomplete[value^=' + Drupal.settings.i18n.interface_path + ']').each(function () {
+        $(this).val($(this).val().replace(Drupal.settings.i18n.interface_path, Drupal.settings.i18n.content_path));
+      });
+    }
+  }
+};
+
+})(jQuery);
diff --git a/kolab.org/www/drupal-7.18/sites/all/modules/i18n/i18n_node/i18n_node.module b/kolab.org/www/drupal-7.18/sites/all/modules/i18n/i18n_node/i18n_node.module
new file mode 100644
index 0000000..754ae69
--- /dev/null
+++ b/kolab.org/www/drupal-7.18/sites/all/modules/i18n/i18n_node/i18n_node.module
@@ -0,0 +1,572 @@
+<?php
+
+/**
+ * @file
+ * Internationalization (i18n) module - Node type handling
+ *
+ * The i18n strings created by this module are:
+ * - node:type:[type]:[name,title,body,help]
+ */
+
+
+/**
+ * Implements hook_field_extra_fields().
+ */
+function i18n_node_field_extra_fields() {
+  $return = array();
+  $info = entity_get_info('node');
+  foreach (array_keys($info['bundles']) as $bundle) {
+    if (i18n_node_type_enabled($bundle)) {
+      $return['node'][$bundle] = i18n_language_field_extra();
+    }
+  }
+  return $return;
+}
+
+/**
+ * Implements hook_menu().
+ */
+function i18n_node_menu() {
+  $items['admin/config/regional/i18n/node'] = array(
+    'title' => 'Node options',
+    'description' => 'Configure extended options for multilingual content and translations.',
+    'page callback' => 'drupal_get_form',
+    'page arguments' => array('variable_group_form', 'i18n_node'),
+    'access arguments' => array('administer site configuration'),
+    'type' => MENU_LOCAL_TASK,
+    'weight' => 10,
+  );
+  $items['i18n/node/autocomplete'] = array(
+    'page callback' => 'i18n_node_autocomplete',
+    'file' => 'i18n_node.pages.inc',
+    'access arguments' => array('administer content translations'),
+    'type' => MENU_CALLBACK,
+  );
+  return $items;
+}
+
+/**
+ * Implements hook_block_view_MODULE_DELTA_alter().
+ *
+ * Set translated help text for node/add pages, replace node_help() text.
+ */
+function i18n_node_block_view_system_help_alter(&$block) {
+  $arg = drupal_help_arg(arg(NULL));
+  if ($arg[0] == 'node' && $arg[1] == 'add' && $arg[2]) {
+    if (($type = node_type_get_type(str_replace('-', '_', $arg[2]))) && !empty($type->help)) {
+      $help = i18n_node_translate_type($type, 'help');
+      if ($help !== $type->help) {
+        $block['content'] = str_replace(filter_xss_admin($type->help), filter_xss_admin($help), $block['content']);
+      }
+    }
+  }
+  elseif ($arg[0] == 'node' && $arg[2] == 'edit') {
+    $node = menu_get_object();
+    if ($node && isset($node->type)) {
+      $type = node_type_get_type($node->type);
+      $help = i18n_node_translate_type($type, 'help');
+      if ($help !== $type->help) {
+        $block['content'] = str_replace(filter_xss_admin($type->help), filter_xss_admin($help), $block['content']);
+      }
+    }
+  }
+}
+
+/**
+ * Implements hook_help().
+ */
+function i18n_node_help($path, $arg) {
+  switch ($path) {
+    case 'admin/help#i18n_node':
+      $output = '<p>' . t('Provides some extended multilingual options for nodes.') . '</p>';
+      $output .= '<p>' . t('Additionally, if <em>String translation</em> enabled, this module will localize all content type configuration texts.') . '</p>';
+      $output .= '<ul>';
+      $output .= '<li>' . t('Content type names') . '</li>';
+      $output .= '<li>' . t('Submission guidelines') . '</li>';
+      $output .= '<li>' . t("Content type descriptions were previously localized so they won't be affected.") . '</li>';
+      $output .= '</ul>';
+      $output .= '<p>' . t('To search and translate strings, use the <a href="@translate-interface">translation interface</a> pages.', array('@translate-interface' => url('admin/config/regional/translate'))) . '</p>';
+      return $output;
+    case 'admin/config/regional/i18n':
+    case 'admin/config/regional/i18n/node':
+      $output = '<p>' . t('You can find some more per content type options on the <a href="@configure_content">Content types administration page</a>.', array('@configure_content' => url('admin/structure/types'))) . '</p>';
+      return $output;
+  }
+}
+
+/**
+ * Implements hook_i18n_context_language().
+ */
+function i18n_node_i18n_context_language() {
+  // Node language when loading specific nodes or creating translations.
+  if (arg(0) == 'node' ) {
+    if (($node = menu_get_object('node')) && !empty($node->language) && i18n_node_type_enabled($node)) {
+      return i18n_language_object($node->language);
+    }
+    elseif (arg(1) == 'add' && !empty($_GET['translation']) && !empty($_GET['target']) && ($source = node_load($_GET['translation'])) && i18n_node_type_enabled($source)) {
+      return i18n_language_object($_GET['target']);
+    }
+  }
+}
+
+/**
+ * Implements hook_i18n_translate_path()
+ */
+function i18n_node_i18n_translate_path($path) {
+  if (preg_match("!^node/(\d+)(/.+|)!", $path, $matches) && ($node = node_load((int) $matches[1])) && i18n_object_langcode($node) && !empty($node->tnid)) {
+    if ($translations = translation_node_get_translations($node->tnid)) {
+      $result = array();
+      foreach ($translations as $langcode => $node_translation) {
+        $result[$langcode] = array(
+          'href' => 'node/' . $node_translation->nid . $matches[2],
+          'title' => $node_translation->title,
+          'object' => $node_translation,
+        );
+      }
+      return $result;
+    }
+  }
+}
+
+/**
+ * Implements hook_menu_alter().
+ *
+ * Take over the node translation page.
+ */
+function i18n_node_menu_alter(&$items) {
+  if (isset($items['node/%node/translate'])) {
+    $items['node/%node/translate']['page callback'] = 'i18n_node_translation_overview';
+    $items['node/%node/translate']['file'] = 'i18n_node.pages.inc';
+    $items['node/%node/translate']['module'] = 'i18n_node';
+  }
+  // Take over node/add pages for string translation
+  $items['node/add']['page callback'] =  'i18n_node_add_page';
+  $items['node/add']['file'] = 'i18n_node.pages.inc';
+  $items['node/add']['file path'] = drupal_get_path('module', 'i18n_node');
+  // @TODO avoid iterating over every router path.
+  foreach (node_type_get_types() as $type) {
+    $path = 'node/add/' . str_replace('_', '-', $type->type);
+    if (isset($items[$path])) {
+      $items[$path]['title callback'] = 'i18n_node_type_name';
+      $items[$path]['title arguments'] = array($type->type, $type->name);
+    }
+  }
+}
+
+/**
+ * Get node language.
+ */
+function i18n_node_get_lang($nid, $default = '') {
+  $lang = db_query('SELECT language FROM {node} WHERE nid = :nid', array(':nid' => $nid))->fetchField();
+  return $lang ? $lang : $default ;
+}
+
+/**
+ * Get allowed languages for node.
+ *
+ * This allows node types to define its own language list implementing hook 'language_list'.
+ *
+ * @param $node
+ *   Node to retrieve language list for.
+ * @param $translate
+ *   Only languages available for translation.
+ * @param $select
+ *   Only languages that can be selected for this node
+ */
+function i18n_node_language_list($node, $translate = FALSE, $select = FALSE) {
+  // Check if the node module manages its own language list.
+  $languages = node_invoke($node, 'language_list', $translate);
+
+  if (!$languages) {
+    $languages = i18n_language_list('name', i18n_node_language_mode($node));
+    if ($translate && isset($node->tnid) && $node->tnid && ($translations = translation_node_get_translations($node->tnid))) {
+      unset($translations[$node->language]);
+      foreach (array_keys($translations) as $langcode) {
+        unset($languages[$langcode]);
+      }
+    }
+    // Language may be locked for this node type, restrict options to current one
+    if ($select && i18n_node_language_options($node, 'lock') && !empty($node->language) && !empty($languages[$node->language])) {
+      $languages = array($node->language => $languages[$node->language]);
+    }
+    // Check language required for this type (no language neutral)
+    elseif (!i18n_node_language_options($node, 'required')) {
+      $languages = array(LANGUAGE_NONE => t('Language neutral')) + $languages;
+    }
+  }
+
+  return $languages;
+}
+
+/**
+ * Check options for node language
+ */
+function i18n_node_language_options($node, $option) {
+  $options = variable_get('i18n_node_options_' . $node->type, array());
+  return in_array($option, $options, TRUE);
+}
+
+/**
+ * Get language mode for node or node type
+ */
+function i18n_node_language_mode($type) {
+  $type = is_object($type) ? $type->type : $type;
+  return variable_get('i18n_node_extended_' . $type, I18N_LANGUAGE_ENABLED);
+}
+
+/**
+ * Implements hook_node_prepare().
+ */
+function i18n_node_node_prepare($node) {
+  $options = variable_get('i18n_node_options_' . $node->type, array());
+  if (i18n_node_type_enabled($node) && empty($node->nid) && !i18n_object_langcode($node) && in_array('current', $options)) {
+    // Set current language for new nodes if option enabled
+    $node->language = i18n_language_content()->language;
+  }
+}
+
+/**
+ * Implements hook_permission().
+ *
+ * Permissions defined
+ * - administer all languages
+ *   Disables language conditions for administration pages, so the user can view objects for all languages at the same time.
+ *   This applies for: menu items, taxonomy
+ * - administer translations
+ *   Will allow to add/remove existing nodes to/from translation sets.
+ */
+function i18n_node_permission() {
+  return array(
+    'administer content translations' => array(
+      'title' => t('Administer content translations'),
+      'description' => t('Add or remove existing content to translation sets.'),
+    ),
+  );
+}
+
+/**
+ * Implements hook_node_view()
+ */
+function i18n_node_node_view($node) {
+  if (i18n_node_type_enabled($node)) {
+    $node->content['language'] = array(
+      '#type' => 'item',
+      '#title' => t('Language'),
+      '#markup' => i18n_language_name($node->language),
+    );
+  }
+}
+
+/**
+ * Implements hook_node_view_alter().
+ *
+ * Handles links for extended languages. Sets current interface language.
+ */
+function i18n_node_node_view_alter(&$build) {
+  $node = $build['#node'];
+  // Hide node translation links.
+  if (variable_get('i18n_hide_translation_links', 0)) {
+    if (isset($build['links']['translation'])) {
+      unset($build['links']['translation']);
+    }
+  }
+  elseif (!empty($node->tnid) && !empty($build['links']['translation']) && i18n_node_language_mode($node) == I18N_LANGUAGE_EXTENDED) {
+    // We only get links for translations for enabled languages
+    // Set the right languages if language support is extended but not visible.
+    $links = &$build['links']['translation']['#links'];
+    $translations = translation_node_get_translations($node->tnid);
+    foreach (i18n_node_language_list($node) as $langcode => $langname) {
+      $index = 'translation_' . $langcode;
+      if ($langcode != $node->language && isset($translations[$langcode]) && $translations[$langcode]->status && !isset($links[$index])) {
+        // This a published translation to a disabled language. As the node is language extended, display the linkso
+        // These links shouldn't switch the interface, though they have a language so the right language icon will be added
+        $language = i18n_language_object($langcode);
+        $links[$index] = array(
+          'href' => 'node/' . $translations[$langcode]->nid,
+          'title' => $language->native,
+          'language' => $language,
+          'attributes' => array(
+            'title' => $translations[$langcode]->title,
+            'class' => array('translation-link'),
+          ),
+        );
+      }
+    }
+  }
+}
+
+/**
+ * Implements hook_node_type_insert()
+ */
+function i18n_node_node_type_insert($info) {
+  i18n_node_node_type_update($info);
+}
+
+/**
+ * Implements hook_node_type_update()
+ */
+function i18n_node_node_type_update($info) {
+  if (!empty($info->old_type) && $info->old_type != $info->type) {
+    i18n_string_update_context("node:type:$info->old_type:*", "node:type:$info->type:*");
+  }
+  i18n_string_object_update('node_type', $info);
+}
+
+/**
+ * Implements hook_node_type_delete()
+ */
+function i18n_node_node_type_delete($info) {
+  i18n_string_object_remove('node_type', $info);
+}
+
+/**
+ * Implements hook_elements().
+ *
+ * Add a process callback for textfields.
+ *
+ *  * @todo Update D7
+ */
+/*
+function i18n_node_elements() {
+  $type = array();
+  $type['textfield'] = array('#process' => array('i18n_node_textfield_process'));
+  return $type;
+}
+*/
+
+/**
+ * Process callback for textfield elements.
+ *
+ * When editing or translating a node, set Javascript to rewrite autocomplete
+ * paths to use the node language prefix rather than the current content one.
+ *
+ * @todo Update D7
+ */
+function i18n_node_textfield_process($element) {
+  global $language;
+  static $sent = FALSE;
+
+  // Ensure we send the Javascript only once.
+  if (!$sent && isset($element['#autocomplete_path']) && !empty($element['#autocomplete_path']) && variable_get('language_negotiation', LANGUAGE_NEGOTIATION_NONE) != LANGUAGE_NEGOTIATION_NONE) {
+    // Add a JS file for node forms.
+    // Determine if we are either editing or translating an existing node.
+    // We can't act on regular node creation because we don't have a specified
+    // node language.
+    $node_edit = $node = menu_get_object() && arg(2) == 'edit' && isset($node->language) && !empty($node->language);
+    $node_translate = arg(0) == 'node' && arg(1) == 'add' && !empty($_GET['translation']) && !empty($_GET['language']);
+    if ($node_edit || $node_translate) {
+      $node_language = $node_edit ? $node->language : $_GET['language'];
+      // Only needed if the node language is different from the interface one.
+      if ($node_language != $language->language) {
+        $languages = language_list();
+        if (isset($languages[$node_language])) {
+          drupal_add_js(drupal_get_path('module', 'i18n_node') . '/i18n_node.js');
+          // Pass the interface and content language base paths.
+          // Remove any trailing forward slash. Doing so prevents a mismatch
+          // that occurs when a language has no prefix and hence gets a path
+          // with a trailing forward slash.
+          $interface = rtrim(url('', array('absolute' => TRUE)), '/');
+          $content = rtrim(url('', array('absolute' => TRUE, 'language' => $languages[$node_language])), '/');
+          $data = array('interface_path' => $interface, 'content_path' => $content);
+          drupal_add_js(array('i18n' => $data), 'setting');
+        }
+      }
+    }
+    $sent = TRUE;
+  }
+  return $element;
+}
+
+/**
+ * Implements hook_form_FORM_ID_alter().
+ */
+function i18n_node_form_search_form_alter(&$form, &$form_state) {
+  // Advanced search form. Add language and localize content type names
+  if ($form['module']['#value'] == 'node' && !empty($form['advanced'])) {
+    // @todo Handle language search conditions
+    //$form['advanced']['language'] = _i18n_language_select();
+    if (!empty($form['advanced']['type'])) {
+      foreach ($form['advanced']['type']['#options'] as $type => $name) {
+        $form['advanced']['type']['#options'][$type] = i18n_node_translate_type($type, 'name', $name);
+      }
+    }
+  }
+}
+
+/**
+ * Implements hook_form_FORM_ID_alter().
+ */
+function i18n_node_form_node_type_form_alter(&$form, &$form_state) {
+  if (isset($form['type'])) {
+    $disabled = !i18n_node_type_enabled($form['#node_type']);
+    $form['i18n'] = array(
+      '#type' => 'fieldset',
+      '#title' => t('Multilingual settings'),
+      '#collapsible' => TRUE,
+      '#collapsed' => TRUE,
+      '#group' => 'additional_settings',
+      '#attributes' => array(
+        'class' => array('i18n-node-type-settings-form'),
+      ),
+      '#description' => t('Extended multilingual options provided by Internationalization module.'),
+      '#disabled' => $disabled,
+    );
+
+    // Some settings about node languages. Add variables for node type from variable definition
+    if ($form['#node_type']->type) {
+      variable_type_include('node_type');
+      $form['i18n'] += node_variable_type_subform($form['#node_type']->type, array('i18n_node_options', 'i18n_node_extended'));
+    }
+    // Add disabled message
+    if ($disabled) {
+      $form['i18n']['#description'] .= ' <em>' . t('These will be available only when you enable Multilingual support in Publishing options above.') . '</em>';
+      foreach (element_children($form['i18n']) as $key) {
+        $form['i18n'][$key]['#disabled'] = TRUE;
+      }
+    }
+  }
+}
+
+/**
+ * Implements hook_form_BASE_FORM_ID_alter().
+ */
+function i18n_node_form_node_form_alter(&$form, $form_state) {
+  $node = $form['#node'];
+  /**
+   * i18n has to override locale.module
+   * drupal_alter() fails to order modules correctly in some cases
+   * for example specific hooks like hook_form_BASE_FORM_ID_alter
+   *
+   * its not possbile to reorder hook_form_BASE_FORM_ID_alter with
+   * hook_module_implements_alter
+   *
+   * @see http://drupal.org/node/765860
+  */
+  
+  // Replace core's node submit callback with our own,
+  // in order to translate the node type name.
+  $key = array_search('node_form_submit', $form['actions']['submit']['#submit']);
+  if ($key !== FALSE) {
+    $form['actions']['submit']['#submit'][$key] = 'i18n_node_form_submit';
+  }
+
+  // call a 'private' implemenation of i18n_node_form_node_form_alter()
+  $form['#after_build'][] = '_i18n_node_form_node_form_alter';
+}
+
+
+/**
+ * Replacement for core's node_form_submit(), taking care of
+ * translating node type names.
+ */
+function i18n_node_form_submit($form, &$form_state) {
+  $node = node_form_submit_build_node($form, $form_state);
+  $insert = empty($node->nid);
+  node_save($node);
+  $node_link = l(t('view'), 'node/' . $node->nid);
+  $type = node_type_get_type($node->type);
+  $type_name = i18n_node_translate_type($type);
+
+  $watchdog_args = array('@type' => $node->type, '%title' => $node->title);
+  $t_args = array('@type' => $type_name, '%title' => $node->title);
+
+  if ($insert) {
+    watchdog('content', '@type: added %title.', $watchdog_args, WATCHDOG_NOTICE, $node_link);
+    drupal_set_message(t('@type %title has been created.', $t_args));
+  }
+  else {
+    watchdog('content', '@type: updated %title.', $watchdog_args, WATCHDOG_NOTICE, $node_link);
+    drupal_set_message(t('@type %title has been updated.', $t_args));
+  }
+  if ($node->nid) {
+    $form_state['values']['nid'] = $node->nid;
+    $form_state['nid'] = $node->nid;
+    $form_state['redirect'] = 'node/' . $node->nid;
+  }
+  else {
+    // In the unlikely case something went wrong on save, the node will be
+    // rebuilt and node form redisplayed the same way as in preview.
+    drupal_set_message(t('The post could not be saved.'), 'error');
+    $form_state['rebuild'] = TRUE;
+  }
+  // Clear the page and block caches.
+  cache_clear_all();
+}
+
+/**
+ * Implements hook_form_BASE_FORM_ID_alter().
+ * Called by i18n_node_form_node_form_alter
+ */
+function _i18n_node_form_node_form_alter($form, &$form_state) {
+  $node = $form['#node'];
+  if (i18n_node_type_enabled($node)) {
+    if (!empty($form['language']['#options'])) {
+      $form['language']['#options'] = i18n_node_language_list($node, TRUE, TRUE);
+    }
+  }
+  elseif (variable_get('i18n_node_default_language_none', 0) && !isset($form['#node']->nid)) {
+    // Override locale module setting default language to nodes. It is already in form_state.
+    $form['language']['#value'] = $form_state['values']['language'] = LANGUAGE_NONE;
+  }
+  // Translate field names for title and body for the node edit form.
+  if (!empty($form['title']['#title'])) {
+    $form['title']['#title'] = i18n_node_translate_type($node->type, 'title_label', $form['title']['#title']);
+  }
+  if (!empty($form['body_field']['body']['#title'])) {
+    $form['body_field']['body']['#title'] = i18n_node_translate_type($node->type, 'body', $form['body_field']['body']['#title']);
+  }
+  // Translate page title for node/add/% and node/%/edit pages.
+  $types = node_type_get_types();
+  if (empty($node->nid) && strpos($_GET['q'], 'node/add/' . str_replace('_', '-', $node->type)) === 0) {
+    drupal_set_title(t('Create @name', array('@name' => i18n_node_translate_type($types[$node->type], 'name'))), PASS_THROUGH);
+  }
+  elseif (!empty($node->nid) && $_GET['q'] == 'node/' . $node->nid . '/edit') {
+    drupal_set_title(t('<em>Edit @type</em> @title', array('@type' => i18n_node_translate_type($types[$node->type], 'name'), '@title' => $node->title)), PASS_THROUGH);
+  }
+  return $form;
+}
+
+/**
+ * Implements hook_theme().
+ */
+function i18n_node_theme() {
+  return array(
+    'i18n_node_select_translation' => array(
+      'render element' => 'element',
+      'file' => 'i18n_node.pages.inc',
+    ),
+  );
+}
+
+/**
+ * Shorthand for translating node type strings
+ *
+ * @param $type
+ *   Node type name or full object
+ */
+function i18n_node_translate_type($type, $property = 'name', $source = NULL, $options = array()) {
+  if (is_object($type)) {
+    $source = $type->$property;
+    $type = $type->type;
+  }
+  return i18n_string_translate(array('node', 'type', $type, $property), $source, $options);
+}
+
+/**
+ * Translate node type name (unfiltered)
+ */
+function i18n_node_type_name($type, $name) {
+  return i18n_string_translate(array('node', 'type', $type, 'name'), $name);
+}
+
+/**
+ * Check whether this is a language enabled node type
+ *
+ * @param $type
+ *   Node, node type object, or node type name
+ */
+function i18n_node_type_enabled($type) {
+  $type = is_object($type) ? $type->type : $type;
+  $mode = variable_get('language_content_type_' . $type, 0);
+  return $mode == 1 || $mode == 2; // 2 == TRANSLATION_ENABLED
+}
diff --git a/kolab.org/www/drupal-7.18/sites/all/modules/i18n/i18n_node/i18n_node.pages.inc b/kolab.org/www/drupal-7.18/sites/all/modules/i18n/i18n_node/i18n_node.pages.inc
new file mode 100644
index 0000000..34cbe4b
--- /dev/null
+++ b/kolab.org/www/drupal-7.18/sites/all/modules/i18n/i18n_node/i18n_node.pages.inc
@@ -0,0 +1,356 @@
+<?php
+
+/**
+ * @file
+ * User page callbacks for the translation module.
+ */
+
+
+/**
+ * Replacement for node_add_page.
+ */
+function i18n_node_add_page() {
+  $item = menu_get_item();
+  $content = system_admin_menu_block($item);
+  // Bypass the node/add listing if only one content type is available.
+  if (count($content) == 1) {
+    $item = array_shift($content);
+    drupal_goto($item['href']);
+  }
+  foreach ($content as &$item) {
+    // Type machine name will be the first page argument
+    $page_arguments = unserialize($item['page_arguments']);
+    // Check whether this has a node type, other items may be here too, see #1264662
+    $type = isset($page_arguments[0]) ? $page_arguments[0] : NULL;
+    if ($type) {
+      // We just need to translate the description, the title is translated by the menu system
+      // The string will be filtered (xss_admin) on the theme layer
+      $item['description'] = i18n_node_translate_type($type, 'description', $item['description']);
+    }
+  }
+  return theme('node_add_list', array('content' => $content));
+}
+
+/**
+ * Overview page for a node's translations.
+ *
+ * @param $node
+ *   Node object.
+ */
+function i18n_node_translation_overview($node) {
+  include_once DRUPAL_ROOT . '/includes/language.inc';
+
+  if (!empty($node->tnid)) {
+    // Already part of a set, grab that set.
+    $tnid = $node->tnid;
+    $translations = translation_node_get_translations($node->tnid);
+  }
+  else {
+    // We have no translation source nid, this could be a new set, emulate that.
+    $tnid = $node->nid;
+    $translations = array($node->language => $node);
+  }
+
+  $type = variable_get('translation_language_type', LANGUAGE_TYPE_INTERFACE);
+  $header = array(t('Language'), t('Title'), t('Status'), t('Operations'));
+
+  // Modes have different allowed languages
+  foreach (i18n_node_language_list($node) as $langcode => $language_name) {
+    if ($langcode == LANGUAGE_NONE) {
+      // Never show language neutral on the overview.
+      continue;
+    }
+    $options = array();
+    if (isset($translations[$langcode])) {
+      // Existing translation in the translation set: display status.
+      // We load the full node to check whether the user can edit it.
+      $translation_node = node_load($translations[$langcode]->nid);
+      $path = 'node/' . $translation_node->nid;
+      $title = i18n_node_translation_link($translation_node->title, $path, $langcode);
+      if (node_access('update', $translation_node)) {
+        $text = t('edit');
+        $path = 'node/' . $translation_node->nid . '/edit';
+        $options[] = i18n_node_translation_link($text, $path, $langcode);
+      }
+      $status = $translation_node->status ? t('Published') : t('Not published');
+      $status .= $translation_node->translate ? ' - <span class="marker">' . t('outdated') . '</span>' : '';
+      if ($translation_node->nid == $tnid) {
+        $language_name = t('<strong>@language_name</strong> (source)', array('@language_name' => $language_name));
+      }
+    }
+    else {
+      // No such translation in the set yet: help user to create it.
+      $title = t('n/a');
+      if (node_access('create', $node->type)) {
+        $text = t('add translation');
+        $path = 'node/add/' . str_replace('_', '-', $node->type);
+        $query = array('query' => array('translation' => $node->nid, 'target' => $langcode));
+        $options[] = i18n_node_translation_link($text, $path, $langcode, $query);
+      }
+      $status = t('Not translated');
+    }
+    $rows[] = array($language_name, $title, $status, implode(" | ", $options));
+  }
+
+  drupal_set_title(t('Translations of %title', array('%title' => $node->title)), PASS_THROUGH);
+
+  $build['translation_node_overview'] = array(
+    '#theme' => 'table',
+    '#header' => $header,
+    '#rows' => $rows,
+  );
+
+  if (user_access('administer content translations')) {
+    $build['translation_node_select'] = drupal_get_form('i18n_node_select_translation', $node, $translations);
+  }
+  return $build;
+}
+
+/**
+ * Create link for node translation. This may be a 'edit' or a 'add translation' link.
+ */
+function i18n_node_translation_link($text, $path, $langcode, $options = array()) {
+  $type = variable_get('translation_language_type', LANGUAGE_TYPE_INTERFACE);
+  $links = language_negotiation_get_switch_links($type, $path);
+  // When node not published, links don't have href so we use path instead
+  // Note: this is a bug in Core translation module, see http://drupal.org/node/1137074
+  if (!empty($links->links[$langcode]) && !empty($links->links[$langcode]['href'])) {
+    $options += array('attributes' => array(), 'html' => FALSE);
+    $options['attributes'] += $links->links[$langcode]['attributes'];
+    $options += $links->links[$langcode];
+    $path = $links->links[$langcode]['href'];
+  }
+  return l($text, $path, $options);
+}
+
+/**
+ * Form to select existing nodes as translation
+ *
+ * This one uses autocomplete fields for all languages
+ */
+function i18n_node_select_translation($form, &$form_state, $node, $translations) {
+  $form['node'] = array('#type' => 'value', '#value' => $node);
+  $form['translations'] = array(
+    '#type' => 'fieldset',
+    '#title' => t('Select translations for %title', array('%title' => $node->title)),
+    '#tree' => TRUE,
+    '#theme' => 'i18n_node_select_translation',
+    '#description' => t("Alternatively, you can select existing nodes as translations of this one or remove nodes from this translation set. Only nodes that have the right language and don't belong to other translation set will be available here.")
+  );
+  foreach (i18n_node_language_list($node) as $langcode => $language_name) {
+    if ($langcode != $node->language && $langcode != LANGUAGE_NONE) {
+      $nid = isset($translations[$langcode]) ? $translations[$langcode]->nid : 0;
+      $form['translations']['nid'][$langcode] = array(
+        '#type' => 'value',
+        '#value' => $nid,
+      );
+      $form['translations']['language'][$langcode] = array(
+        '#type' => 'value',
+        '#value' => $language_name,
+      );
+      $form['translations']['node'][$langcode] = array(
+        '#type' => 'textfield',
+        '#maxlength' => 255,
+        '#autocomplete_path' => 'i18n/node/autocomplete/' . $node->type . '/' . $langcode,
+        '#default_value' => $nid ? i18n_node_nid2autocomplete($nid) : '',
+      );
+    }
+  }
+  $form['actions'] = array('#type' => 'actions');
+  $form['actions']['update'] = array(
+    '#type' => 'submit',
+    '#value' => t('Update translations'),
+  );
+  //$form['buttons']['clean'] = array('#type' => 'submit', '#value' => t('Delete translation set'));
+  return $form;
+}
+
+/**
+ * Form validation
+ */
+function i18n_node_select_translation_validate($form, &$form_state) {
+  foreach ($form_state['values']['translations']['node'] as $lang => $title) {
+    if (!$title) {
+      $nid = 0;
+    }
+    else {
+      $nid = i18n_node_autocomplete2nid($title, "translations][node][$lang", array($form_state['values']['node']->type), array($lang));
+    }
+    $form_state['values']['translations']['nid'][$lang] = $nid;
+  }
+}
+
+/**
+ * Form submission: update / delete the translation set
+ */
+function i18n_node_select_translation_submit($form, &$form_state) {
+  $op = isset($form_state['values']['op']) ? $form_state['values']['op'] : NULL;
+  $node = $form_state['values']['node'];
+  $translations = $node->tnid ? translation_node_get_translations($node->tnid) : array($node->language => $node);
+  foreach ($translations as $trans) {
+    $current[$trans->language] = $trans->nid;
+  }
+  $update = array($node->language => $node->nid) + array_filter($form_state['values']['translations']['nid']);
+  // Compute the difference to see which are the new translations and which ones to remove
+  $new = array_diff_assoc($update, $current);
+  $remove = array_diff_assoc($current, $update);
+
+  // The tricky part: If the existing source is not in the new set, we need to create a new tnid
+  if ($node->tnid && in_array($node->tnid, $update)) {
+    $tnid = $node->tnid;
+    $add = $new;
+  }
+  else {
+    // Create new tnid, which is the source node
+    $tnid = $node->nid;
+    $add = $update;
+  }
+  // Now update values for all nodes
+  if ($add) {
+    db_update('node')
+      ->fields(array(
+        'tnid' => $tnid,
+      ))
+      ->condition('nid', $add)
+      ->execute();
+    if (count($new)) {
+      drupal_set_message(format_plural(count($new), 'Added a node to the translation set.', 'Added @count nodes to the translation set.'));
+    }
+  }
+  if ($remove) {
+    db_update('node')
+      ->fields(array(
+        'tnid' => 0,
+      ))
+      ->condition('nid', $remove)
+      ->execute();
+    drupal_set_message(format_plural(count($remove), 'Removed a node from the translation set.', 'Removed @count nodes from the translation set.'));
+  }
+}
+
+/**
+ * Node title autocomplete callback
+ */
+function i18n_node_autocomplete($type, $language, $string = '') {
+  $params = array('type' => $type, 'language' => $language, 'tnid' => 0);
+  $matches = array();
+  foreach (_i18n_node_references($string, 'contains', $params) as $id => $row) {
+    // Add a class wrapper for a few required CSS overrides.
+    $matches[$row['title'] . " [nid:$id]"] = '<div class="reference-autocomplete">' . $row['rendered'] . '</div>';
+  }
+  drupal_json_output($matches);
+}
+
+/**
+ * Generates 'title [nid:$nid]' for the autocomplete field
+ */
+function i18n_node_nid2autocomplete($nid) {
+  if ($node = node_load($nid)) {
+    return check_plain($node->title) . ' [nid:' . $nid . ']';
+  }
+  else {
+    return t('Not found');
+  }
+}
+
+/**
+ * Reverse mapping from node title to nid
+ *
+ * We also handle autocomplete values (title [nid:x]) and validate the form
+ */
+function i18n_node_autocomplete2nid($name, $field = NULL, $type, $language) {
+  if (!empty($name)) {
+    preg_match('/^(?:\s*|(.*) )?\[\s*nid\s*:\s*(\d+)\s*\]$/', $name, $matches);
+    if (!empty($matches)) {
+      // Explicit [nid:n].
+      list(, $title, $nid) = $matches;
+      if (!empty($title) && ($node = node_load($nid)) && $title != $node->title) {
+        if ($field) {
+          form_set_error($field, t('Node title mismatch. Please check your selection.'));
+        }
+        $nid = NULL;
+      }
+    }
+    else {
+      // No explicit nid.
+      $reference = _i18n_node_references($name, 'equals', array('type' => $type, 'language' => $language), 1);
+      if (!empty($reference)) {
+        $nid = key($reference);
+      }
+      elseif ($field) {
+        form_set_error($field, t('Found no valid post with that title: %title', array('%title' => $name)));
+      }
+    }
+  }
+  return !empty($nid) ? $nid : NULL;
+}
+
+/**
+ * Find node title matches.
+ *
+ * @param $string
+ *   String to match against node title
+ * @param $match
+ *   Match mode: 'contains', 'equals', 'starts_with'
+ * @param $params
+ *   Other query arguments: type, language or numeric ones
+ */
+function _i18n_node_references($string, $match = 'contains', $params = array(), $limit = 10) {
+  $query = db_select('node', 'n')
+    ->fields('n', array('nid', 'title' , 'type'))
+    ->orderBy('n.title')
+    ->orderBy('n.type')
+    ->range(0, $limit);
+
+  foreach ($params as $key => $value) {
+    $query->condition($key, $value);
+  }
+
+  switch ($match) {
+    case 'equals':
+      $query->condition('n.title', $string);
+      break;
+
+    case 'starts_with':
+      $query->condition('n.title', $string . '%', 'LIKE');
+      break;
+
+    case 'contains':
+    default:
+      $query->condition('n.title', '%' . $string . '%', 'LIKE');
+      break;
+  }
+
+  // Disable and reenable i18n selection mode so no language conditions are inserted
+  i18n_select(false);
+  $references = array();
+  foreach ($query->execute() as $node) {
+    $references[$node->nid] = array(
+      'title' => $node->title,
+      'rendered' => check_plain($node->title),
+    );
+  }
+  i18n_select(true);
+  return $references;
+}
+
+/**
+ * Theme select translation form
+ * @ingroup themeable
+ */
+function theme_i18n_node_select_translation($variables) {
+  $elements = $variables['element'];
+  $output = '';
+  if (isset($elements['nid'])) {
+    $rows = array();
+    foreach (element_children($elements['nid']) as $lang) {
+      $rows[] = array(
+        $elements['language'][$lang]['#value'],
+        drupal_render($elements['node'][$lang]),
+      );
+    }
+    $output .= theme('table', array('rows' => $rows));
+    $output .= drupal_render_children($elements);
+  }
+  return $output;
+}
diff --git a/kolab.org/www/drupal-7.18/sites/all/modules/i18n/i18n_node/i18n_node.test b/kolab.org/www/drupal-7.18/sites/all/modules/i18n/i18n_node/i18n_node.test
new file mode 100644
index 0000000..c2f475e
--- /dev/null
+++ b/kolab.org/www/drupal-7.18/sites/all/modules/i18n/i18n_node/i18n_node.test
@@ -0,0 +1,99 @@
+<?php
+
+/**
+ * @file
+ * Contains test cases for the i18n_node module.
+ */
+
+class I18nNodeTestCase extends Drupali18nTestCase {
+  public static function getInfo() {
+    return array(
+      'name' => 'Content translation',
+      'group' => 'Internationalization',
+      'description' => 'Content translation functions',
+    );
+  }
+
+  function setUp() {
+    parent::setUp('translation', 'i18n_node');
+    parent::setUpLanguages(array('administer content translations', 'translate content'));
+    parent::setUpContentTranslation();
+
+    $this->addLanguage('pt-br');
+    // Add a disabled language.
+    $this->addLanguage('it');
+    $edit = array('enabled[it]' => FALSE);
+    $this->drupalPost('admin/config/regional/language', $edit, t('Save configuration'));
+  }
+
+  /**
+   * Tests for adding content to an existing translation set.
+   */
+  function testAddContentToTranslationSet() {
+    module_load_include('inc', 'i18n_node', 'i18n_node.pages');
+
+    // Create 3 nodes in different languages.
+    $en_title = $this->randomName(10);
+    $en_body = $this->randomString(50);
+    $en_node = $this->createNode('page', $en_title, $en_body, 'en');
+
+    $es_title = $this->randomName(10);
+    $es_body = $this->randomString(50);
+    $es_node = $this->createNode('page', $es_title, $es_body, 'es');
+
+    $ptbr_title = $this->randomName(10);
+    $ptbr_body = $this->randomString(50);
+    $ptbr_node = $this->createNode('page', $ptbr_title, $ptbr_body, 'pt-br');
+
+    // Check the autocomplete suggestions.
+    $this->drupalGet('i18n/node/autocomplete/page/es/' . substr($es_title, 0, 3));
+    $this->assertText($es_title);
+    $this->assertNoText($en_title);
+    $this->assertNoText($ptbr_title);
+
+    $this->drupalGet('i18n/node/autocomplete/page/es/' . substr($en_title, 0, 3));
+    $this->assertNoText($es_title);
+    $this->assertNoText($en_title);
+    $this->assertNoText($ptbr_title);
+
+    $this->drupalGet('i18n/node/autocomplete/page/pt-br/' . substr($ptbr_title, 0, 3));
+    $this->assertNoText($es_title);
+    $this->assertNoText($en_title);
+    $this->assertText($ptbr_title);
+
+    // Go to the translations tab.
+    $this->drupalGet('node/' . $en_node->nid);
+    $this->clickLink(t('Translate'));
+
+    // Make sure that the disabled language doesn't show up.
+    $this->assertNoText(t('Italian'));
+
+    // Test validation.
+    $edit = array(
+      'translations[node][es]' => $ptbr_title,
+    );
+    $this->drupalPost(NULL, $edit, t('Update translations'));
+    $this->assertText(t('Found no valid post with that title: @title', array('@title' => $ptbr_title)));
+
+    // Add two translated nodes.
+    $edit = array(
+      'translations[node][pt-br]' => $ptbr_title,
+      'translations[node][es]' => $es_title,
+    );
+    $this->drupalPost(NULL, $edit, t('Update translations'));
+    $this->assertText(t('Added @count nodes to the translation set.', array('@count' => 2)));
+
+    $this->assertFieldByName('translations[node][es]', i18n_node_nid2autocomplete($es_node->nid));
+    $this->assertFieldByName('translations[node][pt-br]', i18n_node_nid2autocomplete($ptbr_node->nid));
+
+    // Remove a translation node again.
+    $edit = array(
+      'translations[node][pt-br]' => '',
+    );
+    $this->drupalPost(NULL, $edit, t('Update translations'));
+    $this->assertText(t('Removed a node from the translation set.'));
+
+    $this->assertFieldByName('translations[node][es]', i18n_node_nid2autocomplete($es_node->nid));
+    $this->assertFieldByName('translations[node][pt-br]', '');
+  }
+}
diff --git a/kolab.org/www/drupal-7.18/sites/all/modules/i18n/i18n_node/i18n_node.variable.inc b/kolab.org/www/drupal-7.18/sites/all/modules/i18n/i18n_node/i18n_node.variable.inc
new file mode 100644
index 0000000..9362fc3
--- /dev/null
+++ b/kolab.org/www/drupal-7.18/sites/all/modules/i18n/i18n_node/i18n_node.variable.inc
@@ -0,0 +1,79 @@
+<?php
+/**
+ * @file
+ * Variable information
+ */
+
+/**
+ * Implements hook_variable_group_info().
+ */
+function i18n_node_variable_group_info() {
+  $groups['i18n_node'] = array(
+    'title' => t('Multilingual node options'),
+    'description' => t('Extended node options for multilingual sites.'),
+    'access' => 'administer site configuration',
+    'path' => 'admin/config/regional/i18n/node',
+  );
+  return $groups;
+}
+
+/**
+ * Implements hook_variable_info().
+ */
+function i18n_node_variable_info($options = array()) {
+  $variables['i18n_hide_translation_links'] = array(
+    'type' => 'boolean',
+    'title' => t('Hide content translation links', array(), $options),
+    'description' => t('Hide the links to translations in content body and teasers. If you choose this option, switching language will only be available from the language switcher block.', array(), $options),
+    'default' => 0,
+    'group' => 'i18n_node',
+  );
+  $variables['i18n_node_default_language_none'] = array(
+    'title' => t('Default language for content types with Multilingual support disabled.', array(), $options),
+    'description' => t('Determines which language will be set for newly created content of types that don\'t have Multilingual support enabled.', array(), $options),
+    'type' => 'select',
+    'options' => array(
+      0 => t('The site\'s default language (Default behaviour).', array(), $options),
+      1 => t('Language neutral (Recommended).', array(), $options)
+    ),
+    'default' => 0,
+    'group' => 'i18n_node',
+  );
+  $variables['i18n_node_options_[node_type]'] = array(
+    'type' => 'multiple',
+    'title' => t('Extended language options', array(), $options),
+    'repeat' => array(
+      'type' => 'options',
+      'options' => array(
+        'current' => t('Set current language as default for new content.', array(), $options),
+        'required' => t('Require language (Do not allow Language Neutral).', array(), $options),
+        'lock' => t('Lock language (Cannot be changed).', array(), $options),
+      ),
+    ),
+    'group' => 'i18n',
+  );
+  $variables['i18n_node_extended_[node_type]'] = array(
+    'type' => 'multiple',
+    'title' => t('Extended language support'),
+    'repeat' => array(
+      'type' => 'select',
+      'options callback' => 'i18n_node_variable_extended_options',
+      'default' => I18N_LANGUAGE_ENABLED,
+    ),
+    'description' => t('If enabled, all defined languages will be allowed for this content type in addition to only enabled ones. This is useful to have more languages for content than for the interface.', array(), $options),
+    'group' => 'i18n',
+  );
+
+  return $variables;
+}
+
+/**
+ * Options callback for i18n_node_extended_
+ */
+function i18n_node_variable_extended_options($variable, $options) {
+  return array(
+    I18N_LANGUAGE_ENABLED => t('Normal - All enabled languages will be allowed.', array(), $options),
+    I18N_LANGUAGE_EXTENDED => t('Extended - All defined languages will be allowed.', array(), $options),
+    I18N_LANGUAGE_EXTENDED | I18N_LANGUAGE_HIDDEN => t('Extended, but not displayed - All defined languages will be allowed for input, but not displayed in links.', array(), $options),
+  );
+}
diff --git a/kolab.org/www/drupal-7.18/sites/all/modules/i18n/i18n_object.inc b/kolab.org/www/drupal-7.18/sites/all/modules/i18n/i18n_object.inc
new file mode 100644
index 0000000..2903f71
--- /dev/null
+++ b/kolab.org/www/drupal-7.18/sites/all/modules/i18n/i18n_object.inc
@@ -0,0 +1,248 @@
+<?php
+/**
+ * @file
+ * i18n Object Class
+ */
+
+/**
+ * Object wrapper
+ */
+class i18n_object_wrapper {
+  protected $type;
+  protected $key;
+  protected $object;
+  // Object translations, static cache.
+  protected $translations;
+
+  /**
+   * Class constructor
+   */
+  public function __construct($type, $key, $object) {
+    $this->type = $type;
+    $this->key = $key;
+    $this->object = $object ? $object : $this->load_object($key);
+  }
+
+  /**
+   * Get edit path for object
+   */
+  function get_edit_path() {
+    return $this->path_replace($this->get_info('edit path'));
+  }
+
+  /**
+   * Get field value from object/array
+   */
+  function get_field($field, $default = NULL) {
+    return i18n_object_field($this->object, $field, $default);
+  }
+
+  /**
+   * Set field value to object/array
+   */
+  function set_field($field, $value) {
+    if (is_object($this->object)) {
+      $this->object->$field = $value;
+    }
+    elseif (is_array($this->object)) {
+      $this->object[$field] = $value;
+    }
+    return $this;
+  }
+
+  /**
+   * Get string numeric key for indexing.
+   */
+  function get_index() {
+    $key = $this->get_key();
+    return is_array($key) ? implode(':', $key) : $key;
+  }
+
+  /**
+   * Get key value from object/array
+   */
+  function get_key($default = NULL) {
+    if ($field = $this->get_info('key')) {
+      return $this->get_field($field, $default);
+    }
+    else {
+      return $default;
+    }
+  }
+
+  /**
+   * Get language code
+   */
+  public function get_langcode() {
+    return i18n_object_langcode($this->object);
+  }
+
+  /**
+   * Get real object or array.
+   */
+  public function get_object() {
+    return $this->object;
+  }
+
+  /**
+   * Load real object or array.
+   *
+   * @param $object
+   */
+  function load_object($object) {
+    if ($callback = $this->get_info('load callback', NULL)) {
+      $this->object = call_user_func($callback, $object);
+    }
+    elseif ($entity_type = $this->get_info('entity', NULL)) {
+      $entity = entity_load($entity_type, array($object));
+      $this->object = $entity ? reset($entity) : FALSE;
+    }
+
+    return $this->get_object();
+  }
+
+  /**
+   * Get menu placehoders for object
+   */
+  protected function get_placeholders() {
+    $placeholders = $this->get_info('placeholders', array());
+    foreach ($placeholders as $name => $field) {
+      $placeholders[$name] = $this->get_field($field);
+    }
+    return $placeholders;
+  }
+
+  /**
+   * Get link for item
+   */
+  public function get_path() {
+    if ($uri = entity_uri($this->type, $this->object)) {
+      return $uri['path'];
+    }
+  }
+
+  /**
+   * Get title from item
+   */
+  public function get_title() {
+    return entity_label($this->type, $this->object);
+  }
+  /**
+   * Get object type
+   */
+  public function get_type() {
+    return $this->type;
+  }
+
+  /**
+   * Menu access callback for mixed translation tab
+   */
+  function get_translate_access() {
+    switch ($this->get_translate_mode()) {
+      case I18N_MODE_TRANSLATE:
+        return $this->translate_access();
+      case I18N_MODE_LOCALIZE:
+        return $this->localize_access();
+      default:
+        return FALSE;
+    }
+  }
+
+  /**
+   * Get translate or localize mode for object
+   */
+  function get_translate_mode() {
+    return I18N_MODE_NONE;
+  }
+
+  /**
+   * Get translation set id if any
+   */
+  function get_tsid() {
+    return $this->get_field($this->get_translation_info('field', 'i18n_tsid'));
+  }
+
+  /**
+   * Set translation set id
+   */
+  function set_tsid($tsid) {
+    return $this->set_field($this->get_translation_info('field', 'i18n_tsid'), $tsid);
+  }
+
+  /**
+   * Localize object if localizable.
+   */
+  function localize($langcode, $options = array()) {
+    if ($this->get_translate_mode() == I18N_MODE_LOCALIZE) {
+      return $this->translate($langcode, $options);
+    }
+    else {
+      return $this->object;
+    }
+  }
+
+  /**
+   * Translate object if translatable.
+   */
+  function translate($langcode, $options = array()) {
+    if (isset($this->translations[$langcode])) {
+      return $this->translations[$langcode];
+    }
+    else {
+      return $this->object;
+    }
+  }
+
+  /**
+   * Translate access (translation sets)
+   */
+  protected function translate_access() {
+    return FALSE;
+  }
+
+  /**
+   * Translate access (localize strings)
+   */
+  protected function localize_access() {
+    return FALSE;
+  }
+
+  /**
+   * Replace path with placeholders
+   *
+   * @param $path
+   *   Path to replace
+   * @param $replacements
+   *   Replacement variables to override or add to placeholders
+   */
+  protected function path_replace($path, $replacements = array()) {
+    if ($path) {
+      $path = strtr($path, $replacements + $this->get_placeholders());
+      // Clean up duplicated and final '/' (empty placeholders)
+      $path = strtr($path, array('//' => '/'));
+      return trim($path, '/');
+    }
+    else {
+      return '';
+    }
+  }
+  /**
+   * Get object info
+   */
+  public function get_info($property, $default = NULL) {
+    return i18n_object_info($this->type, $property, $default);
+  }
+  /**
+   * Get object translation set info
+   */
+  public function get_translation_info($property, $default = NULL) {
+    return function_exists('i18n_translation_set_info') ? i18n_translation_set_info($this->type, $property, $default) : $default;
+  }
+  /**
+   * Get object string translation info
+   */
+  public function get_string_info($property, $default = NULL) {
+    $info = $this->get_info('string translation');
+    return $info && isset($info[$property]) ? $info[$property] : $default;
+  }
+}
diff --git a/kolab.org/www/drupal-7.18/sites/all/modules/i18n/i18n_path/README.txt b/kolab.org/www/drupal-7.18/sites/all/modules/i18n/i18n_path/README.txt
new file mode 100644
index 0000000..3470cb1
--- /dev/null
+++ b/kolab.org/www/drupal-7.18/sites/all/modules/i18n/i18n_path/README.txt
@@ -0,0 +1,25 @@
+
+README.txt
+==========
+Drupal module: Path translation
+==================================
+
+This module provides some basic path translation feature for generic paths.
+
+For paths belonging to objects that have translations, like nodes and taxonomy terms, the system can produce automatic
+links for the language switcher.
+
+For the rest of paths, this module allows to define which path is translation of which. Example:
+
+1. We define a new 'path translation set' like
+   - English: node/1
+   - Spanish: taxonomy/term/3
+
+2. Every time we are on any of these pages, the language switcher will point to the other path for each language.
+
+This module is intended for translation of generic paths that don't have other way of being translated.
+
+Note: path translations must be defined without aliases.
+
+====================================================================
+Jose A. Reyero, http://reyero.net
\ No newline at end of file
diff --git a/kolab.org/www/drupal-7.18/sites/all/modules/i18n/i18n_path/i18n_path.admin.inc b/kolab.org/www/drupal-7.18/sites/all/modules/i18n/i18n_path/i18n_path.admin.inc
new file mode 100644
index 0000000..e2d965b
--- /dev/null
+++ b/kolab.org/www/drupal-7.18/sites/all/modules/i18n/i18n_path/i18n_path.admin.inc
@@ -0,0 +1,150 @@
+<?php
+/**
+ * @file
+ * Administration pages for path translation.
+ */
+
+/**
+ * Path overview page
+ */
+function i18n_path_admin_overview($type = NULL) {
+  module_load_include('admin.inc', 'i18n_translation');
+  return i18n_translation_admin_overview('path');
+}
+
+/**
+ * Path add/edit form
+ */
+function i18n_path_admin_form($form, $form_state, $translation_set = NULL) {
+  $form['translation_set'] = array('#type' => 'value', '#value' => $translation_set);
+  if ($translation_set) {
+    $paths = $translation_set->get_translations();
+  }
+  else {
+    $paths = array();
+  }
+  $form['title'] = array(
+    '#title' => t('Title'),
+    '#type' => 'textfield',
+    '#default_value' => $translation_set ? $translation_set->title : '',
+    '#description' => t('Optional descriptive name for this set.'),
+  );
+  $form['translations'] = array(
+    '#type' => 'fieldset',
+    '#title' => t('Translations'),
+    '#tree' => TRUE,
+    '#description' => t('The path for this menu link. This can be an internal Drupal path such as %add-node or an external URL such as %drupal. Enter %front to link to the front page.', array('%front' => '<front>', '%add-node' => 'node/add', '%drupal' => 'http://drupal.org')),
+  );
+  foreach (i18n_language_list() as $langcode => $name) {
+    $form['translations'][$langcode] = array(
+      '#type' => 'textfield',
+      '#title' => check_plain($name),
+      '#default_value' => !empty($paths[$langcode]) ? $paths[$langcode]->path : '',
+    );
+  }
+
+  $form['controls']['update'] = array(
+    '#type' => 'submit',
+    '#value' => t('Save'),
+    '#name' => 'save',
+  );
+
+  if ($translation_set) {
+    $form['controls']['delete'] = array(
+      '#type' => 'submit',
+      '#value' => t('Delete'),
+      '#name' => 'delete',
+    );
+  }
+  return $form;
+}
+
+/**
+ * Process form validation
+ */
+function i18n_path_admin_form_validate($form, &$form_state)  {
+  if ($form_state['triggering_element']['#name'] == 'save') {
+    $paths = &$form_state['values']['translations'];
+    if ($paths = array_filter($paths)) {
+      module_load_include('inc', 'menu', 'menu.admin');
+      foreach ($paths as $language => &$link_path) {
+        $link_path = i18n_prepare_normal_path($link_path, $language);
+        $validation_form_state = array(
+          'values' => array(
+            'link_path' => $link_path,
+          ),
+        );
+        menu_edit_item_validate(array(), $validation_form_state);
+      }
+    }
+    else {
+      form_set_error('paths', t('There are no path translations to save.'));
+    }
+  }
+}
+
+/**
+ * Process form submission
+ */
+function i18n_path_admin_form_submit($form, &$form_state) {
+  $translation_set = $form_state['values']['translation_set'];
+  switch ($form_state['triggering_element']['#name']) {
+    case 'save':
+      $paths = array_filter($form_state['values']['translations']);
+      $translation_set = $translation_set ? $translation_set : i18n_translation_set_create('path');
+      $translation_set->title = '';
+      $translations = $translation_set->get_translations();
+      foreach ($paths as $lang => $path) {
+        if (isset($translations[$lang])) {
+          $translations[$lang]->path = $path;
+        }
+        else {
+          $translation_set->add_item($path, $lang);
+        }
+      }
+
+      foreach (array_diff(array_keys($translation_set->get_translations()), array_keys($paths)) as $language) {
+        $translation_set->remove_language($language);
+        unset($translations[$language]);
+      }
+
+      if (!empty($form_state['values']['title'])) {
+        $translation_set->title = $form_state['values']['title'];
+      }
+
+      $translation_set->save(TRUE);
+      drupal_set_message(t('The path translation has been saved.'));
+      break;
+    case 'delete':
+      $destination = array();
+      if (isset($_GET['destination'])) {
+        $destination = drupal_get_destination();
+        unset($_GET['destination']);
+      }
+      $form_state['redirect'] = array($translation_set->get_delete_path(), array('query' => $destination));
+      return;
+  }
+  $form_state['redirect'] = 'admin/config/regional/i18n_translation/path';
+}
+
+/**
+ * Save path translation set.
+ */
+function i18n_path_save_translations($paths, $tpid = NULL) {
+  $paths = array_filter($paths);
+  if (lock_acquire('i18n_path')) {
+    if ($tpid) {
+      db_delete('i18n_path')->condition('tpid', $tpid)->execute();
+    }
+    else {
+      $tpid = 1 + (int)db_query('SELECT MAX(tpid) FROM {i18n_path}')->fetchField();
+    }
+    foreach ($paths as $langcode => $path) {
+      db_insert('i18n_path')
+        ->fields(array('tpid' => $tpid, 'language' => $langcode, 'path' => $path))
+        ->execute();
+    }
+    lock_release('i18n_path');
+    return $tpid;
+  }
+}
diff --git a/kolab.org/www/drupal-7.18/sites/all/modules/i18n/i18n_path/i18n_path.inc b/kolab.org/www/drupal-7.18/sites/all/modules/i18n/i18n_path/i18n_path.inc
new file mode 100644
index 0000000..d4847db
--- /dev/null
+++ b/kolab.org/www/drupal-7.18/sites/all/modules/i18n/i18n_path/i18n_path.inc
@@ -0,0 +1,72 @@
+<?php
+/**
+ * @file
+ * Internationalization (i18n) module - Translation set
+ */
+class i18n_path_translation_set extends i18n_translation_set {
+  /**
+   * Add translation item
+   */
+  public function add_item($path, $langcode = NULL) {
+    // Path may be object or plain string
+    $item = is_object($path) ? $path : (object)array('path' => $path, 'language' => $langcode);
+    return parent::add_item($item, $langcode);
+  }
+  /**
+   * Clean path translations.
+   *
+   * Unlike other translation sets this actually deletes paths
+   */
+  public function clean_translations() {
+    $delete = db_delete('i18n_path')
+      ->condition('tsid', $this->tsid)
+      ->condition('language', array_keys($this->get_translations()), 'NOT IN')
+      ->execute();
+  }
+  /**
+   * Delete translation set
+   */
+  public function delete_translations() {
+    return db_delete('i18n_path')
+      ->condition('tsid', $this->tsid)
+      ->execute();
+  }
+  /**
+   * Save all path translations
+   */
+  public function save_translations() {
+    foreach ($this->get_translations() as $lang => $path) {
+      $path = is_object($path) ? $path : (object) array('path' => $path, 'language' => $lang, 'tsid' => $this->tsid);
+      drupal_write_record('i18n_path', $path, !empty($path->tpid) ? 'tpid' : array());
+      $this->add_item($path, $lang);
+    }
+  }
+
+}
+
+/**
+ * Path object
+ */
+class i18n_path_object extends i18n_object_wrapper {
+  /**
+   * Get title from item
+   */
+  public function get_title() {
+    return $this->object->path;
+  }
+
+  /**
+   * Get path for item
+   */
+  public function get_path() {
+    return check_url($this->object->path);
+  }
+
+  /**
+   * Get translate mode
+   */
+  public function get_translate_mode() {
+    return I18N_MODE_TRANSLATE;
+  }
+
+}
\ No newline at end of file
diff --git a/kolab.org/www/drupal-7.18/sites/all/modules/i18n/i18n_path/i18n_path.info b/kolab.org/www/drupal-7.18/sites/all/modules/i18n/i18n_path/i18n_path.info
new file mode 100644
index 0000000..33c0635
--- /dev/null
+++ b/kolab.org/www/drupal-7.18/sites/all/modules/i18n/i18n_path/i18n_path.info
@@ -0,0 +1,14 @@
+name = Path translation
+description = Define translations for generic paths
+dependencies[] = i18n_translation
+package = Multilingual - Internationalization
+core = 7.x
+
+files[] = i18n_path.inc
+files[] = i18n_path.test
+; Information added by drupal.org packaging script on 2013-01-13
+version = "7.x-1.8"
+core = "7.x"
+project = "i18n"
+datestamp = "1358075001"
+
diff --git a/kolab.org/www/drupal-7.18/sites/all/modules/i18n/i18n_path/i18n_path.install b/kolab.org/www/drupal-7.18/sites/all/modules/i18n/i18n_path/i18n_path.install
new file mode 100644
index 0000000..9e0950f
--- /dev/null
+++ b/kolab.org/www/drupal-7.18/sites/all/modules/i18n/i18n_path/i18n_path.install
@@ -0,0 +1,92 @@
+<?php
+
+/**
+ * @file
+ * Install, update and uninstall functions for the text module.
+ */
+
+/**
+ * Implements hook_install().
+ */
+function i18n_path_install() {
+  // Set module weight for it to run after core modules, but before views.
+  db_update('system')
+    ->fields(array('weight' => 5))
+    ->condition('name', 'i18n_path', '=')
+    ->condition('type', 'module', '=')
+    ->execute();
+}
+
+/**
+ * Implements hook_schema().
+ */
+function i18n_path_schema() {
+  $schema['i18n_path'] = array(
+    'description' => 'Path translation',
+    'fields' => array(
+      'tpid' => array(
+        'description' => 'The primary identifier for a path in the translation set.',
+        'type' => 'serial',
+        'unsigned' => TRUE,
+        'not null' => TRUE,
+      ),
+      'tsid' => array(
+        'description' => 'The primary identifier for a translation set.',
+        'type' => 'int',
+        'unsigned' => TRUE,
+        'not null' => TRUE,
+      ),
+      'path' => array(
+        'description' => 'The Drupal path this alias is for; e.g. node/12.',
+        'type' => 'varchar',
+        'length' => 255,
+        'not null' => TRUE,
+        'default' => '',
+      ),
+      'language' => array(
+        'description' => "The language for which this path is a translation.",
+        'type' => 'varchar',
+        'length' => 12,
+        'not null' => TRUE,
+        'default' => '',
+      ),
+      'pid' => array(
+        'description' => 'A unique path alias identifier if the path has an alias.',
+        'type' => 'int',
+        'unsigned' => TRUE,
+        'not null' => TRUE,
+        'default' => 0,
+      ),
+    ),
+    'indexes' => array(
+      'path' => array('path'),
+    ),
+    'unique keys' => array(
+      'set_language' => array('tsid', 'language'),
+    ),
+    'foreign keys' => array(
+      'path_language' => array(
+        'table' => 'languages',
+        'columns' => array('language' => 'language'),
+      ),
+      'translation_set' => array(
+        'table' => 'i18n_translation',
+        'columns' => array('tsid' => 'tsid'),
+      ),
+    ),
+    'primary key' => array('tpid'),
+  );
+  return $schema;
+}
+
+/**
+ * Set module weight.
+ */
+function i18n_path_update_7000(&$sandbox) {
+  // Set module weight for it to run after core modules, but before views.
+  db_update('system')
+    ->fields(array('weight' => 5))
+    ->condition('name', 'i18n_path', '=')
+    ->condition('type', 'module', '=')
+    ->execute();
+}
diff --git a/kolab.org/www/drupal-7.18/sites/all/modules/i18n/i18n_path/i18n_path.module b/kolab.org/www/drupal-7.18/sites/all/modules/i18n/i18n_path/i18n_path.module
new file mode 100644
index 0000000..123b826
--- /dev/null
+++ b/kolab.org/www/drupal-7.18/sites/all/modules/i18n/i18n_path/i18n_path.module
@@ -0,0 +1,141 @@
+<?php
+
+/**
+ * @file
+ * Internationalization (i18n) module - Path translation
+ */
+
+/**
+ * Implements hook_menu()
+ */
+function i18n_path_menu() {
+  $items['admin/config/regional/i18n_translation/path'] = array(
+    'title' => 'Paths',
+    'description' => 'Path translation.',
+    'page callback' => 'i18n_path_admin_overview',
+    'access arguments' => array('administer site configuration'),
+    'file' => 'i18n_path.admin.inc',
+    'type' => MENU_LOCAL_TASK,
+    'weight' => 10,
+  );
+  $items['admin/config/regional/i18n_translation/path/list'] = array(
+    'title' => 'Paths',
+    'type' => MENU_DEFAULT_LOCAL_TASK,
+    'weight' => -10,
+  );
+  $items['admin/config/regional/i18n_translation/path/add'] = array(
+    'title' => 'Add path translation',
+    'page callback' => 'drupal_get_form',
+    'page arguments' => array('i18n_path_admin_form'),
+    'access arguments' => array('administer site configuration'),
+    'file' => 'i18n_path.admin.inc',
+    'type' => MENU_LOCAL_ACTION,
+    'parent' => 'admin/config/regional/i18n_translation',
+  );
+  $items['admin/config/regional/i18n_translation/path/edit/%i18n_path_translation_set'] = array(
+    'title' => 'Edit path translation',
+    'page callback' => 'drupal_get_form',
+    'page arguments' => array('i18n_path_admin_form', 6),
+    'access arguments' => array('administer site configuration'),
+    'file' => 'i18n_path.admin.inc',
+    'type' => MENU_LOCAL_TASK,
+    'context' => MENU_CONTEXT_INLINE,
+  );
+  $items['admin/config/regional/i18n_translation/path/delete/%i18n_path_translation_set'] = array(
+    'title' => 'Delete path translation',
+    'page callback' => 'drupal_get_form',
+    'page arguments' => array('i18n_translation_set_delete_confirm', 6),
+    'access arguments' => array('administer site configuration'),
+    'file' => 'i18n_path.admin.inc',
+    'type' => MENU_LOCAL_TASK,
+    'context' => MENU_CONTEXT_INLINE,
+  );
+  return $items;
+}
+
+/**
+ * Implements hook_url_outbound_alter()
+ */
+/*
+function i18n_path_url_outbound_alter(&$path, &$options, $original_path) {
+  if (!empty($options['language'])) {
+    $langcode = $options['language']->language;
+    $original = $options['alias'] ? drupal_get_normal_path($path, $langcode) : $original_path;
+    if (($translations = i18n_path_get_translations($path)) && !empty($translations[$langcode])) {
+      $path = $options['alias'] ? drupal_get_path_alias($translations[$langcode], $langcode) : $translations[$langcode];
+    }
+  }
+}
+*/
+
+/**
+ * Get translations for path
+ */
+function i18n_path_get_translations($path) {
+  static $translations;
+
+  if (!isset($translations)) {
+    $translations = drupal_static(__FUNCTION__, array());
+  }
+  if (!isset($translations[$path])) {
+    $translations[$path] = db_query('SELECT p.language, p.path FROM {i18n_path} p INNER JOIN {i18n_path} ps ON p.tsid = ps.tsid WHERE ps.path = :path',
+      array(':path' => $path)
+    )->fetchAllKeyed();
+  }
+  return $translations[$path];
+}
+
+
+/**
+ * Implements hook_i18n_object_info().
+ */
+function i18n_path_i18n_object_info() {
+  return array(
+    'path' => array(
+      'title' => t('Path'),
+      'class' => 'i18n_path_object',
+      'key' => array('path', 'language'),
+      'translation set' => TRUE,
+    )
+  );
+}
+
+/**
+ * Implements hook_i18n_translation_set_info()
+ */
+function i18n_path_i18n_translation_set_info() {
+  return array(
+    'path' => array(
+      'title' => t('Path'),
+      'class' => 'i18n_path_translation_set',
+      'table' => 'i18n_path',
+      'field' => 'tsid',
+      'placeholder' => '%i18n_path_translation_set',
+      'edit path' => 'admin/config/regional/i18n_translation/path/edit/%i18n_path_translation_set',
+      'delete path' => 'admin/config/regional/i18n_translation/path/delete/%i18n_path_translation_set',
+      'list path' => 'admin/config/regional/i18n_translation/path',
+    ),
+  );  
+}
+
+/**
+ * Implements hook_i18n_translate_path()
+ */
+function i18n_path_i18n_translate_path($path) {
+  if ($translations = i18n_path_get_translations($path)) {
+    $result = array();
+    foreach ($translations as $langcode => $translated) {
+      $result[$langcode] = array(
+        'href' => $translated,
+      );
+    }
+    return $result;
+  }
+}
+
+/**
+ * Load translation set. Menu loading callback.
+ */
+function i18n_path_translation_set_load($tsid) {
+  return i18n_translation_set_load($tsid, 'path');
+}
diff --git a/kolab.org/www/drupal-7.18/sites/all/modules/i18n/i18n_path/i18n_path.test b/kolab.org/www/drupal-7.18/sites/all/modules/i18n/i18n_path/i18n_path.test
new file mode 100644
index 0000000..68c03bc
--- /dev/null
+++ b/kolab.org/www/drupal-7.18/sites/all/modules/i18n/i18n_path/i18n_path.test
@@ -0,0 +1,88 @@
+<?php
+
+/**
+ * @file
+ * Test case for multilingual menus.
+ */
+class i18nPathTestCase extends Drupali18nTestCase {
+  public static function getInfo() {
+    return array(
+      'name' => 'Path translation',
+      'group' => 'Internationalization',
+      'description' => 'Path translation functions',
+    );
+  }
+
+  function setUp() {
+    parent::setUp('translation', 'i18n_path');
+    parent::setUpLanguages(array('administer site configuration'));
+  }
+
+  function checkTranslationLink($path, $language, $method = 'assertRaw') {
+    $this->{$method}($path, t('Found translation link. :language - :path', array(':language' => $language, ':path' => $path)));
+  }
+
+  function testPathTranslation() {
+    $this->setUpContentType(array('type' => 'page', 'mode' => TRANSLATION_ENABLED));
+
+    // Create 2 nodes in different languages.
+    $first_title = $this->randomName(10);
+    $first_body = $this->randomString(50);
+    $first_node = $this->createNode('page', $first_title, $first_body, $this->default_language);
+
+    $secondary_title = $this->randomName(10);
+    $secondary_body = $this->randomString(50);
+    $secondary_node = $this->createNode('page', $secondary_title, $secondary_body, $this->secondary_language);
+
+    $this->drupalGet('node/' . $first_node->nid);
+    $this->checkTranslationLink('node/' . $first_node->nid, $first_node->language);
+    $this->checkTranslationLink($this->secondary_language . '/node/' . $first_node->nid, $this->secondary_language, 'assertNoRaw');
+
+    $this->drupalGet('node/' . $secondary_node->nid);
+    $this->checkTranslationLink('node/' . $secondary_node->nid, $secondary_node->language);
+    $this->checkTranslationLink($this->secondary_language . '/node/' . $secondary_node->nid, $this->secondary_language);
+
+    $this->drupalGet('admin/config/regional/i18n_translation/path');
+    $this->clickLink(t('Add path translation'));
+
+    // create new translation set with two node links
+    $edit = array(
+      'title' => $this->randomName(10),
+      'translations[' . $this->default_language . ']' => 'node/' . $first_node->nid,
+      'translations[' . $this->secondary_language . ']' => 'node/' . $secondary_node->nid,
+    );
+    $this->drupalPost('admin/config/regional/i18n_translation/path/add', $edit, t('Save'));
+
+    $this->drupalGet('node/' . $first_node->nid);
+    $this->checkTranslationLink('node/' . $first_node->nid, $first_node->language);
+    $this->checkTranslationLink($this->secondary_language . '/node/' . $secondary_node->nid, $this->secondary_language);
+
+    $this->drupalGet('node/' . $secondary_node->nid);
+    $this->checkTranslationLink('node/' . $first_node->nid, $first_node->language);
+    $this->checkTranslationLink('node/' . $secondary_node->nid, $this->secondary_language);
+
+    // create new translation set with one node and one menu "token"
+    $edit = array(
+      'translations[' . $this->default_language . ']' => 'node/' . $first_node->nid,
+      'translations[' . $this->secondary_language . ']' => '<front>',
+    );
+    $this->drupalPost('admin/config/regional/i18n_translation/path/edit/1', $edit, t('Save'));
+
+    $this->drupalGet('node/' . $first_node->nid);
+    $this->checkTranslationLink('node/' . $first_node->nid, $first_node->language);
+    $this->checkTranslationLink('node/' . $secondary_node->nid, $this->secondary_language, 'assertNoLinkByHref');
+    $this->checkTranslationLink($this->secondary_language, $this->secondary_language);
+
+    // create new translation set with one node and an external menu link.
+    $url = 'http://' . $this->randomName(10) . '.' . $this->randomName(2);
+    $edit = array(
+      'translations[' . $this->default_language . ']' => 'node/' . $first_node->nid,
+      'translations[' . $this->secondary_language . ']' => $url,
+    );
+    $this->drupalPost('admin/config/regional/i18n_translation/path/edit/1', $edit, t('Save'));
+
+    $this->drupalGet('node/' . $first_node->nid);
+    $this->checkTranslationLink('node/' . $first_node->nid, $first_node->language);
+    $this->checkTranslationLink($url, $this->secondary_language);
+  }
+}
diff --git a/kolab.org/www/drupal-7.18/sites/all/modules/i18n/i18n_redirect/i18n_redirect.info b/kolab.org/www/drupal-7.18/sites/all/modules/i18n/i18n_redirect/i18n_redirect.info
new file mode 100644
index 0000000..8149e51
--- /dev/null
+++ b/kolab.org/www/drupal-7.18/sites/all/modules/i18n/i18n_redirect/i18n_redirect.info
@@ -0,0 +1,12 @@
+name = Translation redirect
+description = Redirect to translated page when available. SEO for multilingual sites.
+dependencies[] = i18n
+package = Multilingual - Internationalization
+core = 7.x
+
+; Information added by drupal.org packaging script on 2013-01-13
+version = "7.x-1.8"
+core = "7.x"
+project = "i18n"
+datestamp = "1358075001"
+
diff --git a/kolab.org/www/drupal-7.18/sites/all/modules/i18n/i18n_redirect/i18n_redirect.module b/kolab.org/www/drupal-7.18/sites/all/modules/i18n/i18n_redirect/i18n_redirect.module
new file mode 100644
index 0000000..e981dab
--- /dev/null
+++ b/kolab.org/www/drupal-7.18/sites/all/modules/i18n/i18n_redirect/i18n_redirect.module
@@ -0,0 +1,38 @@
+<?php
+
+/**
+ * @file
+ * Internationalization (i18n) module.
+ *
+ * Redirect to language path when we have a translation for the current language.
+ */
+
+/**
+ * Implements hook_help().
+ */
+function i18n_redirect_help($path, $arg) {
+  switch ($path) {
+    case 'admin/config/regional/i18n':
+      if (!module_exists('i18n_node')) {
+        $output = '<p>' . t('To have <em>Translation redirect</em> working with your content you should <a href="@admin_modules">enable the <em>Multilingual content</em> module</a>.', array('@admin_modules' => url('admin/modules'))) . '</p>';
+        return $output;
+      }
+  }
+}
+
+/**
+ * Implements hook_init()
+ */
+function i18n_redirect_init() {
+  $path = $_GET['q'];
+  $language = i18n_language_interface();
+  // Not for logged in users nor for home page
+  if (!$path || drupal_is_front_page() || !empty($GLOBALS['user']->uid)) {
+    return;
+  }
+  elseif ($translations = i18n_get_path_translations($path)) {
+    if (isset($translations[$language->language]) && $translations[$language->language]['href'] != $path) {
+      drupal_goto($translations[$language->language]['href'], array('language' => $language), 301);
+    }
+  }
+}
diff --git a/kolab.org/www/drupal-7.18/sites/all/modules/i18n/i18n_select/i18n_select.admin.inc b/kolab.org/www/drupal-7.18/sites/all/modules/i18n/i18n_select/i18n_select.admin.inc
new file mode 100644
index 0000000..fc7fea1
--- /dev/null
+++ b/kolab.org/www/drupal-7.18/sites/all/modules/i18n/i18n_select/i18n_select.admin.inc
@@ -0,0 +1,27 @@
+<?php
+
+/**
+ * @file
+ * Helper functions for select administration.
+ */
+
+function i18n_select_admin_settings() {
+  $form['types'] = array(
+    '#type' => 'variable_fieldset',
+    '#title' => t('Content to filter by language'),
+    '#variable_list' => array('i18n_select_nodes', 'i18n_select_taxonomy'),
+  );
+  $form['mode'] = array(
+    '#type' => 'variable_fieldset',
+    '#title' => t('Content selection mode'),
+    '#variable_list' => array('i18n_select_missing_translation', 'i18n_select_skip_tags'),
+  );
+  // Enable for specific pages. This works pretty much like block visibility
+  // Note this page requires 'administer site configuration' so we don't need to check permissions
+  $form['pages'] = array(
+    '#type' => 'variable_fieldset',
+    '#title' => t('Enable for specific pages'),
+    '#variable_list' => array('i18n_select_page_mode', 'i18n_select_page_list', 'i18n_select_page_block'),
+  );
+  return system_settings_form($form);
+}
diff --git a/kolab.org/www/drupal-7.18/sites/all/modules/i18n/i18n_select/i18n_select.info b/kolab.org/www/drupal-7.18/sites/all/modules/i18n/i18n_select/i18n_select.info
new file mode 100644
index 0000000..8d84ef4
--- /dev/null
+++ b/kolab.org/www/drupal-7.18/sites/all/modules/i18n/i18n_select/i18n_select.info
@@ -0,0 +1,14 @@
+name = Multilingual select
+description = API module for multilingual content selection
+dependencies[] = i18n
+package = Multilingual - Internationalization
+core = 7.x
+configure = admin/config/regional/i18n/select
+files[] = i18n_select.test
+
+; Information added by drupal.org packaging script on 2013-01-13
+version = "7.x-1.8"
+core = "7.x"
+project = "i18n"
+datestamp = "1358075001"
+
diff --git a/kolab.org/www/drupal-7.18/sites/all/modules/i18n/i18n_select/i18n_select.module b/kolab.org/www/drupal-7.18/sites/all/modules/i18n/i18n_select/i18n_select.module
new file mode 100644
index 0000000..4ba4488
--- /dev/null
+++ b/kolab.org/www/drupal-7.18/sites/all/modules/i18n/i18n_select/i18n_select.module
@@ -0,0 +1,314 @@
+<?php
+/**
+ * @file
+ * Multilingual content selection module.
+ *
+ * Alters content queries to add language conditions.
+ *
+ * Queries tagged with 'i18n_select' or that already have a language condition will not be altered.
+ */
+
+// No language selection
+define('I18N_SELECT_NONE', 0);
+// Content with current language and undefined language
+define('I18N_SELECT_NORMAL', 1);
+// Select default language when current language is missing
+define('I18N_SELECT_MISSING', 2);
+
+/**
+ * Enable on every page except the listed pages.
+ */
+define('I18N_SELECT_PAGE_NOTLISTED', 0);
+/**
+ * Enable on only the listed pages.
+ */
+define('I18N_SELECT_PAGE_LISTED', 1);
+/**
+ * Enable if the associated PHP code returns TRUE.
+ */
+define('I18N_SELECT_PAGE_PHP', 2);
+
+/**
+ * Implements hook_init().
+ */
+function i18n_select_init() {
+  // Determine selection mode for this page
+  i18n_select(i18n_select_page());
+}
+
+/**
+ * Implements hook_block_list_alter().
+ *
+ * Dirty trick to enable selection for blocks though it may be disabled for the current page.
+ */
+function i18n_select_block_list_alter(&$blocks) {
+  // Still, skip for form submission. There are pages like the ones produced
+  // by overlay that render the blocks before the page.
+  // See overlay_init(), overlay_overlay_child_initialize()
+  if (empty($_POST) && !isset($_GET['token']) && variable_get('i18n_select_page_block', TRUE)) {
+    i18n_select(TRUE);
+  }
+}
+
+/**
+ * Implements hook_menu().
+ */
+function i18n_select_menu() {
+  $items['admin/config/regional/i18n/select'] = array(
+    'title' => 'Selection',
+    'description' => 'Configure extended options for multilingual content and translations.',
+    'page callback' => 'drupal_get_form',
+    'page arguments' => array('i18n_select_admin_settings'),
+    'access arguments' => array('administer site configuration'),
+    'file' => 'i18n_select.admin.inc',
+    'type' => MENU_LOCAL_TASK,
+  );
+  return $items;
+}
+
+/**
+ * Get current mode for i18n selection
+ *
+ * @param $type
+ *   Selection type: 'nodes', 'taxonomy', etc..
+ */
+function i18n_select_mode($type = NULL) {
+  if (i18n_select() && (!$type || variable_get('i18n_select_' . $type, TRUE))) {
+    return I18N_SELECT_NORMAL;
+  }
+  else {
+    return I18N_SELECT_NONE;
+  }
+}
+
+/**
+ * Check current path to enable selection
+ *
+ * This works pretty much like block visibility
+ *
+ * @return boolean
+ *   TRUE if content selection should be enabled for this page.
+ */
+function i18n_select_page() {
+  static $mode;
+
+  if (!isset($mode)) {
+    $mode = &drupal_static(__FUNCTION__);
+    $visibility = variable_get('i18n_select_page_mode', I18N_SELECT_PAGE_NOTLISTED);
+    if ($pages = variable_get('i18n_select_page_list', 'admin/*')) {
+      // Convert path to lowercase. This allows comparison of the same path
+      // with different case. Ex: /Page, /page, /PAGE.
+      $pages = drupal_strtolower($pages);
+      if ($visibility < I18N_SELECT_PAGE_PHP) {
+        // Convert the Drupal path to lowercase
+        $path = drupal_strtolower(drupal_get_path_alias($_GET['q']));
+        // Compare the lowercase internal and lowercase path alias (if any).
+        $page_match = drupal_match_path($path, $pages);
+        if ($path != $_GET['q']) {
+          $page_match = $page_match || drupal_match_path($_GET['q'], $pages);
+        }
+        // When $visibility has a value of 0 (I18N_SELECT_PAGE_NOTLISTED),
+        // the block is displayed on all pages except those listed in $pages.
+        // When set to 1 (I18N_SELECT_PAGE_LISTED), it is displayed only on those
+        // pages listed in $pages.
+        $mode = !($visibility xor $page_match);
+      }
+      elseif (module_exists('php')) {
+        $mode = php_eval($pages);
+      }
+      else {
+        $mode = FALSE;
+      }
+    }
+    else {
+      // No pages defined, still respect the setting (unlike blocks)
+      $mode = $visibility == I18N_SELECT_PAGE_NOTLISTED;
+    }
+  }
+
+  return $mode;
+}
+
+/**
+ * Implementation of hook_query_node_access_alter().
+ *
+ * Rewrite node queries so language selection options are enforced.
+ */
+function i18n_select_query_node_access_alter(QueryAlterableInterface $query) {
+  if (i18n_select_mode('nodes') && i18n_select_check_query($query, 'nid') &&
+    ($table_alias = i18n_select_check_table($query, 'node', 'nid'))) {
+    $query->condition($table_alias . '.language', i18n_select_langcodes());
+    // Mark query as altered
+    $query->addTag('i18n_select');
+  }
+}
+
+/**
+ * Implementation of hook_query_term_access_alter().
+ *
+ * Rewrite taxonomy term queries so language selection options are enforced.
+ */
+function i18n_select_query_term_access_alter(QueryAlterableInterface $query) {
+  if (module_exists('i18n_taxonomy') && i18n_select_mode('taxonomy') && i18n_select_check_query($query, 'tid') &&
+    ($table_alias = i18n_select_check_table($query, 'taxonomy_term_data', 'tid'))) {
+    $query->condition($table_alias . '.language', i18n_select_langcodes());
+    // Mark query as altered
+    $query->addTag('i18n_select');
+  }
+}
+
+/**
+ * Check table exists in query and get alias for it.
+ *
+ * @param $query
+ *   Query object
+ * @param $table_name
+ *   Table name to find.
+ * @param $field_name
+ *   field to join the table if needed.
+ *
+ * @return
+ *   Table alias if found, none if not.
+ */
+function i18n_select_check_table($query, $table_name, $field_name) {
+  $tables = $query->getTables();
+  foreach ($tables as $table) {
+    if (!($table instanceof SelectQueryInterface) && $table['table'] == $table_name) {
+      return _i18n_select_table_alias($table);
+    }
+  }
+  // Join the table if we can find the key field on any of the tables
+  // And all the conditions have a table alias (or there's a unique table).
+  if (count($tables) == 1) {
+    $table = reset($tables);
+    $table_alias = _i18n_select_table_alias($table);
+    if (i18n_select_check_conditions($query, $table_alias)) {
+      $join_table = $table_alias;
+    }
+  }
+  elseif (i18n_select_check_conditions($query)) {
+    // Try to find the right field in the table schema.
+    foreach ($tables as $table) {
+      $schema = drupal_get_schema($table['table']);
+      if ($schema && !empty($schema['fields'][$field_name])) {
+        $join_table = _i18n_select_table_alias($table);
+        break;
+      }
+    }
+  }
+  if (!empty($join_table)) {
+    return $query->join($table_name, $table_name, $join_table . '.' . $field_name . ' = %alias.' . $field_name);
+  }
+  else {
+    return FALSE;
+  }
+}
+
+/**
+ * Get table alias
+ */
+function _i18n_select_table_alias($table) {
+  return !empty($table['alias']) ? $table['alias'] : $table['table'];
+}
+
+/**
+ * Check all query conditions have a table alias.
+ *
+ * @param $table_alias
+ *   Optional table alias for fields without table.
+ *
+ * @return boolean
+ *   TRUE if table conditions are ok, FALSE otherwise.
+ */
+function i18n_select_check_conditions($query, $table_alias = NULL) {
+  $conditions =& $query->conditions();
+  foreach ($conditions as $index => $condition) {
+    if (is_array($condition) && isset($condition['field'])) {
+      if (strpos($condition['field'], '.') === FALSE) {
+        if ($table_alias) {
+          // Change the condition to include a table alias.
+          $conditions[$index]['field'] = $table_alias . '.' . $condition['field'];
+        }
+        else {
+          // We won't risk joining anything here.
+          return FALSE;
+        }
+      }
+    }
+  }
+  return TRUE;
+}
+
+/**
+ * Check whether we should apply language conditions here:
+ * - The query has not been tagged with 'i18n_select'
+ * - The query doesn't have already a language condition
+ * - All the conditions have a table alias or there's only one table.
+ * - We are not loading specific objects (no condition for index field).
+ *
+ * @param $query
+ *   Query object.
+ * @param $index_field
+ *   Object index field to check we don't have 'IN' conditions for it.
+ */
+function i18n_select_check_query($query, $index_field = NULL) {
+  static $tags;
+  // Skip queries with certain tags
+  if (!isset($tags)) {
+    $tags = ($skip = variable_get('i18n_select_skip_tags', 'views')) ? array_map('trim', explode(',', $skip)) : array();
+    $tags[] = 'i18n_select';
+  }
+  foreach ($tags as $tag) {
+    if ($query->hasTag($tag)) {
+      return FALSE;
+    }
+  }
+  // Check all the conditions to see whether the query is suitable for altering.
+  foreach ($query->conditions() as $condition) {
+    if (is_array($condition)) {
+      // @todo For some complex queries, like search ones, field is a DatabaseCondition object
+      if (!isset($condition['field']) || !is_string($condition['field'])) {
+        // There's a weird condition field, we won't take any chances.
+        return FALSE;
+      }
+      else {
+        // Just check the condition doesn't include the language field
+        if (strpos($condition['field'], '.') === FALSE) {
+          $field = $condition['field'];
+        }
+        else {
+          list($table, $field) = explode('.', $condition['field']);
+        }
+        if ($field == 'language') {
+          return FALSE;
+        }
+        // Check 'IN' conditions for index field, usually entity loading for specific objects.
+        if ($field == $index_field && $condition['operator'] == 'IN') {
+          return FALSE;
+        }
+      }
+    }
+  }
+  // If the language field is present we don't want to do any filtering.
+  $fields = $query->getFields();
+  if (isset($fields['language'])) {
+    return FALSE;
+  }
+
+  return TRUE;
+}
+
+/**
+ * Get main language for content selection
+ */
+function i18n_select_language() {
+  return $GLOBALS[LANGUAGE_TYPE_CONTENT];
+}
+
+/**
+ * Get language codes for content selection to use in query conditions
+ */
+function i18n_select_langcodes() {
+  return array(i18n_select_language()->language, LANGUAGE_NONE);
+}
+
diff --git a/kolab.org/www/drupal-7.18/sites/all/modules/i18n/i18n_select/i18n_select.test b/kolab.org/www/drupal-7.18/sites/all/modules/i18n/i18n_select/i18n_select.test
new file mode 100644
index 0000000..b46fb98
--- /dev/null
+++ b/kolab.org/www/drupal-7.18/sites/all/modules/i18n/i18n_select/i18n_select.test
@@ -0,0 +1,86 @@
+<?php
+/**
+ * @file
+ * Test language selection modes
+ */
+
+class i18nSelectTestCase extends Drupali18nTestCase {
+
+  public static function getInfo() {
+    return array(
+      'name' => 'Content Selection',
+      'group' => 'Internationalization',
+      'description' => 'Internationalization Content Selection'
+    );
+  }
+
+  function setUp() {
+    parent::setUp('translation', 'i18n_variable', 'i18n_select');
+    parent::setUpLanguages();
+    parent::setUpContentTranslation();
+  }
+
+  function testIi18nSelect() {
+    drupal_static_reset('language_list');
+    $language_list = language_list();
+    $language_count = count($language_list);
+    // Set site name for each language and check pages later
+    variable_set('i18n_variable_list', array('site_name'));
+    foreach (i18n_language_list() as $langcode => $name) {
+      i18n_variable_set('site_name', "Drupal-$name", $langcode);
+    }
+
+    // Enable tags field for page content type.
+    $edit = array(
+      'fields[_add_existing_field][label]' => t('Tags'),
+      'fields[_add_existing_field][field_name]' => 'field_tags',
+      'fields[_add_existing_field][widget_type]' => 'taxonomy_autocomplete',
+    );
+    $this->drupalPost('admin/structure/types/manage/page/fields', $edit, t('Save'));
+    $this->drupalPost(NULL, array(), t('Save settings'));
+
+    // Create some content and check selection modes
+    $this->drupalLogin($this->content_editor);
+
+    // variable_set('language_content_type_story', 1);
+    $neutral = $this->drupalCreateNode(array('type' => 'page', 'promote' => 1));
+    $source = $this->createNode('page', $this->randomName(), $this->randomString(20), language_default('language'), array('field_tags[und]' => $tag_name = $this->randomName()));
+    $translations = $this->createNodeTranslationSet($source);
+
+    drupal_static_reset('translation_node_get_translations');
+    $this->assertEqual(count(translation_node_get_translations($source->tnid)), $language_count, "Created $language_count $source->type translations.");
+
+    // Log in user with access content permission
+    $user = $this->drupalCreateUser(array('access comments', 'access content'));
+    $this->drupalLogin($user);
+    // Default selection mode, only language neutral and current
+    variable_set('i18n_select_nodes', TRUE);
+    foreach (i18n_language_list() as $langcode => $name) {
+      $this->i18nGet($langcode);
+      $this->assertText("Drupal-$name", 'Checked translated site name: Drupal-' . $name);
+      $display = array($translations[$langcode], $neutral);
+      $hide = $translations;
+      unset($hide[$langcode]);
+      $this->assertContent($display, $hide);
+      // Visit the taxonomy page of that node and try again. Only the translated
+      // pages are tagged.
+      unset($display[1]);
+      $this->i18nGet($langcode, 'taxonomy/term/' . $source->field_tags[LANGUAGE_NONE][0]['tid']);
+      $this->assertContent($display, $hide);
+    }
+
+  }
+
+  /**
+   * Check some nodes are displayed, some are not
+   */
+  function assertContent($display, $hide = array()) {
+    $languages = language_list();
+    foreach ($display as $node) {
+      $this->assertText($node->title, 'Content displayed for ' . i18n_language_name($node->language));
+    }
+    foreach ($hide as $node) {
+      $this->assertNoText($node->title, 'Content not displayed for ' . i18n_language_name($node->language));
+    }
+  }
+}
diff --git a/kolab.org/www/drupal-7.18/sites/all/modules/i18n/i18n_select/i18n_select.variable.inc b/kolab.org/www/drupal-7.18/sites/all/modules/i18n/i18n_select/i18n_select.variable.inc
new file mode 100644
index 0000000..5543e78
--- /dev/null
+++ b/kolab.org/www/drupal-7.18/sites/all/modules/i18n/i18n_select/i18n_select.variable.inc
@@ -0,0 +1,73 @@
+<?php
+/**
+ * @file
+ * Variable information
+ */
+
+/**
+ * Implements hook_variable_info().
+ */
+function i18n_select_variable_info($options = array()) {
+  $variables['i18n_select_nodes'] = array(
+    'title' => t('Select nodes by language', array(), $options),
+    'type' => 'boolean',
+    'default' => TRUE,
+    'group' => 'i18n',
+  );
+  $variables['i18n_select_taxonomy'] = array(
+    'title' => t('Select taxonomy terms by language', array(), $options),
+    'type' => 'boolean',
+    'default' => TRUE,
+    'group' => 'i18n',
+    'element' => array('#disabled' => !module_exists('i18n_taxonomy')),
+  );
+
+  // Enable / disable for specific pages
+  $description = t("Specify pages by using their paths. Enter one path per line. The '*' character is a wildcard. Example paths are %blog for the blog page and %blog-wildcard for every personal blog. %front is the front page.", array('%blog' => 'blog', '%blog-wildcard' => 'blog/*', '%front' => '<front>'), $options);
+  if (module_exists('php')) {
+    $title = t('Pages or PHP code');
+    $description .= ' ' . t('If the PHP option is chosen, enter PHP code between %php. Note that executing incorrect PHP code can break your Drupal site.', array('%php' => '<?php ?>'), $options);
+  }
+  else {
+    $title = t('Pages', array(), $options);
+  }
+  $variables['i18n_select_page_mode'] = array(
+    'type' => 'select',
+    'options callback' => 'i18n_select_variable_option_list',
+    'default' => I18N_SELECT_PAGE_NOTLISTED,
+  );
+  $variables['i18n_select_page_list'] = array(
+    'type' => 'text',
+    'title' => $title,
+    'default' => 'admin/*',
+    'description' => $description,
+  );
+  $variables['i18n_select_page_block'] = array(
+    'type' => 'boolean',
+    'title' => t('Enable always for block content though it may be disabled for the page', array(), $options),
+    'default' => TRUE,
+  );
+  $variables['i18n_select_skip_tags'] = array(
+    'title' => t('Skip tags', array(), $options),
+    'type' => 'string',
+    'default' => 'views',
+    'group' => 'i18n',
+    'description' => t('Skip queries with these tags. Enter a list of tags separated by commas.'),
+    'localize' => FALSE,
+  );
+  return $variables;
+}
+
+/**
+ * Options for page selection mode
+ */
+function i18n_select_variable_option_list($variable, $options = array()) {
+  $options = array(
+    I18N_SELECT_PAGE_NOTLISTED => t('All pages except those listed', array(), $options),
+    I18N_SELECT_PAGE_LISTED => t('Only the listed pages', array(), $options),
+  );
+  if (module_exists('php')) {
+    $options += array(I18N_SELECT_PAGE_PHP => t('Pages on which this PHP code returns <code>TRUE</code> (experts only)'));
+  }
+  return $options;
+}
\ No newline at end of file
diff --git a/kolab.org/www/drupal-7.18/sites/all/modules/i18n/i18n_string/i18n_string.admin.inc b/kolab.org/www/drupal-7.18/sites/all/modules/i18n/i18n_string/i18n_string.admin.inc
new file mode 100644
index 0000000..86913e5
--- /dev/null
+++ b/kolab.org/www/drupal-7.18/sites/all/modules/i18n/i18n_string/i18n_string.admin.inc
@@ -0,0 +1,313 @@
+<?php
+
+/**
+ * @file
+ * Helper functions for string administration.
+ */
+
+// Load locale library
+include_once DRUPAL_ROOT . '/includes/locale.inc';
+
+/**
+ * Form callback. Refresh textgroups.
+ */
+function i18n_string_admin_refresh_form() {
+  // Select textgroup/s. Just the ones that have a 'refresh callback'
+  $groups = array();
+  foreach (i18n_string_group_info() as $name => $info) {
+    $groups[$name] = $info['title'];
+  }
+  $form['groups'] = array(
+    '#type' => 'checkboxes',
+    '#title' => t('Select text groups'),
+    '#options' => $groups,
+    '#description' => t('If a text group is no showing up here it means this feature is not implemented for it.'),
+  );
+  $form['delete'] = array(
+    '#type' => 'checkbox',
+    '#title' => t('Clean up left over strings.'),
+    '#default_value' => TRUE,
+  );
+  $form['refresh'] = array(
+    '#type' => 'submit',
+    '#value' => t('Refresh strings'),
+    '#suffix' => '<p>' . t('This will create all the missing strings for the selected text groups.') . '</p>',
+  );
+  return $form;
+}
+
+/**
+ * Form submission.
+ */
+function i18n_string_admin_refresh_form_submit($form, &$form_state) {
+  $op = isset($form_state['values']['op']) ? $form_state['values']['op'] : '';
+  $groups = array_filter($form_state['values']['groups']);
+  $group_names = module_invoke_all('locale', 'groups');
+  if ($op == t('Refresh strings') && $groups) {
+    $batch = i18n_string_refresh_batch($groups, $form_state['values']['delete']);
+    batch_set($batch);
+  }
+}
+
+
+/**
+ * Refresh all user defined strings for a given text group.
+ *
+ * @param $group
+ *   Text group to refresh
+ * @param $delete
+ *   Optional, delete existing (but not refresed, strings and translations)
+ * @return Boolean
+ *   True if the strings have been refreshed successfully. False otherwise.
+ */
+function i18n_string_refresh_group($group, $delete = FALSE) {
+  $result = FALSE;
+
+  // Compile all strings for this group
+  if ($strings = i18n_string_group_string_list($group)) {
+    i18n_string_refresh_string_list($strings);
+    $result = TRUE;
+  }
+  // Invoke refresh hook
+  $result = $result || module_invoke_all('i18n_string_refresh', $group);
+
+  // Now delete all source strings that were not refreshed (they don't have a row in i18n_string)
+  if ($result && $delete) {
+    i18n_string_refresh_cleanup($group);
+  }
+  return $result;
+}
+
+/**
+ * Clean up left over strings for text group
+ */
+function i18n_string_refresh_cleanup($group) {
+  $lids = db_select('locales_source', 's')
+    ->fields('s', array('lid'))
+    ->condition('textgroup', $group)
+    ->condition('version', 0)
+    ->execute()
+    ->fetchCol();
+  if ($lids) {
+    drupal_set_message(t('Performing cleanup for text group %textgroup, deleting @count left over strings.', array('%textgroup' => $group, '@count' => count($lids))));
+    db_delete('locales_target')->condition('lid', $lids)->execute();
+    db_delete('locales_source')->condition('lid', $lids)->execute();
+    db_delete('i18n_string')->condition('lid', $lids)->execute();
+    return count($lids);
+  }
+  else {
+    return 0;
+  }
+}
+
+/**
+ * Prepare group for refreshing, reset version, count strings
+ */
+function i18n_string_refresh_reset($group) {
+  // Mark data on locales_source setting version = 0
+  db_update('locales_source')
+    ->fields(array('version' => 0))
+    ->condition('textgroup', $group)
+    ->execute();
+  return (int)db_query("SELECT COUNT(*) FROM {locales_source} WHERE textgroup = :textgroup", array(':textgroup' => $group))->fetchField();
+}
+
+/**
+ * Refresh string list
+ */
+function i18n_string_refresh_string_list($strings) {
+  $count = 0;
+  foreach ($strings as $textgroup => $group_strings) {
+    foreach ($group_strings as $type => $type_strings) {
+      foreach ($type_strings as $id => $object_strings) {
+        foreach ($object_strings as $key => $string) {
+          if (is_array($string)) {
+            $format = isset($string['format']) ? $string['format'] : NULL;
+            $string = $string['string'];
+          }
+          else {
+            $format = NULL;
+          }
+          i18n_string_update(array($textgroup, $type, $id, $key), $string, array('format' => $format));
+          $count++;
+        }
+      }
+    }
+  }
+  return $count;
+}
+
+/**
+ * Create batch for refreshing strings
+ *
+ * @param $groups
+ *   Array of text groups to refresh
+ * @param $delete
+ *   Optional, delete existing (but not refresed, strings and translations)
+ */
+function i18n_string_refresh_batch($groups, $delete = FALSE) {
+  $operations = array();
+  foreach ($groups as $group) {
+    $operations[] = array('_i18n_string_batch_refresh_prepare', array($group));
+    // First try to find string list
+    $operations[] = array('_i18n_string_batch_refresh_list', array($group));
+    // Then invoke refresh callback
+    $operations[] = array('_i18n_string_batch_refresh_callback', array($group));
+    if ($delete) {
+      $operations[] = array('_i18n_string_batch_refresh_cleanup', array($group));
+    }
+    // Output group summary
+    $operations[] = array('_i18n_string_batch_refresh_summary', array($group));
+  }
+  $batch = array(
+    'operations'    => $operations,
+    'title'         => t('Refreshing user defined strings'),
+    'init_message'  => t('Starting string refresh'),
+    'error_message' => t('Error refreshing user defined strings'),
+    'file' => drupal_get_path('module', 'i18n_string') . '/i18n_string.admin.inc',
+  );
+  return $batch;
+}
+
+/**
+ * Refresh strings for enabled modules
+ */
+function i18n_string_refresh_enabled_modules($modules) {
+  // Check if any of the modules has strings to update
+  $count = 0;
+  foreach ($modules as $module) {
+    if ($strings = i18n_string_module_string_list($module)) {
+      $count += i18n_string_refresh_string_list($strings);
+
+    }
+    // Call module refresh if exists
+    module_invoke($module, 'i18n_string_refresh', 'all');
+  }
+  if ($count) {
+    drupal_set_message(format_plural($count, 'Refreshed one string for enabled modules.', 'Refreshed @count strings for the enabled modules.'));
+  }
+}
+
+/**
+ * Purge uninstalled modules.
+ */
+function i18n_string_refresh_uninstalled_modules($modules) {
+  foreach ($modules as $module) {
+    // If the module defines any textgroup, purge all strings.
+    module_load_include('i18n.inc', $module);
+    if ($string_info = module_invoke($module, 'i18n_string_info')) {
+      foreach ($string_info as $group => $group_info) {
+        i18n_string_refresh_reset($group);
+        i18n_string_refresh_cleanup($group);
+      }
+    }
+  }
+}
+
+/**
+ * Prepare group for refreshing
+ */
+function _i18n_string_batch_refresh_prepare($group, &$context) {
+  $context['results'][$group] = array(
+    'count' => i18n_string_refresh_reset($group),
+    'result' => FALSE,
+  );
+}
+
+/**
+ * Batch operation: Refresh string list for group
+ */
+function _i18n_string_batch_refresh_list($group, &$context) {
+  $count = 0;
+  if ($strings = i18n_string_group_string_list($group)) {
+    $count = i18n_string_refresh_string_list($strings);
+    $context['results'][$group]['result'] = TRUE;
+  }
+  $context['results'][$group]['refresh'] = $count;
+}
+
+/**
+ * Batch operation: Invoke i18n_string_refresh
+ */
+function _i18n_string_batch_refresh_callback($group, &$context) {
+  $result = module_invoke_all('i18n_string_refresh', $group);
+  $count = $result ? array_sum($result) : 0;
+  $context['results'][$group]['refresh'] += $count;
+  if ($count) {
+    $context['results'][$group]['result'] = TRUE;
+  }
+}
+
+/**
+ * Batch callback, delete old strings
+ */
+function _i18n_string_batch_refresh_cleanup($group, &$context) {
+  if (!empty($context['results'][$group]['result'])) {
+    $context['results'][$group]['deleted'] = i18n_string_refresh_cleanup($group);
+  }
+}
+
+/**
+ * Batch operations: Print refresh summary for group
+ */
+function _i18n_string_batch_refresh_summary($group, &$context) {
+  if ($context['results'][$group]['result']) {
+    drupal_set_message(t("Successfully refreshed @count strings for %group", array('@count' => $context['results'][$group]['refresh'], '%group' => i18n_string_group_info($group, 'title'))));
+    if (!empty($context['results'][$group]['deleted'])) {
+      drupal_set_message(t('Deleted @count left over strings.', array('@count' => $context['results'][$group]['deleted'])));
+    }
+  }
+  else {
+    drupal_set_message(t("Cannot refresh strings for %group.", array('%group' => i18n_string_group_info($group, 'title'))), 'warning');
+  }
+}
+
+/**
+ * Get all strings for a text group
+ */
+function i18n_string_group_string_list($group) {
+  // Add strings provided by all modules on hook_string_list().
+  // Note that i18n_string module itself will also collect all strings for this group's objects
+  $strings = module_invoke_all('i18n_string_list', $group);
+  // Invoke hook_i18n_string_list_TEXTGROUP_alter()
+  drupal_alter('i18n_string_list_' . $group, $strings);
+  return $strings;
+}
+
+/**
+ * Get all strings from a module.
+ */
+function i18n_string_module_string_list($module) {
+  $strings = array();
+  // Try loading i18n.inc for the module and some library functions.
+  module_load_include('i18n.inc', $module);
+  module_load_include('i18n.inc', 'i18n_string');
+  // If the module defines any textgroup, get all strings for this group
+  if ($groups = module_invoke($module, 'i18n_string_info')) {
+    foreach (array_keys($groups) as $group) {
+      $strings = i18n_string_array_merge($strings, i18n_string_group_string_list($group));
+    }
+  }
+  else {
+    $groups = array();
+  }
+  // The module may implement i18n_string_list()
+  if ($string_list = module_invoke($module, 'i18n_string_list', 'all')) {
+    foreach ($string_list as $group => $group_strings) {
+      if (!in_array($group, $groups)) {
+        $strings[$group] = $group_strings;
+      }
+    }
+  }
+  // If the module defines any object that has strings in another textgroup
+  if ($object_types = module_invoke($module, 'i18n_object_info')) {
+    foreach ($object_types as $type => $type_info) {
+      if (($group = i18n_string_object_info($type, 'textgroup')) && !in_array($group, $groups)) {
+        if ($group_strings = i18n_string_object_type_string_list($type)) {
+          $strings = i18n_string_array_merge($strings, $group_strings);
+        }
+      }
+    }
+  }
+  return $strings;
+}
diff --git a/kolab.org/www/drupal-7.18/sites/all/modules/i18n/i18n_string/i18n_string.api.php b/kolab.org/www/drupal-7.18/sites/all/modules/i18n/i18n_string/i18n_string.api.php
new file mode 100644
index 0000000..cafec01
--- /dev/null
+++ b/kolab.org/www/drupal-7.18/sites/all/modules/i18n/i18n_string/i18n_string.api.php
@@ -0,0 +1,123 @@
+<?php
+
+/**
+ * @file
+ * API documentation file for String translation module.
+ * 
+ * Basically we are collecting translatable strings for each text group. There are two ways a
+ * module can produce this list of strings. It should be one or the other, not both.
+ * 
+ * 1. Provide a list of objects that are translatable for that text group either defining a
+ *    'list callback' for that object type or implementing hook_i18n_string_objects($type) for
+ *    that object type.
+ * 
+ * 2. Provide a full list of strings for that text group by implementing
+ *    hook_i18n_string_list()
+ *    
+ * Then we have hook_i18n_string_list_TEXTGROUP_alter() for other modules to alter either the
+ * list of strings for a single object or the full list of strings at the end.
+ */
+
+/**
+ * List text groups for string translation.
+ * 
+ * This information will be automatically produced later for hook_locale()
+ */
+function hook_i18n_string_info() {
+  $groups['menu'] = array(
+    'title' => t('Menu'),
+    'description' => t('Translatable menu items: title and description.'),
+    'format' => FALSE, // This group doesn't have strings with format
+    'list' => TRUE, // This group can list all strings
+  );
+  return $groups;
+}
+
+/**
+ * Provide list of translatable strings for text group.
+
+ * A module can provide either a list of translatable strings using hook_i18n_string_list() or
+ * it can provide a list of objects using hook_i18n_string_objects() from which the string
+ * list will be produced automatically. But not both.
+ * 
+ * @param $group
+ *   Text group name.
+ */
+function hook_i18n_string_list($group) {
+  if ($group == 'mygroup') {
+    $strings['mygroup']['type1']['key1']['name'] = 'Name of object type1/key1';
+    $strings['mygroup']['type1']['key1']['description'] = 'Description of object type1/key1';
+    return $strings;
+  }
+}
+
+/**
+ * Alter string list for objects of text group.
+ *
+ * To build a list of translatable strings for a text group, we'll follow these steps:
+ * 1. Invoke hook_i18n_string_list($textgroup), that will get us an array of strings
+ * 2. Get the object types for that textgroup, collectin it from i18n object information.
+ *    @see i18n_string_group_object_types()
+ * 3. For each object type, collect the full list of objects invoking hook_i18n_string_objects($type)
+ *    @see i18n_string_object_type_string_list()
+ *    If an object defines a 'list callback' function that one will be called to get the list of strings.
+ * 4. For each object, collect the properties for that specific object.
+ *    $properties = i18n_object($type, $object)->get_properties();
+ * 5. Run this hook to alter the strings for that specific object. In this case we'll pass the
+ *    $type and $object parameters.
+ * 6. Merge all strings from all objects in an array indexed by textgroup, type, id, name
+ * 7. Run this hook once again to alter *all* strings for this textgroup. In this case we
+ *    don't have a $type and $object parameters.
+ *    
+ * Thus this hook is really invoked once per object and once per textgroup on top of that.
+ * 
+ * @see i18n_string_group_string_list()
+ * @see i18n_string_object_type_string_list()
+ * @see i18n_menu_i18n_string_list_menu_alter()
+ * 
+ * @param $strings
+ *   Associative array with current string list indexed by textgroup, type, id, name
+ * @param $type
+ *   Object type ad defined on i18n_object_info()
+ * @param $object
+ *   Object defined on i18n_object_info()
+ *   
+ * These last parameters are optional. If type and object are not present
+ * we are altering the full list of strings for the text group that happens once at the end.
+ */
+function hook_i18n_string_list_TEXTGROUP_alter(&$strings, $type = NULL, $object = NULL) {
+  if ($type == 'menu_link' && $object) {
+    if (isset($object['options']['attributes']['title'])) {
+    	$strings['menu']['item'][$object['mlid']]['title']['string'] = $object['link_title']; 
+      $strings['menu']['item'][$object['mlid']]['description']['string'] = $object['options']['attributes']['title'];
+    }  
+  }
+}
+
+/**
+ * List objects to collect translatable strings.
+ * 
+ * A module can provide either a list of translatable strings using hook_i18n_string_list() or
+ * it can provide a list of objects using hook_i18n_string_objects() from which the string
+ * list will be produced automatically. But not both.
+ * 
+ * @see i18n_object_info()
+ * @see i18n_menu_i18n_string_objects()
+ * @see i18n_string_i18n_string_list()
+ * 
+ * @param $type string
+ *   Object type
+ * @return $objects array
+ *   Associative array of objects indexed by object id
+ */
+function hook_i18n_string_objects($type) {
+  if ($type == 'menu_link') {
+    // All menu items that have no language and are customized.
+    return db_select('menu_links', 'm')
+      ->fields('m')
+      ->condition('language', LANGUAGE_NONE)
+      ->condition('customized', 1)
+      ->execute()
+      ->fetchAllAssoc('mlid', PDO::FETCH_ASSOC);
+  }
+}
\ No newline at end of file
diff --git a/kolab.org/www/drupal-7.18/sites/all/modules/i18n/i18n_string/i18n_string.i18n.inc b/kolab.org/www/drupal-7.18/sites/all/modules/i18n/i18n_string/i18n_string.i18n.inc
new file mode 100644
index 0000000..98ada90
--- /dev/null
+++ b/kolab.org/www/drupal-7.18/sites/all/modules/i18n/i18n_string/i18n_string.i18n.inc
@@ -0,0 +1,105 @@
+<?php
+/**
+ * @file
+ * Implementation of i18n hooks
+ */
+
+/**
+ * Implements hook_i18n_string_objects().
+ * 
+ * Automate object list for object types that have a 'table' property
+ */
+function i18n_string_i18n_string_objects($type) {
+  if ($function = i18n_object_info($type, 'list callback')) {
+    return call_user_func($function);
+  }
+  elseif ($table = i18n_string_object_info($type, 'table')) {
+    $query = db_select($table, 's')->fields('s');
+    return $query->execute()->fetchAll();
+  }
+}
+
+/**
+ * Implements hook_i18n_string_list().
+ * 
+ * Collect all strings from objects of this group.
+ */
+function i18n_string_i18n_string_list($group) {
+  $strings = array();
+  // It may be for one group or all groups
+  $groups = $group == 'all' ? array_keys(i18n_string_group_info()) : array($group);
+  foreach ($groups as $group) {  
+    // Compile strings for object types for this group
+    foreach (i18n_string_group_object_types($group) as $type) {
+      $type_strings = i18n_string_object_type_string_list($type);
+      if ($type_strings && !empty($type_strings[$group])) {
+        $strings[$group] = isset($strings[$group]) ? i18n_string_array_merge($strings[$group], $type_strings[$group]) : $type_strings[$group];
+      }
+    }
+  }
+  return $strings;
+}
+
+/**
+ * Get object types for text group
+ */
+function i18n_string_group_object_types($group) {
+  $types = array();
+  foreach (i18n_object_info() as $type => $type_info) {
+    if (!empty($type_info['string translation']) && $type_info['string translation']['textgroup'] == $group) {
+      $types[] = $type;
+    }
+  }
+  return $types;
+}
+
+/**
+ * Get object string list that are in this text group.
+ * 
+ * @param $type
+ *   Object type
+ */
+function i18n_string_object_type_string_list($type) {
+  $strings = array();
+  if ($objects = module_invoke_all('i18n_string_objects', $type)) {
+    foreach ($objects as $object) {
+      if ($object_strings = i18n_object($type, $object)->get_properties()) {
+        $strings = i18n_string_array_merge($strings, $object_strings);
+      }
+    }
+  }
+  return $strings;
+}
+
+/**
+ * Merges multiple arrays, recursively, and returns the merged array.
+ *
+ * This function is not equivalent to PHP's array_merge_recursive(),
+ * as this version leaves integer keys intact.
+ *
+ * @see drupal_array_merge_deep(), @see array_merge_recursive()
+ *
+ * @param ...
+ *   Arrays to merge.
+ * @return
+ *   The merged array.
+ */
+function i18n_string_array_merge() {
+  $arrays = func_get_args();
+  $result = array();
+
+  foreach ($arrays as $array) {
+    foreach ($array as $key => $value) {
+      // Recurse when both values are arrays.
+      if (isset($result[$key]) && is_array($result[$key]) && is_array($value)) {
+        $result[$key] = i18n_string_array_merge($result[$key], $value);
+      }
+      // Otherwise, use the latter value, overriding any previous value.
+      else {
+        $result[$key] = $value;
+      }
+    }
+  }
+
+  return $result;
+}
\ No newline at end of file
diff --git a/kolab.org/www/drupal-7.18/sites/all/modules/i18n/i18n_string/i18n_string.inc b/kolab.org/www/drupal-7.18/sites/all/modules/i18n/i18n_string/i18n_string.inc
new file mode 100644
index 0000000..65f4c1b
--- /dev/null
+++ b/kolab.org/www/drupal-7.18/sites/all/modules/i18n/i18n_string/i18n_string.inc
@@ -0,0 +1,1282 @@
+<?php
+/**
+ * @file
+ *   API for internationalization strings
+ */
+
+/**
+ * String object that contains source and translations.
+ *
+ * Note all database operations must go through textgroup object so we can switch storage at some point.
+ */
+class i18n_string_object {
+  // Updated source string
+  public $string;
+  // Properties from locale source
+  public $lid;
+  public $source;
+  public $textgroup;
+  public $location;
+  public $context;
+  public $version;
+  // Properties from i18n_tring
+  public $type;
+  public $objectid;
+  public $property;
+  public $objectkey;
+  public $format;
+  // Properties from metadata
+  public $title;
+  // Array of translations to multiple languages
+  public $translations;
+  // Textgroup object
+  protected $_textgroup;
+
+  /**
+   * Class constructor
+   */
+  public function __construct($data = NULL) {
+    if ($data) {
+      $this->set_properties($data);
+    }
+  }
+  /**
+   * Get message parameters from context and string.
+   */
+  public function get_args() {
+    return array(
+      '%location' => $this->location,
+      '%textgroup' => $this->textgroup,
+      '%string' => ($string = $this->get_string()) ? $string : t('[empty string]'),
+    );
+  }
+  /**
+   * Set context properties
+   */
+  public function set_context($context) {
+    $parts = is_array($context) ? $context : explode(':', $context);
+    $this->context = is_array($context) ? implode(':', $context) : $context;
+    // Location will be the full string name
+    $this->location = $this->textgroup . ':' . $this->context;
+    $this->type = array_shift($parts);
+    $this->objectid = $parts ? array_shift($parts) : '';
+    $this->objectkey = (int)$this->objectid;
+    // Remaining elements glued again with ':'
+    $this->property = $parts ? implode(':', $parts) : '';
+    return $this;
+  }
+  /**
+   * Get string name including textgroup and context
+   */
+  public function get_name() {
+    return $this->textgroup . ':' . $this->type . ':' . $this->objectid . ':' . $this->property;
+  }
+  /**
+   * Get source string
+   */
+  public function get_string() {
+    if (isset($this->string)) {
+      return $this->string;
+    }
+    elseif (isset($this->source)) {
+      return $this->source;
+    }
+    elseif ($this->textgroup()->debug) {
+      return empty($this->lid) ? t('[Source not found]') : t('[String not found]');
+    }
+    else {
+      return '';
+    }
+  }
+  /**
+   * Set source string
+   *
+   * @param $string
+   *   Plain string or array with 'string', 'format', etc...
+   */
+  public function set_string($string) {
+    if (is_array($string)) {
+      $this->string = isset($string['string']) ? $string['string'] : NULL;
+      if (isset($string['format'])) {
+        $this->format = $string['format'];
+      }
+    }
+    else {
+      $this->string = $string;
+    }
+    if (isset($string['title'])) {
+      $this->title = $string['title'];
+    }
+    return $this;
+  }
+  /**
+   * Get string title.
+   */
+  public function get_title() {
+    return isset($this->title) ? $this->title : t('String');
+  }
+  /**
+   * Get translation to language from string object
+   */
+  public function get_translation($langcode) {
+    if (!isset($this->translations[$langcode])) {
+      $translation = $this->textgroup()->load_translation($this, $langcode);
+      if ($translation && isset($translation->translation)) {
+        $this->set_translation($translation, $langcode);
+      }
+      else {
+        // No source, no translation
+        $this->translations[$langcode] = FALSE;
+      }
+    }
+    // Which doesn't mean we've got a translation, only that we've got the result cached
+    return $this->translations[$langcode];
+  }
+  /**
+   * Set translation for language
+   *
+   * @param $translation
+   *   Translation object (from database) or string
+   */
+  public function set_translation($translation, $langcode = NULL) {
+    if (is_object($translation)) {
+      $langcode = $langcode ? $langcode : $translation->language;
+      $string = isset($translation->translation) ? $translation->translation : FALSE;
+      $this->set_properties($translation);
+    }
+    else {
+      $string = $translation;
+    }
+    $this->translations[$langcode] = $string;
+    return $this;
+  }
+
+  /**
+   * Format the resulting translation or the default string applying callbacks
+   *
+   * There's a hidden variable, 'i18n_string_debug', that when set to TRUE will display additional info
+   */
+  public function format_translation($langcode, $options = array()) {
+    $options += array('langcode' => $langcode, 'sanitize' => TRUE, 'cache' => FALSE, 'debug' => $this->textgroup()->debug);
+    if ($translation = $this->get_translation($langcode)) {
+      $string = $translation;
+      if (isset($options['filter'])) {
+        $string = call_user_func($options['filter'], $string);
+      }
+    }
+    else {
+      // Get default source string if no translation.
+      $string = $this->get_string();
+      $options['sanitize'] = !empty($options['sanitize default']);
+    }
+    if (!empty($this->format)) {
+      $options += array('format' => $this->format);
+    }
+    // Add debug information if enabled
+    if ($options['debug']) {
+      $info = array($langcode, $this->textgroup, $this->context);
+      if (!empty($this->format)) {
+        $info[] = $this->format;
+      }
+      $options += array('suffix' => '');
+      $options['suffix'] .= ' [' . implode(':', $info) . ']';
+    }
+    // Finally, apply options, filters, callback, etc...
+    return i18n_string_format($string, $options);
+  }
+
+  /**
+   * Get source string provided a string object.
+   *
+   * @return
+   *   String object if source exists.
+   */
+  public function get_source() {
+    // If already searched and not found we don't have a source,
+    if (isset($this->lid) && !$this->lid) {
+      return NULL;
+    }
+    elseif (!isset($this->lid) || !isset($this->source)) {
+      // We may have lid from loading a translation but not loaded the source yet.
+      if ($source = $this->textgroup()->load_source($this)) {
+        // Set properties but don't override existing ones
+        $this->set_properties($source, FALSE, FALSE);
+        if (!isset($this->string)) {
+          $this->string = $source->source;
+        }
+        return $this;
+      }
+      else {
+        $this->lid = FALSE;
+        return NULL;
+      }
+    }
+    else {
+      return $this;
+    }
+  }
+
+  /**
+   * Set properties from object or array
+   *
+   * @param $properties
+   *   Obejct or array of properties
+   * @param $set_null
+   *   Whether to set null properties too
+   * @param $override
+   *   Whether to set properties that are already set in this object
+   */
+  public function set_properties($properties, $set_null = TRUE, $override = TRUE) {
+    foreach ((array)$properties as $field => $value) {
+      if (property_exists($this, $field) && ($set_null || isset($value)) && ($override || !isset($this->$field))) {
+        $this->$field = $value;
+      }
+    }
+    return $this;
+  }
+  /**
+   * Access textgroup object
+   */
+  protected function textgroup() {
+    if (!isset($this->_textgroup)) {
+      $this->_textgroup = i18n_string_textgroup($this->textgroup);
+    }
+    return $this->_textgroup;
+  }
+  /**
+   * Update this string.
+   */
+  public function update($options = array()) {
+    return $this->textgroup()->string_update($this, $options);
+  }
+  /**
+   * Delete this string.
+   */
+  public function remove($options = array()) {
+    return $this->textgroup()->string_remove($this, $options);
+  }
+  /**
+   * Check whether there is any problem for the  user to translate a this string.
+   *
+   * @param $account
+   *   Optional user account, defaults to current user.
+   *
+   * @return
+   *   None if the user has access to translate the string.
+   *   Error message if the user cannot translate that string.
+   */
+  public function check_translate_access($account = NULL) {
+    return i18n_string_translate_check_string($this, $account);
+  }
+}
+
+/**
+ * Textgroup handler for i18n_string API
+ */
+class i18n_string_textgroup_default {
+  // Text group name
+  public $textgroup;
+  // Debug flag, set to true to print out more information.
+  public $debug;
+  // Cached or preloaded string objects
+  public $strings;
+  // Multiple translations search map
+  protected $cache_multiple;
+
+  /**
+   * Class constructor.
+   *
+   * There are to hidden variables to produce debugging information:
+   * - 'i18n_string_debug', generic for all text groups.
+   * - 'i18n_string_debug_TEXTGROUP', enable debug only for TEXTGROUP.
+   */
+  public function __construct($textgroup) {
+    $this->textgroup = $textgroup;
+    $this->debug = variable_get('i18n_string_debug', FALSE) || variable_get('i18n_string_debug_' . $textgroup, FALSE);
+  }
+  /**
+   * Build string object
+   *
+   * @param $context
+   *   Context array or string
+   * @param $string string
+   *   Current value for string source
+   */
+  public function build_string($context, $string = NULL) {
+    // First try to locate string on cache
+    $context = is_array($context) ? implode(':', $context) : $context;
+    if ($cached = $this->cache_get($context)) {
+      $i18nstring = $cached;
+    }
+    else {
+      $i18nstring = new i18n_string_object();
+      $i18nstring->textgroup = $this->textgroup;
+      $i18nstring->set_context($context);
+      $this->cache_set($context, $i18nstring);
+    }
+    if (isset($string)) {
+      $i18nstring->set_string($string);
+    }
+    return $i18nstring;
+  }
+  /**
+   * Add source string to the locale tables for translation.
+   *
+   * It will also add data into i18n_string table for faster retrieval and indexing of groups of strings.
+   * Some string context doesn't have a numeric oid (I.e. content types), it will be set to zero.
+   *
+   * This function checks for already existing string without context for this textgroup and updates it accordingly.
+   * It is intended for backwards compatibility, using already created strings.
+   *
+   * @param $i18nstring
+   *   String object
+   * @param $format
+   *   Text format, for strings that will go through some filter
+   * @return
+   *   Update status.
+   */
+  protected function string_add($i18nstring, $options = array()) {
+    $options += array('watchdog' => TRUE);
+    // Default return status if nothing happens
+    $status = -1;
+    $source = NULL;
+    $location = $i18nstring->location;
+    // The string may not be allowed for translation depending on its format.
+    if (!$this->string_check($i18nstring, $options)) {
+      // The format may have changed and it's not allowed now, delete the source string
+      return $this->string_remove($i18nstring, $options);
+    }
+    elseif ($source = $i18nstring->get_source()) {
+      if ($source->source != $i18nstring->string || $source->location != $location) {
+        $i18nstring->location = $location;
+        // String has changed, mark translations for update
+        $status = $this->save_source($i18nstring);
+        db_update('locales_target')
+          ->fields(array('i18n_status' => I18N_STRING_STATUS_UPDATE))
+          ->condition('lid', $source->lid)
+          ->execute();
+      }
+      elseif (empty($source->version)) {
+        // When refreshing strings, we've done version = 0, update it
+        $this->save_source($i18nstring);
+      }
+    }
+    else {
+      // We don't have the source object, create it
+      $status = $this->save_source($i18nstring);
+    }
+    // Make sure we have i18n_string part, create or update
+    // This will also create the source object if doesn't exist
+    $this->save_string($i18nstring);
+
+    if ($options['watchdog']) {
+      switch ($status) {
+        case SAVED_UPDATED:
+          watchdog('i18n_string', 'Updated string %location for textgroup %textgroup: %string', $i18nstring->get_args());
+          break;
+        case SAVED_NEW:
+          watchdog('i18n_string', 'Created string %location for text group %textgroup: %string', $i18nstring->get_args());
+          break;
+      }
+    }
+    return $status;
+  }
+
+  /**
+   * Check if string is ok for translation
+   */
+  protected static function string_check($i18nstring, $options = array()) {
+    $options += array('messages' => FALSE, 'watchdog' => TRUE);
+    if (!empty($i18nstring->format) && !i18n_string_allowed_format($i18nstring->format)) {
+      // This format is not allowed, so we remove the string, in this case we produce a warning
+      drupal_set_message(t('The string %location for textgroup %textgroup is not allowed for translation because of its text format.', $i18nstring->get_args()), 'warning');
+      return FALSE;
+    }
+    else {
+      return TRUE;
+    }
+  }
+
+  /**
+   * Filter array of strings
+   *
+   * @param $filter
+   *   Array of name value conditions.
+   */
+  protected static function string_filter($string_list, $filter) {
+    // Remove 'language' and '*' conditions.
+    if (isset($filter['language'])) {
+      unset($filter['language']);
+    }
+    while ($field = array_search('*', $filter)) {
+      unset($filter[$field]);
+    }
+    foreach ($string_list as $key => $string) {
+      foreach ($filter as $field => $value) {
+        if ($string->$field != $value) {
+          unset($string_list[$key]);
+          break;
+        }
+      }
+    }
+    return $string_list;
+  }
+
+  /**
+   * Build query for i18n_string table
+   */
+  protected static function string_query($context, $multiple = FALSE) {
+    // Search the database using lid if we've got it or textgroup, context otherwise
+    $query = db_select('i18n_string', 's')->fields('s');
+    if (!empty($context->lid)) {
+      $query->condition('s.lid', $context->lid);
+    }
+    else {
+      $query->condition('s.textgroup', $context->textgroup);
+      if (!$multiple) {
+        $query->condition('s.context', $context->context);
+      }
+      else {
+        // Query multiple strings
+        foreach (array('type', 'objectid', 'property') as $field) {
+          if (!empty($context->$field)) {
+            $query->condition('s.' . $field, $context->$field);
+          }
+        }
+      }
+    }
+    return $query;
+  }
+
+  /**
+   * Remove string object.
+   *
+   * @return
+   *   SAVED_DELETED | FALSE (If the operation failed because no source)
+   */
+  public function string_remove($i18nstring, $options = array()) {
+    $options += array('watchdog' => TRUE, 'messages' => $this->debug);
+    if ($source = $i18nstring->get_source()) {
+      db_delete('locales_target')->condition('lid', $source->lid)->execute();
+      db_delete('i18n_string')->condition('lid', $source->lid)->execute();
+      db_delete('locales_source')->condition('lid', $source->lid)->execute();
+      $this->cache_set($source->context, NULL);
+      if ($options['watchdog']) {
+        watchdog('i18n_string', 'Deleted string %location for text group %textgroup: %string', $i18nstring->get_args());
+      }
+      if ($options['messages']) {
+        drupal_set_message(t('Deleted string %location for text group %textgroup: %string', $i18nstring->get_args()));
+      }
+      return SAVED_DELETED;
+    }
+    else {
+      if ($options['messages']) {
+        drupal_set_message(t('Cannot delete string, not found %location for text group %textgroup: %string', $i18nstring->get_args()));
+      }
+      return FALSE;
+    }
+  }
+
+  /**
+   * Translate string object
+   *
+   * @param $i18nstring
+   *   String object
+   * @param $options
+   *   Array with aditional options
+   */
+  protected function string_translate($i18nstring, $options = array()) {
+    $langcode = isset($options['langcode']) ? $options['langcode'] : i18n_langcode();
+    // Search for existing translation (result will be cached in this function call)
+    $i18nstring->get_translation($langcode);
+    return $i18nstring;
+  }
+
+  /**
+   * Update / create / remove string.
+   *
+   * @param $name
+   *   String context.
+   * @pram $string
+   *   New value of string for update/create. May be empty for removing.
+   * @param $format
+   *   Text format, that must have been checked against allowed formats for translation
+   * @param $options
+   *   Processing options, the ones used here are:
+   *   - 'watchdog', whether to produce watchdog messages.
+   *   - 'messages', whether to produce user messages.
+   *   - 'check', whether to check string format and then update/delete if not allowed.
+   * @return status
+   *   SAVED_UPDATED | SAVED_NEW | SAVED_DELETED | FALSE (If the string is to be removed but has no source)
+   */
+  public function string_update($i18nstring, $options = array()) {
+    $options += array('watchdog' => TRUE, 'messages' => $this->debug, 'check' => TRUE);
+    if ((!$options['check'] || $this->string_check($i18nstring, $options)) && $i18nstring->get_string()) {
+      // String is ok, has a value so we store it into the database.
+      $status = $this->string_add($i18nstring, $options);
+    }
+    elseif ($i18nstring->get_source()) {
+      // Just remove it if we already had a source created before.
+      $status = $this->string_remove($i18nstring, $options);
+    }
+    else {
+      // String didn't pass validation or we have an empty string but was not stored anyway.
+      $status = FALSE;
+    }
+    if ($options['messages']) {
+      switch ($status) {
+        case SAVED_UPDATED:
+          drupal_set_message(t('Updated string %location for text group %textgroup: %string', $i18nstring->get_args()));
+          break;
+        case SAVED_NEW:
+          drupal_set_message(t('Created string %location for text group %textgroup: %string', $i18nstring->get_args()));
+          break;
+      }
+    }
+    if ($options['watchdog']) {
+      switch ($status) {
+        case SAVED_UPDATED:
+          watchdog('i18n_string', 'Updated string %location for text group %textgroup: %string', $i18nstring->get_args());
+          break;
+        case SAVED_NEW:
+          watchdog('i18n_string', 'Created string %location for text group %textgroup: %string', $i18nstring->get_args());
+          break;
+      }
+    }
+    return $status;
+  }
+
+  /**
+   * Set string object into cache
+   */
+  protected function cache_set($context, $string) {
+    $this->strings[$context] = $string;
+  }
+
+  /**
+   * Get translation from cache
+   */
+  protected function cache_get($context) {
+    return isset($this->strings[$context]) ? $this->strings[$context] : NULL;
+  }
+
+  /**
+   * Reset cache, needed for tests
+   */
+  public function cache_reset() {
+    $this->strings = array();
+    $this->string_format = array();
+    $this->translations = array();
+  }
+
+  /**
+   * Load multiple strings.
+   *
+   * @return array
+   *   List of strings indexed by full string name.
+   */
+  public function load_strings($conditions = array()) {
+    // Add textgroup condition and load all
+    $conditions['textgroup'] = $this->textgroup;
+    $list = array();
+    foreach (i18n_string_load_multiple($conditions) as $string) {
+      $list[$string->get_name()] = $string;
+      $this->cache_set($string->context, $string);
+    }
+    return $list;
+  }
+
+  /**
+   * Load string source from db
+   */
+  public static function load_source($i18nstring) {
+    // Search the database using lid if we've got it or textgroup, context otherwise
+    $query = db_select('locales_source', 's')->fields('s');
+    $query->leftJoin('i18n_string', 'i', 's.lid = i.lid');
+    $query->fields('i', array('format', 'objectid', 'type', 'property', 'objectindex'));
+    if (!empty($i18nstring->lid)) {
+      $query->condition('s.lid', $i18nstring->lid);
+    }
+    else {
+      $query->condition('s.textgroup', $i18nstring->textgroup);
+      $query->condition('s.context', $i18nstring->context);
+    }
+    // Speed up the query, we just need one row
+    return $query->range(0, 1)->execute()->fetchObject();
+  }
+
+  /**
+   * Load translation from db
+   *
+   * @todo Optimize when we've already got the source string
+   */
+  public static function load_translation($i18nstring, $langcode) {
+    // Search the database using lid if we've got it or textgroup, context otherwise
+    if (!empty($i18nstring->lid)) {
+      // We've alreay got lid, we just need translation data
+      $query = db_select('locales_target', 't');
+      $query->condition('t.lid', $i18nstring->lid);
+    }
+    else {
+      // Still don't have lid, load string properties too
+      $query = db_select('i18n_string', 's')->fields('s');
+      $query->leftJoin('locales_target', 't', 's.lid = t.lid');
+      $query->condition('s.textgroup', $i18nstring->textgroup);
+      $query->condition('s.context', $i18nstring->context);
+    }
+    // Add translation fields
+    $query->fields('t', array('translation', 'i18n_status'));
+    $query->condition('t.language', $langcode);
+    // Speed up the query, we just need one row
+    $query->range(0, 1);
+    return $query->execute()->fetchObject();
+  }
+
+  /**
+   * Save / update string object
+   *
+   * There seems to be a race condition sometimes so skip errors, #277711
+   *
+   * @param $string
+   *   Full string object to be saved
+   * @param $source
+   *   Source string object
+   */
+  protected function save_string($string, $update = FALSE) {
+    if (!$string->get_source()) {
+      // Create source string so we get an lid
+      $this->save_source($string);
+    }
+    if (!isset($string->objectkey)) {
+      $string->objectkey = (int)$string->objectid;
+    }
+    if (!isset($string->format)) {
+      $string->format = '';
+    }
+    $status = db_merge('i18n_string')
+      ->key(array('lid' => $string->lid))
+      ->fields(array(
+          'textgroup' => $string->textgroup,
+          'context' => $string->context,
+          'objectid' => $string->objectid,
+          'type' => $string->type,
+          'property' => $string->property,
+          'objectindex' => $string->objectkey,
+          'format' => $string->format,
+      ))
+      ->execute();
+    return $status;
+  }
+
+  /**
+   * Save translation to the db
+   *
+   * @param $string
+   *   Full string object with translation data (language, translation)
+   */
+  protected function save_translation($string, $langcode) {
+    db_merge('locales_target')
+      ->key(array('lid' => $string->lid, 'language' => $langcode))
+      ->fields(array('translation' => $string->get_translation($langcode)))
+      ->execute();
+  }
+
+  /**
+   * Save source string (create / update)
+   */
+  protected static function save_source($source) {
+    if (isset($source->string)) {
+      $source->source = $source->string;
+    }
+    if (empty($source->version)) {
+      $source->version = 1;
+    }
+    return drupal_write_record('locales_source', $source, !empty($source->lid) ? 'lid' : array());
+  }
+
+  /**
+   * Remove source and translations for user defined string.
+   *
+   * Though for most strings the 'name' or 'string id' uniquely identifies that string,
+   * there are some exceptions (like profile categories) for which we need to use the
+   * source string itself as a search key.
+   *
+   * @param $context
+   *   Textgroup and location glued with ':'.
+   * @param $string
+   *   Optional source string (string in default language).
+   */
+  public function context_remove($context, $string = NULL, $options = array()) {
+    $options += array('messages' => $this->debug);
+    $i18nstring = $this->build_string($context, $string);
+    $status = $this->string_remove($i18nstring, $options);
+
+    return $this;
+  }
+
+  /**
+   * Translate source string
+   */
+  public function context_translate($context, $string, $options = array()) {
+    $i18nstring = $this->build_string($context, $string);
+    return $this->string_translate($i18nstring, $options);
+  }
+
+  /**
+   * Update / create translation source for user defined strings.
+   *
+   * @param $name
+   *   Textgroup and location glued with ':'.
+   * @param $string
+   *   Source string in default language. Default language may or may not be English.
+   * @param $options
+   *   Array with additional options:
+   *   - 'format', String format if the string has text format.
+   *   - 'messages', Whether to print out status messages.
+   *   - 'check', whether to check string format and then update/delete if not allowed.
+   */
+  public function context_update($context, $string, $options = array()) {
+    $options += array('format' => FALSE, 'messages' => $this->debug, 'watchdog' => TRUE, 'check' => TRUE);
+    $i18nstring = $this->build_string($context, $string);
+    $i18nstring->format = $options['format'];
+    $this->string_update($i18nstring, $options);
+    return $this;
+  }
+
+  /**
+   * Build combinations of an array of arrays respecting keys.
+   *
+   * Example:
+   *   array(array(a,b), array(1,2)) will translate into
+   *   array(a,1), array(a,2), array(b,1), array(b,2)
+   */
+  protected static function multiple_combine($properties) {
+    $combinations = array();
+    // Get first key, value. We need to make sure the array pointer is reset.
+    $value = reset($properties);
+    $key = key($properties);
+    array_shift($properties);
+    $values = is_array($value) ? $value : array($value);
+    foreach ($values as $value) {
+      if ($properties) {
+        foreach (self::multiple_combine($properties) as $merge) {
+          $combinations[] = array_merge(array($key => $value), $merge);
+        }
+      }
+      else {
+        $combinations[] = array($key => $value);
+      }
+    }
+    return $combinations;
+  }
+
+  /**
+   * Get multiple translations with search conditions.
+   *
+   * @param $translations
+   *   Array of translation objects as loaded from the db.
+   * @param $langcode
+   *   Language code, array of language codes or * to search all translations.
+   *
+   * @return array
+   *   Array of i18n string objects.
+   */
+  protected function multiple_translation_build($translations, $langcode) {
+    $strings = array();
+    foreach ($translations as $translation) {
+      // The string object may be already in list
+      if (isset($strings[$translation->context])) {
+        $string = $strings[$translation->context];
+      }
+      else {
+        $string = $this->build_string($translation->context);
+        $string->set_properties($translation);
+        $strings[$string->context] = $string;
+      }
+      // If this is a translation we set it there too
+      if ($translation->language && $translation->translation) {
+        $string->set_translation($translation);
+      }
+      elseif ($langcode) {
+        // This may only happen when we have a source string but not translation.
+        $string->set_translation(FALSE, $langcode);
+      }
+    }
+    return $strings;
+  }
+
+  /**
+   * Load multiple translations from db
+   *
+   * @todo Optimize when we've already got the source object
+   *
+   * @param $conditions
+   *   Array of field values to use as query conditions.
+   * @param $langcode
+   *   Language code to search.
+   * @param $index
+   *   Field to use as index for the result.
+   * @return array
+   *   Array of string objects with translation set.
+   */
+  protected function multiple_translation_load($conditions, $langcode) {
+    $conditions += array(
+      'language' => $langcode,
+      'textgroup' => $this->textgroup
+    );
+    // We may be querying all translations at the same time or just one language.
+    // The language field needs some special treatment though.
+    $query = db_select('i18n_string', 's')->fields('s');
+    $query->leftJoin('locales_target', 't', 's.lid = t.lid');
+    $query->fields('t', array('translation', 'language', 'i18n_status'));
+    foreach ($conditions as $field => $value) {
+      // Single array value, reduce array
+      if (is_array($value) && count($value) == 1) {
+        $value = reset($value);
+      }
+      if ($value === '*') {
+        continue;
+      }
+      elseif ($field == 'language') {
+        $query->condition('t.language', $value);
+      }
+      else {
+        $query->condition('s.' . $field, $value);
+      }
+    }
+    return $this->multiple_translation_build($query->execute()->fetchAll(), $langcode);
+  }
+
+  /**
+   * Search multiple translations with key combinations.
+   *
+   * Each $context field may be a single value, an array of values or '*'.
+   * Example:
+   * 	array('term', array(1,2), '*')
+   * This will be mapped into the following conditions (provided language code is 'es')
+   *  array('type' => 'term', 'objectid' => array(1,2), 'property' => '*', 'language' => 'es')
+   * And will result in these combinations to search for
+   *  array('type' => 'term', 'objectid' => 1, 'property' => '*', 'language' => 'es')
+   *  array('type' => 'term', 'objectid' => 2, 'property' => '*', 'language' => 'es')
+   *
+   * @param $context array
+   *   Array with String context conditions.
+   *
+   * @return
+   *   Array of translation objects indexed by context.
+   */
+  public function multiple_translation_search($context, $langcode) {
+    // First, build conditions and identify the variable field.
+    $keys = array('type', 'objectid', 'property');
+    $conditions = array_combine($keys, $context) + array('language' => $langcode);
+    // Find existing searches in cache, compile remaining ones.
+    $translations = $search = array();
+    foreach ($this->multiple_combine($conditions) as $combination) {
+      $cached = $this->multiple_cache_get($combination);
+      if (isset($cached)) {
+        // Cache hit. Merge and remove value from search.
+        $translations += $cached;
+      }
+      else {
+        // Not in cache, add to search conditions skipping duplicated values.
+        // As array_merge_recursive() has some bug in PHP 5.2, http://drupal.org/node/1244598
+        // we use our simplified version here, instead of $search = array_merge_recursive($search, $combination);
+        foreach ($combination as $key => $value) {
+          if (!isset($search[$key]) || !in_array($value, $search[$key], TRUE)) {
+            $search[$key][] = $value;
+          }
+        }
+      }
+    }
+    // If we've got any search values left, find translations.
+    if ($search) {
+      // Load translations for conditions and set them to the cache
+      $loaded = $this->multiple_translation_load($search, $langcode);
+      if ($loaded) {
+        $translations += $loaded;
+      }
+      // Set cache for each of the multiple search keys.
+      foreach ($this->multiple_combine($search) as $combination) {
+        $list = $loaded ? $this->string_filter($loaded, $combination) : array();
+        $this->multiple_cache_set($combination, $list);
+      }
+    }
+    return $translations;
+  }
+
+  /**
+   * Set multiple cache.
+   *
+   * @param $context
+   *   String context with language property at the end.
+   * @param $strings
+   *   Array of strings (may be empty) to cache.
+   */
+  protected function multiple_cache_set($context, $strings) {
+    $cache_key = implode(':', $context);
+    $this->cache_multiple[$cache_key] = $strings;
+  }
+
+  /**
+   * Get strings from multiple cache.
+   *
+   * @param $context array
+   *   String context as array with language property at the end.
+   *
+   * @return mixed
+   *   Array of strings (may be empty) if we've got a cache hit.
+   *   Null otherwise.
+   */
+  protected function multiple_cache_get($context) {
+    $cache_key = implode(':', $context);
+    if (isset($this->cache_multiple[$cache_key])) {
+      return $this->cache_multiple[$cache_key];
+    }
+    else {
+      // Now we try more generic keys. For instance, if we are searching 'term:1:*'
+      // we may try too 'term:*:*' and filter out the results.
+      foreach ($context as $key => $value) {
+        if ($value != '*') {
+          $try = array_merge($context, array($key => '*'));
+          $cache_key = implode(':', $try);
+          if (isset($this->cache_multiple[$cache_key])) {
+            // As we've found some more generic key, we need to filter using original conditions.
+            $strings = $this->string_filter($this->cache_multiple[$cache_key], $context);
+            return $strings;
+          }
+        }
+      }
+      // If we've reached here, we didn't find any cache match.
+      return NULL;
+    }
+  }
+
+  /**
+   * Translate array of source strings
+   *
+   * @param $context
+   *   Context array with placeholders (*)
+   * @param $strings
+   *   Optional array of source strings indexed by the placeholder property
+   *
+   * @return array
+   *   Array of string objects (with translation) indexed by the placeholder field
+   */
+  public function multiple_translate($context, $strings = array(), $options = array()) {
+    // First, build conditions and identify the variable field
+    $search = $context = array_combine(array('type', 'objectid', 'property'), $context);
+    $langcode = isset($options['langcode']) ? $options['langcode'] : i18n_langcode();
+    // If we've got keyed source strings set the array of keys on the placeholder field
+    // or if not, remove that condition so we search all strings with that keys.
+    foreach ($search as $field => $value) {
+      if ($value === '*') {
+        $property = $field;
+        if ($strings) {
+          $search[$field] = array_keys($strings);
+        }
+      }
+    }
+    // Now we'll add the language code to conditions and get the translations indexed by the property field
+    $result = $this->multiple_translation_search($search, $langcode);
+    // Remap translations using property field. If we've got strings it is important that they are in the same order.
+    $translations = $strings;
+    foreach ($result as $key => $i18nstring) {
+      $translations[$i18nstring->$property] = $i18nstring;
+    }
+    // Set strings as source or create
+    foreach ($strings as $key => $source) {
+      if (isset($translations[$key]) && is_object($translations[$key])) {
+        $translations[$key]->set_string($source);
+      }
+      else {
+        // Not found any string for this property, create it to map in the response
+        // But make sure we set this language's translation to FALSE so we don't search again
+        $newcontext = $context;
+        $newcontext[$property] = $key;
+        $translations[$key] = $this->build_string($newcontext)
+          ->set_string($source)
+          ->set_translation(FALSE, $langcode);
+      }
+    }
+    return $translations;
+  }
+
+  /**
+   * Update string translation, only if source exists.
+   *
+   * @param $context
+   *   String context as array
+   * @param $langcode
+   *   Language code to create the translation for
+   * @param $translation
+   *   String translation for this language
+   */
+  function update_translation($context, $langcode, $translation) {
+    $i18nstring = $this->build_string($context);
+    if ($source = $i18nstring->get_source()) {
+      $source->set_translation($translation, $langcode);
+      $this->save_translation($source, $langcode);
+      return $source;
+    }
+  }
+
+  /**
+   * Recheck strings after update
+   */
+  public function update_check() {
+    // Find strings in locales_source that have no data in i18n_string
+    $query = db_select('locales_source', 'l')
+    ->fields('l')
+    ->condition('l.textgroup', $this->textgroup);
+    $alias = $query->leftJoin('i18n_string', 's', 'l.lid = s.lid');
+    $query->isNull('s.lid');
+    foreach ($query->execute()->fetchAll() as $string) {
+      $i18nstring = $this->build_string($string->context, $string->source);
+      $this->save_string($i18nstring);
+    }
+  }
+
+}
+
+/**
+ * String object wrapper
+ */
+class i18n_string_object_wrapper extends i18n_object_wrapper {
+  // Text group object
+  protected $textgroup;
+  // Properties for translation
+  protected $properties;
+
+  /**
+   * Get object strings for translation
+   *
+    * This will return a simple array of string objects, indexed by full string name.
+    *
+    * @param $options
+    *   Array with processing options.
+    *   - 'empty', whether to return empty strings, defaults to FALSE.
+   */
+  public function get_strings($options = array()) {
+    $options += array('empty' => FALSE);
+    $strings = array();
+    foreach ($this->get_properties() as $textgroup => $textgroup_list) {
+      foreach ($textgroup_list as $type => $type_list) {
+        foreach ($type_list as $object_id => $object_list) {
+          foreach ($object_list as $key => $string) {
+            if ($options['empty'] || !empty($string['string'])) {
+              // Build string object, that will trigger static caches everywhere.
+              $i18nstring = i18n_string_textgroup($textgroup)
+                ->build_string(array($type, $object_id, $key))
+                ->set_string($string);
+              $strings[$i18nstring->get_name()] = $i18nstring;
+            }
+          }
+        }
+      }
+    }
+    return $strings;
+  }
+  /**
+   * Get object translatable properties
+   *
+   * This will return a big array indexed by textgroup, object type, object id and string key.
+   * Each element is an array with string information, and may have these properties:
+   * - 'string', the string itself, will be NULL if the object doesn't have that string
+   * - 'format', string format when needed
+   * - 'title', string readable name
+   */
+  public function get_properties() {
+    if (!isset($this->properties)) {
+      $this->properties = $this->build_properties();
+      // Call hook_i18n_string_list_TEXTGROUP_alter(), last chance for modules
+      drupal_alter('i18n_string_list_' . $this->get_textgroup(), $this->properties, $this->type, $this->object);
+
+    }
+    return $this->properties;
+  }
+
+  /**
+   * Build properties from object.
+   */
+  protected function build_properties() {
+    list($string_type, $object_id) = $this->get_string_context();
+    $object_keys = array(
+      $this->get_textgroup(),
+      $string_type,
+      $object_id,
+    );
+    $strings = array();
+    foreach ($this->get_string_info('properties', array()) as $field => $info) {
+      $info = is_array($info) ? $info : array('title' => $info);
+      $field_name = isset($info['field']) ? $info['field'] : $field;
+      $value = $this->get_field($field_name);
+      $strings[$this->get_textgroup()][$string_type][$object_id][$field] = array(
+        'string' => is_array($value) || isset($info['empty']) && $value === $info['empty'] ? NULL : $value,
+        'title' => $info['title'],
+        'format' => isset($info['format']) ? $this->get_field($info['format']) : NULL,
+        'name' => array_merge($object_keys, array($field)),
+      );
+    }
+    return $strings;
+  }
+
+  /**
+   * Get string context
+   */
+  public function get_string_context() {
+    return array($this->get_string_info('type'), $this->get_key());
+  }
+
+  /**
+   * Get translate path for object
+   *
+   * @param $langcode
+   * 	 Language code if we want ti for a specific language
+   */
+  public function get_translate_path($langcode = NULL) {
+    $replacements = array('%i18n_language' => $langcode ? $langcode : '');
+    if ($path = $this->get_string_info('translate path')) {
+      return $this->path_replace($path, $replacements);
+    }
+    elseif ($path = $this->get_info('translate tab')) {
+      // If we've got a translate tab path, we just add language to it
+      return $this->path_replace($path . '/%i18n_language', $replacements);
+    }
+  }
+
+  /**
+   * Translation mode for object
+   */
+  public function get_translate_mode() {
+    return !$this->get_langcode() ? I18N_MODE_LOCALIZE : I18N_MODE_NONE;
+  }
+
+  /**
+   * Get textgroup name
+   */
+  public function get_textgroup() {
+    return $this->get_string_info('textgroup');
+  }
+
+  /**
+   * Get textgroup object
+   */
+  protected function textgroup() {
+    if (!isset($this->textgroup)) {
+      $this->textgroup = i18n_string_textgroup($this->get_textgroup());
+    }
+    return $this->textgroup;
+  }
+
+  /**
+   * Translate object.
+   *
+   * Translations are cached so it runs only once per language.
+   *
+   * @return object/array
+   *   A clone of the object with its properties translated.
+   */
+  public function translate($langcode, $options = array()) {
+    // We may have it already translated. As objects are statically cached, translations are too.
+    if (!isset($this->translations[$langcode])) {
+      $this->translations[$langcode] = $this->translate_object($langcode, $options);
+    }
+    return $this->translations[$langcode];
+  }
+
+  /**
+   * Translate access (localize strings)
+   */
+  protected function localize_access() {
+    return user_access('translate interface') && user_access('translate user-defined strings');
+  }
+
+  /**
+   * Translate all properties for object.
+   *
+   * On top of object strings we search for all textgroup:type:objectid:* properties
+   *
+   * @param $langcode
+   *   A clone of the object or array
+   */
+  protected function translate_object($langcode, $options) {
+    // Clone object or array so we don't affect the original one.
+    $object = is_object($this->object) ? clone $this->object : $this->object;
+    // Get object strings for translatable properties.
+    if ($strings = $this->get_strings()) {
+      // We preload some of the property translations with a single query.
+      if ($context = $this->get_translate_context($langcode, $options)) {
+        $found = $this->textgroup()->multiple_translation_search($context, $langcode);
+      }
+      // Replace all strings in object.
+      foreach ($strings as $i18nstring) {
+        $this->translate_field($object, $i18nstring, $langcode, $options);
+      }
+    }
+    return $object;
+  }
+
+  /**
+   * Context to be pre-loaded before translation.
+   */
+  protected function get_translate_context($langcode, $options) {
+    // One-query translation of all textgroup:type:objectid:* properties
+    $context = $this->get_string_context();
+    $context[] = '*';
+    return $context;
+  }
+
+  /**
+   * Translate object property.
+   *
+   * Mot often, this is a direct field set, but sometimes fields may have different formats.
+   */
+  protected function translate_field(&$object, $i18nstring, $langcode, $options) {
+    $field_name = $i18nstring->property;
+    $translation = $i18nstring->format_translation($langcode, $options);
+    if (is_object($object)) {
+      $object->$field_name = $translation;
+    }
+    elseif (is_array($object)) {
+      $object[$field_name] = $translation;
+    }
+  }
+
+  /**
+   * Remove all strings for this object.
+   */
+  public function strings_remove($options = array()) {
+    $result = array();
+    foreach ($this->load_strings() as $key => $string) {
+      $result[$key] = $string->remove($options);
+    }
+    return _i18n_string_result_count($result);
+  }
+
+  /**
+   * Update all strings for this object.
+   */
+  public function strings_update($options = array()) {
+    $options += array('empty' => TRUE, 'update' => TRUE);
+    $result = array();
+    $existing = $this->load_strings();
+    // Update object strings
+    foreach ($this->get_strings($options) as $key => $string) {
+      $result[$key] = $string->update($options);
+      unset($existing[$key]);
+    }
+    // Delete old existing strings.
+    foreach ($existing as $key => $string) {
+      $result[$key] = $string->remove($options);
+    }
+    return _i18n_string_result_count($result);
+  }
+
+  /**
+   * Load all existing strings for this object.
+   */
+  public function load_strings() {
+    list($type, $id) = $this->get_string_context();
+    return $this->textgroup()->load_strings(array('type' => $type, 'objectid' => $id));
+  }
+}
diff --git a/kolab.org/www/drupal-7.18/sites/all/modules/i18n/i18n_string/i18n_string.info b/kolab.org/www/drupal-7.18/sites/all/modules/i18n/i18n_string/i18n_string.info
new file mode 100644
index 0000000..7da968d
--- /dev/null
+++ b/kolab.org/www/drupal-7.18/sites/all/modules/i18n/i18n_string/i18n_string.info
@@ -0,0 +1,18 @@
+name = String translation
+description = Provides support for translation of user defined strings.
+dependencies[] = locale
+dependencies[] = i18n
+package = Multilingual - Internationalization
+core = 7.x
+
+files[] = i18n_string.admin.inc
+files[] = i18n_string.inc
+files[] = i18n_string.test
+configure = admin/config/regional/i18n/strings
+
+; Information added by drupal.org packaging script on 2013-01-13
+version = "7.x-1.8"
+core = "7.x"
+project = "i18n"
+datestamp = "1358075001"
+
diff --git a/kolab.org/www/drupal-7.18/sites/all/modules/i18n/i18n_string/i18n_string.install b/kolab.org/www/drupal-7.18/sites/all/modules/i18n/i18n_string/i18n_string.install
new file mode 100644
index 0000000..511bc8a
--- /dev/null
+++ b/kolab.org/www/drupal-7.18/sites/all/modules/i18n/i18n_string/i18n_string.install
@@ -0,0 +1,260 @@
+<?php
+
+/**
+ * @file
+ * Installation file for i18n_string module.
+ */
+
+/**
+ * Implements hook_enable().
+ */
+function i18n_string_enable() {
+  // Refresh locales for enabled modules
+  $modules = module_implements('i18n_string_refresh');
+  i18n_string_modules_enabled($modules);
+}
+
+/**
+ * Implements hook_install().
+ */
+function i18n_string_install() {
+  // Add a field to track whether a translation needs updating.
+  module_load_install('i18n');
+  i18n_install_create_fields('locales_target', array('i18n_status'));
+  // Set module weight for it to run after core modules.
+  db_query("UPDATE {system} SET weight = 10 WHERE name = 'i18n_string' AND type = 'module'");
+  // If updating from D6, module changed name
+  if (variable_get('i18n_drupal6_update')) {
+    i18n_string_update_7000();
+    i18n_string_update_7001();
+  }
+}
+
+/**
+ * Implements hook_uninstall().
+ */
+function i18n_string_uninstall() {
+  // Drop custom field.
+  db_drop_field('locales_target', 'i18n_status');
+}
+
+/**
+ * Implements hook_schema().
+ */
+function i18n_string_schema() {
+  $schema['i18n_string'] = array(
+    'description' => 'Metadata for source strings.',
+    'fields' => array(
+      'lid' => array(
+        'type' => 'int',
+        'not null' => TRUE,
+        'default' => 0,
+        'description' => 'Source string ID. References {locales_source}.lid.',
+      ),
+      'textgroup' => array(
+        'type' => 'varchar',
+        'length' => 50,
+        'not null' => TRUE,
+        'default' => 'default',
+        'description' => 'A module defined group of translations, see hook_locale().',
+      ),
+      'context' => array(
+        'type' => 'varchar',
+        'length' => 255,
+        'not null' => TRUE,
+        'default' => '',
+        'description' => 'Full string ID for quick search: type:objectid:property.',
+      ),
+      'objectid' => array(
+        'type' => 'varchar',
+        'length' => 255,
+        'not null' => TRUE,
+        'default' => '',
+        'description' => 'Object ID.',
+      ),
+      'type' => array(
+        'type' => 'varchar',
+        'length' => 255,
+        'not null' => TRUE,
+        'default' => '',
+        'description' => 'Object type for this string.',
+      ),
+      'property' => array(
+        'type' => 'varchar',
+        'length' => 255,
+        'not null' => TRUE,
+        'default' => '',
+        'description' => 'Object property for this string.',
+      ),
+      'objectindex' => array(
+        'type' => 'int',
+        'not null' => TRUE,
+        'default' => 0,
+        'description' => 'Integer value of Object ID.',
+      ),
+      'format' => array(
+        'type' => 'varchar',
+        'length' => 255,
+        'not null' => FALSE,
+        'description' => 'The {filter_format}.format of the string.',
+      ),
+
+    ),
+    'primary key' => array('lid'),
+    'indexes' => array(
+      'group_context' => array('textgroup', 'context'),
+    ),
+  );
+  return $schema;
+}
+
+/**
+ * Implements hook_schema_alter().
+ */
+function i18n_string_schema_alter(&$schema) {
+  // Add field for tracking whether translations need updating.
+  $schema['locales_target']['fields']['i18n_status'] = array(
+    'description' => 'A boolean indicating whether this translation needs to be updated.',
+    'type' => 'int',
+    'not null' => TRUE,
+    'default' => 0,
+  );
+}
+
+/**
+ * Helper function to upate strings
+ */
+function i18n_string_install_update_string($string) {
+  $string->context = $string->type . ':' . $string->objectid . ':' . $string->property;
+  $string->location = $string->textgroup . ':' . $string->context;
+  $string->objectindex = (int)$string->objectid;
+  drupal_write_record('i18n_string', $string, 'lid');
+  drupal_write_record('locales_source', $string, 'lid');
+}
+
+/**
+ * Update context for strings.
+ *
+ * As some string locations depend on configurable values, the field needs sometimes to be updated
+ * without losing existing translations. I.e:
+ * - profile fields indexed by field name.
+ * - content types indexted by low level content type name.
+ *
+ * Example:
+ *  'profile:field:oldfield:*' -> 'profile:field:newfield:*'
+ */
+function i18n_string_install_update_context($oldname, $newname) {
+  // Get context replacing '*' with empty string.
+  $oldcontext = explode(':', $oldname);
+  $newcontext = explode(':', $newname);
+  /*
+  i18n_string_context(str_replace('*', '', $oldname));
+  $newcontext = i18n_string_context(str_replace('*', '', $newname));
+  */
+  // Get location with placeholders.
+  foreach (array('textgroup', 'type', 'objectid', 'property') as $index => $field) {
+    if ($oldcontext[$index] != $newcontext[$index]) {
+      $replace[$field] = $newcontext[$index];
+    }
+  }
+
+  // Query and replace if there are any fields. It is possible that under some circumstances fields are the same
+  if (!empty($replace)) {
+    $textgroup = array_shift($oldcontext);
+    $context = str_replace('*', '%', implode(':', $oldcontext));
+    $count = 0;
+    $query = db_select('i18n_string', 's')
+      ->fields('s')
+      ->condition('s.textgroup', $textgroup)
+      ->condition('s.context', $context, 'LIKE');
+
+    foreach ($query->execute()->fetchAll() as $source) {
+      foreach ($replace as $field => $value) {
+        $source->$field = $value;
+      }
+      // Recalculate location, context, objectindex
+      $source->context = $source->type . ':' . $source->objectid . ':' . $source->property;
+      $source->location = $source->textgroup . ':' . $source->context;
+      $source->objectindex = (int)$source->objectid;
+      // Update source string.
+      $update = array(
+        'textgroup' => $source->textgroup,
+        'context' => $source->context,
+      );
+      db_update('locales_source')
+        ->fields($update + array('location' => $source->location))
+        ->condition('lid', $source->lid)
+        ->execute();
+
+      // Update object data.
+      db_update('i18n_string')
+      ->fields($update + array(
+        'type' => $source->type,
+        'objectid' => $source->objectid,
+        'property' => $source->property,
+        'objectindex' => $source->objectindex,
+      ))
+      ->condition('lid', $source->lid)
+      ->execute();
+      $count++;
+    }
+    drupal_set_message(t('Updated @count string names from %oldname to %newname.', array('@count' => $count, '%oldname' => $oldname, '%newname' => $newname)));
+  }
+}
+
+/**
+ * Populate fields from old locale table (textgroup, location) and drop indexes from locales_source
+ */
+function i18n_string_update_7000() {
+  // @todo Update from d6
+  variable_del('i18nstrings_allowed_textgroups');
+  // If we've got old table from D6, move data to new one
+  if (db_table_exists('i18n_strings')) {
+    // First of all clean up strings that don't have a locale source, see http://drupal.org/node/1186692
+    db_query("DELETE FROM {i18n_strings} WHERE lid NOT IN (SELECT lid FROM {locales_source})");
+    db_query("INSERT INTO {i18n_string}(lid, objectid, type, property, objectindex, format) SELECT lid, objectid, type, property, objectindex, format FROM {i18n_strings}");
+    // Update and populate textgroup field
+    db_query("UPDATE {i18n_string} s SET s.textgroup = (SELECT l.textgroup FROM {locales_source} l WHERE l.lid = s.lid)");
+    // Populate context field. We could use CONCAT_WS but I guess this is more standard.
+    db_query("UPDATE {i18n_string} SET context = CONCAT(type, ':', objectid, ':', property)");
+    db_query("UPDATE {locales_source} s INNER JOIN {i18n_string} i ON s.lid = i.lid SET s.context = i.context");
+  }
+}
+
+/**
+ * Drop obsoleted i18n_strings table if exists
+ */
+function i18n_string_update_7001() {
+  if (db_table_exists('i18n_strings')) {
+    db_drop_table('i18n_strings');
+  }
+}
+
+/**
+ * Notes for update script
+ */
+// Added fields: context, textgroup
+//
+// Drop all indexes from locales_source
+// Update format field
+// Update string names: profile, cck => field
+// Update string names:
+
+/**
+ * Old strings to update. All these will be handled by i18n_field module
+ *
+ * 'cck:field:'. $content_type .'-'. $field_name .':widget_label'
+ *  --> 'field:$field_name:$bundle:label' (though not used atm)
+ * 'cck:field:'. $content_type .'-'. $field_name .':widget_description'
+ *  --> 'field:$field_name:$bundle:description'
+ * 'cck:fieldgroup:'. $content_type .'-'. $group_name .':display_description'
+ * 'cck:fieldgroup:'. $content_type .'-'. $group_name .':form_description', $group['settings']['form']['description']);
+ *
+ * Profile:
+ * profile:field:$field_name:title|explanation|options
+ * "profile:category", $field->category
+ *
+ * Node type
+ *  nodetype:type:[type]:[property] -> node:type:[type]:[property]
+ *  Property names: title -> title_label
+ */
\ No newline at end of file
diff --git a/kolab.org/www/drupal-7.18/sites/all/modules/i18n/i18n_string/i18n_string.module b/kolab.org/www/drupal-7.18/sites/all/modules/i18n/i18n_string/i18n_string.module
new file mode 100644
index 0000000..9c232c0
--- /dev/null
+++ b/kolab.org/www/drupal-7.18/sites/all/modules/i18n/i18n_string/i18n_string.module
@@ -0,0 +1,940 @@
+<?php
+
+/**
+ * @file
+ * Internationalization (i18n) package - translatable strings.
+ *
+ * Object oriented string translation using locale and textgroups. As opposed to core locale strings,
+ * all strings handled by this module will have a unique id (name), composed by several parts
+ *
+ * A string name or string id will have the form 'textgroup:type:objectid:property'. Examples:
+ *
+ * - 'profile:field:profile_name:title', will be the title for the profile field 'profile_name'
+ * - 'taxonomy:term:tid:name', will be the name for the taxonomy term tid
+ * - 'views:view_name:display_id:footer', footer text
+ *
+ * Notes:
+ * - The object id must be an integer. This is intended for quick indexing of some properties
+ *
+ * Some concepts
+ * - Textgroup. Group the string belongs to, defined by locale hook.
+ * - Location. Unique id of the string for this textgroup.
+ * - Name. Unique absolute id of the string: textgroup + location.
+ * - Context. Object with textgroup, type, objectid, property.
+ *
+ * Default language
+ * - Default language may be English or not. It will be the language set as default.
+ *   Source strings will be stored in default language.
+ * - In the traditional i18n use case you shouldn't change the default language once defined.
+ *
+ * Default language changes
+ * - You might result in the need to change the default language at a later point.
+ * - Enabling translation of default language will curcumvent previous limitations.
+ * - Check i18n_string_translate_langcode() for more details.
+ *
+ * The API other modules to translate/update/remove user defined strings consists of
+ *
+ * @see i18n_string($name, $string, $langcode)
+ * @see i18n_string_update($name, $string, $format)
+ * @see i18n_string_remove($name, $string)
+ *
+ * @author Jose A. Reyero, 2007
+ */
+
+/**
+ * Translated string is current.
+ */
+define('I18N_STRING_STATUS_CURRENT', 0);
+
+/**
+ * Translated string needs updating as the source has been edited.
+ */
+define('I18N_STRING_STATUS_UPDATE', 1);
+
+/**
+ * Source string is obsoleted, cannot be found anymore. To be deleted.
+ */
+define('I18N_STRING_STATUS_DELETE', 2);
+
+/**
+ * Special string formats/filters: Run through filter_xss
+ */
+define('I18N_STRING_FILTER_XSS', 'FILTER_XSS');
+
+/**
+ * Special string formats/filters: Run through filter_xss_admin
+ */
+define('I18N_STRING_FILTER_XSS_ADMIN', 'FILTER_XSS_ADMIN');
+
+/**
+ * Implements hook_help().
+ */
+function i18n_string_help($path, $arg) {
+  switch ($path) {
+    case 'admin/help#i18n_string':
+      $output = '<p>' . t('This module adds support for other modules to translate user defined strings. Depending on which modules you have enabled that use this feature you may see different text groups to translate.') . '<p>';
+      $output .= '<p>' . t('This works differently to Drupal standard localization system: The strings will be translated from the <a href="@configure-strings">source language</a>, which defaults to the site default language (it may not be English), so changing the default language may cause all these translations to be broken.', array('@configure-strings' => url('admin/config/regional/i18n/strings'))) . '</p>';
+      $output .= '<ul>';
+      $output .= '<li>' . t('To search and translate strings, use the <a href="@translate-interface">translation interface</a> pages.', array('@translate-interface' => url('admin/config/regional/translate'))) . '</li>';
+      $output .= '<li>' . t('If you are missing strings to translate, use the <a href="@refresh-strings">refresh strings</a> page.', array('@refresh-strings' => url('admin/build/translate/refresh'))) . '</li>';
+      $output .= '</ul>';
+      $output .= '<p>' . t('Read more on the <em>Internationalization handbook</em>: <a href="http://drupal.org/node/313293">Translating user defined strings</a>.') . '</p>';
+      return $output;
+
+    case 'admin/config/regional/translate/i18n_string':
+      $output = '<p>' . t('On this page you can refresh and update values for user defined strings.') . '</p>';
+      $output .= '<ul>';
+      $output .= '<li>' . t('Use the refresh option when you are missing strings to translate for a given text group. All the strings will be re-created keeping existing translations.') . '</li>';
+      $output .= '<li>' . t('Use the update option when some of the strings had been previously translated with the localization system, but the translations are not showing up for the configurable strings.') . '</li>';
+      $output .= '</ul>';
+      $output .= '<p>' . t('To search and translate strings, use the <a href="@translate-interface">translation interface</a> pages.', array('@translate-interface' => url('admin/config/regional/translate'))) . '</p>';
+      $output .= '<p>' . t('<strong>Important:</strong> To configure which text formats are safe for translation, visit the <a href="@configure-strings">configure strings</a> page before refreshing your strings.', array('@configure-strings' => url('admin/config/regional/i18n/strings'))) . '</p>';
+      return $output;
+
+    case 'admin/config/regional/language':
+      $output = '<p>' . t('<strong>Warning</strong>: Changing the default language may have unwanted effects on string translations. Check also the <a href="@configure-strings">source language</a> for translations and read more about <a href="@i18n_string-help">String translation</a>', array('@configure-strings' => url('admin/config/regional/i18n/strings'), '@i18n_string-help' => url('admin/help/i18n_string'))) . '</p>';
+      return $output;
+    case 'admin/config/regional/i18n/strings':
+      $output = '<p>' . t('When translating user defined strings that have a text format associated, translators will be able to edit the text before it is filtered which may be a security risk for some filters. An obvious example is when using the PHP filter but other filters may also be dangerous.') . '</p>';
+      $output .= '<p>' . t('As a general rule <strong>do not allow any filtered text to be translated unless the translators already have access to that text format</strong>. However if you are doing all your translations through this site\'s translation UI or the Localization client, and never importing translations for other textgroups than <i>default</i>, filter access will be checked for translators on every translation page.') . '</p>';
+      $output .= '<p>' . t('<strong>Important:</strong> After disallowing some text format, use the <a href="@refresh-strings">refresh strings</a> page so forbidden strings are deleted and not allowed anymore for translators.', array('@refresh-strings' => url('admin/config/regional/translate/i18n_string'))) . '</p>';
+      return $output;
+    case 'admin/config/filters':
+      return '<p>' . t('After updating your text formats do not forget to review the list of formats allowed for string translations on the <a href="@configure-strings">configure translatable strings</a> page.', array('@configure-strings' => url('admin/config/regional/i18n/strings'))) . '</p>';
+  }
+}
+
+/**
+ * Implements hook_menu().
+ */
+function i18n_string_menu() {
+  $items['admin/config/regional/translate/i18n_string'] = array(
+    'title' => 'Strings',
+    'description' => 'Refresh user defined strings.',
+    'weight' => 20,
+    'type' => MENU_LOCAL_TASK,
+    'page callback' => 'drupal_get_form',
+    'page arguments' => array('i18n_string_admin_refresh_form'),
+    'file' => 'i18n_string.admin.inc',
+    'access arguments' => array('translate interface'),
+  );
+  $items['admin/config/regional/i18n/strings'] = array(
+    'title' => 'Strings',
+    'description' => 'Options for user defined strings.',
+    'weight' => 20,
+    'type' => MENU_LOCAL_TASK,
+    'page callback' => 'drupal_get_form',
+    'page arguments' => array('variable_edit_form', array('i18n_string_allowed_formats', 'i18n_string_source_language')),
+    'access arguments' => array('administer site configuration'),
+  );
+  // AJAX callback path for strings.
+  $items['i18n_string/save'] = array(
+    'title' => 'Save string',
+    'page callback' => 'i18n_string_l10n_client_save_string',
+    'access arguments' => array('use on-page translation'),
+    'file' => 'i18n_string.pages.inc',
+    'type' => MENU_CALLBACK,
+  );
+  return $items;
+}
+
+/**
+ * Implements hook_menu_alter().
+ *
+ * Take over the locale translation page.
+ */
+function i18n_string_menu_alter(&$items) {
+  $items['admin/config/regional/translate/edit/%'] = array(
+    'title' => 'Edit string',
+    'page callback' => 'drupal_get_form',
+    'page arguments' => array('i18n_string_locale_translate_edit_form', 5),
+    'access arguments' => array('translate interface'),
+    'file' => 'i18n_string.pages.inc',
+    'file path' => drupal_get_path('module', 'i18n_string'),
+  );
+}
+
+/**
+ * Implements hook_hook_info().
+ */
+function i18n_string_hook_info() {
+  $hooks['i18n_string_info'] =
+  $hooks['i18n_string_list'] =
+  $hooks['i18n_string_refresh'] =
+  $hooks['i18n_string_objects'] = array(
+    'group' => 'i18n',
+  );
+  return $hooks;
+}
+
+/**
+ * Implements hook_locale().
+ *
+ * Provide the information from i18n_string groups to locale module
+ */
+function i18n_string_locale($op = 'groups') {
+  if ($op == 'groups') {
+    $groups = array();
+    foreach (i18n_string_group_info() as $name => $info) {
+      $groups[$name] = $info['title'];
+    }
+    return $groups;
+  }
+}
+
+/**
+ * Implements hook_permission().
+ */
+function i18n_string_permission() {
+  return array(
+    'translate user-defined strings' => array(
+      'title' => t('Translate user-defined strings'),
+      'description' => t('Translate user-defined strings that are created as part of content or configuration.'),
+      'restrict access' => TRUE,
+    ),
+    'translate admin strings' => array(
+      'title' => t('Translate admin strings'),
+      'description' => t('Translate administrative strings with a very permissive XSS/HTML filter that allows all HTML tags.'),
+      'restrict access' => TRUE,
+    ),
+  );
+}
+
+/**
+ * Implements hook_modules_enabled().
+ */
+function i18n_string_modules_enabled($modules) {
+  module_load_include('admin.inc', 'i18n_string');
+  i18n_string_refresh_enabled_modules($modules);
+}
+
+/**
+ * Implements hook_modules_uninstalled().
+ */
+function i18n_string_modules_uninstalled($modules) {
+  module_load_include('admin.inc', 'i18n_string');
+  i18n_string_refresh_uninstalled_modules($modules);
+}
+
+/**
+ * Implements hook_form_FORM_ID_alter()
+ */
+function i18n_string_form_l10n_client_form_alter(&$form, &$form_state) {
+  $form['#action'] = url('i18n_string/save');
+}
+
+/**
+ * Implements hook_form_FORM_ID_alter()
+ */
+function i18n_string_form_locale_translate_export_po_form_alter(&$form, $form_state) {
+  $names = locale_language_list('name', TRUE);
+  if (i18n_string_source_language() != 'en' && array_key_exists('en', $names)) {
+    $form['langcode']['#options']['en'] = $names['en'];
+  }
+}
+
+/**
+ * Implements hook_form_FORM_ID_alter()
+ */
+function i18n_string_form_locale_translate_import_form_alter(&$form, $form_state) {
+  if (i18n_string_source_language() != 'en') {
+    $names = locale_language_list('name', TRUE);
+    if (array_key_exists('en', $names)) {
+      $form['import']['langcode']['#options'][t('Already added languages')]['en'] = $names['en'];
+    }
+    else {
+      $form['import']['langcode']['#options'][t('Languages not yet added')]['en'] = t('English');
+    }
+  }
+  $form['#submit'][] = 'i18n_string_locale_translate_import_form_submit';
+}
+
+/**
+ * Update string data after import form submit
+ */
+function i18n_string_locale_translate_import_form_submit($form, &$form_state) {
+  if (!drupal_get_messages('error', FALSE) && i18n_string_group_info($form_state['values']['group'])) {
+    i18n_string_textgroup($form_state['values']['group'])->update_check();
+  }
+}
+
+/**
+ * Check if translation is required for this language code.
+ *
+ * Translation is required when default language is different from the given
+ * language, or when default language translation is explicitly enabled.
+ *
+ * No UI is provided to enable translation of default language. On the other
+ * hand, you can enable/disable translation for a specific language by adding
+ * the following to your settings.php
+ *
+ * @param $langcode
+ *   Optional language code to check. It will default to current request language.
+ * @code
+ *   // Enable translation of specific language. Language code is 'xx'
+ *   $conf['i18n_string_translate_langcode_xx'] = TRUE;
+ *   // Disable translation of specific language. Language code is 'yy'
+ *   $conf['i18n_string_translate_langcode_yy'] = FALSE;
+ * @endcode
+ */
+function i18n_string_translate_langcode($langcode = NULL) {
+  $translate = &drupal_static(__FUNCTION__);
+  $langcode = isset($langcode) ? $langcode : i18n_langcode();
+  if (!isset($translate[$langcode])) {
+    $translate[$langcode] = variable_get('i18n_string_translate_langcode_' . $langcode, i18n_string_source_language() != $langcode);
+  }
+  return $translate[$langcode];
+}
+
+/**
+ * Create string class from string name
+ */
+function i18n_string_build($name, $string = NULL) {
+  list ($group, $context) = i18n_string_context($name);
+  return i18n_string_textgroup($group)->build_string($context, $string);
+}
+
+/**
+ * Update / create translation source for user defined strings.
+ *
+ * @param $name
+ *   Array or string concatenated with ':' that contains textgroup and string context
+ * @param $string
+ *   Source string in default language. Default language may or may not be English.
+ *   Array of key => string to update multiple strings at once
+ * @param $options
+ *   Array with additional options:
+ *   - 'format', String format if the string has text format
+ *   - 'messages', Whether to print out status messages
+ */
+function i18n_string_update($name, $string, $options = array()) {
+  if (is_array($string)) {
+    return i18n_string_multiple('update', $name, $string, $options);
+  }
+  else {
+    list($textgroup, $context) = i18n_string_context($name);
+    return i18n_string_textgroup($textgroup)->context_update($context, $string, $options);
+  }
+}
+
+/**
+ * Update context for strings.
+ *
+ * As some string locations depend on configurable values, the field needs sometimes to be updated
+ * without losing existing translations. I.e:
+ * - profile fields indexed by field name.
+ * - content types indexted by low level content type name.
+ *
+ * Example:
+ *  'profile:field:oldfield:*' -> 'profile:field:newfield:*'
+ */
+function i18n_string_update_context($oldname, $newname) {
+  module_load_install('i18n_string');
+  return i18n_string_install_update_context($oldname, $newname);
+}
+
+/**
+ * Get textgroup handler
+ */
+function i18n_string_textgroup($textgroup) {
+  $groups = &drupal_static(__FUNCTION__);
+  if (!isset($groups[$textgroup])) {
+    $class = i18n_string_group_info($textgroup, 'class', 'i18n_string_textgroup_default');
+    $groups[$textgroup] = new $class($textgroup);
+  }
+  return $groups[$textgroup];
+}
+
+/**
+ * Check whether a string format is allowed for translation.
+ */
+function i18n_string_allowed_format($format_id = NULL) {
+  if (!$format_id || $format_id === I18N_STRING_FILTER_XSS || $format_id === I18N_STRING_FILTER_XSS_ADMIN) {
+    return TRUE;
+  }
+  else {
+    // Check the format still exists an it is in the allowed formats list.
+    return filter_format_load($format_id) && in_array($format_id, variable_get('i18n_string_allowed_formats', array(filter_fallback_format())), TRUE);
+  }
+}
+
+/**
+ * Convert string name into textgroup and string context
+ *
+ * @param $name
+ *   Array or string concatenated with ':' that contains textgroup and string context
+ * @param $replace
+ *   Parameter to replace the placeholder ('*') if we are dealing with multiple strings
+ *   Or parameter to append to context if there's no placeholder
+ *
+ * @return array
+ *   The first element will be the text group name
+ *   The second one will be an array with string name elements, without textgroup
+ */
+function i18n_string_context($name, $replace = NULL) {
+  $parts = is_array($name) ? $name : explode(':', $name);
+  if ($replace) {
+    $key = array_search('*', $parts);
+    if ($key !== FALSE) {
+      $parts[$key] = $replace;
+    }
+    elseif (count($parts) < 4) {
+      array_push($parts, $replace);
+    }
+  }
+  $textgroup = array_shift($parts);
+  $context = $parts;
+  return array($textgroup, $context);
+}
+
+/**
+ * Mark form element as localizable
+ */
+function i18n_string_element_mark(&$element) {
+  $description = '<strong>' . t('This string will be localizable. You can translate it using the <a href="@translate-interface">translate interface</a> pages.', array('@translate-interface' => url('admin/config/regional/translate/translate'))) . '</strong>';
+  if (empty($element['#description'])) {
+    $element['#description'] = $description;
+  }
+  else {
+    $element['#description'] .= ' ' . $description;
+  }
+}
+
+
+/**
+ * Get source string object.
+ *
+ * This returns the i18nstring object only when it has a source.
+ *
+ * @return i18n_string_object
+ */
+function i18n_string_get_source($name) {
+  return i18n_string_build($name)->get_source();
+}
+
+/**
+ * Get full string object.
+ *
+ * Builds string and loads the source if available.
+ *
+ * @return i18n_string_object
+ */
+function i18n_string_get_string($name, $default = NULL) {
+  $i18nstring = i18n_string_build($name, $default);
+  $i18nstring->get_source();
+  return $i18nstring;
+}
+
+/**
+ * Get full string object by lid.
+ */
+function i18n_string_get_by_lid($lid) {
+  $strings = i18n_string_load_multiple(array('lid' => $lid));
+  return reset($strings);
+}
+
+/**
+ * Load multiple strings, including string source
+ *
+ * @param $conditions
+ *   Array of conditions for i18n_string table.
+ *
+ * @return $strings
+ *   List of i18n string objects
+ */
+function i18n_string_load_multiple($conditions) {
+  // The primary table here will be i18n_string, though we add source too.
+  $query = db_select('i18n_string', 'i')
+    ->fields('i');
+  $query->leftJoin('locales_source', 's', 'i.lid = s.lid');
+  $query->fields('s', array('source', 'version', 'location'));
+  // Add text group condition and add conditions to the query
+  foreach ($conditions as $field => $value) {
+    $alias = in_array($field, array('source', 'version', 'location')) ? 's' : 'i';
+    $query->condition($alias . '.' . $field, $value);
+  }
+  // this patch  is a workaround for core file bug in file
+  // include/database/prefetch.inc (see: http://drupal.org/node/1567216)
+  // return $query->execute()->fetchAll(PDO::FETCH_CLASS, 'i18n_string_object');
+  $stmt = $query->execute();
+  $stmt->setFetchMode(PDO::FETCH_CLASS, 'i18n_string_object');
+  return $stmt->fetchAll();
+}
+
+/**
+ * Get textgroup info, from hook_locale('info')
+ *
+ * @param $group
+ *   Text group name.
+ * @param $default
+ *   Default value to return for a property if not set.
+ */
+function i18n_string_group_info($group = NULL, $property = NULL, $default = NULL) {
+  $info = &drupal_static(__FUNCTION__ , NULL);
+
+  if (!isset($info)) {
+    $info = module_invoke_all('i18n_string_info');
+    drupal_alter('i18n_string_info', $info);
+  }
+
+  if ($group && $property) {
+    return isset($info[$group][$property]) ? $info[$group][$property] : $default;
+  }
+  elseif ($group) {
+    return isset($info[$group]) ? $info[$group] : array();
+  }
+  else {
+    return $info;
+  }
+}
+
+
+/**
+ * Translate / update multiple strings
+ *
+ * @param $strings
+ *   Array of name => string pairs
+ */
+function i18n_string_multiple($operation, $name, $strings, $options = array()) {
+  $result = array();
+  // Strings may be an array of properties, we need to shift it
+  if ($operation == 'remove') {
+    $strings = array_flip($strings);
+  }
+  foreach ($strings as $key => $string) {
+    list($textgroup, $context) = i18n_string_context($name, $key);
+    array_unshift($context, $textgroup);
+    $result[$key] = call_user_func('i18n_string_' . $operation, $context, $string, $options);
+  }
+  return $result;
+}
+
+/**
+ * @ingroup i18napi
+ * @{
+ */
+
+/**
+ * Get translation for user defined string.
+ *
+ * This function is intended to return translations for plain strings that have NO text format
+ *
+ * @param $name
+ *   Array or string concatenated with ':' that contains textgroup and string context
+ * @param $string
+ *   String in default language or array of strings to be translated
+ * @param $options
+ *   An associative array of additional options, with the following keys:
+ *   - 'langcode' (defaults to the current language) The language code to translate to a language other than what is used to display the page.
+ *   - 'filter' Filtering callback to apply to the translated string only
+ *   - 'format' Input format to apply to the translated string only
+ *   - 'callback' Callback to apply to the result (both to translated or untranslated string
+ *   - 'sanitize' Whether to filter the translation applying the text format if any, default is TRUE
+ *   - 'sanitize default' Whether to filter the default value if no translation found, default is FALSE
+ */
+function i18n_string_translate($name, $string, $options = array()) {
+  if (is_array($string)) {
+    return i18n_string_translate_list($name, $string, $options);
+  }
+  else {
+    $options['langcode'] = $langcode = isset($options['langcode']) ? $options['langcode'] : i18n_langcode();
+    if (i18n_string_translate_langcode($langcode)) {
+      list($textgroup, $context) = i18n_string_context($name);
+      $translation = i18n_string_textgroup($textgroup)->context_translate($context, $string, $options);
+      // Add for l10n client if available, we pass translation object that contains the format
+      i18n_string_l10n_client_add($translation, $langcode);
+      return $translation->format_translation($langcode, $options);
+    }
+    else {
+      // If we don't want to translate to this language, format and return
+      $options['sanitize'] = !empty($options['sanitize default']);
+      return i18n_string_format($string, $options);
+    }
+  }
+}
+
+/**
+ * Check user access to translate a specific string.
+ *
+ * If the string has a format the user is not allowed to edit, it will return FALSE
+ *
+ * @param $string_format;
+ *   String object or $format_id
+ */
+function i18n_string_translate_access($string_format, $account = NULL) {
+  $format_id = is_object($string_format) ? i18n_object_field($string_format, 'format') : $string_format;
+  return user_access('translate interface', $account) &&
+    (empty($format_id) || i18n_string_allowed_format($format_id) && ($format = filter_format_load($format_id)) && filter_access($format, $account));
+}
+
+/**
+ * Check whether there is any problem for the  user to translate a specific string.
+ *
+ * Here we assume the user has 'translate interface' access that should have
+ * been checked for the page. Possible reasons a user cannot translate a string:
+ *
+ * @param $i18nstring
+ *   String object.
+ * @param $account
+ *   Optional user account, defaults to current user.
+ *
+ * @return
+ *   None or empty string if the user has access to translate the string.
+ *   Message if the user cannot translate that string.
+ */
+function i18n_string_translate_check_string($i18nstring, $account = NULL) {
+  if (!user_access('translate interface', $account) || !user_access('translate user-defined strings', $account)) {
+    return t('This is a user-defined string. You are not allowed to translate these strings.');
+  }
+  elseif (!empty($i18nstring->format)) {
+    if (!i18n_string_allowed_format($i18nstring->format)) {
+      $format = filter_format_load($i18nstring->format);
+      return t('This string uses the %name text format. Strings with this format are not allowed for translation.', array('%name' => $format->name));
+    }
+    elseif ($format = filter_format_load($i18nstring->format)) {
+      // It is a text format, check user access to that text format.
+      if (!filter_access($format, $account)) {
+        return t('This string uses the %name text format. You are not allowed to translate or edit texts with this format.', array('%name' => $format->name));
+      }
+    }
+    else {
+      // This is one of our special formats, I18N_STRING_FILTER_*
+      if ($i18nstring->format == I18N_STRING_FILTER_XSS_ADMIN && !user_access('translate admin strings', $account)) {
+        return t('The source string is an administrative string. You are not allowed to translate these strings.');
+      }
+    }
+  }
+  // No error message, it should be OK to translate.
+  return '';
+}
+
+/**
+ * Format the resulting translation or the default string applying callbacks
+ *
+ * @param $string
+ *   Text string.
+ * @param $options
+ *   Array of options for string formatting:
+ *   - 'format', text format to apply to the string, defaults to none.
+ *   - 'sanitize', whether to apply the text format, defaults to TRUE.
+ *   - 'cache', text format parameter.
+ *   - 'langcode', text format parameter, defaults to current page language.
+ *   - 'allowed_tags', allowed HTML tags when format is I18N_STRING_FILTER_XSS
+ */
+function i18n_string_format($string, $options = array()) {
+  $options += array('langcode' => i18n_langcode(), 'format' => FALSE, 'sanitize' => TRUE, 'cache' => FALSE);
+  // Apply format and callback
+  if ($string) {
+    if ($options['sanitize']) {
+      if ($options['format']) {
+        // Handle special format values (xss, xss_admin)
+        switch ($options['format']) {
+          case I18N_STRING_FILTER_XSS:
+            $string = !empty($options['allowed_tags']) ? filter_xss($string, $options['allowed_tags']) : filter_xss($string);
+            break;
+          case I18N_STRING_FILTER_XSS_ADMIN:
+            $string = filter_xss_admin($string);
+            break;
+          default:
+            $string = check_markup($string, $options['format'], $options['langcode'], $options['cache']);
+        }
+      }
+      else {
+        $string = check_plain($string);
+      }
+    }
+    if (isset($options['callback'])) {
+      $string = call_user_func($options['callback'], $string);
+    }
+  }
+  // Finally, apply prefix and suffix
+  $options += array('prefix' => '', 'suffix' => '');
+  return $options['prefix'] . $string . $options['suffix'];
+}
+
+/**
+ * Get filtered translation.
+ *
+ * This function is intended to return translations for strings that have a text format
+ *
+ * @param $name
+ *   Array or string concatenated with ':' that contains textgroup and string context
+ * @param $default
+ *   Default string to return if not found, already filtered
+ * @param $options
+ *   Array with additional options.
+ */
+function i18n_string_text($name, $default, $options = array()) {
+  $options += array('format' => filter_fallback_format(), 'sanitize' => TRUE);
+  return i18n_string_translate($name, $default, $options);
+}
+
+/**
+ * Translation for plain string. In case it finds a translation it applies check_plain() to it
+ *
+ * @param $name
+ *   Array or string concatenated with ':' that contains textgroup and string context
+ * @param $default
+ *   Default string to return if not found
+ * @param $options
+ *   Array with additional options
+ */
+function i18n_string_plain($name, $default, $options = array()) {
+  $options += array('filter' => 'check_plain');
+  return i18n_string_translate($name, $default, $options);
+}
+
+/**
+ * Get source language code for translations
+ */
+function i18n_string_source_language() {
+  return variable_get('i18n_string_source_language', language_default('language'));
+}
+
+/**
+ * Translation for list of options
+ *
+ * @param $options
+ *   Array with additional options, some changes
+ *   - 'index' => field that will be mapped to the array key (defaults to 'property')
+ */
+function i18n_string_translate_list($name, $strings, $options = array()) {
+  $options['langcode'] = $langcode = isset($options['langcode']) ? $options['langcode'] : i18n_langcode();
+  // If language is default, just return
+  if (i18n_string_translate_langcode($langcode)) {
+    // Get textgroup context, preserve placeholder
+    list($textgroup, $context) = i18n_string_context($name, '*');
+    $translations = i18n_string_textgroup($textgroup)->multiple_translate($context, $strings, $options);
+    // Add for l10n client if available, we pass translation object that contains the format
+    foreach ($translations as $index => $translation) {
+      i18n_string_l10n_client_add($translation, $langcode);
+      $strings[$index] = $translation->format_translation($langcode, $options);
+    }
+  }
+  else {
+    // Format and return
+    foreach ($strings as $key => $string) {
+      $strings[$key] = i18n_string_format($string, $options);
+    }
+  }
+  return $strings;
+}
+
+/**
+ * Remove source and translations for user defined string.
+ *
+ * Though for most strings the 'name' or 'string id' uniquely identifies that string,
+ * there are some exceptions (like profile categories) for which we need to use the
+ * source string itself as a search key.
+ *
+ * @param $name
+ *   String name
+ * @param $string
+ *   Optional source string (string in default language).
+ *   Array of string properties to remove
+ */
+function i18n_string_remove($name, $string = NULL, $options = array()) {
+  if (is_array($string)) {
+    return i18n_string_multiple('remove', $name, $string, $options);
+  }
+  else {
+    list($textgroup, $context) = i18n_string_context($name);
+    return i18n_string_textgroup($textgroup)->context_remove($context, $string, $options);
+  }
+}
+
+/**
+ * @} End of "ingroup i18napi".
+ */
+
+/*** l10n client related functions ***/
+
+/**
+ * Add string to l10n strings if enabled and allowed for this string
+ *
+ * @param $context
+ *   String object
+ */
+function i18n_string_l10n_client_add($string, $langcode) {
+  // If current language add to l10n client list for later on page translation.
+  // If langcode translation was disabled we are not supossed to reach here.
+  if (($langcode == i18n_langcode()) && function_exists('l10_client_add_string_to_page') && user_access('translate interface')) {
+    if (!$string->check_translate_access()) {
+      $translation = $string->get_translation($langcode);
+      $source = !empty($string->source) ? $string->source : $string->string;
+      l10_client_add_string_to_page($source, $translation ? $translation : TRUE, $string->textgroup, $string->context);
+    }
+  }
+}
+
+/**
+ * Get information about object string translation
+ */
+function i18n_string_object_info($type = NULL, $property = NULL) {
+  if ($type) {
+    if (($info = i18n_object_info($type, 'string translation'))) {
+      if ($property) {
+        return isset($info[$property]) ? $info[$property] : NULL;
+      }
+      else {
+        return $info;
+      }
+    }
+  }
+  else {
+    $list = array();
+    foreach (i18n_object_info() as $type => $info) {
+      if (!empty($info['string translation'])) {
+        $list[$type] = $info;
+      }
+    }
+    return $list;
+  }
+}
+
+/**
+ * Implements hook_i18n_object_info_alter().
+ *
+ * Set a different default object wrapper for objects that have string translation.
+ */
+function i18n_string_i18n_object_info_alter(&$object_info) {
+  foreach ($object_info as $type => &$info) {
+    if (!empty($info['string translation']) && (empty($info['class']) || $info['class'] == 'i18n_object_wrapper')) {
+      $info['class'] = 'i18n_string_object_wrapper';
+    }
+  }
+}
+
+/**
+ * Translate object properties
+ *
+ * We clone the object previously so we don't risk translated properties being saved
+ *
+ * @param $type
+ *   Object type
+ * @param $object
+ *   Object or array
+ */
+function i18n_string_object_translate($type, $object, $options = array()) {
+  $langcode = isset($options['langcode']) ? $options['langcode'] : i18n_langcode();
+  if (i18n_string_translate_langcode($langcode)) {
+    // Object properties will be returned without filtering as in the original one.
+    $options += array('sanitize' => FALSE);
+    return i18n_object($type, $object)->translate($langcode, $options);
+  }
+  else {
+    return $object;
+  }
+}
+
+/**
+ * Remove object strings, because object is deleted
+ *
+ * @param $type
+ *   Object type
+ * @param $object
+ *   Object or array
+ */
+function i18n_string_object_remove($type, $object, $options = array()) {
+  return i18n_object($type, $object)->strings_remove($options);
+}
+
+/**
+ * Update object properties.
+ *
+ * @param $type
+ *   Object type
+ * @param $object
+ *   Object or array
+ */
+function i18n_string_object_update($type, $object, $options = array()) {
+  return i18n_object($type, $object)->strings_update($options);
+}
+
+/**
+ * Generic translation page for i18n_strings objects.
+ */
+function i18n_string_object_translate_page($object_type, $object_value, $language = NULL) {
+  module_load_include('inc', 'i18n_string', 'i18n_string.pages');
+  return i18n_string_translate_page_object($object_type, $object_value, $language);
+}
+
+/**
+ * Preload all strings for this textroup/context.
+ *
+ * This is a performance optimization to load all needed strings with a single query.
+ *
+ * Examples of valid string name to search are:
+ *  - 'taxonomy:term:*:title'
+ *    This will find all titles for taxonomy terms
+ *  - array('taxonomy', 'term', array(1,2), '*')
+ *    This will find all properties for taxonomy terms 1 and 2
+ *
+ * @param $name
+ *   Specially crafted string name, it may take '*' and array parameters for each element.
+ * @param $langcode
+ *   Language code to search translations. Defaults to current language.
+ *
+ * @return array()
+ *   String objects indexed by context.
+ */
+function i18n_string_translation_search($name, $langcode = NULL) {
+  $langcode = isset($langcode) ? $langcode : i18n_langcode();
+  list ($textgroup, $context) = i18n_string_context($name);
+  return i18n_string_textgroup($textgroup)->multiple_translation_search($context, $langcode);
+}
+
+/**
+ * Update / create translation for a certain source.
+ *
+ * @param $name
+ *   Array or string concatenated with ':' that contains textgroup and string context
+ * @param $translation
+ *   Translation string for this language code
+ * @param $langcode
+ *   The language code to translate to a language other than what is used to display the page.
+ * @param $source_string
+ *   Optional source string, just in case it needs to be created.
+ *
+ * @return mixed
+ *   Source string object if update was successful.
+ *   Null if source string not found.
+ *   FALSE if use doesn't have permission to edit this translation.
+ */
+function i18n_string_translation_update($name, $translation, $langcode, $source_string = NULL) {
+  if (is_array($translation)) {
+    return i18n_string_multiple('translation_update', $name, $translation, $langcode);
+  }
+  elseif ($source = i18n_string_get_source($name)) {
+    if ($langcode == i18n_string_source_language()) {
+      // It's the default language so we should update the string source as well.
+      i18n_string_update($name, $translation);
+    }
+    else {
+      list ($textgroup, $context) = i18n_string_context($name);
+      i18n_string_textgroup($textgroup)->update_translation($context, $langcode, $translation);
+    }
+    return $source;
+  }
+  elseif ($source_string) {
+    // We don't have a source in the database, so we need to create it, but only if we've got the source too.
+    // Note this string won't have any format.
+    i18n_string_update($name, $source_string);
+    return i18n_string_translation_update($name, $translation, $langcode);
+  }
+  else {
+    return NULL;
+  }
+}
+
+/**
+ * Count operation results by result value
+ */
+function _i18n_string_result_count($list) {
+  $result = array();
+  foreach ($list as $value) {
+    $key = (string)$value;
+    $result[$key] = isset($result[$key]) ? $result[$key] +1 : 1;
+  }
+  return $result;
+}
diff --git a/kolab.org/www/drupal-7.18/sites/all/modules/i18n/i18n_string/i18n_string.pages.inc b/kolab.org/www/drupal-7.18/sites/all/modules/i18n/i18n_string/i18n_string.pages.inc
new file mode 100644
index 0000000..86e7432
--- /dev/null
+++ b/kolab.org/www/drupal-7.18/sites/all/modules/i18n/i18n_string/i18n_string.pages.inc
@@ -0,0 +1,429 @@
+<?php
+
+/**
+ * @file
+ * Internationalization (i18n) package - translatable strings reusable admin UI.
+ *
+ * @author Jose A. Reyero, 2007
+ */
+
+// Load locale libraries
+include_once DRUPAL_ROOT . '/includes/locale.inc';
+include_once drupal_get_path('module', 'locale') . '/locale.admin.inc';
+
+/**
+ * Generate translate page from object
+ */
+function i18n_string_translate_page_object($object_type, $object_value, $language = NULL) {
+  // For backwards compatibility, ensure parameter is a language object
+  $language = i18n_language_object($language);
+  $langcode = $language ? $language->language : NULL;
+  // Get base keys for all these strings. Object key may be multiple like for blocks (module, delta)
+  $object = i18n_object($object_type, $object_value);
+  $strings = $object->get_strings(array('empty' => TRUE));
+
+  if (empty($langcode)) {
+    drupal_set_title(t('Translate !name', array('!name' => i18n_object_info($object_type, 'title'))));
+    return i18n_string_translate_page_overview($object, $strings);
+  }
+  else {
+    drupal_set_title(t('Translate to !language', array('!language' => i18n_language_name($langcode))));
+    return drupal_get_form('i18n_string_translate_page_form', $strings, $langcode);
+  }
+}
+
+/**
+ * Provide a core translation module like overview page for this object.
+ */
+function i18n_string_translate_page_overview($object, $strings) {
+  $build['i18n_overview'] = drupal_get_form('i18n_string_translate_page_overview_form', $object, $strings);
+  return $build;
+}
+
+/**
+ * Provide a core translation module like overview page for this object.
+ */
+function i18n_string_translate_page_overview_form($form, &$form_state, $object, $strings) {
+  //include_once DRUPAL_ROOT . '/includes/language.inc';
+  // Set the default item key, assume it's the first.
+  $item_title = reset($strings);
+  $header = array(
+    'language' => t('Language'),
+    'title' => t('Title'),
+    'status' => t('Status'),
+    'operations' => t('Operations')
+  );
+  $source_language = variable_get_value('i18n_string_source_language');
+  $rows = array();
+
+  foreach (language_list() as $langcode => $language) {
+    if ($langcode == $source_language) {
+      $items = array(
+        'language' => check_plain($language->name) . ' ' . t('(source)'),
+        'title' => check_plain($item_title->get_string()),
+        'status' => t('original'),
+        'operations' => l(t('edit'), $object->get_edit_path()),
+      );
+    }
+    else {
+      // Try to figure out if this item has any of its properties translated.
+      $translated = FALSE;
+      foreach ($strings as $i18nstring) {
+        if ($i18nstring->get_translation($langcode)) {
+          $translated = TRUE;
+          break;
+        }
+      }
+      // Translate the item that was requested to be displayed as title.
+      $items = array(
+        'language' => check_plain($language->name),
+        'title' => $item_title->format_translation($langcode, array('sanitize default' => TRUE)),
+        'status' => $translated ? t('translated') : t('not translated'),
+        'operations' => l(t('translate'), $object->get_translate_path($langcode), array('query' => drupal_get_destination())),
+      );
+    }
+    foreach ($items as $key => $markup) {
+      $rows[$langcode][$key] = $markup;
+      //$form['#rows'][$langcode][$key]['#markup'] = $markup;
+    }
+  }
+  // Build a form so it can be altered later, with all this information.
+  $form['object'] = array('#type' => 'value', '#value' => $object);
+  $form['source_language'] = array('#type' => 'value', '#value' => $source_language);
+  $form['languages'] = array(
+    '#header' => $header,
+    '#rows' => $rows,
+    '#theme' => 'table',
+  );
+  return $form;
+}
+
+/**
+ * Form builder callback for in-place string translation.
+ *
+ * @param $strings
+ *   Array of strings indexed by string name (may be indexed by group key too if $groups is present)
+ * @param $langcode
+ *   Language code to translate to.
+ * @param $groups
+ *   Optional groups to provide some string grouping. Array with group key and title pairs.
+ */
+function i18n_string_translate_page_form($form, &$form_state, $strings, $langcode, $groups = NULL) {
+  $form = i18n_string_translate_page_form_base($form, $langcode);
+  if ($groups) {
+    // I we've got groups, string list is grouped by group key.
+    $form['string_groups'] = array('#type' => 'value', '#value' => $strings);
+    foreach ($groups as $key => $title) {
+      $form['display'] = array(
+        '#type' => 'vertical_tabs',
+      );
+      $form['strings'][$key] = array(
+        '#group' => 'display',
+        '#title' => $title,
+        '#type' => 'fieldset',
+      ) + i18n_string_translate_page_form_strings($strings[$key], $langcode);
+    }
+  }
+  else {
+    // We add a single group with key 'all', but no tabs.
+    $form['string_groups'] = array('#type' => 'value', '#value' => array('all' => $strings));
+    $form['strings']['all'] = i18n_string_translate_page_form_strings($strings, $langcode);
+  }
+  return $form;
+}
+
+/**
+ * Create base form for string translation
+ */
+function i18n_string_translate_page_form_base($form, $langcode, $redirect = NULL) {
+  $form['langcode'] = array(
+    '#type' => 'value',
+    '#value' => $langcode,
+  );
+  $form['submit'] = array(
+    '#type' => 'submit',
+    '#value' => t('Save translation'),
+    '#weight' => 10,
+  );
+  if ($redirect) {
+    $form['#redirect'] = array(
+      $redirect,
+    );
+  }
+  // Add explicit validate and submit hooks so this can be used from inside any form.
+  $form['#submit'] = array('i18n_string_translate_page_form_submit');
+  return $form;
+}
+
+/**
+ * Create field elements for strings
+ */
+function i18n_string_translate_page_form_strings($strings, $langcode) {
+  $formats = filter_formats();
+  foreach ($strings as $item) {
+    // We may have a source or not. Load it, our string may get the format from it.
+    $source = $item->get_source();
+    $format_id = $source ? $source->format : $item->format;
+    $description = '';
+    // Check permissions to translate this string, depends on format, etc..
+    if ($message = $item->check_translate_access()) {
+      // We'll display a disabled element with the reason it cannot be translated.
+      $disabled = TRUE;
+      $description = $message;
+    }
+    else {
+      $disabled = FALSE;
+      $description = '';
+      // If we don't have a source and it can be translated, we create it.
+      if (!$source) {
+        // Enable messages just as a reminder these strings are not being updated properly.
+        $status = $item->update(array('messages' => TRUE));
+        if ($status === FALSE || $status === SAVED_DELETED) {
+          // We don't have a source string so nothing to translate here
+          $disabled = TRUE;
+        }
+        else {
+          $source = $item->get_source();
+        }
+      }
+    }
+
+    $default_value = $item->format_translation($langcode, array('langcode' => $langcode, 'sanitize' => FALSE, 'debug' => FALSE));
+    $form[$item->get_name()] = array(
+      '#title' => $item->get_title(),
+      '#type' => 'textarea',
+      '#default_value' => $default_value,
+      '#disabled' => $disabled,
+      '#description' => $description . _i18n_string_translate_format_help($format_id),
+      //'#i18n_string_format' => $source ? $source->format : 0,
+      // If disabled, provide smaller textarea (that can be expanded anyway).
+      '#rows' => $disabled ? 1 : min(ceil(str_word_count($default_value) / 12), 10),
+      // Change the parent for disabled strings so we don't get empty values later
+      '#parents' => array($disabled ? 'disabled_strings': 'strings', $item->get_name()),
+    );
+  }
+  return $form;
+}
+
+/**
+ * Form submission callback for in-place string translation.
+ */
+function i18n_string_translate_page_form_submit($form, &$form_state) {
+  $count = $success = 0;
+  foreach ($form_state['values']['strings'] as $name => $value) {
+    $count++;
+    list($textgroup, $context) = i18n_string_context(explode(':', $name));
+    $result = i18n_string_textgroup($textgroup)->update_translation($context, $form_state['values']['langcode'], $value);
+    $success += ($result ? 1 : 0);
+  }
+  if ($success) {
+    drupal_set_message(format_plural($success, 'A translation was saved successfully.', '@count translations were saved successfully.'));
+  }
+  if ($error = $count - $success) {
+    drupal_set_message(format_plural($error, 'A translation could not be saved.', '@count translations could not be saved.'), 'warning');
+  }
+  if (isset($form['#redirect'])) {
+    $form_state['redirect'] = $form['#redirect'];
+  }
+}
+
+/**
+ * Menu callback. Saves a string translation coming as POST data.
+ */
+function i18n_string_l10n_client_save_string() {
+  global $user, $language;
+
+  if (user_access('use on-page translation')) {
+    $textgroup = !empty($_POST['textgroup']) ? $_POST['textgroup'] : 'default';
+    // Other textgroups will be handled by l10n_client module
+    if (!i18n_string_group_info($textgroup)) {
+      return l10n_client_save_string();
+    }
+    elseif (isset($_POST['source']) && isset($_POST['target']) && !empty($_POST['context']) && !empty($_POST['form_token']) && drupal_valid_token($_POST['form_token'], 'l10n_client_form')) {
+      $name = $textgroup . ':' . $_POST['context'];
+      if ($i18nstring = i18n_string_get_source($name)) {
+        // Since this is not a real form, we double check access permissions here too.
+        if ($error = $i18nstring->check_translate_access()) {
+          $message = theme('l10n_client_message', array('message' => t('Not saved due to: !reason', array('!reason' => $error)), 'level' => WATCHDOG_WARNING));
+        }
+        else {
+          $result = i18n_string_translation_update($name, $_POST['target'], $language->language, $_POST['source']);
+          if ($result) {
+            $message = theme('l10n_client_message', array('message' => t('Translation saved locally for user defined string.'), 'level' => WATCHDOG_INFO));
+          }
+          elseif ($result === FALSE) {
+            $message = theme('l10n_client_message', array('message' => t('Not saved due to insufficient permissions.')));
+          }
+          else {
+            $message = theme('l10n_client_message', array('message' => t('Not saved due to unknown reason.')));
+          }
+        }
+      }
+      else {
+        $message = theme('l10n_client_message', array('message' => t('Not saved due to source string missing.')));
+      }
+    }
+    else {
+      $message = theme('l10n_client_message', array('message' => t('Not saved due to missing form values.')));
+    }
+    drupal_json_output($message);
+    exit;
+  }
+}
+
+
+/**
+ * User interface for string editing.
+ */
+function i18n_string_locale_translate_edit_form($form, &$form_state, $lid) {
+  // Fetch source string, if possible.
+  $source = db_query('SELECT source, context, textgroup, location FROM {locales_source} WHERE lid = :lid', array(':lid' => $lid))->fetchObject();
+  if (!$source) {
+    drupal_set_message(t('String not found.'), 'error');
+    drupal_goto('admin/config/regional/translate/translate');
+  }
+
+  // Add original text to the top and some values for form altering.
+  $form['original'] = array(
+    '#type'  => 'item',
+    '#title' => t('Original text'),
+    '#markup' => check_plain(wordwrap($source->source, 0)),
+  );
+  if (!empty($source->context)) {
+    $form['context'] = array(
+      '#type' => 'item',
+      '#title' => t('Context'),
+      '#markup' => check_plain($source->context),
+    );
+  }
+  $form['lid'] = array(
+    '#type'  => 'value',
+    '#value' => $lid
+  );
+  $form['textgroup'] = array(
+    '#type'  => 'value',
+    '#value' => $source->textgroup,
+  );
+  $form['location'] = array(
+    '#type'  => 'value',
+    '#value' => $source->location
+  );
+
+  // Include default form controls with empty values for all languages.
+  // This ensures that the languages are always in the same order in forms.
+  $languages = language_list();
+
+  // We don't need the default language value, that value is in $source.
+  $omit = $source->textgroup == 'default' ? 'en' : i18n_string_source_language();
+  unset($languages[($omit)]);
+  $form['translations'] = array('#tree' => TRUE);
+  // Approximate the number of rows to use in the default textarea.
+  $rows = min(ceil(str_word_count($source->source) / 12), 10);
+  foreach ($languages as $langcode => $language) {
+    $form['translations'][$langcode] = array(
+      '#type' => 'textarea',
+      '#title' => t($language->name),
+      '#rows' => $rows,
+      '#default_value' => '',
+    );
+  }
+
+  // Fetch translations and fill in default values in the form.
+  $result = db_query("SELECT DISTINCT translation, language FROM {locales_target} WHERE lid = :lid AND language <> :omit", array(':lid' => $lid, ':omit' => $omit));
+  foreach ($result as $translation) {
+    $form['translations'][$translation->language]['#default_value'] = $translation->translation;
+  }
+
+  $form['actions'] = array('#type' => 'actions');
+  $form['actions']['submit'] = array('#type' => 'submit', '#value' => t('Save translations'));
+
+  // Restrict filter permissions and handle validation and submission for i18n strings.
+  if (i18n_string_group_info($source->textgroup)) {
+    if ($i18nstring = i18n_string_get_by_lid($form['lid']['#value'])) {
+      $form['i18n_string'] = array('#type' => 'value', '#value' => $i18nstring);
+      if ($message = $i18nstring->check_translate_access()) {
+        drupal_set_message($message);
+        $disabled = TRUE;
+      }
+      // Add format help anyway, though the form may be disabled.
+      $form['translations']['format_help']['#markup'] = _i18n_string_translate_format_help($i18nstring->format);
+    }
+    else {
+      drupal_set_message(t('Source string not found.'), 'warning');
+      $disabled = TRUE;
+    }
+    if (!empty($disabled)) {
+      // Disable all form elements
+      $form['submit']['#disabled'] = TRUE;
+      foreach (element_children($form['translations']) as $langcode) {
+        $form['translations'][$langcode]['#disabled'] = TRUE;
+      }
+    }
+  }
+  return $form;
+}
+
+/**
+ * Process string editing form validations.
+ *
+ * If it is an allowed format, skip default validation, the text will be filtered later
+ */
+function i18n_string_locale_translate_edit_form_validate($form, &$form_state) {
+  if (empty($form_state['values']['i18n_string'])) {
+    // If not i18n string use regular locale validation.
+    $copy_state = $form_state;
+    locale_translate_edit_form_validate($form, $copy_state);
+  }
+}
+
+/**
+ * Process string editing form submissions.
+ *
+ * Mark translations as current.
+ */
+function i18n_string_locale_translate_edit_form_submit($form, &$form_state) {
+  // Invoke locale submission.
+  locale_translate_edit_form_submit($form, $form_state);
+  $lid = $form_state['values']['lid'];
+  foreach ($form_state['values']['translations'] as $key => $value) {
+    if (!empty($value)) {
+      // An update has been made, so we assume the translation is now current.
+      db_update('locales_target')
+        ->fields(array('i18n_status' => I18N_STRING_STATUS_CURRENT))
+        ->condition('lid', $lid)
+        ->condition('language', $key)
+        ->execute();
+    }
+  }
+}
+
+/**
+ * Help for text format.
+ */
+function _i18n_string_translate_format_help($format_id) {
+  $output = '';
+  if ($format = filter_format_load($format_id)) {
+    $title = t('Text format: @name', array('@name' => $format->name));
+    $tips =  theme('filter_tips', array('tips' => _filter_tips($format_id, FALSE)));
+  }
+  elseif ($format_id == I18N_STRING_FILTER_XSS) {
+    $title = t('Standard XSS filter.');
+    $allowed_html = '<a> <em> <strong> <cite> <blockquote> <code> <ul> <ol> <li> <dl> <dt> <dd>';
+    $tips[] = t('Allowed HTML tags: @tags', array('@tags' => $allowed_html));
+  }
+  elseif ($format_id == I18N_STRING_FILTER_XSS_ADMIN) {
+    $title = t('Administration XSS filter.');
+    $tips[] = t('It will allow most HTML tags but not scripts nor styles.');
+  }
+  elseif ($format_id) {
+    $title = t('Unknown filter: @name', array('@name' => $format_id));
+  }
+
+  if (!empty($title)) {
+    $output .= '<h5>' . $title . '</h5>';
+  }
+  if (!empty($tips)) {
+    $output .= is_array($tips) ? theme('item_list', array('items' => $tips)) : $tips;
+  }
+  return $output;
+}
diff --git a/kolab.org/www/drupal-7.18/sites/all/modules/i18n/i18n_string/i18n_string.test b/kolab.org/www/drupal-7.18/sites/all/modules/i18n/i18n_string/i18n_string.test
new file mode 100644
index 0000000..0d62642
--- /dev/null
+++ b/kolab.org/www/drupal-7.18/sites/all/modules/i18n/i18n_string/i18n_string.test
@@ -0,0 +1,92 @@
+<?php
+
+/**
+ * @file
+ * Test case for multilingual string
+ */
+
+/**
+ * Class for testing i18n_string and modules using these features
+ *
+ * Tests basic API functions
+ */
+
+
+class i18nStringTestCase extends Drupali18nTestCase {
+
+  public static function getInfo() {
+    return array(
+      'name' => 'String translation API',
+      'group' => 'Internationalization',
+      'description' => 'User defined strings translation functions'
+    );
+  }
+
+  function setUp() {
+    // We can use any of the modules that define a text group, to use it for testing
+    parent::setUp('i18n_string', 'i18n_menu');
+    parent::setUpLanguages();
+    $this->translator = $this->drupalCreateUser(array('translate interface', 'translate user-defined strings'));
+  }
+
+  /**
+   * Test base i18n_string API
+   */
+  function testStringsAPI() {
+    // Create a bunch of strings for all languages
+    $textgroup = 'menu';
+    $strings = $this->stringCreateArray(2);
+    $translations = array();
+    // Save source strings and store translations
+    foreach ($strings as $key => $string) {
+      $name = "$textgroup:item:$key:title";
+      i18n_string_update($name, $string);
+      $translations[$key] = $this->createStringTranslation($textgroup, $string);
+    }
+    // Reset cache for text group
+    i18n_string_textgroup($textgroup)->cache_reset();
+    // Check translations using the API
+    foreach ($this->getOtherLanguages() as $language) {
+      foreach ($strings as $key => $value) {
+        $name = "$textgroup:item:$key:title";
+        $translation = i18n_string_translate($name, 'NOT FOUND', array('langcode' => $language->language));
+        $this->assertEqual($translation, $translations[$key][$language->language], "The right $language->name ($language->language) translation has been retrieved for $name, $translation");
+      }
+    }
+  }
+
+  /**
+   * Create strings for all languages
+   */
+  public static function stringCreateAll($number = 10, $length = 100) {
+    foreach (language_list() as $lang => $language) {
+      $strings[$lang] = self::stringCreateArray($number, $length);
+    }
+    return $strings;
+  }
+  /**
+   * Create a bunch of random strings to test the API
+   */
+  public static function stringCreateArray($number = 10, $length = 100) {
+    for ($i=1 ; $i <= $number ; $i++) {
+      $strings[$i] = self::randomName($length);
+    }
+    return $strings;
+  }
+  /**
+   * Create and store one translation into the db
+   */
+  public static function stringCreateTranslation($name, $lang, $length = 20) {
+    $translation = $this->randomName($length);
+    if (self::stringSaveTranslation($name, $lang, $translation)) {
+      return $translation;
+    }
+  }
+  /**
+   * Translate one string into the db
+   */
+  public static function stringSaveTranslation($name, $lang, $translation) {
+    list($textgroup, $context) = i18n_string_context($name);
+    return i18n_string_textgroup($textgroup)->update_translation($context, $lang, $translation);
+  }
+}
diff --git a/kolab.org/www/drupal-7.18/sites/all/modules/i18n/i18n_string/i18n_string.variable.inc b/kolab.org/www/drupal-7.18/sites/all/modules/i18n/i18n_string/i18n_string.variable.inc
new file mode 100644
index 0000000..cb677e3
--- /dev/null
+++ b/kolab.org/www/drupal-7.18/sites/all/modules/i18n/i18n_string/i18n_string.variable.inc
@@ -0,0 +1,57 @@
+<?php
+/**
+ * @file
+ * Variable information
+ */
+
+/**
+ * Implements hook_variable_info().
+ */
+function i18n_string_variable_info($options = array()) {
+  $variables['i18n_string_translate_langcode_[language]'] = array(
+    'type' => 'multiple_language',
+    'title' => t('Enable translation for language'),
+    'multiple values' => array('type' => 'boolean'),
+    'group' => 'i18n',
+  );
+  $variables['i18n_string_allowed_formats'] = array(
+    'title' => t('Translatable text formats'),
+    'options callback' => 'i18n_string_variable_format_list',
+    'type' => 'options',
+    'default callback' => 'i18n_string_variable_format_default',
+    'access' => 'administer filters',
+    'description' => t('The translation system only translates strings with the selected text formats. All other strings will be ignored and removed from the list of translatable strings.'),
+  );
+  $variables['i18n_string_source_language'] = array(
+    'title' => t('Source language'),
+    'type' => 'language',
+    'default callback' => 'i18n_string_source_language',
+    'description' => t('Language that will be used as the source language for string translations. The default is the site default language.'),
+  );
+  $variables['i18n_string_debug'] = array(
+    'type' => 'enable',
+    'title' => t('Debug string translation', array(), $options),
+    'default' => 0,
+    'group' => 'debug',
+  );
+  return $variables;
+}
+
+/**
+ * Options callback, format list
+ */
+function i18n_string_variable_format_list() {
+  $list = array();
+  // As the user has administer filters permissions we get a full list here
+  foreach (filter_formats() as $fid => $format) {
+    $list[$fid] = $format->name;
+  }
+  return $list;
+}
+
+/**
+ * Allowed formats default value
+ */
+function i18n_string_variable_format_default() {
+  return array(filter_fallback_format());
+}
\ No newline at end of file
diff --git a/kolab.org/www/drupal-7.18/sites/all/modules/i18n/i18n_sync/README.txt b/kolab.org/www/drupal-7.18/sites/all/modules/i18n/i18n_sync/README.txt
new file mode 100644
index 0000000..ce7ea6a
--- /dev/null
+++ b/kolab.org/www/drupal-7.18/sites/all/modules/i18n/i18n_sync/README.txt
@@ -0,0 +1,22 @@
+
+README.txt
+==========
+Drupal module: i18n_sync (Synchronization)
+
+This module will handle content synchronization accross translations.
+
+The available list of fields to synchronize will include standard node fields and cck fields.
+To have aditional fields, add the list in a variable in the settings.php file, like this:
+
+// Available fields for synchronization, for all node types.
+$conf['i18n_sync_fields_node'] = array(
+  'field1' => t('Field 1 name'),
+  'field2' => t('Field 2 name'),
+  ...
+);
+
+// More fields for a specific content type 'nodetype' only.
+$conf['i18n_sync_fields_node_nodetype'] = array(
+  'field3' => t('Field 3 name'),
+   ...
+);
\ No newline at end of file
diff --git a/kolab.org/www/drupal-7.18/sites/all/modules/i18n/i18n_sync/i18n_sync.api.php b/kolab.org/www/drupal-7.18/sites/all/modules/i18n/i18n_sync/i18n_sync.api.php
new file mode 100644
index 0000000..1b56d7c
--- /dev/null
+++ b/kolab.org/www/drupal-7.18/sites/all/modules/i18n/i18n_sync/i18n_sync.api.php
@@ -0,0 +1,68 @@
+<?php 
+/**
+ * @file
+ * Synchronization API documentation
+ */
+
+/**
+ * Provide information about which fields to synchronize for each entity type.
+ * 
+ * @see i18n_sync_options()
+ * 
+ * Field definitions defined on hook_field_info() may contain a synchronization
+ * callback used for that field to be synchronized. This callback can be set by:
+ * $field['i18n_sync_callback'] = 'sychcronize_function_callback
+ * 
+ * This callback will be invoked with the following parameters
+ * - $entity_type, $entity, $field, $instance, $langcode, $items, $source_entity, $source_langcode);
+ * 
+ * @see i18n_sync_field_info_alter()
+ * @see i18n_sync_field_file_sync()
+ * 
+ * @return array
+ *   Array of fields indexed by field name that will be presented as options 
+ *   to be synchronized. Each element is an array with the following keys:
+ *   - 'title', Field title to be displayed
+ *   - 'description', Field description to be displayed.
+ *   - 'field_name', Field name for configurable Fields.
+ *   - 'group', Group for the UI only to display this field.
+ *   
+ */
+function hook_i18n_sync_options($entity_type, $bundle_name) {
+  if ($entity_type == 'node') {
+    return array(
+      'parent' => array(
+        'title' => t('Book outline'),
+        'description' => t('Set the translated parent for each translation if possible.')
+      ),
+    );
+  }
+}
+
+/**
+ * Alter information about synchronization options for entities/field
+ * 
+ * @see hook_i18n_sync_options()
+ */
+function hook_i18n_sync_options_alter(&$fields, $entity_type, $bundle_name) {
+
+}
+
+/**
+ * Perform aditional synchronization on entities
+ * 
+ * @param $entity_type
+ * @param $translation
+ *   Translated entity.
+ * @param $translation_language
+ *   Translated entity language code.
+ * @param $source
+ *   Source entity.
+ * @param $source_language
+ *   Source entity language code.
+ * @param $field_names
+ *   Array of field names to synchronize.
+ */
+function hook_i18n_sync_translation($entity_type, $translation, $translation_language, $source, $source_language, $field_names) {
+  
+}
\ No newline at end of file
diff --git a/kolab.org/www/drupal-7.18/sites/all/modules/i18n/i18n_sync/i18n_sync.features.inc b/kolab.org/www/drupal-7.18/sites/all/modules/i18n/i18n_sync/i18n_sync.features.inc
new file mode 100644
index 0000000..65be587
--- /dev/null
+++ b/kolab.org/www/drupal-7.18/sites/all/modules/i18n/i18n_sync/i18n_sync.features.inc
@@ -0,0 +1,26 @@
+<?php
+/**
+ * @file
+ * Feature integration
+ */
+
+/**
+ * Implements hook_features_pipe_node_alter().
+ */
+function i18n_sync_features_pipe_node_alter(&$pipe, $data, $export) {
+  if (!empty($data) && module_exists('variable')) {
+    variable_include();
+    foreach (variable_list_module('i18n_sync') as $variable) {
+      if (isset($variable['multiple']) && $variable['multiple'] === 'node_type') {
+        $children = variable_build($variable['name']);
+        if (!empty($children['children'])) {
+          foreach ($children['children'] as $child_variable) {
+            if (in_array($child_variable['index'], $data)) {
+              $pipe['variable'][] = $child_variable['name'];
+            }
+          }
+        }
+      }
+    }
+  }
+}
\ No newline at end of file
diff --git a/kolab.org/www/drupal-7.18/sites/all/modules/i18n/i18n_sync/i18n_sync.info b/kolab.org/www/drupal-7.18/sites/all/modules/i18n/i18n_sync/i18n_sync.info
new file mode 100644
index 0000000..3d66753
--- /dev/null
+++ b/kolab.org/www/drupal-7.18/sites/all/modules/i18n/i18n_sync/i18n_sync.info
@@ -0,0 +1,18 @@
+name = Synchronize translations
+description = Synchronizes taxonomy and fields accross translations of the same content.
+dependencies[] = i18n
+dependencies[] = translation
+package = Multilingual - Internationalization
+core = 7.x
+
+files[] = i18n_sync.module
+files[] = i18n_sync.install
+files[] = i18n_sync.module.inc
+files[] = i18n_sync.node.inc
+files[] = i18n_sync.test
+; Information added by drupal.org packaging script on 2013-01-13
+version = "7.x-1.8"
+core = "7.x"
+project = "i18n"
+datestamp = "1358075001"
+
diff --git a/kolab.org/www/drupal-7.18/sites/all/modules/i18n/i18n_sync/i18n_sync.install b/kolab.org/www/drupal-7.18/sites/all/modules/i18n/i18n_sync/i18n_sync.install
new file mode 100644
index 0000000..1846535
--- /dev/null
+++ b/kolab.org/www/drupal-7.18/sites/all/modules/i18n/i18n_sync/i18n_sync.install
@@ -0,0 +1,32 @@
+<?php
+
+/**
+ * @file
+ * Installation file for i18n_sync module.
+ */
+
+/**
+ * Set module weight.
+ *
+ * Make sure this runs after taxonomy, i18n and translation modules
+ * and ideally after all other modules implementing nodeapi hook.
+ */
+function i18n_sync_install() {
+  db_query("UPDATE {system} SET weight = 100 WHERE name = 'i18n_sync' AND type = 'module'");
+  // If updating from D6, module changed name
+  if (variable_get('i18n_drupal6_update')) {
+    i18n_sync_update_7000();
+  }
+}
+
+/*
+ * Update variable names from Drupal 6
+ */
+function i18n_sync_update_7000() {
+  foreach (node_type_get_types() as $type => $info) {
+    if ($fields = variable_get('i18nsync_nodeapi_' . $type)) {
+      variable_set('i18n_sync_node_type_' . $type, $fields);
+      variable_del('i18nsync_nodeapi_' . $type);
+    }
+  }
+}
\ No newline at end of file
diff --git a/kolab.org/www/drupal-7.18/sites/all/modules/i18n/i18n_sync/i18n_sync.module b/kolab.org/www/drupal-7.18/sites/all/modules/i18n/i18n_sync/i18n_sync.module
new file mode 100644
index 0000000..908159e
--- /dev/null
+++ b/kolab.org/www/drupal-7.18/sites/all/modules/i18n/i18n_sync/i18n_sync.module
@@ -0,0 +1,359 @@
+<?php
+
+/**
+ * @file
+ * Internationalization (i18n) package. Synchronization of translations
+ *
+ * Keeps vocabulary terms in sync for translations.
+ * This is a per-vocabulary option.
+ *
+ * Ref: http://drupal.org/node/115463
+ *
+ * Notes:
+ * This module needs to run after taxonomy, i18n, translation. Check module weight.
+ *
+ * @ TODO Test with CCK when possible, api may have changed.
+ */
+
+/**
+ * Global switch to enable / disable syncing and check whether we are synching at the moment
+ *
+ * @return boolean
+ *   TRUE if we need to run sync operations. FALSE during syncing so we don't have recursion.
+ */
+function i18n_sync($status = NULL) {
+  static $current = TRUE;
+  if (isset($status)) {
+    $current = $status;
+  }
+  return $current;
+}
+
+/**
+ * Implements hook_help().
+ */
+function i18n_sync_help($path, $arg) {
+  switch ($path) {
+    case 'admin/help#i18n_sync' :
+      $output = '<p>' . t('This module synchronizes content taxonomy and fields accross translations:') . '</p>';
+      $output .= '<p>' . t('First you need to select which fields should be synchronized. Then, after a node has been updated, all enabled vocabularies and fields will be synchronized as follows:') . '</p>';
+      $output .= '<ul>';
+      $output .= '<li>' . t('All the node fields selected for synchronization will be set to the same value for all translations.') . '</li>';
+      $output .= '<li>' . t('For multilingual vocabularies, the terms for all translations will be replaced by the translations of the original node terms.') . '</li>';
+      $output .= '<li>' . t('For other vocabularies, the terms will be just copied over to all the translations.') . '</li>';
+      $output .= '</ul>';
+      $output .= '<p><strong>' . t('Note that permissions are not checked for each node. So if someone can edit a node and it is set to synchronize, all the translations will be synchronized anyway.') . '</strong></p>';
+      $output .= '<p>' . t('To enable synchronization check content type options to select which fields to synchronize for each node type.') . '</p>';
+      $output .= '<p>' . t('The list of available fields for synchronization will include some standard node fields and all CCK fields. You can add more fields to the list in a configuration variable. See README.txt for how to do it.') . '</p>';
+      $output .= '<p>' . t('For more information, see the online handbook entry for <a href="@i18n">Internationalization module</a>.', array('@i18n' => 'http://drupal.org/node/133977')) . '</p>';
+      return $output;
+  }
+}
+
+/**
+ * Implements hook_hook_info().
+ */
+function i18n_sync_hook_info() {
+  $hooks['i18n_sync_options'] = array(
+    'group' => 'i18n',
+  );
+  return $hooks;
+}
+
+/**
+ * Implements hook_field_info_alter()
+ */
+function i18n_sync_field_info_alter(&$fields) {
+  foreach (array('file', 'image') as $type) {
+    if (isset($fields[$type])) {
+      $fields[$type]['i18n_sync_callback'] = 'i18n_sync_field_file_sync';
+    }
+  }
+}
+
+/**
+ * Implements hook_form_FORM_ID_alter().
+ */
+function i18n_sync_form_node_admin_content_alter(&$form, &$form_state) {
+  if (!empty($form['operation']) && $form['operation']['#value'] == 'delete') {
+    $form['#submit'] = array_merge(array('i18n_sync_node_delete_submit'), $form['#submit']);
+  }
+}
+
+/**
+ * Implements hook_form_FORM_ID_alter().
+ */
+function i18n_sync_form_node_delete_confirm_alter(&$form, &$form_state) {
+  // Intercept form submission so we can handle uploads, replace callback
+  $form['#submit'] = array_merge(array('i18n_sync_node_delete_submit'), $form['#submit']);
+}
+
+
+
+/**
+ * Implements hook_form_FORM_ID_alter().
+ */
+function i18n_sync_form_node_type_form_alter(&$form, &$form_state) {
+  if (isset($form['type'])) {
+    $type = $form['#node_type']->type;
+    $disabled = !translation_supported_type($type);
+    $form['i18n_sync'] = array(
+      '#type' => 'fieldset',
+      '#title' => t('Synchronize translations'),
+      '#collapsible' => TRUE,
+      '#collapsed' => TRUE,
+      '#group' => 'additional_settings',
+      '#attributes' => array(
+        'class' => array('i18n-node-type-settings-form'),
+      ),
+      '#description' => t('Select which fields to synchronize for all translations of this content type.'),
+      '#disabled' => $disabled,
+    );
+
+    $form['i18n_sync']['i18n_sync_node_type'] = array(
+      '#tree' => TRUE,
+    );
+
+    // Each set provides title and options. We build a big checkboxes control for it to be
+    // saved as an array.
+    $current = i18n_sync_node_fields($type);
+    // Group options, group fields by type.
+    $groups = array(
+      'node' => t('Standard node fields'),
+      'fields' => t('Configurable fields'),
+    );
+    $fields = array();
+    foreach (i18n_sync_node_options($type) as $field => $info) {
+      $group = isset($info['group']) && isset($groups[$info['group']]) ? $info['group'] : 'node';
+      $fields[$group][$field] = $info;
+    }
+    foreach ($fields as $group => $group_fields) {
+      $form['i18n_sync']['i18n_sync_node_type']['i18n_sync_group_' . $group] = array(
+        '#prefix' => '<strong>', '#suffix' => '</strong>',
+        '#markup' => $groups[$group],
+      );
+      foreach ($group_fields as $field => $info) {
+        $form['i18n_sync']['i18n_sync_node_type'][$field] = array(
+          '#title' => $info['title'],
+          '#type' => 'checkbox',
+          '#default_value' => in_array($field, $current),
+          '#disabled' => $disabled,
+          '#description' => isset($info['description']) ? $info['description'] : '',
+        );
+      }
+    }
+  }
+}
+
+/**
+ * Submit callback for
+ * - node delete confirm
+ * - node multiple delete confirm
+ */
+function i18n_sync_node_delete_submit($form, $form_state) {
+  if ($form_state['values']['confirm']) {
+    if (!empty($form_state['values']['nid'])) {
+      // Single node
+      i18n_sync_node_delete_prepare($form_state['values']['nid']);
+    }
+    elseif (!empty($form_state['values']['nodes'])) {
+      // Multiple nodes
+      foreach ($form_state['values']['nodes'] as $nid => $value) {
+        i18n_sync_node_delete_prepare($nid);
+      }
+    }
+  }
+  // Then it will go through normal form submission
+}
+
+/**
+ * Prepare node for deletion, work out synchronization issues
+ */
+function i18n_sync_node_delete_prepare($nid) {
+  $node = node_load($nid);
+  // Delete file associations when files are shared with existing translations
+  // so they are not removed by upload module
+  if (!empty($node->tnid) && module_exists('upload')) {
+    $result = db_query('SELECT u.* FROM {upload} u WHERE u.nid = %d AND u.fid IN (SELECT t.fid FROM {upload} t WHERE t.fid = u.fid AND t.nid <> u.nid)', $nid);
+    while ($up = db_fetch_object($result)) {
+      db_query("DELETE FROM {upload} WHERE fid = %d AND vid = %d", $up->fid, $up->vid);
+    }
+  }
+}
+
+/**
+ * Check whether this node is to be synced
+ */
+function i18n_sync_node_check($node) {
+  return translation_supported_type($node->type) && i18n_object_langcode($node) && i18n_sync();
+}
+
+/**
+ * Get node translations if any, excluding the node itself
+ *
+ * @param $reset
+ *   Whether to reset the translation_node_get_translations cache.
+ */
+function i18n_sync_node_get_translations($node, $reset = FALSE) {
+  if (!empty($node->tnid)) {
+    if ($reset) {
+      // Clear the static cache of translation_node_get_translations
+      $translations_cache = &drupal_static('translation_node_get_translations', array());
+      unset($translations_cache[$node->tnid]);
+    }
+    // Maybe translations are already here
+    if ($translations = translation_node_get_translations($node->tnid)) {
+      unset($translations[$node->language]);
+      return $translations;
+    }
+  }
+}
+
+/**
+ * Implements hook_node_insert().
+ */
+function i18n_sync_node_insert($node) {
+  // When creating a translation, there are some aditional steps, different from update
+  if (i18n_sync_node_check($node) && !empty($node->translation_source)) {
+    i18n_sync_node_update($node);
+  }
+}
+
+/**
+ * Implements hook_node_update().
+ */
+function i18n_sync_node_update($node) {
+  // Let's go with field synchronization.
+  if (i18n_sync_node_check($node) && !empty($node->tnid) && ($fields = i18n_sync_node_fields($node->type)) && ($translations = i18n_sync_node_get_translations($node, TRUE))) {
+    module_load_include('node.inc', 'i18n_sync');
+    i18n_sync_node_translation($node, $translations, $fields, 'update');
+  }
+}
+
+/**
+ * Implements hook_node_prepare().
+ */
+function i18n_sync_node_prepare($node) {
+  // If creating a translation, copy over all the fields to be synchronized.
+  if (empty($node->nid) && !empty($node->translation_source) && ($sync_fields = i18n_sync_node_fields($node->type))) {
+    $source = $node->translation_source;
+    $i18n_options = i18n_sync_node_options($node->type);
+    // Copy over standard node fields. The rest are handled by field_attach_prepare_translation()
+    foreach ($sync_fields as $field) {
+      if (!empty($source->$field) && empty($i18n_options[$field]['field_name'])) {
+        if ($field == 'uid') {
+          $node->name = $source->name;
+        }
+        elseif ($field == 'created') {
+          $node->date = format_date($source->created, 'custom', 'Y-m-d H:i:s O');
+        }
+        else {
+          $node->$field = $source->$field;
+        }
+      }
+    }
+  }
+}
+
+/**
+ * Returns list of fields to synchronize for a given content type.
+ *
+ * @param $type
+ *   Node type.
+ * @param $field
+ *   Optional field name to check whether it is in the list
+ */
+function i18n_sync_node_fields($type, $field = NULL) {
+  $fields = variable_get('i18n_sync_node_type_' . $type, array());
+  return $field ? in_array($field, $fields) : $fields;
+}
+
+/**
+ * Returns list of available fields for given content type.
+ *
+ * Fields can also be changed using hook_i18n_sync_fields_alter($fields, $type)
+ *
+ * @param $type
+ *   Node type.
+ */
+function i18n_sync_node_options($type) {
+  return i18n_sync_options('node', $type);
+}
+
+/**
+ * Returns list of available fields for given entity / bundle.
+ *
+ * Fields can also be changed using hook_i18n_sync_options_alter($fields, $type)
+ *
+ * @param $entity_type
+ *   Entity type.
+ * @param
+ */
+function i18n_sync_options($entity_type, $bundle_name) {
+  $cache = &drupal_static(__FUNCTION__);
+
+  if (!isset($cache[$entity_type][$bundle_name])) {
+    module_load_include('modules.inc', 'i18n_sync');
+    $fields = module_invoke_all('i18n_sync_options', $entity_type, $bundle_name);
+    // Give a chance to modules to change/remove/add their own fields
+    drupal_alter('i18n_sync_options', $fields, $entity_type, $bundle_name);
+    $cache[$entity_type][$bundle_name] = $fields;
+  }
+
+  return $cache[$entity_type][$bundle_name];
+}
+
+/**
+ * Synchronize entity field translation
+ */
+function i18n_sync_field_translation_sync($entity_type, $bundle_name, $entity, $langcode, $source_entity, $source_langcode, $field_name) {
+  $field = field_info_field($field_name);
+  $instance = field_info_instance($entity_type, $field_name, $bundle_name);
+  $source_lang = field_language($entity_type, $source_entity, $field_name);
+  $translation_lang = field_is_translatable($entity_type, $field) ? $entity->language : LANGUAGE_NONE;
+  if (isset($source_entity->{$field_name}[$source_lang])) {
+    $items = $source_entity->{$field_name}[$source_lang];
+    // Determine the function to synchronize this field. If none, just copy over.
+    $type_info = field_info_field_types($field['type']);
+    if (isset($type_info['i18n_sync_callback'])) {
+      $function = $type_info['i18n_sync_callback'];
+      $function($entity_type, $entity, $field, $instance, $langcode, $items, $source_entity, $source_langcode);
+    }
+    $entity->{$field_name}[$translation_lang] = $items;
+  }
+  else {
+    // If source not set, unset translation too
+    unset($entity->{$field_name}[$translation_lang]);
+  }
+}
+
+/**
+ * Sync a file or image field (i18n_sync_callback)
+ *
+ * - file-id's (fid) are synced
+ * - order of fid's is synced
+ * - description, alt, title is kept if already existing, copied otherwise
+ */
+function i18n_sync_field_file_sync($entity_type, $entity, $field, $instance, $langcode, &$items, $source_entity, $source_language) {
+  $field_name = $instance['field_name'];
+  // Build a copy of the existing files in the translation node
+  // indexed by fid for easy retrieval in the copy loop below
+  $existing_files = array();
+  $field_language = field_language($entity_type, $entity, $field_name, $langcode);
+  if (isset($entity->{$field_name}[$field_language])) {
+    foreach ($entity->{$field_name}[$field_language] as $delta => $file) {
+      $existing_files[$file['fid']] = $file;
+    }
+  }
+  // Start creating the translated copy
+  foreach ($items as $delta => &$file) {
+    // keep alt, title, description if they already exist
+    if (isset($existing_files[$file['fid']])) {
+      foreach (array('title', 'description', 'alt') as $property) {
+        if (!empty($existing_files[$file['fid']][$property])) {
+          $file[$property] = $existing_files[$file['fid']][$property];
+        }
+      }
+    }
+  }
+}
diff --git a/kolab.org/www/drupal-7.18/sites/all/modules/i18n/i18n_sync/i18n_sync.modules.inc b/kolab.org/www/drupal-7.18/sites/all/modules/i18n/i18n_sync/i18n_sync.modules.inc
new file mode 100644
index 0000000..dfd51b5
--- /dev/null
+++ b/kolab.org/www/drupal-7.18/sites/all/modules/i18n/i18n_sync/i18n_sync.modules.inc
@@ -0,0 +1,68 @@
+<?php
+
+/**
+ * @file
+ * Internationalization (i18n) package. Synchronization of translations
+ *
+ * Implements hook_i18n_sync_node_fields() for several core modules.
+ */
+
+/**
+ * Book module. Implements hook_i18n_sync_options().
+ */
+function book_i18n_sync_options($entity_type, $bundle_name) {
+  if ($entity_type == 'node') {
+    return array(
+      'parent' => array(
+        'title' => t('Book outline'),
+        'description' => t('Set the translated parent for each translation if possible.')
+      ),
+    );
+  }
+}
+
+/**
+ * Comment module. Implements hook_i18n_sync_options().
+ */
+function comment_i18n_sync_options($entity_type, $bundle_name) {
+  if ($entity_type == 'node') {
+    $fields['comment'] = array('title' =>  t('Comment settings'));
+    return $fields;
+  }
+}
+
+/**
+ * Field module. Implements hook_i18n_sync_options().
+ */
+function field_i18n_sync_options($entity_type, $bundle_name) {
+  $sync_fields = array();
+  if ($bundle_name) {
+    $instances = field_info_instances($entity_type, $bundle_name);
+    foreach ($instances as $name => $instance) {
+      $sync_fields[$name] = array(
+        'title' => $instance['label'],
+        'description' => $instance['description'],
+        'field_name' => $instance['field_name'],
+        'group' => 'fields',
+      );
+    }
+  }
+  return $sync_fields;
+}
+
+/**
+ * Node module. Implements hook_i18n_sync_options().
+ */
+function node_i18n_sync_options($entity_type, $bundle_name) {
+  if ($entity_type == 'node') {
+    return array(
+      'uid' => array('title' => t('Author')),
+      'status' => array('title' => t('Status')),
+      'created' => array('title' => t('Post date')),
+      'promote' => array('title' => t('Promote')),
+      'moderate' =>  array('title' => t('Moderate')),
+      'sticky' => array('title' => t('Sticky')),
+      'revision' =>  array('title' => t('Revision'), 'description' => t('Create also new revision for translations')),
+    );
+  }
+}
diff --git a/kolab.org/www/drupal-7.18/sites/all/modules/i18n/i18n_sync/i18n_sync.node.inc b/kolab.org/www/drupal-7.18/sites/all/modules/i18n/i18n_sync/i18n_sync.node.inc
new file mode 100644
index 0000000..0480eb7
--- /dev/null
+++ b/kolab.org/www/drupal-7.18/sites/all/modules/i18n/i18n_sync/i18n_sync.node.inc
@@ -0,0 +1,170 @@
+<?php
+
+/**
+ * @file
+ * Internationalization (i18n) package. Synchronization of translations
+ *
+ * Node synchronization.
+ */
+
+/**
+ * Synchronizes fields for node translation.
+ *
+ * There's some specific handling for known fields like:
+ * - files, for file attachments.
+ * - iid (CCK node attachments, translations for them will be handled too).
+ *
+ * All the rest of the fields will be just copied over.
+ * The 'revision' field will have the special effect of creating a revision too for the translation.
+ *
+ * @param $node
+ *   Source node being edited.
+ * @param $translations
+ *   Node translations to synchronize, just needs nid property.
+ * @param $fields
+ *   List of fields to synchronize.
+ * @param $op
+ *   Node operation (insert|update).
+ */
+function i18n_sync_node_translation($node, $translations, $field_names, $op) {
+  $count = 0;
+  // Disable language selection and synchronization temporarily, enable it again later
+  $i18n_select = i18n_select(FALSE);
+  i18n_sync(FALSE);
+  foreach ($translations as $translation) {
+    // If translation is the same node, we cannot synchronize with itself
+    if ($node->nid == $translation->nid) {
+      continue;
+    }
+    // Load full node, we need all data here.
+    $translation = node_load($translation->nid, NULL, TRUE);
+    $i18n_options = i18n_sync_node_options($node->type);
+    // Invoke callback for each field, the default is just copy over
+    foreach ($field_names as $field) {
+      if (!empty($i18n_options[$field]['field_name'])) {
+        i18n_sync_field_translation_sync('node', $node->type, $translation, $translation->language, $node, $node->language, $i18n_options[$field]['field_name']);
+      }
+      elseif (isset($node->$field)) {
+        // Standard node field, just copy over.
+        $translation->$field = $node->$field;
+      }
+    }
+    // Give a chance to other modules for additional sync
+    module_invoke_all('i18n_sync_translation', 'node', $translation, $translation->language, $node, $node->language, $field_names);
+    node_save($translation);
+    $count++;
+  }
+  i18n_sync(TRUE);
+  i18n_select($i18n_select);
+  drupal_set_message(format_plural($count, 'One node translation has been synchronized.', 'All @count node translations have been synchronized.'));
+}
+
+/**
+ * Node attachments (CCK) that may have translation.
+ */
+function i18n_sync_node_translation_attached_node(&$node, &$translation, $field) {
+  if ($attached = node_load($node->$field)) {
+    $translation->$field = i18n_sync_node_translation_reference_field($attached, $node->$field, $translation->language);
+  }
+}
+
+/**
+ * Translating a nodereference field (cck).
+ */
+function i18n_sync_node_translation_nodereference_field(&$node, &$translation, $field) {
+  $translated_references = array();
+  foreach ($node->$field as $reference) {
+    if ($reference_node = node_load($reference['nid'])) {
+      $translated_references[] = array(
+        'nid' => i18n_sync_node_translation_reference_field($reference_node, $reference['nid'], $translation->language)
+      );
+    }
+  }
+  $translation->$field = $translated_references;
+}
+
+/**
+ * Helper function to which translates reference field. We try to use translations for reference, otherwise fallback.
+ * Example:
+ *   English A references English B and English C.
+ *   English A and B are translated to German A and B, but English C is not.
+ *   The syncronization from English A to German A would it German B and English C.
+ */
+function i18n_sync_node_translation_reference_field(&$reference_node, $default_value, $langcode) {
+  if (isset($reference_node->tnid) && translation_supported_type($reference_node->type)) {
+    // This content type has translations, find the one.
+    if (($reference_trans = translation_node_get_translations($reference_node->tnid)) && isset($reference_trans[$langcode])) {
+      return $reference_trans[$langcode]->nid;
+    }
+    else {
+      // No requested language found, just copy the field.
+      return $default_value;
+    }
+  }
+  else {
+    // Content type without language, just copy the field.
+    return $default_value;
+  }
+}
+
+/**
+ * Synchronize configurable field
+ *
+ * @param $field_info
+ *   Field API field information.
+ */
+function i18n_sync_node_translation_default_field($node, $translation, $field, $field_info) {
+  switch ($field_info['field']['type']) {
+    case 'file':
+    case 'image':
+      i18n_sync_node_translation_file_field($node, $translation, $field);
+      break;
+    default:
+      // For fields that don't need special handling, just copy it over if defined.
+      // Field languages are completely unconsistent, for not to say broken
+      // both in Drupal core and entity translation. Let's hope this works.
+      $source_lang = field_language('node', $node, $field);
+      $translation_lang = field_language('node', $translation, $field);
+      if (isset($node->{$field}[$source_lang])) {
+        $translation->{$field}[$translation_lang] = $node->{$field}[$source_lang];
+      }
+      break;
+  }
+}
+
+/**
+ * Sync a file or image field:
+ * - file-id's (fid) are synced
+ * - order of fid's is synced
+ * - description, alt, title is kept if already existing, copied otherwise
+ *
+ * @param object $node the node whose changes are to be synced
+ * @param object $translation a node to which the changes need to be synced
+ * @param string $field field name
+ */
+function i18n_sync_node_translation_file_field($node, $translation, $field) {
+  if (isset($node->{$field}[$node->language])) {
+    // Build a copy of the existing files in the translation node
+    // indexed by fid for easy retrieval in the copy loop below
+    $existing_files = array();
+    if (isset($translation->{$field}[$translation->language])) {
+      foreach ($translation->{$field}[$translation->language] as $delta => $file) {
+        $existing_files[$file['fid']] = $file;
+      }
+    }
+
+    // Start creating the translated copy
+    $translated_files = $node->{$field}[$node->language];
+    foreach ($translated_files as $delta => &$file) {
+      // keep alt, title, description if they already exist
+      if (array_key_exists($file['fid'], $existing_files)) {
+        foreach (array('title', 'description', 'alt') as $property) {
+          if (!empty($existing_files[$file['fid']][$property])) {
+            $file[$property] = $existing_files[$file['fid']][$property];
+          }
+        }
+      }
+    }
+    $translation->{$field}[$translation->language] = $translated_files;
+  }
+}
diff --git a/kolab.org/www/drupal-7.18/sites/all/modules/i18n/i18n_sync/i18n_sync.test b/kolab.org/www/drupal-7.18/sites/all/modules/i18n/i18n_sync/i18n_sync.test
new file mode 100644
index 0000000..f9a673e
--- /dev/null
+++ b/kolab.org/www/drupal-7.18/sites/all/modules/i18n/i18n_sync/i18n_sync.test
@@ -0,0 +1,85 @@
+<?php
+/**
+ * @file
+ * Test field synchronization
+ */
+
+class i18nSyncTestCase extends Drupali18nTestCase {
+
+  public static function getInfo() {
+    return array(
+      'name' => 'Synchronize translations',
+      'group' => 'Internationalization',
+      'description' => 'Internationalization Content Synchronization'
+    );
+  }
+
+  function setUp() {
+    parent::setUp('translation', 'i18n_string', 'i18n_sync', 'i18n_node', 'i18n_taxonomy');
+    parent::setUpLanguages();
+    parent::setUpContentTranslation();
+  }
+
+  function testIi18nSync() {
+    drupal_static_reset('language_list');
+    $language_list = language_list();
+    $language_count = count($language_list);
+
+    // Enable tags field for page content type.
+    $edit = array(
+      'fields[_add_existing_field][label]' => t('Tags'),
+      'fields[_add_existing_field][field_name]' => 'field_tags',
+      'fields[_add_existing_field][widget_type]' => 'taxonomy_autocomplete',
+    );
+    $this->drupalPost('admin/structure/types/manage/page/fields', $edit, t('Save'));
+    $this->drupalPost(NULL, array(), t('Save settings'));
+
+    // Create some content and check selection modes
+    $this->drupalLogin($this->content_editor);
+
+    // variable_set('language_content_type_story', 1);
+    $source = $this->createNode('page', $this->randomName(), $this->randomString(20), language_default('language'), array('field_tags[und]' => $tag_name = $this->randomName()));
+    $this->drupalGet('node/' . $source->nid . '/translate');
+    $translations = $this->createNodeTranslationSet($source);
+
+    drupal_static_reset('translation_node_get_translations');
+    $this->assertEqual(count(translation_node_get_translations($source->tnid)), $language_count, "Created $language_count $source->type translations.");
+
+    // Set up fields for synchronization: promoted, field_tags
+    $this->drupalLogin($this->admin_user);
+    $edit = array(
+      'i18n_sync_node_type[sticky]' => 1,
+      'i18n_sync_node_type[promote]' => 1,
+      'i18n_sync_node_type[field_tags]' => 1,
+    );
+    $this->drupalPost('admin/structure/types/manage/page', $edit, t('Save content type'));
+    $this->drupalGet('admin/structure/types/manage/page');
+    // Update source fields and check translations have been updated.
+    $new_title = $this->randomName();
+    $new_tag = $this->randomName();
+    $edit = array(
+      'promote' => 1,
+      'sticky' => 1,
+      'field_tags[und]' => $new_tag,
+    );
+    $this->drupalPost('node/' . $source->nid . '/edit', $edit, t('Save'));
+    $terms = taxonomy_get_term_by_name($new_tag);
+    $term = reset($terms);
+    // Refresh cache and load translations again.
+    drupal_static_reset('translation_node_get_translations');
+
+    // For some reason the field cache for the node translation gets outdated values.
+    // This only happens when running unit tests.
+    // If we don't do this, we miss the field_tags value for the second node.
+    field_cache_clear();
+
+    $translations = translation_node_get_translations($source->tnid);
+    foreach ($translations as $lang => $node) {
+      $node = node_load($node->nid, NULL, TRUE);
+      $this->assertTrue($node->promote && $node->sticky, "Translation for language $lang has been promoted an made sticky.");
+      $this->assertEqual($node->field_tags['und'][0]['tid'], $term->tid, "Tag for translation $lang has been properly updated.");
+      $this->drupalGet('node/' . $node->nid);
+      $this->assertRaw($new_tag, "New tag for translation $lang is showing up.");
+    }
+  }
+}
diff --git a/kolab.org/www/drupal-7.18/sites/all/modules/i18n/i18n_sync/i18n_sync.variable.inc b/kolab.org/www/drupal-7.18/sites/all/modules/i18n/i18n_sync/i18n_sync.variable.inc
new file mode 100644
index 0000000..5f56940
--- /dev/null
+++ b/kolab.org/www/drupal-7.18/sites/all/modules/i18n/i18n_sync/i18n_sync.variable.inc
@@ -0,0 +1,20 @@
+<?php
+/**
+ * @file
+ * Variable information
+ */
+
+/**
+ * Implements hook_variable_info().
+ */
+function i18n_sync_variable_info($options = array()) {
+  $variables['i18n_sync_node_type_[node_type]'] = array(
+    'title' => t('Synchronize fields for node type.', array(), $options),
+    'type' => 'multiple',
+    'repeat' => array(
+      'type' => 'array',
+    ),
+    'group' => 'i18n',
+  );
+  return $variables;
+}
\ No newline at end of file
diff --git a/kolab.org/www/drupal-7.18/sites/all/modules/i18n/i18n_taxonomy/i18n_taxonomy.admin.inc b/kolab.org/www/drupal-7.18/sites/all/modules/i18n/i18n_taxonomy/i18n_taxonomy.admin.inc
new file mode 100644
index 0000000..47371b4
--- /dev/null
+++ b/kolab.org/www/drupal-7.18/sites/all/modules/i18n/i18n_taxonomy/i18n_taxonomy.admin.inc
@@ -0,0 +1,225 @@
+<?php
+
+/**
+ * @file
+ * Helper functions for taxonomy administration.
+ */
+
+/**
+ * This is the callback for taxonomy translations.
+ *
+ * Gets the urls:
+ *  admin/content/taxonomy/%taxonomy_vocabulary/translation
+ *  admin/content/taxonomy/i18n/term/xx
+ *  admin/content/taxonomy/i18n/term/new/xx
+ *  admin/content/taxonomy/vid/translation/op/trid
+ */
+function i18n_taxonomy_page_vocabulary($vocabulary, $op = NULL, $tid = NULL) {
+  switch ($op) {
+    case 'edit':
+      drupal_set_title(t('Edit term translations'));
+      $output = drupal_get_form('i18n_taxonomy_translation_term_form', $vocabulary, $tid);
+      break;
+
+    default:
+      $output = i18n_taxonomy_translation_overview($vocabulary);
+  }
+  return $output;
+}
+
+/**
+ * Produces a vocabulary translation form.
+ */
+function i18n_taxonomy_translation_term_form($form, $form_state, $vocabulary, $translation_set = NULL, $source = NULL) {
+  $form['translation_set'] = array('#type' => 'value', '#value' => $translation_set);
+  $translations = $translation_set ? $translation_set->get_translations() : array();
+  $form['vocabulary'] = array('#type' => 'value', '#value' => $vocabulary);
+  $form['translations'] = array(
+    '#type' => 'fieldset',
+    '#title' => t('Select translations'),
+    '#description' => t('Select existing terms or type new ones that will be created for each language.'),
+    '#tree' => TRUE
+  );
+  // List of terms for languages.
+  foreach (i18n_language_list() as $lang => $langname) {
+    if ($source && $source->language == $lang) {
+      // This is the source term, we don't change it
+      $form['source_term'] = array('#type' => 'value', '#value' => $source);
+      $form['translations'][$lang] = array(
+        '#title' => $langname,
+        '#type' => 'item',
+        '#markup' => $source->name,
+        '#langcode' => $lang,
+      );
+    }
+    else {
+      $form['translations'][$lang] = array(
+        '#title' => $langname,
+        '#type' => 'textfield',
+        '#default_value' => isset($translations[$lang]) ? $translations[$lang]->name : '',
+        '#autocomplete_path' => 'i18n/taxonomy/autocomplete/vocabulary/' . $vocabulary->machine_name . '/' . $lang,
+        '#langcode' => $lang,
+        '#maxlength' => 1024,
+        '#element_validate' => array('i18n_taxonomy_autocomplete_validate'),
+      );
+    }
+
+  }
+  $form['submit'] = array(
+    '#type' => 'submit',
+    '#value' => t('Save')
+  );
+  if ($translation_set) {
+    $form['delete'] = array(
+      '#type' => 'submit',
+      '#value' => t('Delete')
+    );
+  }
+  return $form;
+}
+
+/**
+ * Form element validate handler for taxonomy term autocomplete element.
+ */
+function i18n_taxonomy_autocomplete_validate($element, &$form_state) {
+  // Autocomplete widgets do not send their tids in the form, so we must detect
+  // them here and process them independently.
+  $value = array();
+  if ($tags = $element['#value']) {
+    // Collect candidate vocabularies.
+    $vocabulary = $form_state['values']['vocabulary'];
+    $vocabularies[$vocabulary->vid] = $vocabulary;
+
+    // Translate term names into actual terms.
+    $typed_terms = drupal_explode_tags($tags);
+    foreach ($typed_terms as $typed_term) {
+      // See if the term exists in the chosen vocabulary and return the tid;
+      // otherwise, create a new 'autocreate' term for insert/update.
+      if ($possibilities = taxonomy_term_load_multiple(array(), array('name' => trim($typed_term), 'vid' => $vocabulary->vid, 'language' => $element['#langcode']))) {
+        $term = array_pop($possibilities);
+      }
+      else {
+        $vocabulary = reset($vocabularies);
+        $term = array(
+          'tid' => 'autocreate',
+          'vid' => $vocabulary->vid,
+          'name' => $typed_term,
+          'vocabulary_machine_name' => $vocabulary->machine_name,
+          'language' => $element['#langcode'],
+        );
+      }
+      $value[] = (array)$term;
+    }
+  }
+
+  form_set_value($element, $value, $form_state);
+}
+
+/**
+ * Form callback: Process vocabulary translation form.
+ */
+function i18n_taxonomy_translation_term_form_submit($form, &$form_state) {
+  $translation_set = $form_state['values']['translation_set'];
+  $vocabulary = $form_state['values']['vocabulary'];
+  switch ($form_state['values']['op']) {
+    case t('Save'):
+      $term_translations = array_filter($form_state['values']['translations']);
+      foreach ($term_translations as $lang => $lang_terms) {
+        $item = reset($lang_terms);
+        if ($item['tid'] == 'autocreate') {
+          $term = (object) $item;
+          unset($term->tid);
+          taxonomy_term_save($term);
+        }
+        else {
+          $term = (object) $item;
+        }
+        $translations[$lang] = $term;
+      }
+      if (!empty($form_state['values']['source_term'])) {
+        $term = $form_state['values']['source_term'];
+        $translations[$term->language] = $term;
+      }
+      if (!empty($translations)) {
+        $translation_set = $translation_set ? $translation_set : i18n_translation_set_create('taxonomy_term', $vocabulary->machine_name);
+        $translation_set
+          ->reset_translations($translations)
+          ->save(TRUE);
+        drupal_set_message(t('Term translations have been updated.'));
+      }
+      else {
+        drupal_set_message(t('There are no translations to be saved.'), 'error');
+      }
+      break;
+
+    case t('Delete'):
+      $translation_set->delete(TRUE);
+      drupal_set_message(t('The term translation has been deleted.'));
+      $form_state['redirect'] = 'admin/structure/taxonomy/' . $form_state['values']['vocabulary']->machine_name . '/translation';
+      break;
+  }
+}
+
+/**
+ * Generate a tabular listing of translations for vocabularies.
+ */
+function i18n_taxonomy_translation_sets_overview($vocabulary) {
+  module_load_include('admin.inc', 'i18n_translation');
+  drupal_set_title($vocabulary->name);
+  $query = db_select('i18n_translation_set', 't')
+    ->condition('t.bundle', $vocabulary->machine_name);
+  return i18n_translation_admin_overview('taxonomy_term', $query);
+}
+
+/**
+ * Callback for term translation tab.
+ */
+function i18n_taxonomy_translation_term_overview($term) {
+  if ($term->i18n_tsid) {
+    // Already part of a set, grab that set.
+    $i18n_tsid = $term->i18n_tsid;
+    $translation_set = i18n_translation_set_load($term->i18n_tsid);
+    $translation_set->get_translations();
+    $translations = $translation_set->get_translations();
+  }
+  else {
+    // We have no translation source nid, this could be a new set, emulate that.
+    $i18n_tsid = $term->tid;
+    $translations = array($term->language => $term);
+  }
+  $type = variable_get('translation_language_type', LANGUAGE_TYPE_INTERFACE);
+  $header = array(t('Language'), t('Title'), t('Operations'));
+
+  foreach (i18n_language_list() as $langcode => $language_name) {
+    $options = array();
+    if (isset($translations[$langcode])) {
+      // Existing translation in the translation set: display status.
+      // We load the full node to check whether the user can edit it.
+      $translation_term = taxonomy_term_load($translations[$langcode]->tid);
+      $path = 'taxonomy/term/' . $translation_term->tid;
+      $title = l($translation_term->name, $path);
+
+      $options[] = l(t('edit'), $path . '/edit');
+
+      if ($translation_term->tid == $i18n_tsid) {
+        $language_name = t('<strong>@language_name</strong> (source)', array('@language_name' => $language_name));
+      }
+    }
+    else {
+      // No such translation in the set yet: help user to create it.
+      $title = t('n/a');
+      $options[] = l(t('add translation'), 'admin/structure/taxonomy/' . $term->vocabulary_machine_name . '/add', array('query' => array('translation' => $term->tid, 'target' => $langcode) + drupal_get_destination()));
+    }
+    $rows[] = array($language_name, $title, implode(" | ", $options));
+  }
+
+  drupal_set_title(t('Translations of term %title', array('%title' => $term->name)), PASS_THROUGH);
+
+  $build['translation_node_overview'] = array(
+    '#theme' => 'table',
+    '#header' => $header,
+    '#rows' => $rows,
+  );
+
+  return $build;
+}
diff --git a/kolab.org/www/drupal-7.18/sites/all/modules/i18n/i18n_taxonomy/i18n_taxonomy.i18n.inc b/kolab.org/www/drupal-7.18/sites/all/modules/i18n/i18n_taxonomy/i18n_taxonomy.i18n.inc
new file mode 100644
index 0000000..8c666a7
--- /dev/null
+++ b/kolab.org/www/drupal-7.18/sites/all/modules/i18n/i18n_taxonomy/i18n_taxonomy.i18n.inc
@@ -0,0 +1,136 @@
+<?php
+/**
+ * @file
+ * Internationalization (i18n) hooks
+ */
+
+/**
+ * Implements hook_i18n_object_info().
+ */
+function i18n_taxonomy_i18n_object_info() {
+  $info['taxonomy_term'] = array(
+    'title' => t('Taxonomy term'),
+    'class' => 'i18n_taxonomy_term',
+    'entity' => 'taxonomy_term',
+    'key' => 'tid',
+    'placeholders' => array(
+      '%taxonomy_term' => 'tid',
+    ),
+    // Auto generate edit path
+    'edit path' => 'taxonomy/term/%taxonomy_term/edit',
+    // Auto-generate translate tab
+    'translate tab' => 'taxonomy/term/%taxonomy_term/translate',
+    'translation set' => TRUE,
+    'string translation' => array(
+      'textgroup' => 'taxonomy',
+      'type' => 'term',
+      'properties' => array(
+        'name' => t('Name'),
+        'description' => array(
+          'title' => t('Description'),
+          'format' => 'format',
+        ),
+      ),
+    )
+  );
+  $info['taxonomy_vocabulary'] = array(
+    'title' => t('Vocabulary'),
+    'entity' => 'taxonomy_vocabulary',
+    'key' => 'vid',
+    'placeholders' => array(
+      '%taxonomy_vocabulary_machine_name' => 'machine_name',
+    ),
+    // Auto generate edit path
+    'edit path' => 'admin/structure/taxonomy/%taxonomy_vocabulary_machine_name/edit',
+    // Auto-generate translate tab
+    'translate tab' => 'admin/structure/taxonomy/%taxonomy_vocabulary_machine_name/translate',
+    // We can easily list all these objects
+    'list callback' => 'taxonomy_get_vocabularies',
+    // Metadata for string translation
+    'string translation' => array(
+      'textgroup' => 'taxonomy',
+      'type' => 'vocabulary',
+      'properties' => array(
+        'name' => t('Name'),
+        'description' => t('Description'),
+      ),
+    ),
+    'translation container' => array(
+      'name' => t('vocabulary'),
+      'item type' => 'taxonomy_term',
+      'item name' => t('terms'),
+      'options' => array(I18N_MODE_NONE, I18N_MODE_LOCALIZE, I18N_MODE_TRANSLATE, I18N_MODE_LANGUAGE),
+    ),
+  );
+  return $info;
+}
+
+/**
+ * Implements hook_i18n_translation_set_info()
+ */
+function i18n_taxonomy_i18n_translation_set_info() {
+  $info['taxonomy_term'] = array(
+    'title' => t('Taxonomy term'),
+    'class' => 'i18n_taxonomy_translation_set',
+    'table' => 'taxonomy_term_data',
+    'field' => 'i18n_tsid',
+    'parent' => 'taxonomy_vocabulary',
+    'placeholder' => '%i18n_taxonomy_translation_set',
+    'list path' => 'admin/structure/taxonomy/%taxonomy_vocabulary_machine_name/list/sets',
+    'edit path' => 'admin/structure/taxonomy/%taxonomy_vocabulary_machine_name/list/sets/edit/%i18n_taxonomy_translation_set',
+    'delete path' => 'admin/structure/taxonomy/%taxonomy_vocabulary_machine_name/list/sets/delete/%i18n_taxonomy_translation_set',
+    'page callback' => 'i18n_taxonomy_term_translation_page',
+  );
+  return $info;
+}
+
+/**
+ * Implements hook_i18n_string_info()
+ */
+function i18n_taxonomy_i18n_string_info() {
+  $groups['taxonomy'] = array(
+    'title' => t('Taxonomy'),
+    'description' => t('Vocabulary titles and term names for localizable vocabularies.'),
+    'format' => FALSE, // This group doesn't have strings with format
+    'list' => FALSE, // This group cannot list all strings
+    'refresh callback' => 'i18n_taxonomy_i18n_string_refresh',
+  );
+  return $groups;
+}
+
+/**
+ * Implements hook_i18n_string_objects()
+ */
+function i18n_taxonomy_i18n_string_objects($type) {
+  if ($type == 'taxonomy_term') {
+    $terms = array();
+    foreach (taxonomy_get_vocabularies() as $vid => $vocabulary) { 
+      if (isset($vocabulary->i18n_mode) && $vocabulary->i18n_mode & I18N_MODE_LOCALIZE) {
+        foreach (taxonomy_get_tree($vid, 0) as $term) {
+          $terms[] = $term;
+        }
+      }
+    }
+    return $terms;
+  }
+}
+
+/**
+ * Callback for term translation tab.
+ * 
+ * @param $type
+ *   Should be always 'taxonomy_term'
+ * @pram $term
+ *   Taxonomy term object
+ */
+function i18n_taxonomy_term_translation_page($type, $term) {
+  module_load_include('admin.inc', 'i18n_taxonomy');
+  $vocabulary = taxonomy_vocabulary_load($term->vid);
+  $translation_set = !empty($term->i18n_tsid) ? i18n_translation_set_load($term->i18n_tsid) : NULL;
+
+  $translation_overview = i18n_taxonomy_translation_term_overview($term);
+
+  $translation_term_form = drupal_get_form('i18n_taxonomy_translation_term_form', $vocabulary, $translation_set, $term);
+
+  return $translation_overview + $translation_term_form;
+}
diff --git a/kolab.org/www/drupal-7.18/sites/all/modules/i18n/i18n_taxonomy/i18n_taxonomy.inc b/kolab.org/www/drupal-7.18/sites/all/modules/i18n/i18n_taxonomy/i18n_taxonomy.inc
new file mode 100644
index 0000000..104f0c0
--- /dev/null
+++ b/kolab.org/www/drupal-7.18/sites/all/modules/i18n/i18n_taxonomy/i18n_taxonomy.inc
@@ -0,0 +1,52 @@
+<?php
+/**
+ * @file
+ * Internationalization (i18n) module - Translation set
+ */
+/**
+ * @file
+ * Internationalization (i18n) module - Translation set
+ */
+class i18n_taxonomy_translation_set extends i18n_translation_set {
+  /**
+   * Load all term translations
+   */
+  public function load_translations() {
+    return i18n_translation_set_index(taxonomy_term_load_multiple(array(), array('i18n_tsid' => $this->tsid)));
+  }
+
+  /**
+   * Get placeholder values for path replacement
+   */
+  function get_path_placeholders($op = 'list') {
+    $values = parent::get_path_placeholders($op);
+    if (!empty($this->bundle)) {
+      $values['%taxonomy_vocabulary_machine_name'] = $this->bundle;
+    }
+    return $values;
+  }
+}
+
+/**
+ * Taxonomy textgroup handler
+ */
+class i18n_taxonomy_term extends i18n_string_object_wrapper {
+  /**
+   * Translation mode for object
+   */
+  public function get_translate_mode() {
+    $mode = i18n_taxonomy_vocabulary_mode($this->object->vid);
+    if ($this->get_langcode()) {
+      return $mode & I18N_MODE_TRANSLATE;
+    }
+    else {
+      return $mode & I18N_MODE_LOCALIZE;
+    }
+  }
+  /**
+   * Access to object translation. This should check object properties and permissions
+   */
+  protected function translate_access() {
+    return taxonomy_term_edit_access($this->object) && $this->get_langcode() && user_access('translate interface');
+  }
+}
diff --git a/kolab.org/www/drupal-7.18/sites/all/modules/i18n/i18n_taxonomy/i18n_taxonomy.info b/kolab.org/www/drupal-7.18/sites/all/modules/i18n/i18n_taxonomy/i18n_taxonomy.info
new file mode 100644
index 0000000..0d26403
--- /dev/null
+++ b/kolab.org/www/drupal-7.18/sites/all/modules/i18n/i18n_taxonomy/i18n_taxonomy.info
@@ -0,0 +1,19 @@
+name = Taxonomy translation
+description = Enables multilingual taxonomy.
+dependencies[] = taxonomy
+dependencies[] = i18n_string
+dependencies[] = i18n_translation
+package = Multilingual - Internationalization
+core = 7.x
+
+files[] = i18n_taxonomy.inc
+files[] = i18n_taxonomy.pages.inc
+files[] = i18n_taxonomy.admin.inc
+files[] = i18n_taxonomy.test
+
+; Information added by drupal.org packaging script on 2013-01-13
+version = "7.x-1.8"
+core = "7.x"
+project = "i18n"
+datestamp = "1358075001"
+
diff --git a/kolab.org/www/drupal-7.18/sites/all/modules/i18n/i18n_taxonomy/i18n_taxonomy.install b/kolab.org/www/drupal-7.18/sites/all/modules/i18n/i18n_taxonomy/i18n_taxonomy.install
new file mode 100644
index 0000000..d058eb9
--- /dev/null
+++ b/kolab.org/www/drupal-7.18/sites/all/modules/i18n/i18n_taxonomy/i18n_taxonomy.install
@@ -0,0 +1,130 @@
+<?php
+
+/**
+ * @file
+ * Installation file for i18n_taxonomy module.
+ */
+
+/**
+ * Set language field in its own table.
+ * Do not drop node.language now, just in case.
+ * TO-DO: Drop old tables, fields
+ */
+function i18n_taxonomy_install() {
+  module_load_install('i18n');
+  i18n_install_create_fields('taxonomy_vocabulary', array('language', 'i18n_mode'));
+  i18n_install_create_fields('taxonomy_term_data', array('language', 'i18n_tsid'));
+  // Set module weight for it to run after core modules, but before views.
+  db_query("UPDATE {system} SET weight = 5 WHERE name = 'i18n_taxonomy' AND type = 'module'");
+  // Set vocabulary mode if updating from D6, module changed name
+  if (variable_get('i18n_drupal6_update')) {
+    i18n_taxonomy_update_7000();
+  }
+}
+
+/**
+ * Implements hook_uninstall().
+ */
+function i18n_taxonomy_uninstall() {
+  db_drop_field('taxonomy_vocabulary', 'language');
+  db_drop_field('taxonomy_vocabulary', 'i18n_mode');
+  db_drop_field('taxonomy_term_data', 'language');
+  db_drop_field('taxonomy_term_data', 'i18n_tsid');
+  variable_del('i18n_taxonomy_vocabulary');
+}
+
+/**
+ * Implements hook_schema_alter().
+ */
+function i18n_taxonomy_schema_alter(&$schema) {
+  $schema['taxonomy_vocabulary']['fields']['language'] = array('type' => 'varchar', 'length' => 12, 'not null' => TRUE, 'default' => LANGUAGE_NONE);
+  $schema['taxonomy_vocabulary']['fields']['i18n_mode'] = array('type' => 'int', 'unsigned' => TRUE, 'not null' => TRUE, 'default' => 0);
+  $schema['taxonomy_term_data']['fields']['language'] = array('type' => 'varchar', 'length' => 12, 'not null' => TRUE, 'default' => LANGUAGE_NONE);
+  $schema['taxonomy_term_data']['fields']['i18n_tsid'] = array('type' => 'int', 'unsigned' => TRUE, 'not null' => TRUE, 'default' => 0);
+}
+
+/**
+ * Implements hook_disable()
+ */
+function i18n_taxonomy_disable() {
+  foreach (field_info_fields() as $fieldname => $field) {
+    if ($field['type'] == 'taxonomy_term_reference' && $field['settings']['options_list_callback'] == 'i18n_taxonomy_allowed_values') {
+      $field['settings']['options_list_callback'] = NULL;
+      field_update_field($field);
+    }
+  }
+}
+
+/**
+ * Set vocabulary modes from D6 variable
+ */
+function i18n_taxonomy_update_7000() {
+  if ($options = variable_get('i18ntaxonomy_vocabulary')) {
+    foreach ($options as $vid => $mode) {
+      $mode = $mode == 3 ? I18N_MODE_TRANSLATE : $mode;
+      db_update('taxonomy_vocabulary')
+        ->fields(array('i18n_mode' => $mode))
+        ->condition('vid', $vid)
+        ->execute();
+    }
+    variable_del('i18ntaxonomy_vocabulary');
+  }
+  // Move to new translation set system
+  if (db_field_exists('taxonomy_term_data', 'trid')) {
+    $query = db_select('taxonomy_term_data', 't');
+    $query->join('taxonomy_vocabulary', 'v', 't.vid = v.vid');
+    $query
+      ->fields('t', array('trid'))
+      ->fields('v', array('machine_name'))
+      ->condition('t.trid', 0, '>')
+      ->distinct();
+
+    foreach ($query->execute() as $record) {
+      $tset = i18n_translation_set_create('taxonomy_term', $record->machine_name);
+      db_update('taxonomy_term_data')
+        ->fields(array('trid' => 0, 'i18n_tsid' => $tset->tsid))
+        ->condition('trid', $record->trid)
+        ->execute();
+    }
+    db_drop_field('taxonomy_term_data', 'trid');
+  }
+}
+
+/**
+ * Drop trid column used in D6 if exists
+ */
+function i18n_taxonomy_update_7001() {
+  if (db_field_exists('taxonomy_term_data', 'trid')) {
+    db_drop_field('taxonomy_term_data', 'trid');
+  }
+}
+
+/**
+ * Switch back to real taxonomy fields and override option_list_callback.
+ */
+function i18n_taxonomy_update_7002(&$sandbox) {
+  foreach (field_info_fields() as $fieldname => $field) {
+    if ($field['type'] == 'taxonomy_term_reference') {
+      $field['module'] = 'taxonomy';
+      $field['settings']['options_list_callback'] =  module_exists('i18n_taxonomy') ? 'i18n_taxonomy_allowed_values' : NULL;
+      drupal_write_record('field_config', $field, array('id'));
+    }
+  }
+}
+
+/**
+ * Unset option_list_callback if i18n_taxonomy is disabled.
+ */
+function i18n_taxonomy_update_7003(&$sandbox) {
+  if (!function_exists('i18n_taxonomy_allowed_values')) {
+    i18n_taxonomy_disable();
+  }
+}
+
+/**
+ * Update D6 language fields in {taxonomy_vocabulary} and {taxonomy_term_data}.
+ */
+function i18n_taxonomy_update_7004() {
+  i18n_install_create_fields('taxonomy_vocabulary', array('language'));
+  i18n_install_create_fields('taxonomy_term_data', array('language'));
+}
diff --git a/kolab.org/www/drupal-7.18/sites/all/modules/i18n/i18n_taxonomy/i18n_taxonomy.module b/kolab.org/www/drupal-7.18/sites/all/modules/i18n/i18n_taxonomy/i18n_taxonomy.module
new file mode 100644
index 0000000..d5c1793
--- /dev/null
+++ b/kolab.org/www/drupal-7.18/sites/all/modules/i18n/i18n_taxonomy/i18n_taxonomy.module
@@ -0,0 +1,1269 @@
+<?php
+
+/**
+ * @file
+ * i18n taxonomy module
+ *
+ * Internationalization (i18n) package.
+ *
+ * This module groups together all existing i18n taxonomy functionality
+ * providing several options for taxonomy translation.
+ *
+ * Translates taxonomy term for selected vocabularies running them through the localization system.
+ * It also translates terms for views filters and views results.
+ *
+ * @author Jose A. Reyero, 2004
+ */
+
+/**
+ * Implements hook_help().
+ */
+function i18n_taxonomy_help($path, $arg) {
+  switch ($path) {
+    case 'admin/help#i18n_taxonomy' :
+      $output = '<p>' . t('This module adds support for multilingual taxonomy. You can set up multilingual options for each vocabulary:') . '</p>';
+      $output .= '<ul>';
+      $output .= '<li>' . t('A language can be assigned globaly for a vocabulary.') . '</li>';
+      $output .= '<li>' . t('Different terms for each language with translation relationships.') . '</li>';
+      $output .= '<li>' . t('Terms can be common to all languages, but may be localized.') . '</li>';
+      $output .= '</ul>';
+      $output .= '<p>' . t('To search and translate strings, use the <a href="@translate-interface">translation interface</a> pages.', array('@translate-interface' => url('admin/config/regional/translate'))) . '</p>';
+      $output .= '<p>' . t('For more information, see the online handbook entry for <a href="@i18n">Internationalization module</a>.', array('@i18n' => 'http://drupal.org/node/133977')) . '</p>';
+      return $output;
+
+    case 'admin/config/regional/i18n':
+      $output = '<p>' . t('To set up multilingual options for vocabularies go to <a href="@configure_taxonomy">Taxonomy configuration page</a>.', array('@configure_taxonomy' => url('admin/structure/taxonomy'))) . '</p>';
+      return $output;
+
+    case 'admin/structure/taxonomy/%':
+      $vocabulary = taxonomy_vocabulary_machine_name_load($arg[3]);
+      switch (i18n_taxonomy_vocabulary_mode($vocabulary)) {
+        case I18N_MODE_LOCALIZE:
+          return '<p>' . t('%capital_name is a localizable vocabulary. You will be able to translate term names and descriptions using the <a href="@translate-interface">translate interface</a> pages.', array('%capital_name' => drupal_ucfirst($vocabulary->name), '%name' => $vocabulary->name, '@translate-interface' => url('admin/config/regional/translate'))) . '</p>';
+
+        case I18N_MODE_LANGUAGE:
+          return '<p>' . t('%capital_name is a vocabulary with a fixed language. All the terms in this vocabulary will have %language language.', array('%capital_name' => drupal_ucfirst($vocabulary->name), '%name' => $vocabulary->name, '%language' => i18n_language_property($vocabulary->language, 'name'))) . '</p>';
+
+        case I18N_MODE_TRANSLATE:
+          return '<p>' . t('%capital_name is a full multilingual vocabulary. You will be able to set a language for each term and create translation relationships.', array('%capital_name' => drupal_ucfirst($vocabulary->name))) . '</p>';
+      }
+      break;
+
+  }
+}
+
+/**
+ * Implements hook_menu().
+ */
+function i18n_taxonomy_menu() {
+  $items['admin/structure/taxonomy/%taxonomy_vocabulary_machine_name/list/list'] = array(
+    'title' => 'Terms',
+    'type' => MENU_DEFAULT_LOCAL_TASK,
+    'weight' => -20,
+  );
+  $items['admin/structure/taxonomy/%taxonomy_vocabulary_machine_name/list/sets'] = array(
+    'title' => 'Translation sets',
+    'page callback' => 'i18n_taxonomy_translation_sets_overview',
+    'page arguments' => array(3),
+    'access callback' => 'i18n_taxonomy_vocabulary_translation_tab_sets_access',
+    'access arguments' => array(3),
+    'type' => MENU_LOCAL_TASK,
+    'file' => 'i18n_taxonomy.admin.inc',
+  );
+  $items['admin/structure/taxonomy/%taxonomy_vocabulary_machine_name/list/sets/add'] = array(
+    'title' => 'Create new translation',
+    'page callback' => 'drupal_get_form',
+    'page arguments' => array('i18n_taxonomy_translation_term_form', 3),
+    'access callback' => 'i18n_taxonomy_vocabulary_translation_tab_sets_access',
+    'access arguments' => array(3),
+    'type' => MENU_LOCAL_ACTION,
+    //'parent' => 'admin/content/taxonomy/%taxonomy_vocabulary',
+    'file' => 'i18n_taxonomy.admin.inc',
+  );
+  $items['admin/structure/taxonomy/%taxonomy_vocabulary_machine_name/list/sets/edit/%i18n_taxonomy_translation_set'] = array(
+    'title' => 'Edit translation',
+    'page callback' => 'drupal_get_form',
+    'page arguments' => array('i18n_taxonomy_translation_term_form', 3, 7),
+    'access callback' => 'i18n_taxonomy_vocabulary_translation_tab_sets_access',
+    'access arguments' => array(3),
+    'type' => MENU_CALLBACK,
+    //'parent' => 'admin/content/taxonomy/%taxonomy_vocabulary',
+    'file' => 'i18n_taxonomy.admin.inc',
+  );
+  $items['admin/structure/taxonomy/%taxonomy_vocabulary_machine_name/list/sets/delete/%i18n_taxonomy_translation_set'] = array(
+    'title' => 'Delete translation',
+    'page callback' => 'drupal_get_form',
+    'page arguments' => array('i18n_translation_set_delete_confirm', 7),
+    'access callback' => 'i18n_taxonomy_vocabulary_translation_tab_sets_access',
+    'access arguments' => array(3),
+    'type' => MENU_CALLBACK,
+  );
+  $items['i18n/taxonomy/autocomplete/vocabulary/%taxonomy_vocabulary_machine_name/%'] = array(
+    'title' => 'Autocomplete taxonomy',
+    'page callback' => 'i18n_taxonomy_autocomplete_language',
+    'page arguments' => array(5, 4),
+    'access arguments' => array('access content'),
+    'type' => MENU_CALLBACK,
+    'file' => 'i18n_taxonomy.pages.inc',
+  );
+  $items['i18n/taxonomy/autocomplete/language/%'] = array(
+    'title' => 'Autocomplete taxonomy',
+    'page callback' => 'i18n_taxonomy_autocomplete_language',
+    'page arguments' => array(4, NULL),
+    'access arguments' => array('access content'),
+    'type' => MENU_CALLBACK,
+    'file' => 'i18n_taxonomy.pages.inc',
+  );
+  return $items;
+}
+
+/**
+ * Implements hook_admin_paths().
+ */
+function i18n_taxonomy_admin_paths() {
+  $paths = array(
+    'taxonomy/*/translate' => TRUE,
+    'taxonomy/*/translate/*' => TRUE,
+  );
+  return $paths;
+}
+
+/**
+ * Implements hook_menu_alter().
+ *
+ * Take over the taxonomy pages
+ */
+function i18n_taxonomy_menu_alter(&$items) {
+  // If ctool's page manager is active for the path skip this modules override.
+  // Also views module takes over this page so this won't work if views enabled.
+  // Skip when taxonomy_display enabled, see http://drupal.org/node/1280194
+  if (variable_get('page_manager_term_view_disabled', TRUE) && !module_exists('taxonomy_display')) {
+    // Taxonomy term page. Localize terms.
+    $items['taxonomy/term/%taxonomy_term']['page callback'] = 'i18n_taxonomy_term_page';
+    $items['taxonomy/term/%taxonomy_term']['title callback'] = 'i18n_taxonomy_term_name';
+    $items['taxonomy/term/%taxonomy_term']['file'] = 'i18n_taxonomy.pages.inc';
+    $items['taxonomy/term/%taxonomy_term']['file path'] = drupal_get_path('module', 'i18n_taxonomy');
+  }
+
+  // Localize autocomplete
+  $items['taxonomy/autocomplete']['page callback'] = 'i18n_taxonomy_autocomplete_field';
+  $items['taxonomy/autocomplete']['file'] = 'i18n_taxonomy.pages.inc';
+  $items['taxonomy/autocomplete']['file path'] = drupal_get_path('module', 'i18n_taxonomy');
+}
+
+/**
+ * Menu access callback for vocabulary translation tab. Show tab only for full multilingual vocabularies.
+ */
+function i18n_taxonomy_vocabulary_translation_tab_sets_access($vocabulary) {
+  return user_access('administer taxonomy') && i18n_taxonomy_vocabulary_mode($vocabulary->vid, I18N_MODE_TRANSLATE);
+}
+
+/**
+ * Menu access callback for term translation tab. Show tab only for translatable terms
+ *
+ * @todo This should work also for localizable terms when we've got that part implemented
+ */
+function i18n_taxonomy_term_translation_tab_access($term) {
+  return taxonomy_term_edit_access($term) && i18n_taxonomy_vocabulary_mode($term->vid) & I18N_MODE_MULTIPLE && user_access('translate interface');
+}
+
+/**
+ * Implements hook_field_formatter_info().
+ */
+function i18n_taxonomy_field_formatter_info() {
+  return array(
+    'i18n_taxonomy_term_reference_link' => array(
+      'label' => t('Link (localized)'),
+      'field types' => array('taxonomy_term_reference'),
+    ),
+    'i18n_taxonomy_term_reference_plain' => array(
+      'label' => t('Plain text (localized)'),
+      'field types' => array('taxonomy_term_reference'),
+    ),
+  );
+}
+
+ /**
+- * Implements hook_field_formatter_prepare_view().
+- *
+- * This preloads all taxonomy terms for multiple loaded objects at once and
+- * unsets values for invalid terms that do not exist.
+- */
+function i18n_taxonomy_field_formatter_prepare_view($entity_type, $entities, $field, $instances, $langcode, &$items, $displays) {
+  return taxonomy_field_formatter_prepare_view($entity_type, $entities, $field, $instances, $langcode, $items, $displays);
+}
+
+/**
+ * Implements hook_field_formatter_view().
+ */
+function i18n_taxonomy_field_formatter_view($entity_type, $entity, $field, $instance, $langcode, $items, $display) {
+  $element = array();
+  $language = i18n_language_interface();
+
+  // Terms whose tid is 'autocreate' do not exist
+  // yet and $item['taxonomy_term'] is not set. Theme such terms as
+  // just their name.
+
+  switch ($display['type']) {
+    case 'i18n_taxonomy_term_reference_link':
+      foreach ($items as $delta => $item) {
+        if ($item['tid'] == 'autocreate') {
+          $element[$delta] = array(
+            '#markup' => check_plain($item['name']),
+          );
+        }
+        else {
+          $term = $item['taxonomy_term'];
+          $uri = entity_uri('taxonomy_term', $term);
+          $element[$delta] = array(
+            '#type' => 'link',
+            '#title' => i18n_taxonomy_term_name($term, $language->language),
+            '#href' => $uri['path'],
+            '#options' => $uri['options'],
+          );
+        }
+      }
+      break;
+
+    case 'i18n_taxonomy_term_reference_plain':
+      foreach ($items as $delta => $item) {
+        $name = ($item['tid'] != 'autocreate' ? i18n_taxonomy_term_name($item['taxonomy_term'], $language->language): $item['name']);
+        $element[$delta] = array(
+          '#markup' => check_plain($name),
+        );
+      }
+      break;
+  }
+
+  return $element;
+}
+
+/**
+ * Implements hook_field_extra_fields().
+ */
+function i18n_taxonomy_field_extra_fields() {
+  $return = array();
+  $info = entity_get_info('taxonomy_term');
+  foreach (array_keys($info['bundles']) as $bundle) {
+    $vocabulary = taxonomy_vocabulary_machine_name_load($bundle);
+    if ($vocabulary && i18n_taxonomy_vocabulary_mode($vocabulary, I18N_MODE_TRANSLATE)) {
+      $return['taxonomy_term'][$bundle] = i18n_language_field_extra();
+    }
+  }
+  return $return;
+}
+
+/**
+ * Implements hook_field_attach_view_alter().
+ */
+function i18n_taxonomy_field_attach_view_alter(&$output, $context) {
+  // Copied from rdf_field_attach_view_alter of modules/rdf/rdf.module since we have another #formatter
+  // Append term mappings on displayed taxonomy links.
+  foreach (element_children($output) as $field_name) {
+    $element = &$output[$field_name];
+    if (isset($element['#field_type']) && $element['#field_type'] == 'taxonomy_term_reference' && $element['#formatter'] == 'i18n_taxonomy_term_reference_link') {
+      foreach ($element['#items'] as $delta => $item) {
+        // This function is invoked during entity preview when taxonomy term
+        // reference items might contain free-tagging terms that do not exist
+        // yet and thus have no $item['taxonomy_term'].
+        if (isset($item['taxonomy_term'])) {
+          $term = $item['taxonomy_term'];
+          if (!empty($term->rdf_mapping['rdftype'])) {
+            $element[$delta]['#options']['attributes']['typeof'] = $term->rdf_mapping['rdftype'];
+          }
+          if (!empty($term->rdf_mapping['name']['predicates'])) {
+            $element[$delta]['#options']['attributes']['property'] = $term->rdf_mapping['name']['predicates'];
+          }
+        }
+      }
+    }
+  }
+  // Add language field for display
+  if ($context['entity_type'] == 'taxonomy_term' && i18n_taxonomy_vocabulary_mode($context['entity']->vid, I18N_MODE_TRANSLATE)) {
+    $output['language'] = array(
+      '#type' => 'item',
+      '#title' => t('Language'),
+      '#markup' => i18n_language_name($context['entity']->language),
+    );
+  }
+}
+
+/**
+ * Implements hook_field_info_alter()
+ */
+function i18n_taxonomy_field_info_alter(&$info) {
+  // Change default formatter for term reference fields
+  $info['taxonomy_term_reference']['default_formatter'] = 'i18n_taxonomy_term_reference_link';
+
+  // Translate field values.
+  $info['taxonomy_term_reference']['options_list_callback'] = 'i18n_taxonomy_allowed_values';
+
+  // Sync callback for field translations
+  $info['taxonomy_term_reference']['i18n_sync_callback'] = 'i18n_taxonomy_field_prepare_translation';
+}
+
+/**
+ * Implements hook_field_storage_details_alter().
+ *
+ * We don't alter the storage details but the stored details of the field itself...
+ *
+ * @param array $field
+ *   The field record just read from the database.
+ */
+function i18n_taxonomy_field_storage_details_alter(&$details, &$field) {
+  if ($field['type'] === 'taxonomy_term_reference') {
+    $field['settings']['options_list_callback'] = 'i18n_taxonomy_allowed_values';
+  }
+}
+
+/**
+ * Implements hook_field_attach_prepare_translation_alter().
+
+ * Prepare and synchronize translation for term reference fields.
+ */
+function i18n_taxonomy_field_attach_prepare_translation_alter(&$entity, $context) {
+  $entity_type = $context['entity_type'];
+  $source_entity = $context['source_entity'];
+
+  $options = array(
+    'default' => FALSE,
+    'deleted' => FALSE,
+    'language' => NULL,
+  );
+
+  // Determine the list of instances to iterate on.
+  list(, , $bundle) = entity_extract_ids($entity_type, $source_entity);
+  $instances = _field_invoke_get_instances($entity_type, $bundle, $options);
+  if (!empty($instances)) {
+    foreach ($instances as $field_info) {
+      $field = field_info_field($field_info['field_name']);
+      if ($field['type'] == 'taxonomy_term_reference' && isset($entity->{$field_info['field_name']})) {
+        // iterate over languages.
+        foreach ($entity->{$field_info['field_name']} as $language => &$items) {
+          i18n_taxonomy_field_prepare_translation($entity_type, $entity, $field, $field_info, $entity->language, $items, $source_entity, $source_entity->language);
+        }
+      }
+    }
+  }
+}
+
+/**
+ * Prepare and synchronize translation for term reference fields
+ */
+function i18n_taxonomy_field_prepare_translation($entity_type, $entity, $field, $instance, $langcode, &$items, $source_entity, $source_langcode) {
+  foreach ($items as $index => $item) {
+    $term = isset($item['taxonomy_term']) ? $item['taxonomy_term'] : taxonomy_term_load($item['tid']);
+    if ($translation = i18n_taxonomy_term_get_translation($term, $langcode)) {
+      $items[$index] = array(
+        'taxonomy_term' => $translation,
+        'tid' => $translation->tid
+      );
+    }
+    $field['settings']['options_list_callback'] = 'i18n_taxonomy_allowed_values';
+  }
+}
+
+/**
+ * Returns the set of valid terms for a taxonomy field.
+ *
+ * @param $field
+ *   The field definition.
+ * @return
+ *   The array of valid terms for this field, keyed by term id.
+ */
+function i18n_taxonomy_allowed_values($field) {
+  $options = array();
+  foreach ($field['settings']['allowed_values'] as $tree) {
+    if ($vocabulary = taxonomy_vocabulary_machine_name_load($tree['vocabulary'])) {
+      if ($terms = taxonomy_get_tree($vocabulary->vid, $tree['parent'])) {
+        foreach ($terms as $term) {
+          $options[$term->tid] = str_repeat('-', $term->depth) . i18n_taxonomy_term_name($term);
+        }
+      }
+    }
+  }
+  return $options;
+}
+
+/**
+ * Implements hook_i18n_translate_path()
+ */
+function i18n_taxonomy_i18n_translate_path($path) {
+  if (strpos($path, 'taxonomy/term/') === 0) {
+    return i18n_taxonomy_translate_path($path);
+  }
+}
+
+/**
+ * Implements hook_i18n_context_language().
+ */
+function i18n_taxonomy_i18n_context_language() {
+  if (arg(0) == 'taxonomy') {
+    // Taxonomy term pages
+    if (arg(1) == 'term' && is_numeric(arg(2)) && ($term = menu_get_object('taxonomy_term', 2)) && ($langcode = i18n_object_langcode($term))) {
+      return i18n_language_object($langcode);
+    }
+  }
+}
+
+/**
+ * Find translations for taxonomy paths.
+ *
+ * @param $path
+ *   Path to translate.
+ * @param $path_prefix
+ *   Path prefix, including trailing slash, defaults to 'taxonomy/term/'.
+ *   It will be different for taxonomy term pages and for forum pages.
+ *
+ * @return
+ *   Array of links (each an array with href, title), indexed by language code.
+ */
+function i18n_taxonomy_translate_path($path, $path_prefix = 'taxonomy/term/') {
+  $prefix_match = strtr($path_prefix, array('/' => '\/'));
+  if (preg_match("/^($prefix_match)([^\/]*)(.*)$/", $path, $matches)) {
+    $links = array();
+    $term = FALSE;
+    // If single term, treat it differently
+    if (is_numeric($matches[2]) && !$matches[3]) {
+      $term = taxonomy_term_load($matches[2]);
+      if (!empty($term->i18n_tsid)) {
+        $set = i18n_translation_set_load($term->i18n_tsid);
+      }
+    }
+    foreach (language_list() as $langcode => $language) {
+      if ($term) {
+        if (!empty($set) && ($translation = $set->get_item($langcode))) {
+          $links[$langcode] = array(
+            'href' => $path_prefix . $translation->tid,
+            'title' => $translation->name,
+          );
+        }
+        else {
+          $links[$langcode] = array(
+            'href' => $path,
+            'title' => i18n_taxonomy_term_name($term, $langcode),
+          );
+        }
+      }
+      elseif ($str_tids = i18n_taxonomy_translation_tids($matches[2], $langcode)) {
+        $links[$langcode]['href'] = $path_prefix . $str_tids . $matches[3];
+      }
+    }
+    return $links;
+  }
+}
+/**
+ * Implements hook_theme().
+ */
+function i18n_taxonomy_theme() {
+  return array(
+    'i18n_taxonomy_term_page' => array(
+      'arguments' => array('tids' => array(), 'result' => NULL),
+      'file' => 'i18n_taxonomy.pages.inc',
+    ),
+  );
+}
+
+/**
+ * Get localized term name unfiltered.
+ */
+function i18n_taxonomy_term_name($term, $langcode = NULL) {
+  return i18n_taxonomy_vocabulary_mode($term->vid, I18N_MODE_LOCALIZE) ? i18n_string(array('taxonomy', 'term', $term->tid, 'name'), $term->name, array('langcode' => $langcode, 'sanitize' => FALSE)) : $term->name;
+}
+
+
+/**
+ * Get localized term description unfiltered.
+ */
+function i18n_taxonomy_term_description($term, $langcode = NULL) {
+  return i18n_taxonomy_vocabulary_mode($term->vid, I18N_MODE_LOCALIZE) ? i18n_string(array('taxonomy', 'term', $term->tid, 'description'), $term->description, array('langcode' => $langcode, 'sanitize' => FALSE)) : $term->description;
+}
+
+/**
+ * Find term translation from translation set.
+ *
+ * @param $term
+ *   Term object to find translation.
+ * @param $langcode
+ *   Language code to find translation for.
+ * @result object Taxonomy Term
+ *   Translation if exists.
+ */
+function i18n_taxonomy_term_get_translation($term, $langcode) {
+  if (i18n_object_langcode($term)) {
+    if ($term->language == $langcode) {
+      // Translation is the term itself.
+      return $term;
+    }
+    elseif (!empty($term->i18n_tsid)) {
+      return i18n_translation_set_load($term->i18n_tsid)->get_item($langcode);
+    }
+    else {
+      return NULL;
+    }
+  }
+  else {
+    // Term has no language, translation should be the same
+    return $term;
+  }
+}
+
+/**
+ * Get localized vocabulary name, unfiltered.
+ */
+function i18n_taxonomy_vocabulary_name($vocabulary, $langcode = NULL) {
+  return i18n_object_langcode($vocabulary) ? $vocabulary->name : i18n_string(array('taxonomy', 'vocabulary', $vocabulary->vid, 'name'), $vocabulary->name, array('langcode' => $langcode, 'sanitize' => FALSE));
+}
+
+/**
+ * Get localized vocabulary description, unfiltered.
+ */
+function i18n_taxonomy_vocabulary_description($vocabulary, $langcode = NULL) {
+  return i18n_object_langcode($vocabulary) ? $vocabulary->description : i18n_string(array('taxonomy', 'vocabulary', $vocabulary->vid, 'description'), $vocabulary->description, array('langcode' => $langcode, 'sanitize' => FALSE));
+}
+
+/**
+ * Get translated term's tid.
+ *
+ * @param $tid
+ *   Node nid to search for translation.
+ * @param $language
+ *   Language to search for the translation, defaults to current language.
+ * @param $default
+ *   Value that will be returned if no translation is found.
+ * @return
+ *   Translated term tid if exists, or $default.
+ */
+function i18n_taxonomy_translation_term_tid($tid, $language = NULL, $default = NULL) {
+  $translation = db_query('SELECT t.tid FROM {taxonomy_term_data} t INNER JOIN {taxonomy_term_data} a ON t.i18n_tsid = a.i18n_tsid AND t.tid <> a.tid WHERE a.tid = :tid AND t.language = :language AND t.i18n_tsid > 0', array(
+    ':tid' => $tid,
+    ':language' => $language ? $language : i18n_language_content()->language
+  ))->fetchField();
+  return $translation ? $translation : $default;
+}
+
+/**
+ *  Returns an url for the translated taxonomy-page, if exists.
+ */
+function i18n_taxonomy_translation_tids($str_tids, $lang) {
+  if (preg_match('/^([0-9]+[+ ])+[0-9]+$/', $str_tids)) {
+    $separator = '+';
+    // The '+' character in a query string may be parsed as ' '.
+    $tids = preg_split('/[+ ]/', $str_tids);
+  }
+  elseif (preg_match('/^([0-9]+,)*[0-9]+$/', $str_tids)) {
+    $separator = ',';
+    $tids = explode(',', $str_tids);
+  }
+  else {
+    return;
+  }
+  $translated_tids = array();
+  foreach ($tids as $tid) {
+    if ($translated_tid = i18n_taxonomy_translation_term_tid($tid, $lang)) {
+      $translated_tids[] = $translated_tid;
+    }
+  }
+  return implode($separator, $translated_tids);
+}
+
+/**
+ * Implements hook_taxonomy_display_breadcrumb_parents_alter().
+ */
+function i18n_taxonomy_taxonomy_display_breadcrumb_parents_alter(&$parents) {
+  $parents = i18n_taxonomy_localize_terms($parents);
+}
+
+/**
+ * Implements hook_taxonomy_display_term_page_term_object_alter().
+ */
+function i18n_taxonomy_taxonomy_display_term_page_term_object_alter(&$term) {
+  $term = i18n_taxonomy_localize_terms($term);
+}
+
+/**
+ * Implements hook_taxonomy_term_insert()
+ */
+function i18n_taxonomy_taxonomy_term_insert($term) {
+  i18n_taxonomy_taxonomy_term_update($term);
+}
+
+/**
+ * Implements hook_taxonomy_term_update()
+ */
+function i18n_taxonomy_taxonomy_term_update($term) {
+  if (i18n_taxonomy_vocabulary_mode($term->vid, I18N_MODE_LOCALIZE)) {
+    i18n_string_object_update('taxonomy_term', $term);
+  }
+  // Multilingual terms, translatable. Link / unlink from translation set.
+  if (i18n_taxonomy_vocabulary_mode($term->vid, I18N_MODE_TRANSLATE) && !empty($term->translation_set)) {
+    if (i18n_object_langcode($term)) {
+      $term->translation_set
+        ->add_item($term)
+        ->save();
+    }
+    elseif (!empty($term->original)) {
+      // Term set to language neutral, remove it from translation set and update set (delete if empty)
+      $term->translation_set
+        ->remove_item($term->original)
+        ->update_delete();
+    }
+  }
+}
+
+/**
+ * Implements hook_taxonomy_term_delete()
+ */
+function i18n_taxonomy_taxonomy_term_delete($term) {
+  // If a translation set exists for this term, remove this term from the set.
+  if (isset($term->i18n_tsid) && $term->i18n_tsid) {
+    $translation_set = i18n_translation_set_load($term->i18n_tsid);
+    $translation_set->get_translations();
+
+    $translation_set->remove_language($term->language);
+
+    // If there are no terms left in this translation set, delete the set.
+    // Otherwise update the set.
+    $translation_set->update_delete();
+  }
+  // Just in case there's any left over string we run it for all terms.
+  i18n_string_object_remove('taxonomy_term', $term);
+}
+
+/**
+ * Implements hook_taxonomy_vocabulary_insert()
+ */
+function i18n_taxonomy_taxonomy_vocabulary_insert($vocabulary) {
+  i18n_taxonomy_taxonomy_vocabulary_update($vocabulary);
+}
+
+/**
+ * Implements hook_taxonomy_vocabulary_update()
+ */
+function i18n_taxonomy_taxonomy_vocabulary_update($vocabulary) {
+  // Update language for related terms
+  switch (i18n_taxonomy_vocabulary_mode($vocabulary)) {
+    case I18N_MODE_LANGUAGE:
+      $update['language'] = $vocabulary->language;
+      break;
+    case I18N_MODE_NONE:
+      $update['language'] = LANGUAGE_NONE;
+      break;
+  }
+  if (isset($update)) {
+    db_update('taxonomy_term_data')
+      ->fields($update)
+      ->condition('vid', $vocabulary->vid)
+      ->execute();
+    drupal_set_message(t('Reset language for all terms.'));
+  }
+  // Update strings, always add translation if no language
+  if (!i18n_object_langcode($vocabulary)) {
+    i18n_string_object_update('taxonomy_vocabulary', $vocabulary);
+  }
+}
+
+/**
+ * Implements hook_taxonomy_vocabulary_delete()
+ */
+function i18n_taxonomy_taxonomy_vocabulary_delete($vocabulary) {
+  i18n_string_object_remove('taxonomy_vocabulary', $vocabulary);
+}
+
+/**
+ * Implements hook_taxonomy_term_presave()
+ */
+function i18n_taxonomy_taxonomy_term_presave($term) {
+  switch (i18n_taxonomy_vocabulary_mode($term->vid)) {
+    case I18N_MODE_LANGUAGE: // Predefined language for all terms
+      $term->language = taxonomy_vocabulary_load($term->vid)->language;
+      break;
+    case I18N_MODE_TRANSLATE: // Multilingual terms, translatable
+      if (!isset($term->language)) {
+         // The term may come from a node tags field, just if this is not a taxonomy form.
+         // Or from any other object we are editing. So we use 'context' language here.
+        $term->language = i18n_language_context()->language;
+      }
+      // Only for the case the term has no language, it may need to be removed from translation set
+      if (empty($term->language)) {
+        $term->i18n_tsid = 0;
+      }
+      break;
+  }
+}
+
+/**
+ * Implements hook_form_FORM_ID_alter()
+ */
+function i18n_taxonomy_form_taxonomy_form_vocabulary_alter(&$form, &$form_state) {
+  if (!isset($form_state['confirm_delete'])) {
+    $vocabulary = $form_state['vocabulary'];
+    $i18n_mode = i18n_taxonomy_vocabulary_mode($vocabulary);
+    $langcode = i18n_object_langcode($vocabulary, LANGUAGE_NONE);
+    // Define the replacement names to add some logic to the translation mode options.
+    $form += i18n_translation_mode_element('taxonomy_vocabulary', $i18n_mode, $langcode);
+    if (user_access('translate interface')) {
+      $form['actions']['translate'] = array(
+        '#type' => 'submit',
+        '#name'   => 'save_translate',
+        '#value' => t('Save and translate'),
+        '#weight' => 5,
+        '#states' => array(
+          'invisible' => array(
+            // Hide the button if language mode is selected value needs to be a
+            // string so that the javascript-side matching works.
+            'input[name=i18n_mode]' => array('value' => (string)I18N_MODE_LANGUAGE),
+          )
+        )
+      );
+      // Make sure the delete buttons shows up last.
+      if (isset($form['actions']['delete'])) {
+        $form['actions']['delete']['#weight'] = 10;
+      }
+    }
+    $form['#validate'][] = 'i18n_taxonomy_form_vocabulary_validate';
+    $form['#submit'][] = 'i18n_taxonomy_form_vocabulary_submit';
+  }
+}
+
+/**
+ * Form submit callback to redirect when using the save and translate button.
+ */
+function i18n_taxonomy_form_vocabulary_submit($form, &$form_state) {
+  if ($form_state['triggering_element']['#name'] == 'save_translate') {
+    $form_state['redirect'] = 'admin/structure/taxonomy/' . $form_state['values']['machine_name'] . '/translate';
+  }
+}
+
+/**
+ * Implements hook_form_FORM_ID_alter()
+ */
+function i18n_taxonomy_form_taxonomy_form_term_alter(&$form, &$form_state) {
+  // Check for confirmation forms
+  if (isset($form_state['confirm_delete']) || isset($form_state['confirm_parents'])) return;
+
+  $term = $form_state['term'];
+  $vocabulary = $form['#vocabulary'];
+
+  // Mark form so we can know later when saving the term it came from a taxonomy form
+  $form['i18n_taxonomy_form'] = array('#type' => 'value', '#value' => 1);
+
+  // Add language field or not depending on taxonomy mode.
+  switch (i18n_taxonomy_vocabulary_mode($vocabulary->vid)) {
+    case I18N_MODE_TRANSLATE:
+      $form['language'] = array(
+        '#description' => t('This term belongs to a multilingual vocabulary. You can set a language for it.'),
+      ) + i18n_element_language_select($term);
+
+      // If the term to be added will be a translation of a source term,
+      // set the default value of the option list to the target language and
+      // create a form element for storing the translation set of the source term.
+      if (empty($term->tid) && isset($_GET['translation']) && isset($_GET['target']) && ($source_term = taxonomy_term_load($_GET['translation'])) && ($target_language = i18n_language_object($_GET['target']))) {
+        // Set context language to target language.
+        i18n_language_context($target_language);
+
+        // Add the translation set to the form so we know the new term
+        // needs to be added to that set.
+        if (!empty($source_term->i18n_tsid)) {
+          $translation_set = i18n_taxonomy_translation_set_load($source_term->i18n_tsid);
+        }
+        else {
+          // No translation set yet, build a new one with the source term.
+          $translation_set = i18n_translation_set_create('taxonomy_term', $vocabulary->machine_name)
+          ->add_item($source_term);
+        }
+
+        $form['language']['#default_value'] = $target_language->language;
+        $form['language']['#disabled'] = TRUE;
+
+        drupal_set_title(t('%language translation of term %title', array('%language' => locale_language_name($_GET['target']), '%title' => $source_term->name)), PASS_THROUGH);
+      }
+      elseif (!empty($term->tid) && i18n_object_langcode($term)) {
+        // Set context language to term language.
+        i18n_language_context(i18n_language_object($term->language));
+        // If the current term is part of a translation set,
+        // remove all other languages of the option list.
+        if (!empty($term->i18n_tsid)) {
+          $translation_set = i18n_taxonomy_translation_set_load($term->i18n_tsid);
+          $translations = $translation_set->get_translations();
+          // If the number of translations equals 1, there's only a source translation.
+          if (count($translations) > 1) {
+            //unset($form['language']['#options'][LANGUAGE_NONE]);
+            foreach ($translations as $langcode => $translation) {
+              if ($translation->tid !== $term->tid) {
+                unset($form['language']['#options'][$langcode]);
+              }
+            }
+            $form['language']['#description'] = t('This term already belongs to a <a href="@term-translation">translation set</a>. Changing language to <i>Language neutral</i> will remove it from the set.', array('@term-translation' => url('taxonomy/term/' . $term->tid . '/translate')));
+          }
+        }
+      }
+      // If we've got a translation set, add it to the form.
+      if (!empty($translation_set)) {
+        $form['translation_set'] = array(
+          '#type' => 'value',
+          '#value' => $translation_set,
+        );
+      }
+      break;
+
+    case I18N_MODE_LANGUAGE:
+      // Set context language to vocabulary language and add value to the form.
+      i18n_language_context(i18n_language_object($vocabulary->language));
+      $form['language'] = array(
+        '#type' => 'value',
+        '#value' => $vocabulary->language
+      );
+      $form['identification']['language_info'] = array('#value' => t('All terms in this vocabulary have a fixed language: %language', array('%language' => i18n_language_name($vocabulary->language))));
+      break;
+
+    case I18N_MODE_LOCALIZE:
+      $form['language'] = array(
+        '#type' => 'value',
+        '#value' => LANGUAGE_NONE,
+      );
+      break;
+
+    case I18N_MODE_NONE:
+    default:
+      $form['language'] = array(
+        '#type' => 'value',
+        '#value' => LANGUAGE_NONE,
+      );
+      break;
+  }
+
+  if (user_access('translate interface') && i18n_taxonomy_vocabulary_mode($vocabulary->vid) & I18N_MODE_MULTIPLE) {
+    $form['actions']['translate'] = array(
+      '#type' => 'submit',
+      '#name'   => 'save_translate',
+      '#value' => t('Save and translate'),
+      '#weight' => 5,
+      '#states' => array(
+        'invisible' => array(
+          // Hide the button if term is language neutral.
+          'select[name=language]' => array('value' => LANGUAGE_NONE),
+        ),
+      ),
+    );
+    // Make sure the delete buttons shows up last.
+    if (isset($form['actions']['delete'])) {
+      $form['actions']['delete']['#weight'] = 10;
+    }
+    $form['#submit'][] = 'i18n_taxonomy_form_term_submit';
+  }
+}
+
+/**
+ * Form submit callback to redirect when using the save and translate button.
+ */
+function i18n_taxonomy_form_term_submit($form, &$form_state) {
+  if ($form_state['triggering_element']['#name'] == 'save_translate') {
+    // When using the edit link on the list terms, a destination param is
+    // added that needs to be unset to make the redirection work.
+    unset($_GET['destination']);
+    $form_state['redirect'] = 'taxonomy/term/' . $form_state['values']['tid'] . '/translate';
+  }
+}
+
+/**
+ * Implements hook_form_alter().
+ *
+ * This is the place to add language fields to all forms.
+ *
+ * @ TO DO The vocabulary form needs some javascript.
+ */
+function i18n_taxonomy_form_alter(&$form, $form_state, $form_id) {
+  switch ($form_id) {
+    case 'taxonomy_overview_vocabularies':
+      $vocabularies = taxonomy_get_vocabularies();
+      foreach ($vocabularies as $vocabulary) {
+        if (i18n_object_langcode($vocabulary)) {
+          $form[$vocabulary->vid]['name']['#markup'] .= ' (' . i18n_language_name($vocabulary->language) . ')';
+        }
+      }
+      break;
+
+    case 'taxonomy_overview_terms':
+      // We check for vocabulary object first, because when confirming alphabetical ordering it uses the same form
+      if (!empty($form['#vocabulary']) && i18n_taxonomy_vocabulary_mode($form['#vocabulary']->vid) & I18N_MODE_TRANSLATE) {
+        foreach (element_children($form) as $key) {
+          if (isset($form[$key]['#term']) && ($lang = i18n_object_langcode($form[$key]['#term']))) {
+            $form[$key]['view']['#suffix'] = ' (' . i18n_language_name($lang) . ')';
+          }
+        }
+      }
+      break;
+
+    case 'search_form':
+      // Localize category selector in advanced search form.
+      if (!empty($form['advanced']) && !empty($form['advanced']['category'])) {
+        i18n_taxonomy_form_all_localize($form['advanced']['category']);
+      }
+      break;
+  }
+}
+
+/**
+ * Validate multilingual options for vocabulary form
+ */
+function i18n_taxonomy_form_vocabulary_validate($form, &$form_state) {
+  if ($form_state['values']['i18n_mode'] & I18N_MODE_LANGUAGE) {
+    if ($form_state['values']['language'] == LANGUAGE_NONE) {
+      form_set_error('language', t('If selecting "Set language to vocabulary" you need to set a language to this vocabulary. Either change the translation mode or select a language.'));
+    }
+  }
+  else {
+    $form_state['values']['language'] = LANGUAGE_NONE;
+  }
+}
+
+/**
+ * Localize a taxonomy_form_all() kind of control
+ *
+ * The options array is indexed by vocabulary name and then by term id, with tree structure
+ * We just need to localize vocabulary name and localizable terms. Multilingual vocabularies
+ * should have been taken care of by query rewriting.
+ **/
+function i18n_taxonomy_form_all_localize(&$item) {
+  $options = &$item['#options'];
+  foreach (taxonomy_get_vocabularies() as $vid => $vocabulary) {
+    if (!empty($options[$vocabulary->name])) {
+      // Localize vocabulary name if translated
+      $vname = i18n_taxonomy_vocabulary_name($vocabulary);
+      if ($vname != $vocabulary->name) {
+        $options[$vname] = $options[$vocabulary->name];
+        unset($options[$vocabulary->name]);
+      }
+      if (i18n_taxonomy_vocabulary_mode($vid) & I18N_MODE_LOCALIZE) {
+        $tree = taxonomy_get_tree($vid);
+        if ($tree && (count($tree) > 0)) {
+          foreach ($tree as $term) {
+            if (isset($options[$vname][$term->tid])) {
+              $options[$vname][$term->tid] = str_repeat('-', $term->depth) . i18n_taxonomy_term_name($term);
+            }
+          }
+        }
+      }
+    }
+  }
+}
+
+/**
+ * Translate an array of taxonomy terms.
+ *
+ * Translates all terms with language, just passing over terms without it.
+ * Filter out terms with a different language
+ *
+ * @param $taxonomy
+ *   Array of term objects or tids or multiple arrays or terms indexed by vid
+ * @param $langcode
+ *   Language code of target language
+ * @param $fullterms
+ *   Whether to return full $term objects, returns tids otherwise
+ * @return
+ *   Array with translated terms: tid -> $term
+ *   Array with vid and term array
+ */
+function i18n_taxonomy_translate_terms($taxonomy, $langcode, $fullterms = TRUE) {
+  $translation = array();
+  if (is_array($taxonomy) && $taxonomy) {
+    foreach ($taxonomy as $index => $tdata) {
+      if (is_array($tdata)) {
+        // Case 1: Index is vid, $tdata is an array of terms
+        $mode = i18n_taxonomy_vocabulary_mode($index);
+        // We translate just some vocabularies: translatable, fixed language
+        // Fixed language ones may have terms translated, though the UI doesn't support it
+        if ($mode & I18N_MODE_LANGUAGE || $mode & I18N_MODE_TRANSLATE) {
+          $translation[$index] = i18n_taxonomy_translate_terms($tdata, $langcode, $fullterms);
+        }
+        elseif ($fullterms) {
+          $translation[$index] = array_map('_i18n_taxonomy_filter_terms', $tdata);
+        }
+        else {
+          $translation[$index] = array_map('_i18n_taxonomy_filter_tids', $tdata);
+        }
+        continue;
+      }
+      elseif (is_object($tdata)) {
+        // Case 2: This is a term object
+        $term = $tdata;
+      }
+      elseif (is_numeric($tdata) && ($tid = (int)$tdata)) {
+        // Case 3: This is a term tid, load the full term
+        $term = taxonomy_term_load($tid);
+      }
+      // Translate the term if we got it
+      if (empty($term)) {
+        // Couldn't identify term, pass through whatever it is
+        $translation[$index] = $tdata;
+      }
+      elseif ($term->language && $term->language != $langcode) {
+        $translation_set = i18n_translation_set_load($term->i18n_tsid);
+        $translations = $translation_set->get_translations();
+
+        if ($translations && !empty($translations[$langcode])) {
+          $newterm = $translations[$langcode];
+          $translation[$newterm->tid] = $fullterms ? $newterm : $newterm->tid;
+        }
+      }
+      else {
+        // Term has no language. Should be ok.
+        $translation[$index] = $fullterms ? $term : $term->tid;
+      }
+    }
+  }
+  return $translation;
+}
+
+/**
+ * Localize taxonomy terms for localizable vocabularies.
+ *
+ * @param $terms
+ *   Array of term objects or term object.
+ * @return
+ *   Array of terms with the right ones localized.
+ */
+function i18n_taxonomy_localize_terms($terms) {
+  // If not localizable language just return. Performance optimizations.
+  if (!i18n_string_translate_langcode()) {
+    return $terms;
+  }
+  $object_info = i18n_object_info('taxonomy_term');
+  $list = is_array($terms) ? $terms : array($terms);
+  foreach ($list as $index => $term) {
+    if (i18n_taxonomy_vocabulary_mode($term->vid, I18N_MODE_LOCALIZE)) {
+      $localize[$index] = $term->tid;
+    }
+  }
+  // If multiple terms, preload all translations, then run object translation.
+  if (!empty($localize)) {
+    i18n_string_translation_search(array('taxonomy', 'term', $localize, '*'));
+    foreach ($localize as $index => $tid) {
+      $list[$index] = i18n_string_object_translate('taxonomy_term', $list[$index]);
+    }
+  }
+  // Return array or simple object, depending on incoming format.
+  return is_array($terms) ? $list : reset($list);
+}
+
+/**
+ * Taxonomy vocabulary settings.
+ *
+ * @param $vid
+ *   Vocabulary object or vocabulary id.
+ * @param $mode
+ *   Vocabulary mode to compare with.
+ *
+ */
+function i18n_taxonomy_vocabulary_mode($vid, $mode = NULL) {
+  $modes = &drupal_static(__FUNCTION__);
+  if (is_object($vid)) {
+    $vid_mode = i18n_object_field($vid, 'i18n_mode', I18N_MODE_NONE);
+    return isset($mode) ? $mode & $vid_mode : $vid_mode;
+  }
+  else {
+    if (!isset($modes[$vid])) {
+      $modes[$vid] = i18n_object_field(taxonomy_vocabulary_load($vid), 'i18n_mode', I18N_MODE_NONE);
+    }
+    return isset($mode) ? $mode & $modes[$vid] : $modes[$vid];
+  }
+}
+
+/**
+ * Get taxonomy tree for a given language
+ *
+ * @param $vid
+ *   Vocabulary id
+ * @param $lang
+ *   Language code
+ * @param $parent
+ *   Parent term id for the tree
+ */
+function i18n_taxonomy_get_tree($vid, $langcode, $parent = 0, $max_depth = NULL, $load_entities = FALSE) {
+  $children = &drupal_static(__FUNCTION__, array());
+  $parents = &drupal_static(__FUNCTION__ . ':parents', array());
+  $terms = &drupal_static(__FUNCTION__ . ':terms', array());
+
+  // We cache trees, so it's not CPU-intensive to call get_tree() on a term
+  // and its children, too.
+  if (!isset($children[$vid][$langcode])) {
+    $children[$vid][$langcode] = array();
+    $parents[$vid][$langcode] = array();
+    $terms[$vid][$langcode] = array();
+
+    $query = db_select('taxonomy_term_data', 't');
+    $query->join('taxonomy_term_hierarchy', 'h', 'h.tid = t.tid');
+    $result = $query
+      ->addTag('translatable')
+      ->addTag('term_access')
+      ->fields('t')
+      ->fields('h', array('parent'))
+      ->condition('t.vid', $vid)
+      ->condition('t.language', $langcode)
+      ->orderBy('t.weight')
+      ->orderBy('t.name')
+      ->execute();
+
+    foreach ($result as $term) {
+      $children[$vid][$langcode][$term->parent][] = $term->tid;
+      $parents[$vid][$langcode][$term->tid][] = $term->parent;
+      $terms[$vid][$langcode][$term->tid] = $term;
+    }
+  }
+
+  // Load full entities, if necessary. The entity controller statically
+  // caches the results.
+  if ($load_entities) {
+    $term_entities = taxonomy_term_load_multiple(array_keys($terms[$vid][$langcode]));
+  }
+
+  $max_depth = (!isset($max_depth)) ? count($children[$vid][$langcode]) : $max_depth;
+  $tree = array();
+
+  // Keeps track of the parents we have to process, the last entry is used
+  // for the next processing step.
+  $process_parents = array();
+  $process_parents[] = $parent;
+
+  // Loops over the parent terms and adds its children to the tree array.
+  // Uses a loop instead of a recursion, because it's more efficient.
+  while (count($process_parents)) {
+    $parent = array_pop($process_parents);
+    // The number of parents determines the current depth.
+    $depth = count($process_parents);
+    if ($max_depth > $depth && !empty($children[$vid][$langcode][$parent])) {
+      $has_children = FALSE;
+      $child = current($children[$vid][$langcode][$parent]);
+      do {
+        if (empty($child)) {
+          break;
+        }
+        $term = $load_entities ? $term_entities[$child] : $terms[$vid][$langcode][$child];
+        if (count($parents[$vid][$langcode][$term->tid]) > 1) {
+          // We have a term with multi parents here. Clone the term,
+          // so that the depth attribute remains correct.
+          $term = clone $term;
+        }
+        $term->depth = $depth;
+        unset($term->parent);
+        $term->parents = $parents[$vid][$langcode][$term->tid];
+        $tree[] = $term;
+        if (!empty($children[$vid][$langcode][$term->tid])) {
+          $has_children = TRUE;
+
+          // We have to continue with this parent later.
+          $process_parents[] = $parent;
+          // Use the current term as parent for the next iteration.
+          $process_parents[] = $term->tid;
+
+          // Reset pointers for child lists because we step in there more often
+          // with multi parents.
+          reset($children[$vid][$langcode][$term->tid]);
+          // Move pointer so that we get the correct term the next time.
+          next($children[$vid][$langcode][$parent]);
+          break;
+        }
+      } while ($child = next($children[$vid][$langcode][$parent]));
+
+      if (!$has_children) {
+        // We processed all terms in this hierarchy-level, reset pointer
+        // so that this function works the next time it gets called.
+        reset($children[$vid][$langcode][$parent]);
+      }
+    }
+  }
+
+  return $tree;
+}
+
+/**
+ * Recursive array filtering, convert all terms to 'tid'.
+ */
+function _i18n_taxonomy_filter_tids($tid) {
+  if (is_array($tid)) {
+    return array_map('_i18n_taxonomy_filter_tids', $tid);
+  }
+  else {
+    return is_object($tid) ? $tid->tid : (int)$tid;
+  }
+}
+
+/**
+ * Recursive array filtering, convert all terms to 'term object'
+ */
+function _i18n_taxonomy_filter_terms($term) {
+  if (is_array($term)) {
+    return array_map('_i18n_taxonomy_filter_terms', $term);
+  }
+  else {
+    return is_object($term) ? $term : taxonomy_term_load($term);
+  }
+}
+
+/**
+ * Load translation set. Menu loading callback.
+ */
+function i18n_taxonomy_translation_set_load($tsid) {
+  return i18n_translation_set_load($tsid, 'taxonomy_term');
+}
+
+/**
+ * Implements hook_field_uuid_load().
+ */
+function i18n_taxonomy_field_uuid_load($entity_type, $entity, $field, $instance, $langcode, &$items) {
+  taxonomy_field_uuid_load($entity_type, $entity, $field, $instance, $langcode, $items);
+}
+
+/**
+ * Implements hook_field_uuid_presave().
+ */
+function i18n_taxonomy_field_uuid_presave($entity_type, $entity, $field, $instance, $langcode, &$items) {
+  taxonomy_field_uuid_presave($entity_type, $entity, $field, $instance, $langcode, $items);
+}
+/**
+ * Implements hook_entity_info_alter().
+ */
+function i18n_taxonomy_entity_info_alter(&$entity_info) {
+  if (isset($entity_info['taxonomy_term'])) {
+    // Core doesn't provide a label callback for taxonomy terms. By setting one
+    // we can use it to return the correct localized term name.
+    $entity_info['taxonomy_term']['label callback'] = 'i18n_taxonomy_taxonomy_term_label';
+  }
+}
+
+/**
+ * Providing a hook_entity_info() 'label callback' to ensure modules that use
+ * entity_label() get the correct localized taxonomy term.
+ */
+function i18n_taxonomy_taxonomy_term_label($term, $entity_type) {
+  return i18n_taxonomy_term_name($term, i18n_language_interface()->language);
+}
+
+/**
+ * Implements hook_views_api().
+ */
+function i18n_taxonomy_views_api() {
+  return array(
+    'api' => 3,
+  );
+}
+
+/**
+ * Implements hook_module_enabled().
+ *
+ * Updates options_list_callback for taxonomy term fields.
+ *
+ * @param $modules
+ */
+function i18n_taxonomy_modules_enabled($modules) {
+  $modules = drupal_map_assoc($modules);
+  if (isset($modules['i18n_taxonomy'])) {
+    foreach (field_info_fields() as $fieldname => $field) {
+        if ($field['type'] == 'taxonomy_term_reference') {
+          $field['settings']['options_list_callback'] = 'i18n_taxonomy_allowed_values';
+          field_update_field($field);
+        }
+      }
+  }
+}
diff --git a/kolab.org/www/drupal-7.18/sites/all/modules/i18n/i18n_taxonomy/i18n_taxonomy.pages.inc b/kolab.org/www/drupal-7.18/sites/all/modules/i18n/i18n_taxonomy/i18n_taxonomy.pages.inc
new file mode 100644
index 0000000..e05b5fd
--- /dev/null
+++ b/kolab.org/www/drupal-7.18/sites/all/modules/i18n/i18n_taxonomy/i18n_taxonomy.pages.inc
@@ -0,0 +1,169 @@
+<?php
+
+/**
+ * @file
+ * Page callbacks for the taxonomy module, i18n remake.
+ */
+
+/**
+ * Menu callback; displays all nodes associated with a term.
+ *
+ * @param $term
+ *   The taxonomy term.
+ * @return
+ *   The page content.
+ */
+function i18n_taxonomy_term_page($term) {
+  $term = i18n_taxonomy_localize_terms($term);
+
+  // Assign the term name as the page title.
+  drupal_set_title($term->name);
+
+  // Build breadcrumb based on the hierarchy of the term.
+  $current = (object) array(
+    'tid' => $term->tid,
+  );
+  // @todo This overrides any other possible breadcrumb and is a pure hard-coded
+  //   presumption. Make this behavior configurable per vocabulary or term.
+  $breadcrumb = array();
+  while ($parents = taxonomy_get_parents($current->tid)) {
+    $parents = i18n_taxonomy_localize_terms($parents);
+    $current = array_shift($parents);
+    $breadcrumb[] = l($current->name, 'taxonomy/term/' . $current->tid);
+  }
+  $breadcrumb[] = l(t('Home'), NULL);
+  $breadcrumb = array_reverse($breadcrumb);
+  drupal_set_breadcrumb($breadcrumb);
+  drupal_add_feed('taxonomy/term/' . $term->tid . '/feed', 'RSS - ' . $term->name);
+
+  $build = array();
+
+  $build['term_heading'] = array(
+    '#prefix' => '<div class="term-listing-heading">',
+    '#suffix' => '</div>',
+    'term' => taxonomy_term_view($term, 'full'),
+  );
+
+  if ($nids = taxonomy_select_nodes($term->tid, TRUE, variable_get('default_nodes_main', 10))) {
+    $nodes = node_load_multiple($nids);
+    $build += node_view_multiple($nodes);
+    $build['pager'] = array(
+      '#theme' => 'pager',
+      '#weight' => 5,
+    );
+  }
+  else {
+    $build['no_content'] = array(
+      '#prefix' => '<p>',
+      '#markup' => t('There is currently no content classified with this term.'),
+      '#suffix' => '</p>',
+    );
+  }
+  return $build;
+}
+
+/**
+ * Render a taxonomy term page HTML output.
+ *
+ * @param $tids
+ *   An array of term ids.
+ * @param $result
+ *   A pager_query() result, such as that performed by taxonomy_select_nodes().
+ *
+ * @ingroup themeable
+ */
+function theme_i18n_taxonomy_term_page($tids, $result) {
+  drupal_add_css(drupal_get_path('module', 'taxonomy') . '/taxonomy.css');
+
+  $output = '';
+
+  // Only display the description if we have a single term, to avoid clutter and confusion.
+  if (count($tids) == 1) {
+    $term = i18n_taxonomy_localize_terms(taxonomy_term_load($tids[0]));
+    // Check that a description is set.
+    if (!empty($term->description)) {
+      $output .= '<div class="taxonomy-term-description">';
+      $output .= filter_xss_admin($term->description);
+      $output .= '</div>';
+    }
+  }
+
+  $output .= taxonomy_render_nodes($result);
+
+  return $output;
+}
+
+/**
+ * Helper function for autocompletion. Replacement for taxonomy_autocomplete
+ */
+function i18n_taxonomy_autocomplete_field($field_name, $tags_typed = '') {
+  // Part of the criteria for the query come from the field's own settings.
+  $field = field_info_field($field_name);
+  $vids = array();
+  $vocabularies = taxonomy_vocabulary_get_names();
+  foreach ($field['settings']['allowed_values'] as $tree) {
+    $vids[] = $vocabularies[$tree['vocabulary']]->vid;
+  }
+  // This has been redirected from taxonomy module so we add current language and no language
+  // Because some of the vocabularies may not have language
+  $langcode = array(i18n_langcode(), LANGUAGE_NONE);
+  return _i18n_taxonomy_autocomplete($langcode, $vids, $tags_typed);
+}
+
+/**
+ * Helper function for autocompletion. Select by language
+ */
+function i18n_taxonomy_autocomplete_language($langcode, $vocabulary, $tags_typed = '') {
+  $vids = $vocabulary ? array($vocabulary->vid) : NULL;
+  return _i18n_taxonomy_autocomplete($langcode, $vids, $tags_typed);
+}
+
+/**
+ * Helper function for autocompletion
+ */
+function _i18n_taxonomy_autocomplete($langcode, $vids, $tags_typed = '') {
+  // The user enters a comma-separated list of tags. We only autocomplete the last tag.
+  $tags_typed = drupal_explode_tags($tags_typed);
+  $tag_last = drupal_strtolower(array_pop($tags_typed));
+
+  $matches = array();
+  if ($langcode && $tag_last != '') {
+    $query = db_select('taxonomy_term_data', 't')
+      ->fields('t', array('tid', 'name'));
+    $query->addTag('translatable');
+    $query->addTag('term_access');
+    // Disable i18n_select for this query
+    $query->addTag('i18n_select');
+    // Add language condition
+    $query->condition('t.language', $langcode);
+
+    // Do not select already entered terms.
+    if (!empty($tags_typed)) {
+      $query->condition('t.name', $tags_typed, 'NOT IN');
+    }
+    // There may be vocabulary restrictions
+    if ($vids) {
+      $query->condition('t.vid', $vids);
+    }
+    // Select rows that match by term name.
+    $tags_return = $query
+      ->condition('t.name', '%' . db_like($tag_last) . '%', 'LIKE')
+      ->range(0, 10)
+      ->execute()
+      ->fetchAllKeyed();
+
+    $prefix = count($tags_typed) ? drupal_implode_tags($tags_typed) . ', ' : '';
+
+    $term_matches = array();
+    foreach ($tags_return as $tid => $name) {
+      $n = $name;
+      // Term names containing commas or quotes must be wrapped in quotes.
+      if (strpos($name, ',') !== FALSE || strpos($name, '"') !== FALSE) {
+        $n = '"' . str_replace('"', '""', $name) . '"';
+      }
+      $term_matches[$prefix . $n] = check_plain($name);
+    }
+  }
+
+  drupal_json_output($term_matches);
+}
diff --git a/kolab.org/www/drupal-7.18/sites/all/modules/i18n/i18n_taxonomy/i18n_taxonomy.test b/kolab.org/www/drupal-7.18/sites/all/modules/i18n/i18n_taxonomy/i18n_taxonomy.test
new file mode 100644
index 0000000..efa8c53
--- /dev/null
+++ b/kolab.org/www/drupal-7.18/sites/all/modules/i18n/i18n_taxonomy/i18n_taxonomy.test
@@ -0,0 +1,251 @@
+<?php
+/**
+ * @file
+ * Test case for multilingual taxonomy
+ */
+
+
+class i18nTaxonomyTestCase extends Drupali18nTestCase {
+
+  public static function getInfo() {
+    return array(
+      'name' => 'Taxonomy translation',
+      'group' => 'Internationalization',
+      'description' => 'Taxonomy translation functions'
+    );
+  }
+
+  function setUp() {
+    parent::setUp(array('i18n_taxonomy', 'field_test'));
+    parent::setUpLanguages();
+
+    // Create users.
+    $filtered_html_format = filter_format_load('filtered_html');
+    $full_html_format = filter_format_load('full_html');
+    $this->admin_user = $this->drupalCreateUser(array(
+      'access field_test content',
+      'administer field_test content',
+      'administer taxonomy',
+      'administer languages',
+      'administer site configuration',
+      filter_permission_name($filtered_html_format),
+      filter_permission_name($full_html_format),
+    ));
+    $this->translator = $this->drupalCreateUser(array('translate interface', 'translate user-defined strings'));
+  }
+
+function testTaxonomyTermLocalize() {
+    $this->drupalLogin($this->admin_user);
+    // Make Input Format "Filter Text" translatable
+    $edit = array(
+      'i18n_string_allowed_formats[filtered_html]' => 'filtered_html',
+      'i18n_string_allowed_formats[plain_text]' => 'plain_text',
+    );
+    $this->drupalPost('admin/config/regional/i18n/strings', $edit, t('Save configuration'));
+
+    // Create a localizable vocabulary.
+    $vocab = $this->createVocabulary(array('i18n_mode' => I18N_MODE_LOCALIZE));
+    $this->assertEqual(i18n_taxonomy_vocabulary_mode($vocab->vid), I18N_MODE_LOCALIZE, 'A vocabulary has been created and it is localizable.');
+
+    $this->field_name = $this->createTermField($vocab->machine_name);
+
+    // Create a term to be localized. We use a common prefix to facilitate the testing of autocomplete suggestions.
+    $prefix = $this->randomName() . '_';
+    $term = $this->createTerm(array('vid' => $vocab->vid, 'name' => $prefix . $this->randomName()));
+
+    $this->drupalLogin($this->translator);
+
+    // Create and Save Spanish translation, again using the same prefix.
+    $term_translation = array(
+      'name' => $this->createStringTranslation('taxonomy', $term->name, array($this->secondary_language => $prefix . $this->randomName())),
+      'description' => $this->createStringTranslation('taxonomy', $term->description, array($this->secondary_language => $prefix . $this->randomName())),
+    );
+
+    $this->drupalLogin($this->admin_user);
+
+    $langcode = LANGUAGE_NONE;
+    $edit = array(
+      "{$this->field_name}[$langcode]" => array($term->tid),
+    );
+
+    // Test the widgets in the original language.
+    $this->drupalGet('test-entity/add/test-bundle');
+    $this->assertText($term->name, 'Widget values are displayed correctly in default language.');
+
+    $this->drupalPost(NULL, $edit, t('Save'));
+    $this->assertText($term->name, 'Field values are displayed correctly in default language.');
+
+    // Terms should be localized in the field widget.
+    $this->drupalGet($this->secondary_language . '/test-entity/add/test-bundle');
+    $this->assertText($term_translation['name'][$this->secondary_language], 'Widget values are displayed correctly in non-default languages.');
+
+    $this->drupalPost(NULL, $edit, t('Save'));
+    $this->assertText($term_translation['name'][$this->secondary_language], 'Field values are displayed correctly in non-default languages.');
+
+    // Term name and term description should be localized
+    $this->drupalGet('taxonomy/term/' . $term->tid, array('language' => i18n_language_object($this->default_language)));
+    $this->assertText($term->name, 'Term title is displayed correctly in default language.');
+    $this->assertText($term->description, 'Term description is displayed correctly in default language.');
+
+    // Term name and term description should be localized
+    $this->drupalGet('taxonomy/term/' . $term->tid, array('language' => i18n_language_object($this->secondary_language)));
+    $this->assertText($term_translation['name'][$this->secondary_language], 'Term title is displayed correctly in non-default language.');
+    $this->assertText($term_translation['description'][$this->secondary_language], 'Term description is displayed correctly in non-default language.');
+
+    // Autocomplete should respect localization.
+    $autocomplete_path = 'taxonomy/autocomplete/' . $this->field_name . '/' . $prefix;
+    $autocomplete_values = $this->drupalGetAJAX($autocomplete_path);
+    $this->assertTrue(isset($autocomplete_values[$term->name]), 'Correct autocomplete suggestions in default language.');
+    $this->assertFalse(isset($autocomplete_values[$term_translation['name'][$this->secondary_language]]), 'No incorrect autocomplete suggestions in non-default languages');
+
+ // Autocomplete should respect localization, but doesn't.
+ //   $autocomplete_path = $this->secondary_language . '/taxonomy/autocomplete/' . $this->field_name . '/' . $prefix;
+ //   $autocomplete_values = $this->drupalGetAJAX($autocomplete_path);
+ //   $this->assertFalse(isset($autocomplete_values[$term->name]), 'Correct autocomplete suggestions in non-default languages.');
+ //   $this->assertTrue(isset($autocomplete_values[$term_translation[$this->secondary_language]]), 'No incorrect autocomplete suggestions in non-default languages.');
+  }
+
+  function testTaxonomyTermTranslate() {
+    // Create a translateable vocabulary.
+    $vocab = $this->createVocabulary(array('i18n_mode' => I18N_MODE_TRANSLATE));
+    $this->assertEqual(i18n_taxonomy_vocabulary_mode($vocab->vid), I18N_MODE_TRANSLATE, 'A vocabulary has been created and it is translateable.');
+
+    $this->field_select = $this->createTermField($vocab->machine_name);
+    $this->field_autocomplete = $this->createTermField($vocab->machine_name, 'taxonomy_autocomplete');
+
+    // Create a term to be translated.
+    $en_term = $this->createTerm(array('vid' => $vocab->vid, 'language' => $this->default_language));
+    $es_term = $this->createTerm(array('vid' => $vocab->vid, 'language' => $this->secondary_language));
+
+    $this->drupalLogin($this->admin_user);
+
+    // Set terms as translations of each other.
+    $edit = array(
+      'translations[' . $this->default_language . ']' => $en_term->name,
+      'translations[' . $this->secondary_language . ']' => $es_term->name,
+    );
+    $this->drupalPost('admin/structure/taxonomy/' . $vocab->machine_name . '/list/sets/add', $edit, t('Save'));
+    $this->drupalGet('admin/structure/taxonomy/' . $vocab->machine_name . '/list/sets');
+
+    // Freetagging creates terms with the correct language.
+    $new_term_name = $this->randomName();
+    $langcode = LANGUAGE_NONE;
+    $edit = array(
+      "{$this->field_autocomplete}[$langcode]" => $new_term_name,
+    );
+    $this->drupalPost($this->secondary_language . '/test-entity/add/test-bundle', $edit, t('Save'));
+    $new_term = current(taxonomy_get_term_by_name($new_term_name));
+    $this->assertEqual($new_term->language,  $this->secondary_language, 'Freetagging creates terms with the correct language.');
+
+    // Term translations are used for language switching.
+    $language_switcher = language_negotiation_get_switch_links(LANGUAGE_TYPE_INTERFACE, 'taxonomy/term/' . $en_term->tid);
+    $this->assertEqual($language_switcher->links[$this->secondary_language]['href'], 'taxonomy/term/' . $es_term->tid, 'Term translations are used for language switching.');
+  }
+
+  /**
+   * Tests the implementation of 'options_list_callback' for term reference fields.
+   * Enable and disable the callback properly. Avoid WSOD!
+   */
+  function testTaxonomyFieldCallback() {
+    $field_name = 'taxonomy_term_test_field';
+    $field = field_create_field(array(
+      'field_name' => $field_name,
+      'type' => 'taxonomy_term_reference',
+    ));
+    $field = field_info_field($field_name);
+    $callback = 'i18n_taxonomy_allowed_values';
+    $this->assertTrue(function_exists($callback), "Function $callback exists.");
+    $this->assertEqual($field['settings']['options_list_callback'], $callback, "$callback ist option list callback.");
+    module_disable(array('i18n_taxonomy'));
+    $field = field_info_field($field_name);
+    $this->assertNotEqual($field['settings']['options_list_callback'], $callback, "$callback ist option list callback.");
+  }
+
+  // Create vocabulary with given fields
+  function drupalCreateVocabulary($vocab = array()) {
+    $vocab += array('name' => $this->randomName(10), 'description' => $this->randomName(20));
+    taxonomy_vocabulary_save($vocab);
+    return (object)$vocab;
+  }
+  // Create term with given fields
+  function drupalCreateTerms($number = 1, $data = array()) {
+    $list = array();
+    for ($i = 1; $i <= $number ; $i++ ) {
+      $term = $this->createTerm($data);
+      $list[$term->tid] = $term;
+    }
+    return $list;
+  }
+
+  /**
+   * Returns a new vocabulary with random properties.
+   */
+  function createVocabulary($data = array()) {
+    // Create a vocabulary.
+    $data += array(
+      'i18n_mode' => I18N_MODE_LOCALIZE,
+      'name' => $this->randomName(),
+      'description' => $this->randomName(),
+      'machine_name' => drupal_strtolower($this->randomName()),
+      'help' => '',
+      'nodes' => array('article' => 'article'),
+      'weight' => mt_rand(0, 10),
+    );
+    $vocabulary = (object)$data;
+    taxonomy_vocabulary_save($vocabulary);
+    return $vocabulary;
+  }
+
+  /**
+   * Returns a new term with random properties in vocabulary $vid.
+   */
+  function createTerm($data = array()) {
+    $data += array(
+      'name' => $this->randomName(),
+      'description' => $this->randomName(),
+      // Use the first available text format and vocabulary.
+      'format' => filter_default_format(),
+      'vid' => 1,
+    );
+    $term = (object)$data;
+    taxonomy_term_save($term);
+    return $term;
+  }
+
+  /**
+   * Setup a field and instance.
+   */
+  function createTermField($machine_name, $widget = 'options_select') {
+    $field_name = drupal_strtolower($this->randomName());
+
+    $this->field = array(
+      'field_name' => $field_name,
+      'type' => 'taxonomy_term_reference',
+      'settings' => array(
+        'allowed_values' => array(
+          array(
+            'vocabulary' => $machine_name,
+            'parent' => '0',
+          ),
+        ),
+      )
+    );
+    field_create_field($this->field);
+    $this->instance = array(
+      'field_name' => $field_name,
+      'entity_type' => 'test_entity',
+      'bundle' => 'test_bundle',
+      'widget' => array(
+        'type' => $widget,
+      ),
+      'display' => array(
+        'full' => array(
+          'type' => 'taxonomy_term_reference_link',
+        ),
+      ),
+    );
+    field_create_instance($this->instance);
+
+    return $field_name;
+  }
+}
diff --git a/kolab.org/www/drupal-7.18/sites/all/modules/i18n/i18n_taxonomy/i18n_taxonomy.tokens.inc b/kolab.org/www/drupal-7.18/sites/all/modules/i18n/i18n_taxonomy/i18n_taxonomy.tokens.inc
new file mode 100644
index 0000000..a9a753a
--- /dev/null
+++ b/kolab.org/www/drupal-7.18/sites/all/modules/i18n/i18n_taxonomy/i18n_taxonomy.tokens.inc
@@ -0,0 +1,111 @@
+<?php
+
+/**
+ * @file
+ * Builds placeholder replacement tokens for taxonomy terms and vocabularies.
+ */
+
+/**
+ * Implements hook_token_info().
+ */
+function i18n_taxonomy_token_info() {
+
+  // Taxonomy term related variables.
+  $term['i18n-name'] = array(
+    'name' => t("Name (localized)"),
+    'description' => t("The name of the taxonomy term."),
+  );
+  $term['i18n-description'] = array(
+    'name' => t("Description (localized)"),
+    'description' => t("The optional description of the taxonomy term."),
+  );
+
+  // Taxonomy vocabulary related variables.
+  $vocabulary['i18n-name'] = array(
+    'name' => t("Name (localized)"),
+    'description' => t("The name of the taxonomy vocabulary."),
+  );
+  $vocabulary['i18n-description'] = array(
+    'name' => t("Description (localized)"),
+    'description' => t("The optional description of the taxonomy vocabulary."),
+  );
+
+  // Chained tokens for taxonomies
+  $term['i18n-vocabulary'] = array(
+    'name' => t("Vocabulary (localized)"),
+    'description' => t("The vocabulary the taxonomy term belongs to."),
+    'type' => 'vocabulary',
+  );
+  $term['i18n-parent'] = array(
+    'name' => t("Parent term (localized)"),
+    'description' => t("The parent term of the taxonomy term, if one exists."),
+    'type' => 'term',
+  );
+
+  return array(
+    'tokens' => array(
+      'term' => $term,
+      'vocabulary' => $vocabulary,
+    ),
+  );
+}
+
+/**
+ * Implements hook_tokens().
+ */
+function i18n_taxonomy_tokens($type, $tokens, array $data = array(), array $options = array()) {
+  $replacements = array();
+  $sanitize = !empty($options['sanitize']);
+  $langcode = isset($options['language']) ? $options['language']->language : i18n_langcode();
+
+  if ($type == 'term' && !empty($data['term'])) {
+    $term = $data['term'];
+
+    foreach ($tokens as $name => $original) {
+      switch ($name) {
+
+        case 'i18n-name':
+          $name = i18n_taxonomy_term_name($term, $langcode);
+          $replacements[$original] = $sanitize ? check_plain($name) : $name;
+          break;
+
+        case 'i18n-description':
+          $replacements[$original] = i18n_string_text(array('taxonomy', 'term', $term->tid, 'description'), $term->description, array('langcode' => $langcode, 'format' => $term->format, 'sanitize' => $sanitize, 'cache' => TRUE));
+          break;
+
+        case 'i18n-vocabulary':
+          $vocabulary = taxonomy_vocabulary_load($term->vid);
+          $replacements[$original] = check_plain(i18n_taxonomy_vocabulary_name($vocabulary, $langcode));
+          break;
+
+        case 'i18n-parent':
+          if ($parents = taxonomy_get_parents($term->tid)) {
+            $parent = array_pop($parents);
+            $replacements[$original] = check_plain(i18n_taxonomy_term_name($parent, $langcode));
+          }
+          break;
+      }
+    }
+  }
+
+  elseif ($type == 'vocabulary' && !empty($data['vocabulary'])) {
+    $vocabulary = $data['vocabulary'];
+
+    foreach ($tokens as $name => $original) {
+      switch ($name) {
+
+        case 'i18n-name':
+          $name = i18n_taxonomy_vocabulary_name($vocabulary, $langcode);
+          $replacements[$original] = $sanitize ? check_plain($name) : $name;
+          break;
+
+        case 'i18n-description':
+          $description = i18n_object_langcode($vocabulary) ? $vocabulary->description : i18n_string(array('taxonomy', 'vocabulary', $vocabulary->vid, 'description'), $vocabulary->description, array('langcode' => $langcode));
+          $replacements[$original] = $sanitize ? filter_xss($description) : $description;
+          break;
+      }
+    }
+  }
+
+  return $replacements;
+}
diff --git a/kolab.org/www/drupal-7.18/sites/all/modules/i18n/i18n_taxonomy/i18n_taxonomy.views.inc b/kolab.org/www/drupal-7.18/sites/all/modules/i18n/i18n_taxonomy/i18n_taxonomy.views.inc
new file mode 100644
index 0000000..7727f98
--- /dev/null
+++ b/kolab.org/www/drupal-7.18/sites/all/modules/i18n/i18n_taxonomy/i18n_taxonomy.views.inc
@@ -0,0 +1,19 @@
+<?php
+/**
+ * @file
+ * Views integration for i18n_taxonomy fields.
+ */
+
+/**
+ * Implements hook_field_views_data().
+ */
+function i18n_taxonomy_field_views_data($field) {
+  return taxonomy_field_views_data($field);
+}
+
+/**
+ * Implements hook_field_views_data_views_data_alter().
+ */
+function i18n_taxonomy_field_views_data_views_data_alter(&$data, $field) {
+  return taxonomy_field_views_data_views_data_alter($data, $field);
+}
\ No newline at end of file
diff --git a/kolab.org/www/drupal-7.18/sites/all/modules/i18n/i18n_translation/README.txt b/kolab.org/www/drupal-7.18/sites/all/modules/i18n/i18n_translation/README.txt
new file mode 100644
index 0000000..7bafbdb
--- /dev/null
+++ b/kolab.org/www/drupal-7.18/sites/all/modules/i18n/i18n_translation/README.txt
@@ -0,0 +1,47 @@
+
+README.txt
+==========
+Drupal module: Translation set API
+==================================
+
+This is a generic API to handle translation sets. It is being used for now
+for path translation and taxonomy term translation inside i18n package.
+
+Translation sets can hold a collection of entities or other objects. A translation set is itself
+an Entity thus leveraging all the power of the Entity API.
+
+It also provides some basic storage for translation sets and a generator of new translation set id.
+However, each module is responsible for storing which objects belong to which translation set for which
+it needs to verride some methods of the base i18n_translation_set class.
+
+- load_translations()
+- save_translations()
+- clean_translations()
+- delete_translations()
+
+Once these are implemented, to get the objects belonging to a translation set, indexed by language code,
+you can invoke this method on a translation set object:
+
+- get_translations()
+
+To define a new type of translation set, a module must implement hook_i18n_translation_set_info() 
+as in this example:
+
+/**
+ * Implements hook_i18n_translation_set_info().
+ */
+function i18n_path_i18n_translation_set_info() {
+  return array(
+    'path' => array(
+      'title' => t('Paths'),
+      'class' => 'i18n_path_translation_set',
+    )
+  );
+}
+
+See examples of overriding and extending this API:
+- i18n_path/i18n_path.inc
+- i18n_taxonomy/i18n_taxonomy.inc
+
+====================================================================
+Jose A. Reyero, http://reyero.net
\ No newline at end of file
diff --git a/kolab.org/www/drupal-7.18/sites/all/modules/i18n/i18n_translation/i18n_translation.admin.inc b/kolab.org/www/drupal-7.18/sites/all/modules/i18n/i18n_translation/i18n_translation.admin.inc
new file mode 100644
index 0000000..805f282
--- /dev/null
+++ b/kolab.org/www/drupal-7.18/sites/all/modules/i18n/i18n_translation/i18n_translation.admin.inc
@@ -0,0 +1,98 @@
+<?php
+
+/**
+ * @file
+ * Internationalization (i18n) module. Translation sets admin
+ */
+
+/**
+ * Overview page for translation sets
+ * 
+ * @param $type
+ *   Translation set type to get a listing for this type only
+ * @param $query
+ *   Base query to build upon
+ */
+function i18n_translation_admin_overview($type = NULL, $query = NULL) {
+  // Build the sortable table header.
+  $header['title'] = array('data' => t('Title'), 'field' => 't.title');
+  if (!$type) {
+    $header['type'] = array('data' => t('Type'), 'field' => 't.type');
+  }
+  $header['items'] = t('Items');
+  $header['created'] = array('data' => t('Created'), 'field' => 't.created');
+  $header['changed'] = array('data' => t('Updated'), 'field' => 't.changed', 'sort' => 'desc');
+  $header['operations'] = array('data' => t('Operations'));
+
+  // Get the translation sets for this form
+  $query = $query ? $query : db_select('i18n_translation_set', 't');
+  $query = $query->extend('PagerDefault')->extend('TableSort');
+  if ($type) {
+    $query->condition('t.type', $type);
+  }
+  $tsids = $query
+    ->fields('t', array('tsid'))
+    ->limit(20)
+    ->orderByHeader($header)
+    ->execute()
+    ->fetchCol();
+  $translations = $tsids ? entity_load('i18n_translation', $tsids) : array();
+  
+  $form = drupal_get_form('i18n_translation_admin_form', $translations, $header);
+
+  $form['pager'] = array('#markup' => theme('pager'));  
+  return $form;  
+}
+
+/**
+ * Admin form
+ */
+function i18n_translation_admin_form($form, &$form_state, $translations, $header) {
+  $destination = drupal_get_destination();
+  $rows = array();
+  foreach ($translations as $set) {
+    $info = i18n_object_info($set->type);
+    $rows[$set->tsid]['title'] = check_plain($set->get_title());
+    if (isset($header['type'])) {
+      $rows[$set->tsid]['type'] = isset($info['title']) ? $info['title'] : t('Unknown');
+    }
+    $rows[$set->tsid]['items'] = ($items = $set->get_links()) ? theme('links', array('links' => $items)) : '';
+    $rows[$set->tsid] += array(
+      'created' => format_date($set->created, 'short'),
+      'changed' => format_date($set->changed, 'short'),
+      'operations' => '',
+    );
+
+    // Build a list of all the accessible operations for the current set.
+    $operations = $set->get_operations();
+    if (count($operations) > 1) {
+      // Render an unordered list of operations links.
+      $rows[$set->tsid]['operations'] = array(
+        'data' => array(
+          '#theme' => 'links__node_operations',
+          '#links' => $operations,
+          '#attributes' => array('class' => array('links', 'inline')),
+        ),
+      );
+    }
+    elseif (!empty($operations)) {
+      // Render the first and only operation as a link.
+      $link = reset($operations);
+      $rows[$set->tsid]['operations'] = array(
+        'data' => array(
+          '#type' => 'link',
+          '#title' => $link['title'],
+          '#href' => $link['href'],
+          '#options' => array('query' => $link['query']),
+        ),
+      );
+    }
+  }
+  $form['translations'] = array(
+    '#theme' => 'table',
+    '#header' => $header,
+    '#rows' => $rows,
+    '#empty' => t('No translation sets available.'),
+  );
+  return $form;
+}
\ No newline at end of file
diff --git a/kolab.org/www/drupal-7.18/sites/all/modules/i18n/i18n_translation/i18n_translation.api.php b/kolab.org/www/drupal-7.18/sites/all/modules/i18n/i18n_translation/i18n_translation.api.php
new file mode 100644
index 0000000..c601380
--- /dev/null
+++ b/kolab.org/www/drupal-7.18/sites/all/modules/i18n/i18n_translation/i18n_translation.api.php
@@ -0,0 +1,45 @@
+<?php
+/**
+ * @file
+ * API documentation for Internationalization module
+ *
+ * Most i18n hooks can be placed on each module.i18n.inc file but in this case
+ * such file must be listed in the module.info file.
+ */
+
+/**
+ * Provide information about translation sets and involved objects.
+ *
+ * @see i18n_translation_set_info()
+ *
+ * @see hook_i18n_object_info()
+ *
+ * This feature relies on object information provided by i18n_object_info().
+ */
+function hook_i18n_translation_set_info() {
+  $info['taxonomy_term'] = array(
+    'title' => t('Taxonomy term'),
+    // The class that handles this translation sets
+    'class' => 'i18n_taxonomy_translation_set',
+    // Table and field into that table that keeps the translation set id for each item.
+    'table' => 'taxonomy_term_data',
+    'field' => 'i18n_tsid',
+    // This is the parent object (i18n object type).
+    'parent' => 'taxonomy_vocabulary',
+    // Placeholders and path information for generating translation set pages for administration.
+    'placeholder' => '%i18n_taxonomy_translation_set',
+    'list path' => 'admin/structure/taxonomy/%taxonomy_vocabulary_machine_name/list/sets',
+    'edit path' => 'admin/structure/taxonomy/%taxonomy_vocabulary_machine_name/list/sets/edit/%i18n_taxonomy_translation_set',
+    'delete path' => 'admin/structure/taxonomy/%taxonomy_vocabulary_machine_name/list/sets/delete/%i18n_taxonomy_translation_set',
+    'page callback' => 'i18n_taxonomy_term_translation_page',
+  );
+  return $info;
+}
+
+/**
+ * Alter i18n object information provided by modules with the previous hook
+ *
+ * @see i18n_translation_set_info()
+ */
+function hook_i18n_translation_set_info_alter(&$info) {
+}
\ No newline at end of file
diff --git a/kolab.org/www/drupal-7.18/sites/all/modules/i18n/i18n_translation/i18n_translation.inc b/kolab.org/www/drupal-7.18/sites/all/modules/i18n/i18n_translation/i18n_translation.inc
new file mode 100644
index 0000000..de8c724
--- /dev/null
+++ b/kolab.org/www/drupal-7.18/sites/all/modules/i18n/i18n_translation/i18n_translation.inc
@@ -0,0 +1,474 @@
+<?php
+/**
+ * @file
+ * Internationalization (i18n) module - Translation set
+ */
+class i18n_translation_set {
+  public $tsid = NULL;
+  public $type;
+  public $bundle = '';
+  public $status = 0;
+  public $master_id = 0;
+  // It may optionally have a title
+  public $title = '';
+  // Translations indexed by language
+  protected $translations = NULL;
+  // Translation languages indexed by oid
+  protected $object_languages = array();
+  // Related translation sets indexed by tsid
+  // Keep track of old translation sets objects belong to.
+  protected $related_translations = array();
+  /**
+   * Constructor from object/array
+   */
+  public function __construct($translation = NULL) {
+    if ($translation) {
+      foreach ((array)$translation as $key => $value) {
+        $this->$key = $value;
+      }
+    }
+  }
+
+  /**
+   * Delete a translation set
+   * 
+   * @param $delete_translations
+   *   Whether to unlink translations from the set. Detaults to TRUE.
+   */
+  public function delete($delete_translations = TRUE) {
+    db_delete('i18n_translation_set')
+      ->condition('tsid', $this->tsid)
+      ->execute();
+    if ($delete_translations) {
+      $this->delete_translations();
+    }
+    $this->invoke_all('delete');
+    $this->tsid = NULL;
+  }
+
+  /**
+   * Invoke all modules
+   */
+  public function invoke_all($op) {
+    module_invoke_all('i18n_translation_set_' . $op, $this);
+    module_invoke_all('entity_' . $op, $this, 'i18n_translation');
+  }
+
+  /**
+   * Create a new translation set
+   * 
+   * @param $save_translations
+   *   Whether to update linked objects so they belong to this set.
+   */
+  public function insert($save_translations = TRUE) {
+    $this->created = $this->changed = REQUEST_TIME;
+    $status = drupal_write_record('i18n_translation_set', $this);
+    if ($save_translations) {
+      $this->save_translations();
+      $this->update_related();
+    }
+    $this->invoke_all('insert');
+    return $status;
+  }
+
+  /**
+   * Save translation set
+   * 
+   * @param $save_translations
+   *   Whether to update linked objects so they belong to this set.
+   */
+  public function save($save_translations = TRUE) {
+    $this->invoke_all('presave');
+    return empty($this->tsid) ? $this->insert($save_translations) : $this->update($save_translations);
+  }
+
+  /**
+   * Update a translation set
+   * 
+   * @param $update_translations
+   *   Whether to update objects linked to this set.
+   */
+  public function update($update_translations = TRUE) {
+    $this->changed = REQUEST_TIME;
+    $status = drupal_write_record('i18n_translation_set', $this, 'tsid');
+    if ($update_translations) {
+      $this->clean_translations();
+      $this->save_translations();
+      $this->update_related();
+    }
+    $this->invoke_all('update');
+    return $status;
+  }
+
+  /**
+   * Update a translation set or delete if empty.
+   */
+  public function update_delete() {
+    if ($this->get_translations()) {
+      $result = $this->save(TRUE);
+      // Update related translation sets too.
+      $this->update_related();
+      return $result;
+    }
+    else {
+      return $this->delete(TRUE);
+    }
+  }
+
+  /**
+   * Update related translation sets
+   */
+  protected function update_related($op = 'update_delete') {
+    foreach ($this->related_translations as $translation_set) {
+      $translation_set->$op();
+    }    
+  }
+  
+  /**
+   * Clean all items in this translation set
+   *
+   * Unlink other items (not current translations from this translation set)
+   */
+  public function clean_translations() {
+    if (($table = $this->get_table()) && ($field = $this->get_field())) {
+      $query = db_update($table)
+        ->fields(array($field => 0))
+        ->condition($field, $this->tsid);
+      if ($translations = $this->get_translations()) {
+        $query->condition('language', array_keys($translations), 'NOT IN');
+      }
+      return $query->execute();
+    }
+  }
+
+  /**
+   * Save translations in this translation set
+   */
+  public function save_translations() {
+    if (($table = $this->get_table()) && ($field = $this->get_field())) {
+      if ($keys = $this->get_translations_keys()) {
+        return db_update($table)
+          ->fields(array($field => $this->tsid))
+          ->condition($this->key_field(), $keys)
+          ->execute();
+      }
+      else {
+        return $this->delete_translations();
+      }
+    }
+  }
+
+  /**
+   * Delete translations in this translation set
+   *
+   * It won't delete objects, just unlink them from translation set
+   */
+  public function delete_translations() {
+    if (($table = $this->get_table()) && ($field = $this->get_field())) {
+      return db_update($table)
+        ->fields(array($field => 0))
+        ->condition($field, $this->tsid)
+        ->execute();
+    }
+  }
+
+  /**
+   * Get translations, indexed by language
+   */
+  public function get_translations() {
+    $translations = array();
+    foreach ($this->get_objects() as $lang => $object) {
+      $translations[$lang] = $object->get_object();
+    }
+    return $translations;
+  }
+  /**
+   * Reset translations, set empty array or new array of translations.
+   * 
+   * @param $translations array
+   *   Array of langcode => item
+   */
+  public function reset_translations($translations = array()) {
+    $this->translations = array();
+    $this->add_translations($translations);
+    return $this;
+  }
+  /**
+   * Get translations as i18n objects, indexed by language
+   */
+  public function get_objects() {
+    if (!isset($this->translations)) {
+      $this->translations = array();
+      // Disable selection query altering, just in case
+      $previous = i18n_select(FALSE);
+      $this->add_translations($this->load_translations());
+      i18n_select($previous);
+    }
+    return $this->translations;
+  }
+
+  /**
+   * Get item for language
+   */
+  public function get_item($langcode) {
+    if (($translations = $this->get_translations()) && isset($translations[$langcode])) {
+      return $translations[$langcode];
+    }
+    else {
+      return NULL;
+    }
+  }
+  /**
+   * Get translations keys, indexed by language
+   */
+  public function get_translations_keys() {
+    $keys = array();
+    foreach ($this->get_objects() as $lang => $object) {
+      if ($id = $object->get_key()) {
+        $keys[$lang] = $id;
+      }
+    }
+    return array_filter($keys);
+  }
+  /**
+   * Get edit path for this translation set
+   */
+  public function get_edit_path() {
+    if ($path = $this->get_info('edit path')) {
+      return strtr($path, $this->get_path_placeholders('delete'));
+    }
+    else {
+      return '';
+    }
+  }
+  /**
+   * Get operations as renderable links
+   */
+  public function get_operations() {
+    $destination = drupal_get_destination();
+    $operations = array();
+    if ($path = $this->get_edit_path()) {
+      $operations['edit'] = array(
+        'title' => t('edit'),
+        'href' => $path,
+        'query' => $destination,
+      );
+    }
+    if ($path = $this->get_delete_path()) {
+      $operations['delete'] = array(
+        'title' => t('delete'),
+        'href' => $path,
+        'query' => $destination,
+      );
+    }
+    return $operations;  
+  }
+  /**
+   * Get items as renderable links
+   */
+  public function get_links() {
+    $language_list = language_list();
+    $items = array();
+    foreach ($this->get_objects() as $langcode => $object) {
+      $title = $object->get_title();
+      $path = $object->get_path();
+      $language = isset($language_list[$langcode]) ? $language_list[$langcode] : NULL;
+      $items[$langcode] = array(
+        'title' => $title,
+        'href' => $path ? $path : NULL,
+        'language' => $language,
+      );
+      if ($language && function_exists('languageicons_link_add')) {
+        languageicons_link_add($items[$langcode]);
+      }
+    }
+    return $items;    
+  }
+  /**
+   * Get overview (list) path for this translation set
+   */
+  public function get_list_path() {
+    return $this->get_info('list path');
+  }
+  /**
+   * Get delete path for this translation set
+   */
+  public function get_delete_path() {
+    if ($path = $this->get_info('delete path')) {
+      return strtr($path, $this->get_path_placeholders('delete'));
+    }
+    else {
+      return '';
+    }
+  }
+  /**
+   * Get placeholder values for path replacement
+   */
+  function get_path_placeholders($op = 'list') {
+    $values['%operation'] = $op;
+    $values['%type'] = $this->type;
+    $values['%i18n_translation_set'] = $this->tsid;
+    if ($placeholder = $this->get_info('placeholder')) {
+      $values[$placeholder] = $this->tsid;
+    }
+    return $values;
+  }
+  
+  /**
+   * Get field on translations table that stores the translation set id (tsid)
+   */
+  protected function get_field() {
+    return $this->get_info('field');
+  }
+
+  /**
+   * Get property from translation set info
+   */
+  public function get_info($property, $default = NULL) {
+    $info = i18n_translation_set_info($this->type);
+    return $info && isset($info[$property]) ? $info[$property] : $default;
+  }
+
+  /**
+   * Get table name, where translation items are stored.
+   */
+  protected function get_table() {
+    return $this->get_info('table');
+  }
+
+  /**
+   * Get title for this set
+   */
+  public function get_title() {
+    if (!empty($this->title)) {
+      return $this->title;
+    }
+    elseif ($translations = $this->get_objects()) {
+      foreach ($translations as $object) {
+        $names[] = $object->get_title();
+      }
+      return implode(' / ', $names);
+    }
+    else {
+      return t('Undefined');
+    }
+  }
+
+  /**
+   * Get item list
+   */
+  public function item_list() {
+    $language_list = language_list();
+    $items = array();
+    foreach ($this->get_objects() as $langcode => $object) {
+      $title = $object->get_title();
+      $path = $object->get_path();
+      if ($title && $path) {
+        $options = isset($language_list[$langcode]) ? array('language' => $language_list[$langcode]) : array();
+        $items[$langcode] = l($title, $path, $options);
+      }
+      elseif ($title) {
+        $items[$langcode] = check_plain($title);
+      }
+    }
+    return $items;
+  }
+
+  /**
+   * Add array of translation items
+   * 
+   * @param $translations array
+   *   Translation items indexed by language code
+   */
+  public function add_translations($translations) {
+    foreach ($translations as $langcode => $item) {
+      $this->add_item($item, $langcode);
+    }
+    return $this;
+  }
+
+  /**
+   * Add translation item
+   */
+  public function add_item($item, $langcode = NULL) {
+    $object = i18n_object($this->type, $item);
+    $langcode = $langcode ? $langcode : $object->get_langcode();
+    // Check whether this item belongs to another translation set
+    $old_tsid = $object->get_tsid();
+    if ($old_tsid && $old_tsid != $this->tsid) {
+      $this->related_translations[$old_tsid] = i18n_translation_set_load($old_tsid);
+      $this->related_translations[$old_tsid]->remove_object($object);
+    }    
+    if ($langcode) {
+      $this->get_translations();
+      $object->set_tsid($this->tsid);
+      $this->translations[$langcode] = $object;
+      $this->object_languages[$object->get_index()] = $langcode;
+    }
+    return $this;
+  }
+
+  /**
+   * Remove item / language from translation set
+   * 
+   * @param $item
+   *   Item to remove from this translation set, it must have a language property.
+   */
+  public function remove_item($item) {
+    $this->remove_object(i18n_object($this->type, $item));
+    return $this;
+  }
+  
+  /**
+   * Remove i18n object from translation set.
+   */
+  public function remove_object($object) {
+    // As object's language may have changed, we use our object_languages index better.
+    $index = $object->get_index();
+    $this->get_translations();
+    if (isset($this->object_languages[$index])) {
+      $langcode = $this->object_languages[$index];
+      unset($this->translations[$langcode]);
+      unset($this->object_languages[$index]);
+    }
+    return $this;
+  }
+
+  /**
+   * Remove language from translation set.
+   * 
+   * @param $langcode
+   *   Language to remove from this translation set.
+   */
+  public function remove_language($langcode) {
+    $this->get_translations();
+    if (isset($this->translations[$langcode])) {
+      $this->remove_object($this->translations[$langcode]);
+    }
+    return $this;   
+  }
+
+  /**
+   * Load all translations as objects indexed by language
+   */
+  public function load_translations() {
+    if (($table = $this->get_table()) && ($field = $this->get_field())) {
+      return db_select($table, 't')
+        ->fields('t')
+        ->condition('t.' . $field, $this->tsid)
+        ->execute()
+        ->fetchAllAssoc('language');
+    }
+    else {
+      return array();
+    }
+  }
+
+  /**
+   * Get key field for this translation items
+   */
+  protected function key_field() {
+    $info = i18n_object_info($this->type);
+    return $info['key'];
+  }
+}
\ No newline at end of file
diff --git a/kolab.org/www/drupal-7.18/sites/all/modules/i18n/i18n_translation/i18n_translation.info b/kolab.org/www/drupal-7.18/sites/all/modules/i18n/i18n_translation/i18n_translation.info
new file mode 100644
index 0000000..b9b54e7
--- /dev/null
+++ b/kolab.org/www/drupal-7.18/sites/all/modules/i18n/i18n_translation/i18n_translation.info
@@ -0,0 +1,14 @@
+name = Translation sets
+description = Simple translation sets API for generic objects
+dependencies[] = i18n
+package = Multilingual - Internationalization
+core = 7.x
+
+files[] = i18n_translation.inc
+
+; Information added by drupal.org packaging script on 2013-01-13
+version = "7.x-1.8"
+core = "7.x"
+project = "i18n"
+datestamp = "1358075001"
+
diff --git a/kolab.org/www/drupal-7.18/sites/all/modules/i18n/i18n_translation/i18n_translation.install b/kolab.org/www/drupal-7.18/sites/all/modules/i18n/i18n_translation/i18n_translation.install
new file mode 100644
index 0000000..d809fce
--- /dev/null
+++ b/kolab.org/www/drupal-7.18/sites/all/modules/i18n/i18n_translation/i18n_translation.install
@@ -0,0 +1,83 @@
+<?php
+
+/**
+ * @file
+ * Install, update and uninstall functions for the text module.
+ */
+
+/**
+ * Implements hook_install().
+ */
+function i18n_translation_install() {
+
+}
+
+/**
+ * Implements hook_schema().
+ */
+function i18n_translation_schema() {
+  $schema['i18n_translation_set'] = array(
+    'description' => 'Translation set.',
+    'fields' => array(
+      'tsid' => array(
+        'description' => 'The primary identifier for a translation set.',
+        'type' => 'serial',
+        'unsigned' => TRUE,
+        'not null' => TRUE,
+      ),
+      'title' => array(
+        'description' => 'The title of this translation set, always treated as non-markup plain text.',
+        'type' => 'varchar',
+        'length' => 255,
+        'not null' => TRUE,
+        'default' => '',
+      ),
+      'type' => array(
+        'description' => 'Object type or entity type.',
+        'type' => 'varchar',
+        'length' => 32,
+        'not null' => TRUE,
+        'default' => ''
+      ),
+      'bundle' => array(
+        'description' => 'Optional bundle for entity translation sets.',
+        'type' => 'varchar',
+        'length' => 128,
+        'not null' => TRUE,
+        'default' => ''
+      ),
+      'master_id' => array(
+        'description' => 'The master object/entity id (the others will be synchronized with this one).',
+        'type' => 'int',
+        'unsigned' => TRUE,
+        'not null' => TRUE,
+        'default' => 0,
+      ),
+      'status' => array(
+        'description' => 'Status of this translation set. TBD.',
+        'type' => 'int',
+        'not null' => TRUE,
+        'default' => 1,
+      ),
+      'created' => array(
+        'description' => 'The Unix timestamp when the set was created.',
+        'type' => 'int',
+        'not null' => TRUE,
+        'default' => 0,
+      ),
+      'changed' => array(
+        'description' => 'The Unix timestamp when the set was most recently saved.',
+        'type' => 'int',
+        'not null' => TRUE,
+        'default' => 0,
+      ),
+    ),
+    'indexes' => array(
+      'entity_bundle' => array('type', 'bundle'),
+    ),
+    'primary key' => array('tsid'),
+  );
+
+  return $schema;
+}
+
diff --git a/kolab.org/www/drupal-7.18/sites/all/modules/i18n/i18n_translation/i18n_translation.module b/kolab.org/www/drupal-7.18/sites/all/modules/i18n/i18n_translation/i18n_translation.module
new file mode 100644
index 0000000..de4aad4
--- /dev/null
+++ b/kolab.org/www/drupal-7.18/sites/all/modules/i18n/i18n_translation/i18n_translation.module
@@ -0,0 +1,351 @@
+<?php
+
+/**
+ * @file
+ * Internationalization (i18n) module - Entity translations
+ */
+
+// Language list with only enabled languages
+define('I18N_ENTITY_LANGUAGES_ENABLED', 0);
+// Language list with all languages
+define('I18N_ENTITY_LANGUAGES_EXTENDED', 1);
+
+/**
+ * Default entity controller for notifications objects
+ */
+class I18nTranslationSetController extends DrupalDefaultEntityController {
+  /**
+   * Builds objects of specific classes upon loading.
+   *
+   * @param $queried_entities
+   *   Associative array of query results, keyed on the entity ID.
+   * @param $revision_id
+   *   ID of the revision that was loaded, or FALSE if teh most current revision
+   *   was loaded.
+   */
+  protected function attachLoad(&$queried_entities, $revision_id = FALSE) {
+    foreach ($queried_entities as $id => $entity) {
+      $queried_entities[$id] = i18n_translation_set_build($entity->type, $entity);
+    }
+    return parent::attachLoad($queried_entities, $revision_id);
+  }
+}
+
+/**
+ * Implements hook_entity_info().
+ */
+function i18n_translation_entity_info() {
+  $bundles = array();
+  foreach (i18n_translation_set_info() as $type => $info) {
+    $bundles[$type] = array(
+      'label' => $info['title'],
+    );
+  }
+  $return = array(
+    'i18n_translation' => array(
+      'label' => t('Translation set'),
+      'controller class' => 'I18nTranslationSetController',
+      'base table' => 'i18n_translation_set',
+      //'uri callback' => 'taxonomy_term_uri',
+      'fieldable' => FALSE,
+      'entity keys' => array(
+        'id' => 'tsid',
+        'bundle' => 'type',
+        'label' => 'title',
+      ),
+      'bundle keys' => array(
+        'bundle' => 'type',
+      ),
+      'bundles' => $bundles,
+    ),
+  );
+
+  return $return;
+}
+
+/**
+ * Implements hook_menu()
+ */
+function i18n_translation_menu() {
+  $items['admin/config/regional/i18n_translation'] = array(
+    'title' => 'Translation sets',
+    'description' => 'Translation sets overview.',
+    'page callback' => 'i18n_translation_admin_overview',
+    //'page arguments' => array('i18n_translation_set_overview'),
+    'access arguments' => array('administer site configuration'),
+    'file' => 'i18n_translation.admin.inc',
+    'weight' => 10,
+  );
+  $items['admin/config/regional/i18n_translation/configure'] = array(
+    'title' => 'Translation sets',
+    'description' => 'Overview of existing translation sets.',
+    'type' => MENU_DEFAULT_LOCAL_TASK,
+  );
+  return $items;
+}
+
+/**
+ * Implements hook_hook_info().
+ */
+function i18n_translation_hook_info() {
+  $hooks['i18n_translation_set_info'] = array(
+    'group' => 'i18n',
+  );
+  return $hooks;
+}
+
+/**
+ * Check whether this object can be part of a translation set
+ */
+function i18n_translation_check_object($type, $object) {
+  if ($info = i18n_translation_set_info($type)) {
+
+  }
+}
+
+/**
+ * Get form element for translation mode and language
+ *
+ * @param $object_type
+ *   Object type for the container element
+ * @param $i18n_mode
+ *   Current or default translation mode
+ * @param $langcode
+ *   Current or default language code
+ * @param $options
+ *   Restricted list of translation modes if we don't want all of them
+ */
+function i18n_translation_mode_element($object_type, $i18n_mode = I18N_MODE_NONE, $langcode = LANGUAGE_NONE, $options = NULL) {
+  $form['i18n_translation'] = array(
+    '#type' => 'fieldset',
+    '#title' => t('Multilingual options'),
+    '#collapsible' => TRUE,
+  );
+  $form['i18n_translation']['i18n_mode'] = array(
+    '#type' => 'radios',
+    '#title' => t('Translation mode'),
+    '#options' => i18n_translation_options($object_type, $options),
+    '#default_value' => $i18n_mode,
+    '#description' => t('For localizable elements, to have all items available for translation visit the <a href="@locale-refresh">translation refresh</a> page.', array('@locale-refresh' => url('admin/config/regional/translate/i18n_string'))),
+  );
+  $form['i18n_translation']['language'] = array(
+    '#default_value' => $langcode ? $langcode : LANGUAGE_NONE,
+    '#description' => t('Predefined language. If set, it will apply to all items.'),
+    '#required' => TRUE,
+    '#states' => array(
+      'visible' => array('input[name="i18n_mode"]' => array('value' => (string)I18N_MODE_LANGUAGE)),
+    ),
+  ) + i18n_element_language_select();
+
+  // The option value 'Language neutral' makes no sense here.
+  $form['i18n_translation']['language']['#options'][LANGUAGE_NONE] = t('- Select a language -');
+  return $form;
+}
+
+/**
+ * Get list of translation modes
+ *
+ * @param $container_type
+ *   Object type for the container
+ * @param $options
+ *   Options to include. If none, defaults for container type will be returned.
+ */
+function i18n_translation_options($container_type, $options = NULL) {
+  // Get names and translation options for container object and items
+  $container_info = i18n_object_info($container_type, 'translation container');
+  $replacements = array(
+    '@container_name' => $container_info['name'],
+    '@item_name_multiple' => $container_info['item name'],
+    '@item_name_multiple_capitalized' => ucfirst($container_info['item name']),
+  );
+  $options = $options ? $options : $container_info['options'];
+  return i18n_translation_options_list($replacements, $options);
+}
+
+/**
+ * Get list of translation modes
+ */
+function i18n_translation_options_list($replacements = array(), $options = array()) {
+  $list = array(
+    I18N_MODE_NONE => t('No multilingual options for @item_name_multiple. Only the @container_name will be translatable.', $replacements),
+    I18N_MODE_LOCALIZE => t('Localize. @item_name_multiple_capitalized are common for all languages, but their name and description may be localized.', $replacements),
+    I18N_MODE_TRANSLATE => t('Translate. Different @item_name_multiple will be allowed for each language and they can be translated.', $replacements),
+    I18N_MODE_MULTIPLE => t('Translate and Localize. @item_name_multiple_capitalized with language will allow translations. @item_name_multiple_capitalized without language will be localized.', $replacements),
+    I18N_MODE_LANGUAGE => t('Fixed Language. @item_name_multiple_capitalized will have a global language and they will only show up for pages in that language.', $replacements),
+  );
+  if ($options) {
+    foreach (array_keys($list) as $key) {
+      if (!in_array($key, $options, TRUE)) {
+        unset($list[$key]);
+      }
+    }
+  }
+  return $list;
+}
+
+/**
+ * Build translation fieldset for object
+ */
+function i18n_translation_set_element($type, $object) {
+  $element = array(
+    '#type' => 'fieldset',
+    '#title' => t('Translations'),
+  );
+  if ($set = i18n_translation_object($type, $object)) {
+    $element['values']['#markup'] = i18n_translation_format_items($set->item_list());
+  }
+  else {
+    $element['message']['#markup'] = t('No translations');
+  }
+  return $element;
+}
+
+/**
+ * Format translation set info as table
+ */
+function i18n_translation_format_items($translations) {
+  foreach ($translations as $langcode => $item) {
+    $rows[] = array(i18n_language_name($langcode), $item);
+  }
+  return !empty($rows) ? theme('table', array('rows' => $rows)) : '';
+}
+
+/**
+ * Get translation set for object
+ */
+function i18n_translation_object($type, $object, $create = FALSE) {
+  if (($field = i18n_translation_set_info($type, 'field', 'i18n_tsid')) && ($tsid = i18n_object_field($object, $field))) {
+    return i18n_translation_set_load($tsid, $type);
+  }
+  elseif ($create) {
+    $set = i18n_translation_set_build($type);
+    if ($langcode = i18n_object_langcode($object)) {
+      $set->add_item($object, $langcode);
+    }
+    return $set;
+  }
+}
+
+/**
+ * Get information about translation sets
+ */
+function i18n_translation_set_info($type = NULL, $property = NULL, $default = NULL) {
+  $info = &drupal_static(__FUNCTION__);
+  if (!$info) {
+    $info = module_invoke_all('i18n_translation_set_info');
+    drupal_alter('i18n_translation_set_info', $info);
+  }
+  if ($property && $type) {
+    return isset($info[$type][$property]) ? $info[$type][$property] : $default;
+  }
+  elseif ($type) {
+    return isset($info[$type]) ? $info[$type] : $default;
+  }
+  else {
+    return $info;
+  }
+}
+
+/**
+ * Build a translation set from type, data
+ */
+function i18n_translation_set_build($type, $data = array()) {
+  $class = i18n_translation_set_info($type, 'class', 'i18n_translation_set');
+  $set = new $class((array)$data);
+  $set->type = $type;
+  return $set;
+}
+
+/**
+ * Create a new translation set
+ */
+function i18n_translation_set_create($type, $bundle = '', $translations = NULL, $master_id = 0) {
+  $set = i18n_translation_set_build($type, array('type' => $type, 'bundle' => $bundle, 'master_id' => $master_id, 'translations' => $translations));
+  $set->insert();
+  return $set;
+}
+
+/**
+ * Load single translation set.
+ *
+ * @param int $tsid
+ *   Translation set id.
+ * @param string $type
+ *   (Optional) translation set type (bundle).
+ */
+function i18n_translation_set_load($tsid, $type = NULL) {
+  $conditions['tsid'] = $tsid;
+  $list = entity_load('i18n_translation', array($tsid));
+  $entity = reset($list);
+  if ($entity && $type && $entity->type != $type) {
+    return NULL;
+  }
+  return $entity;
+}
+
+/**
+ * Index objects in translation set by language
+ */
+function i18n_translation_set_index($translations) {
+  $list = array();
+  foreach ($translations as $object) {
+    if ($lang = i18n_object_langcode($object)) {
+      $list[$lang] = $object;
+    }
+  }
+  return $list;
+}
+
+/**
+ * Translation set generic form
+ */
+function i18n_translation_set_overview($form, &$form_state, $type = NULL, $tsids = NULL) {
+  module_load_include('admin.inc', 'i18n_translation');
+  return i18n_translation_admin_form($form, $form_state, $type, $tsids);
+}
+
+/**
+ * Generate a tabular listing of translations for this type.
+ */
+function i18n_translation_set_list_manage($type) {
+  module_load_include('admin.inc', 'i18n_translation');
+  return i18n_translation_admin_overview($type);
+}
+
+/**
+ * Ask for confirmation of translation set deletion
+ */
+function i18n_translation_set_delete_confirm($form, &$form_state, $translation_set) {
+  $form['#translation_set'] = $translation_set;
+  $form['tsid'] = array(
+    '#type' => 'value',
+    '#value' => $translation_set->tsid,
+  );
+  if ($items = $translation_set->item_list()) {
+    $form['items'] = array(
+      '#type' => 'item',
+      '#title' => t('Items in this translation set'),
+      '#markup' => theme('item_list', array('items' => $items)),
+    );
+  }
+  return confirm_form($form,
+    t('Are you sure you want to delete %title translation set?', array('%title' => $translation_set->get_title())),
+    i18n_translation_set_info($translation_set->type, 'list path'),
+    t('This action cannot be undone.'),
+    t('Delete'),
+    t('Cancel')
+  );
+}
+
+/**
+ * Execute translation set deletion
+ */
+function i18n_translation_set_delete_confirm_submit($form, &$form_state) {
+  if ($form_state['values']['confirm']) {
+    $set = i18n_translation_set_load($form_state['values']['tsid']);
+    $set->delete(TRUE);
+    drupal_set_message(t('The translation set has been deleted.'));
+  }
+
+  $form_state['redirect'] = i18n_translation_set_info($set->type, 'list path');
+}
\ No newline at end of file
diff --git a/kolab.org/www/drupal-7.18/sites/all/modules/i18n/i18n_translation/i18n_translation.pages.inc b/kolab.org/www/drupal-7.18/sites/all/modules/i18n/i18n_translation/i18n_translation.pages.inc
new file mode 100644
index 0000000..4ef3d2a
--- /dev/null
+++ b/kolab.org/www/drupal-7.18/sites/all/modules/i18n/i18n_translation/i18n_translation.pages.inc
@@ -0,0 +1,13 @@
+<?php
+/**
+ * @file
+ * Generic translation page for objects
+ */
+
+/**
+ * Translate object, create translation set
+ */
+function i18n_translation_object_translate_page($type, $object) {
+  $page = i18n_translation_set_info($type, 'page callback');
+  return call_user_func($page, $type, $object);
+}
\ No newline at end of file
diff --git a/kolab.org/www/drupal-7.18/sites/all/modules/i18n/i18n_user/i18n_user.info b/kolab.org/www/drupal-7.18/sites/all/modules/i18n/i18n_user/i18n_user.info
new file mode 100644
index 0000000..e438321
--- /dev/null
+++ b/kolab.org/www/drupal-7.18/sites/all/modules/i18n/i18n_user/i18n_user.info
@@ -0,0 +1,12 @@
+name = User mail translation
+description = "Translate emails sent from the User module."
+core = 7.x
+package = Multilingual - Internationalization
+dependencies[] = i18n_variable
+
+; Information added by drupal.org packaging script on 2013-01-13
+version = "7.x-1.8"
+core = "7.x"
+project = "i18n"
+datestamp = "1358075001"
+
diff --git a/kolab.org/www/drupal-7.18/sites/all/modules/i18n/i18n_user/i18n_user.install b/kolab.org/www/drupal-7.18/sites/all/modules/i18n/i18n_user/i18n_user.install
new file mode 100644
index 0000000..57c5d77
--- /dev/null
+++ b/kolab.org/www/drupal-7.18/sites/all/modules/i18n/i18n_user/i18n_user.install
@@ -0,0 +1,15 @@
+<?php
+
+/**
+ * @file
+ * Installation file for User mail translation module.
+ */
+
+
+/**
+ * Implements hook_install().
+ */
+function i18n_user_install() {
+  // Set module weight for it to run before any other module implementing hook_mail_alter().
+  db_query("UPDATE {system} SET weight = -10000 WHERE name = 'i18n_user' AND type = 'module'");
+}
diff --git a/kolab.org/www/drupal-7.18/sites/all/modules/i18n/i18n_user/i18n_user.module b/kolab.org/www/drupal-7.18/sites/all/modules/i18n/i18n_user/i18n_user.module
new file mode 100644
index 0000000..4d13b3b
--- /dev/null
+++ b/kolab.org/www/drupal-7.18/sites/all/modules/i18n/i18n_user/i18n_user.module
@@ -0,0 +1,69 @@
+<?php
+/**
+ * @file
+ * User mail translation module.
+ */
+
+/**
+ * Implements hook_mail_alter().
+ */
+function i18n_user_mail_alter(&$message) {
+  if ($message['module'] == 'user') {
+    $language = $message['language'];
+    $variables = array('user' => $message['params']['account']);
+    $key = $message['key'];
+
+    $components = array('subject', 'body');
+    foreach ($components as $component) {
+      $text = i18n_variable_get('user_mail_' . $key . '_' . $component, $language->language, FALSE);
+      if ($text) {
+        $text = token_replace($text, $variables, array('language' => $language, 'callback' => 'i18n_user_user_mail_tokens', 'sanitize' => FALSE));
+
+        switch ($component) {
+          case 'subject':
+            $message[$component] = $text;
+            break;
+
+          case 'body':
+            $message[$component] = array($text);
+            break;
+        }
+      }
+    }
+  }
+}
+
+/**
+ * Overrides user_mail_tokens().
+ *
+ * @see i18n_user_user_tokens_alter()
+ * @see user_mail_tokens()
+ */
+function i18n_user_user_mail_tokens(&$replacements, $data, $options) {
+  if (isset($data['user'])) {
+    $replacements['[user:one-time-login-url]'] = i18n_user_user_pass_reset_url($data['user']);
+    $replacements['[user:cancel-url]'] = i18n_user_user_cancel_url($data['user']);
+  }
+}
+
+/**
+ * Overrides user_pass_reset_url().
+ * Generates a unique and localized URL for a user to login and reset their password.
+ *
+ * @see user_pass_reset_url().
+ */
+function i18n_user_user_pass_reset_url($account) {
+  $timestamp = REQUEST_TIME;
+  return url("user/reset/$account->uid/$timestamp/" . user_pass_rehash($account->pass, $timestamp, $account->login), array('absolute' => TRUE, 'language' => i18n_language($account->language)));
+}
+
+/**
+ * Overrides user_pass_cancel_url().
+ * Generates a localized URL to confirm an account cancellation request.
+ *
+ * @see i18n_user_user_cancel_url()
+ */
+function i18n_user_user_cancel_url($account) {
+  $timestamp = REQUEST_TIME;
+  return url("user/$account->uid/cancel/confirm/$timestamp/" . user_pass_rehash($account->pass, $timestamp, $account->login), array('absolute' => TRUE, 'language' => i18n_language($account->language)));
+}
diff --git a/kolab.org/www/drupal-7.18/sites/all/modules/i18n/i18n_variable/i18n_variable.class.inc b/kolab.org/www/drupal-7.18/sites/all/modules/i18n/i18n_variable/i18n_variable.class.inc
new file mode 100644
index 0000000..4feef59
--- /dev/null
+++ b/kolab.org/www/drupal-7.18/sites/all/modules/i18n/i18n_variable/i18n_variable.class.inc
@@ -0,0 +1,43 @@
+<?php
+/**
+ * @file
+ * Variable Realm controller.
+ */
+
+/**
+ * Controller for Language realms.
+ */
+class I18nVariableLanguageRealm extends VariableRealmDefaultController {
+  /**
+   * Implementation of VariableRealmControllerInterface::getAvailableVariables().
+   */
+  public function getAvailableVariables() {
+    $translatable = array();
+    $conf = variable_get('i18n_variables', array());
+    foreach (variable_get_info() as $name => $variable) {
+      if (!empty($variable['localize']) || in_array($name, $conf)) {
+        $translatable[] = $name;
+      }
+    }
+    return $translatable;
+  }
+  /**
+   * Implementation of VariableRealmControllerInterface::getDefaultKey().
+   */
+  public function getDefaultKey() {
+    // The default key will match the default language.
+    return language_default('language');
+  }
+  /**
+   * Implementation of VariableRealmControllerInterface::getRequestKey().
+   */
+  public function getRequestKey() {
+    return i18n_variable_language()->language;
+  }
+  /**
+   * Implementation of VariableRealmControllerInterface::getAllKeys().
+   */
+  public function getAllKeys() {
+    return locale_language_list('name', TRUE);
+  }
+}
diff --git a/kolab.org/www/drupal-7.18/sites/all/modules/i18n/i18n_variable/i18n_variable.info b/kolab.org/www/drupal-7.18/sites/all/modules/i18n/i18n_variable/i18n_variable.info
new file mode 100644
index 0000000..59a4674
--- /dev/null
+++ b/kolab.org/www/drupal-7.18/sites/all/modules/i18n/i18n_variable/i18n_variable.info
@@ -0,0 +1,18 @@
+name = Variable translation
+description = Multilingual variables that switch language depending on page language.
+dependencies[] = i18n
+dependencies[] = variable_store (7.x-2.x)
+dependencies[] = variable_realm (7.x-2.x)
+package = Multilingual - Internationalization
+core = 7.x
+configure = admin/config/regional/i18n/variable
+
+files[] = i18n_variable.class.inc
+files[] = i18n_variable.test
+
+; Information added by drupal.org packaging script on 2013-01-13
+version = "7.x-1.8"
+core = "7.x"
+project = "i18n"
+datestamp = "1358075001"
+
diff --git a/kolab.org/www/drupal-7.18/sites/all/modules/i18n/i18n_variable/i18n_variable.install b/kolab.org/www/drupal-7.18/sites/all/modules/i18n/i18n_variable/i18n_variable.install
new file mode 100644
index 0000000..7926a8e
--- /dev/null
+++ b/kolab.org/www/drupal-7.18/sites/all/modules/i18n/i18n_variable/i18n_variable.install
@@ -0,0 +1,83 @@
+<?php
+
+/**
+ * @file
+ * Installation file for Internationalization (i18n) module.
+ */
+
+/**
+ * Implements hook_install().
+ */
+function i18n_variable_install() {
+  // Set module weight for it to run before most modules, but after variable_realm
+  db_query("UPDATE {system} SET weight = -900 WHERE name = 'i18n_variable' AND type = 'module'");
+  // Update from d6, module changed name
+  if (variable_get('i18n_drupal6_update')) {
+    i18n_variable_update_7000();
+    i18n_variable_update_7001();
+    i18n_variable_update_7002();
+  }
+  // Set some minimum variables to be translated.
+  variable_set('variable_realm_list_language', array('site_name', 'site_slogan'));
+}
+
+/**
+ * Implements hook_install().
+ */
+function i18n_variable_uninstall() {
+  if (drupal_load('module', 'variable_store')) {
+    // Delete all our variables from store.
+    variable_store_delete_all('language', NULL);
+  }
+}
+
+/**
+ * Update multilingual variables variable name
+ */
+function i18n_variable_update_7000() {
+  variable_set('i18n_variable_list', variable_get('i18n_variables', array()));
+  variable_set('i18n_variable_conf', variable_get('i18n_variables', array()));
+  variable_del('i18n_variables');
+}
+
+/**
+ * Move variables from old storage to new table
+ */
+function i18n_variable_update_7001() {
+  drupal_load('module', 'variable_store');
+  foreach (db_select('i18n_variable', 'v')->fields('v')->execute()->fetchAll() as $variable) {
+    variable_store_set('language', $variable->language, $variable->name, unserialize($variable->value));
+  }
+}
+
+/**
+ * Drop i18n_variable table if exists
+ */
+function i18n_variable_update_7002() {
+  if (db_table_exists('i18n_variable')) {
+    db_drop_table('i18n_variable');
+  }
+}
+
+/**
+ * Update list of realm variables.
+ */
+function i18n_variable_update_7003() {
+  drupal_load('module', 'variable_store');
+  $variable_list = variable_get('i18n_variable_conf', array());
+  variable_set('variable_realm_list_language', $variable_list);
+  // Delete old variables from store that are not in the list.
+  $old_variables = array_diff(variable_store_list_all('language'), variable_children($variable_list));
+  foreach ($old_variables as $name) {
+    variable_store_delete_all('language', NULL, $name);
+  }
+}
+
+/**
+ * Delete obsoleted variable realm variables.
+ */
+function i18n_variable_update_7004() {
+  variable_del('i18n_variable_conf');
+  variable_del('i18n_variable_list');
+}
+
diff --git a/kolab.org/www/drupal-7.18/sites/all/modules/i18n/i18n_variable/i18n_variable.module b/kolab.org/www/drupal-7.18/sites/all/modules/i18n/i18n_variable/i18n_variable.module
new file mode 100644
index 0000000..63e8fb1
--- /dev/null
+++ b/kolab.org/www/drupal-7.18/sites/all/modules/i18n/i18n_variable/i18n_variable.module
@@ -0,0 +1,138 @@
+<?php
+/**
+ * @file
+ * Internationalization (i18n) package. Multilingual variables API.
+ */
+
+/**
+ * The type of language used for variables.
+ */
+define('I18N_VARIABLE_LANGUAGE_TYPE', 'i18n_language_variable');
+
+/**
+ * Implements hook_language_init()
+ */
+function i18n_variable_language_init() {
+  if (drupal_multilingual()) {
+    module_invoke('variable_realm', 'initialize', 'language');
+  }
+}
+
+/**
+ * Implements hook_variable_realm_info().
+ */
+function i18n_variable_variable_realm_info() {
+  $realm['language'] = array(
+    'title' => t('Language'),
+    'weight' => 100,
+    'controller class' => 'I18nVariableLanguageRealm',
+    'store class' => 'VariableStoreRealmStore',
+    // Variables for this realm can be selected from a list.
+    'select' => TRUE,
+    'select path' => 'admin/config/regional/i18n/variable',
+    // Name for variables that belong to this realm: 'multilingual' variable/s
+    'variable name' => t('multilingual'),
+    'variable class' => 'i18n-variable',
+    // Automatically handle these variables in system settings forms.
+    'form settings' => TRUE,
+    'form switcher' => TRUE,
+  );
+  return $realm;
+}
+
+/**
+ * Implements hook_menu()
+ */
+function i18n_variable_menu() {
+  $items['admin/config/regional/i18n/variable'] = array(
+    'title' => 'Variables',
+    'description' => 'Configure multilingual variables.',
+    'page callback' => 'drupal_get_form',
+    'page arguments' => array('variable_realm_select_variables_form', 'language'),
+    'access arguments' => array('administer site configuration'),
+    'file' => 'variable_realm.form.inc',
+    'file path' => drupal_get_path('module', 'variable_realm'),
+    'type' => MENU_LOCAL_TASK,
+  );
+  return $items;
+}
+
+
+/**
+ * Get variables language, make sure it is initialized
+ */
+function i18n_variable_language() {
+  // If we've got a variables language, it will be that one
+  if (!isset($GLOBALS[I18N_VARIABLE_LANGUAGE_TYPE])) {
+    $GLOBALS[I18N_VARIABLE_LANGUAGE_TYPE] = i18n_language_interface();
+  }
+  return $GLOBALS[I18N_VARIABLE_LANGUAGE_TYPE];
+}
+
+/**
+ * Get original value for global variable/s
+ */
+function i18n_variable_global($name = NULL, $default = NULL) {
+  return variable_realm_global_get($name, $default);
+}
+
+/**
+ * Get list of multilingual variables or check whether a variable is multilingual
+ */
+function i18n_variable_list($name = NULL) {
+  $variables = &drupal_static(__FUNCTION__);
+  if (!isset($variables)) {
+    $variables = variable_children(variable_get('variable_realm_list_language', array()));
+  }
+  return $name ? in_array($name, $variables) : $variables;
+}
+
+/**
+* Load language variables into array.
+*
+* Pull variables from the store but filter out the ones that are not multilingual.
+*/
+function i18n_variable_load($langcode) {
+  $variables = array();
+  foreach (variable_store('language', $langcode) as $name => $value) {
+    if (i18n_variable_list($name)) {
+      $variables[$name] = $value;
+    }
+  }
+  return $variables;
+}
+
+/**
+ * Set a persistent language dependent variable.
+ *
+ * @param $name
+ *   The name of the variable to set.
+ * @param $value
+ *   The value to set. This can be any PHP data type; these functions take care
+ *   of serialization as necessary.
+ * @param $langcode
+ *   Language code.
+ */
+function i18n_variable_set($name, $value, $langcode) {
+  variable_realm_set('language', $langcode, $name, $value);
+}
+
+/**
+ * Get single multilingual variable
+ */
+function i18n_variable_get($name, $langcode, $default = NULL) {
+  return variable_realm_get('language', $langcode, $name, $default);
+}
+
+/**
+ * Unset a persistent multilingual variable.
+ *
+ * @param $name
+ *   The name of the variable to undefine.
+ * @param $langcode
+ *   Language code.
+ */
+function i18n_variable_del($name, $langcode) {
+  variable_realm_del('language', $langcode, $name);
+}
+
diff --git a/kolab.org/www/drupal-7.18/sites/all/modules/i18n/i18n_variable/i18n_variable.test b/kolab.org/www/drupal-7.18/sites/all/modules/i18n/i18n_variable/i18n_variable.test
new file mode 100644
index 0000000..ff91fb5
--- /dev/null
+++ b/kolab.org/www/drupal-7.18/sites/all/modules/i18n/i18n_variable/i18n_variable.test
@@ -0,0 +1,91 @@
+<?php
+/**
+ * @file
+ * Test case for multilingual variables
+ */
+
+
+class i18nVariableTestCase extends Drupali18nTestCase {
+
+  public static function getInfo() {
+    return array(
+      'name' => 'Variable translation',
+      'group' => 'Internationalization',
+      'description' => 'Variable translation functions'
+    );
+  }
+
+  function setUp() {
+    parent::setUp('i18n_variable', 'translation');
+    parent::setUpLanguages(array('administer site configuration'));
+  }
+
+  /**
+   * Set up translation for content type (page)
+   */
+  function setUpContentTranslation($settings = array()) {
+    $this->translator = $this->drupalCreateUser(array('create page content', 'edit own page content', 'translate content'));
+    $this->drupalLogin($this->admin_user);
+    // Set "Basic page" content type to use multilingual support with
+    // translation.
+    $this->drupalGet('admin/structure/types/manage/page');
+    $edit = array();
+    $edit['language_content_type'] = 2;
+    // Mark status and promoted
+    $edit['node_options[status]'] = 1;
+    $this->drupalPost('admin/structure/types/manage/page', $edit, t('Save content type'));
+    $this->assertRaw(t('The content type %type has been updated.', array('%type' => 'Basic page')), t('Basic page content type has been updated.'));
+
+    // Enable the language switcher block.
+    $language_type = LANGUAGE_TYPE_INTERFACE;
+    $edit = array("blocks[locale_$language_type][region]" => 'sidebar_first');
+    $this->drupalPost('admin/structure/block', $edit, t('Save blocks'));
+  }
+
+  function testVariableLocalize() {
+    $this->setUpContentTranslation();
+
+    // Create 2 nodes in different languages.
+    $first_title = $this->randomName(10);
+    $first_body = $this->randomString(50);
+    $first_node = $this->createNode('page', $first_title, $first_body, $this->default_language);
+
+    $secondary_title = $this->randomName(10);
+    $secondary_body = $this->randomString(50);
+    $secondary_node = $this->createNode('page', $secondary_title, $secondary_body, $this->secondary_language);
+
+    $this->drupalGet('<front>', array('language' => i18n_language_object($this->default_language)));
+    $this->assertText(t('No front page content has been created yet.'));
+
+    $this->drupalGet('<front>', array('language' => i18n_language_object($this->secondary_language)));
+    $this->assertText(t('No front page content has been created yet.'));
+
+    $edit = array(
+      'variables[site_name]' => 1,
+      'variables[site_frontpage]' => 1,
+    );
+    $this->drupalPost('admin/config/regional/i18n/variable', $edit, t('Save configuration'));
+
+    $edit_first = array(
+      'site_frontpage' => 'node/' . $first_node->nid,
+      'site_name' => $this->randomName(10),
+    );
+    $this->drupalPost('admin/config/system/site-information', $edit_first, t('Save configuration'), array('language' => i18n_language_object($this->default_language)));
+
+    $edit_secondary = array(
+      'site_frontpage' => 'node/' . $secondary_node->nid,
+      'site_name' => $this->randomName(10),
+    );
+    $this->drupalPost('admin/config/system/site-information', $edit_secondary, t('Save configuration'), array('language' => i18n_language_object($this->secondary_language)));
+
+    $this->drupalGet('<front>', array('language' => i18n_language_object($this->default_language)));
+    $this->assertText($edit_first['site_name']);
+    $this->assertNoText(t('No front page content has been created yet.'));
+    $this->assertText($first_title);
+
+    $this->drupalGet('<front>', array('language' => i18n_language_object($this->secondary_language)));
+    $this->assertText($edit_secondary['site_name']);
+    $this->assertNoText(t('No front page content has been created yet.'));
+    $this->assertText($secondary_title);
+  }
+}
diff --git a/kolab.org/www/drupal-7.18/sites/all/modules/i18n/i18n_variable/i18n_variable.variable.inc b/kolab.org/www/drupal-7.18/sites/all/modules/i18n/i18n_variable/i18n_variable.variable.inc
new file mode 100644
index 0000000..e4c6705
--- /dev/null
+++ b/kolab.org/www/drupal-7.18/sites/all/modules/i18n/i18n_variable/i18n_variable.variable.inc
@@ -0,0 +1,25 @@
+<?php
+/**
+ * @file
+ * Variable information
+ */
+
+/**
+ * Implements hook_variable_info().
+ */
+function i18n_variable_variable_info($options = array()) {
+  $variables['i18n_variable_conf'] = array(
+    'title' => t('Multilingual variables, main variable names.', array(), $options),
+    'type' => 'array',
+    'group' => 'i18n',
+  );
+  $variables['i18n_variable_list'] = array(
+    'title' => t('Multilingual variables, real variable names.', array(), $options),
+    'type' => 'array',
+    'group' => 'i18n',
+  );
+  return $variables;
+}
+
+
+
diff --git a/kolab.org/www/drupal-7.18/sites/all/modules/i18n/tests/i18n_test.info b/kolab.org/www/drupal-7.18/sites/all/modules/i18n/tests/i18n_test.info
new file mode 100644
index 0000000..f764aa0
--- /dev/null
+++ b/kolab.org/www/drupal-7.18/sites/all/modules/i18n/tests/i18n_test.info
@@ -0,0 +1,15 @@
+name = Internationalization tests
+description = Helper module for testing i18n (do not enable manually)
+dependencies[] = locale
+dependencies[] = translation
+dependencies[] = i18n
+package = Testing
+core = 6.x
+hidden = TRUE
+
+; Information added by drupal.org packaging script on 2013-01-13
+version = "7.x-1.8"
+core = "7.x"
+project = "i18n"
+datestamp = "1358075001"
+
diff --git a/kolab.org/www/drupal-7.18/sites/all/modules/i18n/tests/i18n_test.module b/kolab.org/www/drupal-7.18/sites/all/modules/i18n/tests/i18n_test.module
new file mode 100644
index 0000000..e579de8
--- /dev/null
+++ b/kolab.org/www/drupal-7.18/sites/all/modules/i18n/tests/i18n_test.module
@@ -0,0 +1,46 @@
+<?php
+
+/**
+ * @file
+ *   Helper module for testing i18n
+ */
+
+// Add some multilingual variables, override existing ones from settings so
+// we have a known list and we don't need any addition to the settings file for testing i18n
+_i18n_test_variable_init();
+
+/**
+ * Implements hook_init()
+ */
+function i18n_test_init() {
+  // We just implement this hook so this one is loaded always on bootstap
+}
+
+/**
+ * Set default multilingual variables and add any others defined by testing scripts
+ *
+ * More variables can be added using 'i18n_variables_test';
+ */
+function _i18n_test_variable_init() {
+  global $conf;
+  $conf['i18n_variables'] = array_merge(array('site_name', 'site_frontpage'), variable_get('i18n_variables_test', array()));
+}
+
+/**
+ * Implements hook_i18n_string_info()
+ */
+function i18n_test_i18n_string_info() {
+  $groups['test'] = array(
+    'title' => t('Test'),
+    'description' => t('Translatable menu items: title and description.'),
+    'format' => FALSE, // This group doesn't have strings with format
+    'refresh callback' => 'i18n_test_i18n_string_refresh',
+  );
+  return $groups;
+}
+/**
+ * Locale refresh
+ */
+function i18n_test_i18n_string_refresh() {
+  return TRUE;
+}
\ No newline at end of file


commit 025ef4e4be959c3f448a5381598528ac18a53035
Author: Torsten Grote <grote at kolabsys.com>
Date:   Wed Jul 3 15:29:36 2013 +0200

    update twitter module

diff --git a/kolab.org/www/drupal-7.18/sites/all/modules/twitter/README.txt b/kolab.org/www/drupal-7.18/sites/all/modules/twitter/README.txt
index c8ded0d..0b74eae 100644
--- a/kolab.org/www/drupal-7.18/sites/all/modules/twitter/README.txt
+++ b/kolab.org/www/drupal-7.18/sites/all/modules/twitter/README.txt
@@ -7,18 +7,14 @@ provides useful input filters to easily link Twitter accounts and searches withi
 Twitter's submodules allow posting to twitter, executing actions/rules when tweeting or login
 with a Twitter account.
 
-OAuth
-=====
-Except for just listing tweets, OAuth module is required to authenticate against Twitter. If you 
-just want to list tweets in a block, follow the steps at http://drupal.org/node/1253026.
-
-When you download the OAuth module, get the latest stable release available at http://drupal.org/project/oauth
+Installation
+============
+OAuth module is required for all requests to the Twitter REST API 1.1. When you download the OAuth module, get the latest stable release available at http://drupal.org/project/oauth
 
-Once OAuth has been enabled, go to admin/config/services/twitter and follow instructions.
+Once OAuth and Twitter have been enabled, go to admin/config/services/twitter and follow instructions in order
+to provide your Twitter Application keys.
 
-How to add a Twitter account to a Drupal user account
-=====================================================
-Read http://drupal.org/node/1253026 for details.
+You can find further installation instructions at http://drupal.org/node/1346824
 
 How to use the username and hashtag input filters
 =================================================
@@ -41,6 +37,8 @@ How to post to Twitter
 3. Verify permissions at admin/people/permissions.
 4. Add a Twitter account and try to edit or post content.
 
+Further information can be found at http://drupal.org/node/1016584.
+
 How to sign in with Twitter
 ===========================
 Existing and new users can sign in with Twitter by enabling the twitter_signin module. The following scenarios are being contemplated so far:
diff --git a/kolab.org/www/drupal-7.18/sites/all/modules/twitter/TODO.txt b/kolab.org/www/drupal-7.18/sites/all/modules/twitter/TODO.txt
deleted file mode 100644
index 1fbaead..0000000
--- a/kolab.org/www/drupal-7.18/sites/all/modules/twitter/TODO.txt
+++ /dev/null
@@ -1,6 +0,0 @@
-## Things left to do before release
-
- * Migrate twitter_actions to new API
- * Implement the twitter search API
- * Implement geo features in the API & integrate with location/geo
- * Implement support for DMs (direct messages)
diff --git a/kolab.org/www/drupal-7.18/sites/all/modules/twitter/tests/twitter_mock.info b/kolab.org/www/drupal-7.18/sites/all/modules/twitter/tests/twitter_mock.info
index 0bcd06b..28baae7 100644
--- a/kolab.org/www/drupal-7.18/sites/all/modules/twitter/tests/twitter_mock.info
+++ b/kolab.org/www/drupal-7.18/sites/all/modules/twitter/tests/twitter_mock.info
@@ -5,9 +5,9 @@ hidden = TRUE
 dependencies[] = twitter
 dependencies[] = simpletest
 
-; Information added by drupal.org packaging script on 2012-08-11
-version = "7.x-3.2"
+; Information added by drupal.org packaging script on 2013-06-03
+version = "7.x-5.8"
 core = "7.x"
 project = "twitter"
-datestamp = "1344714197"
+datestamp = "1370303463"
 
diff --git a/kolab.org/www/drupal-7.18/sites/all/modules/twitter/tweet.tpl.php b/kolab.org/www/drupal-7.18/sites/all/modules/twitter/tweet.tpl.php
new file mode 100644
index 0000000..e411b54
--- /dev/null
+++ b/kolab.org/www/drupal-7.18/sites/all/modules/twitter/tweet.tpl.php
@@ -0,0 +1,43 @@
+<?php
+/**
+ * @file
+ * Renders a tweet as it does look like at Twitter.com.
+ * @see twitter.css
+ */
+?>
+<div class="twitter-status clearfix">
+  <div class="avatar">
+    <a alt="<?php print $author->name; ?>" title="<?php print $author->name; ?>" href=
+    "https://twitter.com/<?php print $author->screen_name; ?>"><img src=
+    "<?php print $author->profile_image_url; ?>"></a>
+  </div>
+
+  <div class="timestamp">
+    <?php print $status->time_ago; ?>
+  </div>
+
+  <div class="name-handle">
+    <div class="name">
+      <a href="http://twitter.com/<?php print $author->screen_name; ?>"><?php print $author->name; ?></a>
+    </div>
+
+    <div class="handle">
+      <a href="http://twitter.com/<?php print $author->screen_name; ?>">@<?php print $author->screen_name; ?></a>
+    </div>
+  </div>
+
+  <div class="text">
+    <?php print _twitter_filter_link(_twitter_filter_hashtag(_twitter_filter_username($status->text, NULL), NULL), NULL); ?>
+  </div>
+
+  <ul class="actions">
+    <li><a href=
+    "https://twitter.com/intent/tweet?in_reply_to=<?php print $status->twitter_id; ?>">Reply</a></li>
+
+    <li><a href=
+    "https://twitter.com/intent/retweet?tweet_id=<?php print $status->twitter_id; ?>">Retweet</a></li>
+
+    <li><a href=
+    "https://twitter.com/intent/favorite?tweet_id=<?php print $status->twitter_id; ?>">Favorite</a></li>
+  </ul>
+</div>
diff --git a/kolab.org/www/drupal-7.18/sites/all/modules/twitter/twitter.api.php b/kolab.org/www/drupal-7.18/sites/all/modules/twitter/twitter.api.php
index 62e6a32..0507b7d 100644
--- a/kolab.org/www/drupal-7.18/sites/all/modules/twitter/twitter.api.php
+++ b/kolab.org/www/drupal-7.18/sites/all/modules/twitter/twitter.api.php
@@ -21,6 +21,17 @@ function hook_twitter_accounts($account) {}
  *
  * @param $status
  *   stdClass containing information about the status message.
- * @see twitter_status_save() for details about the contents of $status.
+ * @see https://dev.twitter.com/docs/platform-objects/tweets for details about the contents of $status.
  */
 function hook_twitter_status_save($status) {}
+
+/**
+ * Notifies of a saved twitter account.
+ *
+ * @param $account
+ *   User account object.
+ * @param $values
+ *   Twitter account values.
+ * @see twitter_account_save()
+ */
+function hook_twitter_account_save($account, $values) {}
diff --git a/kolab.org/www/drupal-7.18/sites/all/modules/twitter/twitter.css b/kolab.org/www/drupal-7.18/sites/all/modules/twitter/twitter.css
new file mode 100644
index 0000000..926ea0c
--- /dev/null
+++ b/kolab.org/www/drupal-7.18/sites/all/modules/twitter/twitter.css
@@ -0,0 +1,90 @@
+/**
+ * Styles to render a tweet as it is shown at Twitter.com.
+ *
+ * Thanks @makangus.
+ */
+div.view-tweets ul{
+  list-style-type: none;
+  padding: 0px;
+}
+
+.twitter-status{
+  background: #ffffff;
+  color: #333333;
+  padding: 10px 10px 0;
+  margin-bottom: 2px;
+  font-size: 1.077em;
+  line-height: 1.42857142857143em;
+}
+
+.twitter-status .timestamp{
+  float: right;
+  color: #999999;
+  font-size: 0.85714285714286em;
+}
+
+.twitter-status .name-handle{
+  margin-left: 60px;
+}
+
+.twitter-status .text{
+  margin-left: 60px;
+}
+
+.twitter-status .name-handle .name, .twitter-status .name-handle .handle{
+  display: inline;
+}
+
+.twitter-status .name-handle .name a{
+  font-weight: bold;
+  margin-right: 5px;
+  color: #333333;
+}
+
+.twitter-status .name-handle .handle a{
+  color: #999999;
+  font-size: 0.85714285714286em;
+}
+
+.twitter-status .avatar{
+  float: left;
+}
+
+.twitter-status .avatar img{
+  border-radius: 3px;
+}
+
+.twitter-status a {
+  color: #30a9ff;
+}
+
+.twitter-status a:hover {
+  text-decoration: underline;
+}
+
+.twitter-status .actions{
+  text-align: right;
+  font-size: 0.85714285714286em;
+  padding: 0;
+  margin: 0;
+  visibility: hidden;
+}
+
+.twitter-status:hover .actions{
+  visibility: visible;
+}
+
+.twitter-status .actions li{
+  display: inline-block;
+  list-style: none;
+  padding: 0;
+  margin: 0;
+}
+
+.twitter-status .actions a{
+  display: inline-block;
+  line-height: 16px;
+  margin-left: 10px;
+  vertical-align: top;
+  color: #999999;
+}
diff --git a/kolab.org/www/drupal-7.18/sites/all/modules/twitter/twitter.drush.inc b/kolab.org/www/drupal-7.18/sites/all/modules/twitter/twitter.drush.inc
new file mode 100644
index 0000000..12f68e4
--- /dev/null
+++ b/kolab.org/www/drupal-7.18/sites/all/modules/twitter/twitter.drush.inc
@@ -0,0 +1,84 @@
+<?php
+/**
+ * @file
+ * Drush commands for the Twitter module.
+ */
+
+/**
+ * Implements COMMANDFILE_drush_command()
+ */
+function twitter_drush_command() {
+  $items = array();
+
+  $items['twitter-search'] = array(
+    'description' => 'Searches the Twitter API for something.',
+    'arguments' => array(
+      'keyword' => 'The keyword you are searching for. Add @ for usernames and # for hashtags.',
+    ),
+    'required-arguments' => TRUE,
+    'options' => array(
+      'limit' => 'Limit the number of results to be printed.',
+      'randomize' => 'Randomize the result.',
+    ),
+    'examples' => array(
+      'drush twitter-search \'#drupal\'' => 'Pull tweets containing the hashtag #drupal.',
+      'drush twitter-search \'#drupalconmunich\' --limit=1 --randomize' =>
+        'Picks a random tweet containing \'#drupalconmunich\'.',
+    ),
+    'aliases' => array('tws'),
+    'bootstrap' => DRUSH_BOOTSTRAP_DRUPAL_SITE,
+    );
+  return $items;
+}
+
+/**
+ * Implements drush_COMMANDFILE_COMMANDNAME()
+ *
+ * Searches for a keyword at Twitter and return the results.
+ */
+function drush_twitter_search($keyword) {
+  $keyword = urlencode($keyword);
+  // This is not even using the Twitter library at twitter.lib.inc, but it will.
+  $url = 'http://search.twitter.com/search.json?rpp=100&q=';
+
+  $response = drupal_http_request($url . $keyword);
+
+  if (isset($response->code) && ($response->code == 200)) {
+    $data = json_decode($response->data);
+    if (!count($data->results)) {
+      drush_set_error(dt('No tweets found for this keyword.'));
+    }
+    else {
+      drush_print(dt('There are !total tweets containing \'@keyword\'.', array(
+        '!total' => count($data->results),
+        '@keyword' => $keyword,
+      )));
+      $tweets = $data->results;
+
+      // Should we randomize?
+      if (drush_get_option('randomize')) {
+        $results = shuffle($tweets);
+      }
+
+      // Should we limit the list of results?
+      if (drush_get_option('limit')) {
+        $tweets = array_slice($tweets, 0, drush_get_option('limit'));
+      }
+
+      // Print results
+      foreach ($tweets as $tweet) {
+        drush_print('');
+        drush_print(dt('User "@!user", tweeted "!tweet".', array(
+          '!user' => $tweet->from_user,
+          '!tweet' => $tweet->text,
+        )));
+        drush_print('');
+      }
+    }
+  }
+  else {
+    drush_set_error(dt('There was an error. Full raw response was !response', array(
+      '!response' => print_r($response, TRUE)
+    )));
+  }
+}
diff --git a/kolab.org/www/drupal-7.18/sites/all/modules/twitter/twitter.inc b/kolab.org/www/drupal-7.18/sites/all/modules/twitter/twitter.inc
index 92b18cd..be2993c 100644
--- a/kolab.org/www/drupal-7.18/sites/all/modules/twitter/twitter.inc
+++ b/kolab.org/www/drupal-7.18/sites/all/modules/twitter/twitter.inc
@@ -5,31 +5,42 @@
  * Twitter API functions
  */
 
+module_load_include('php', 'oauth_common', 'lib/OAuth');
+
 /**
- * Connect to the API using the 'proper' version (Oauth vs. standard)
+ * Connect to the Twitter API.
+ *
+ * @param object $account
+ *   An authenticated twitter_account object to be used to authenticate against
+ *   Twitter.
+ * @return
+ *   a Twitter object ready to be used to query the Twitter API or FALSE.
  */
-function twitter_connect($account) {
-  $auth = $account->get_auth();
-  if (_twitter_use_oauth() && $auth['oauth_token'] && $auth['oauth_token_secret']) {
-    module_load_include('lib.php', 'oauth_common');
-    return new TwitterOAuth(variable_get('twitter_consumer_key', ''), variable_get('twitter_consumer_secret', ''),
-                            $auth['oauth_token'], $auth['oauth_token_secret']);
-  }
-  elseif ($auth['password']) {
-    return new Twitter($account->screen_name, $auth['password']);
+function twitter_connect($account = NULL) {
+  if (!$account) {
+    // Load the first authenticated account.
+    $twitter_uid = db_query("SELECT twitter_uid
+                             FROM {twitter_account}
+                             WHERE oauth_token <> ''
+                               AND oauth_token_secret <> '' ")->fetchField();
+    $account = twitter_account_load($twitter_uid);
   }
-  else {
-    return new Twitter;
+  if ($account) {
+    $auth = $account->get_auth();
+    if (isset($auth['oauth_token']) && isset($auth['oauth_token_secret'])) {
+      return new Twitter(variable_get('twitter_consumer_key', ''), variable_get('twitter_consumer_secret', ''),
+                              $auth['oauth_token'], $auth['oauth_token_secret']);
+    }
   }
+  return FALSE;
 }
 
 /**
  * Saves a TwitterUser object to {twitter_account}
  */
-function twitter_account_save($twitter_user, $save_auth = FALSE, $account = NULL) {
+function twitter_account_save($twitter_user, $save_auth = FALSE) {
   $values = (array) $twitter_user;
   $values['twitter_uid'] = $values['id'];
-  // bool => int for DB storage
   foreach (array('protected', 'verified', 'profile_background_tile') as $k) {
     if (isset($values[$k])) {
       $values[$k] = (int) $values[$k];
@@ -38,11 +49,6 @@ function twitter_account_save($twitter_user, $save_auth = FALSE, $account = NULL
 
   if ($save_auth) {
     $values += $twitter_user->get_auth();
-    if (empty($account)) {
-      global $user;
-      $account = $user;
-    }
-    $values['uid'] = $account->uid;
   }
   $schema = drupal_get_schema('twitter_account');
   foreach ($values as $k => $v) {
@@ -50,40 +56,97 @@ function twitter_account_save($twitter_user, $save_auth = FALSE, $account = NULL
       unset($values[$k]);
     }
   }
-
   db_merge('twitter_account')
     ->key(array('twitter_uid' => $values['twitter_uid']))
     ->fields($values)
     ->execute();
+
+  // Notify other modules of the twitter account save
+  module_invoke_all('twitter_account_save', $values);
 }
 
 /**
  * Load a Twitter account from {twitter_account}.
  *
- * @param $id
- *   Twitter UID
+ * @param mixed $id
+ *   int Twitter User id or string Twitter user screen name.
  *
  * @return
- *   TwitterUser object
- *
+ *   TwitterUser object or NULL.
  */
 function twitter_account_load($id) {
-  if ( $values = db_query("SELECT * FROM {twitter_account} WHERE twitter_uid = :twitter_uid", array(':twitter_uid' => $id))->fetchAssoc() ) {
+  $values = db_query('SELECT *
+                      FROM {twitter_account}
+                      WHERE twitter_uid = :id_1
+                      OR screen_name  = :id_2',
+                      array(':id_1' => $id, ':id_2' => $id))
+              ->fetchAssoc();
+  if (!empty($values)) {
     $values['id'] = $values['twitter_uid'];
     $account = new TwitterUser($values);
     $account->set_auth($values);
-    $account->uid = $values['uid'];
     $account->import = $values['import'];
+    $account->mentions = $values['mentions'];
     $account->is_global = $values['is_global'];
     return $account;
   }
+  return NULL;
+}
+
+/**
+ * Loads all Twitter accounts added by a user.
+ *
+ * @return
+ *   array of TwitterUser objects.
+ */
+function twitter_account_load_all() {
+  $accounts = array();
+  $result = db_query('SELECT twitter_uid
+                      FROM {twitter_account}
+                      WHERE uid <> 0
+                      ORDER BY screen_name');
+  foreach ($result as $account) {
+    $accounts[] = twitter_account_load($account->twitter_uid);
+  }
+  return $accounts;
+}
+
+/**
+ * Returns a list of authenticated Twitter accounts.
+ *
+ * @return
+ *   array of TwitterUser objects.
+ */
+function twitter_load_authenticated_accounts() {
+  $accounts = twitter_account_load_all();
+  $auth_accounts = array();
+  foreach ($accounts as $index => $account) {
+    if ($account->is_auth()) {
+      $auth_accounts[] = $account;
+    }
+  }
+  return $auth_accounts;
+}
+
+/**
+ * Load a Twitter status.
+ *
+ * @param $status_id
+ *   The status id of this tweet.
+ *
+ * @return
+ *   An instance of stdClass object with the Tweet data or FALSE.
+ */
+function twitter_status_load($status_id) {
+  return db_query("SELECT * FROM {twitter} WHERE twitter_id = :status_id",
+           array(':status_id' => $status_id))->fetchObject();
 }
 
 /**
  * Saves a TwitterStatus object to {twitter}
  */
 function twitter_status_save($status) {
-  $status = array(
+  $row = array(
     'twitter_id' => $status->id,
     'screen_name' => $status->user->screen_name,
     'created_time' => strtotime($status->created_at),
@@ -95,10 +158,10 @@ function twitter_status_save($status) {
     'truncated' => (int) $status->truncated,
   );
   db_merge('twitter')
-    ->key(array('twitter_id' => $status['twitter_id']))
-    ->fields($status)
+    ->key(array('twitter_id' => $row['twitter_id']))
+    ->fields($row)
     ->execute();
-  // Let other modules know that an status has been just saved.
+  // Let other modules know that a status has been saved.
   module_invoke_all('twitter_status_save', $status);
 }
 
@@ -114,39 +177,72 @@ function twitter_status_save($status) {
  */
 function twitter_set_status($twitter_account, $status) {
   $twitter = twitter_connect($twitter_account);
-  return $twitter->status_update($status);
+  return $twitter->statuses_update($status);
 }
 
 /**
- * Fetches a user's timeline
+ * Fetches a user's timeline.
  */
 function twitter_fetch_user_timeline($id) {
   $account = twitter_account_load($id);
-
   $since = db_query("SELECT MAX(twitter_id) FROM {twitter} WHERE screen_name = :screen_name", array(':screen_name' => $account->screen_name))->fetchField();
 
-  $twitter = twitter_connect($account);
-
+  // Connect to the Twitter's API.
+  $twitter = twitter_connect();
   $params = array();
   if ($since) {
     $params['since_id'] = $since;
   }
 
-  $statuses = $twitter->user_timeline($account->id, $params, $account->protected);
+  // Fetch tweets.
+  $statuses = $twitter->user_timeline($id, $params);
   foreach ($statuses as $status) {
     twitter_status_save($status);
   }
 
   if (count($statuses) > 0) {
+    // Update account details.
     twitter_account_save($statuses[0]->user);
   }
+}
 
-  db_update('twitter_account')
-    ->fields(array(
-      'last_refresh' => REQUEST_TIME,
-    ))
-    ->condition('twitter_uid', $account->id)
-    ->execute();
+/**
+ * Fetches user's mentions of an authenticated account.
+ */
+function twitter_fetch_mentions_timeline($id) {
+  $account = twitter_account_load($id);
+  // Connect to Twitter's API using the authenticated account to fetch mentions.
+  $twitter = twitter_connect($account);
+
+  $params = array();
+  $statuses = $twitter->mentions_timeline($params);
+  foreach ($statuses as $status) {
+    if (!twitter_account_load($status->user->id)) {
+      twitter_account_save($status->user);
+    }
+    twitter_status_save($status);
+  }
+}
+
+/**
+ * Pulls tweets from the database.
+ *
+ * @param string $screen_name
+ *   Optionally provide a screen_name to filter.
+ */
+function twitter_tweets($screen_name = NULL) {
+  $query = db_select('twitter', 't')
+    ->fields('t');
+  if (isset($screen_name)) {
+    $query->condition('t.screen_name', $screen_name);
+  }
+  $result = $query->execute();
+
+  $tweets = array();
+  foreach ($result as $row) {
+    $tweets[] = $row;
+  }
+  return $tweets;
 }
 
 /**
diff --git a/kolab.org/www/drupal-7.18/sites/all/modules/twitter/twitter.info b/kolab.org/www/drupal-7.18/sites/all/modules/twitter/twitter.info
index dfeeace..b8bc15c 100644
--- a/kolab.org/www/drupal-7.18/sites/all/modules/twitter/twitter.info
+++ b/kolab.org/www/drupal-7.18/sites/all/modules/twitter/twitter.info
@@ -2,15 +2,17 @@ name = Twitter
 description = Adds integration with the Twitter microblogging service.
 php = 5.1
 core = 7.x
-files[] = twitter.lib.php
 files[] = twitter_views_field_handlers.inc
+files[] = twitter.lib.php
 files[] = tests/core.test
 files[] = tests/input_filters.test
+dependencies[] = oauth_common
+dependencies[] = views
 configure = admin/config/services/twitter
 
-; Information added by drupal.org packaging script on 2012-08-11
-version = "7.x-3.2"
+; Information added by drupal.org packaging script on 2013-06-03
+version = "7.x-5.8"
 core = "7.x"
 project = "twitter"
-datestamp = "1344714197"
+datestamp = "1370303463"
 
diff --git a/kolab.org/www/drupal-7.18/sites/all/modules/twitter/twitter.install b/kolab.org/www/drupal-7.18/sites/all/modules/twitter/twitter.install
index 3892f45..e721596 100644
--- a/kolab.org/www/drupal-7.18/sites/all/modules/twitter/twitter.install
+++ b/kolab.org/www/drupal-7.18/sites/all/modules/twitter/twitter.install
@@ -1,5 +1,4 @@
 <?php
-
 /**
  * @file
  * Install, update and uninstall functions for the twitter module.
@@ -7,6 +6,34 @@
  */
 
 /**
+ * Implements hook_requirements()
+ */
+function twitter_requirements($phase) {
+  $requirements = array();
+  $t = get_t();
+  if ($phase == 'runtime') {
+    // Verify that the Twitter Application keys are set.
+    $requirements['twitter_keys'] = array('title' => $t('Twitter Application keys'));
+    $consumer_key = variable_get('twitter_consumer_key', NULL);
+    $consumer_secret = variable_get('twitter_consumer_secret', NULL);
+    if (empty($consumer_key) || empty($consumer_secret)) {
+      $requirements['twitter_keys']['value'] = $t('Missing');
+      $requirements['twitter_keys']['description'] =
+        $t('In order to interact with Twitter, you need to create an application at ' .
+           '<a href="http://dev.twitter.com" target="_blank">http://dev.twitter.com</a> ' .
+           'and set the generated Application keys at the ' .
+           '<a href="/admin/config/services/twitter">Twitter settings page</a>');
+      $requirements['twitter_keys']['severity'] = REQUIREMENT_ERROR;
+    }
+    else {
+      $requirements['twitter_keys']['value'] = $t('Configured');
+      $requirements['twitter_keys']['severity'] = REQUIREMENT_OK;
+    }
+  }
+  return $requirements;
+}
+
+/**
  * Implements hook_schema().
  */
 function twitter_schema() {
@@ -99,13 +126,6 @@ function twitter_schema() {
         'not null' => TRUE,
         'default' => 0,
       ),
-      'uid' => array(
-        'description' => "The {users}.uid of the owner of this account",
-        'type' => 'int',
-        'unsigned' => TRUE,
-        'size' => 'big',
-        'not null' => TRUE,
-      ),
       'host' => array(
         'description' => 'The host for this account can be a laconi.ca instance',
         'type' => 'varchar',
@@ -260,7 +280,14 @@ function twitter_schema() {
         'type' => 'int',
         'unsigned' => TRUE,
         'not null' => TRUE,
-        'default' => 1,
+        'default' => 0,
+      ),
+      'mentions' => array(
+        'description' => "Boolean flag indicating whether the {twitter_user}'s mentions should be pulled in by the site.",
+        'type' => 'int',
+        'unsigned' => TRUE,
+        'not null' => TRUE,
+        'default' => 0,
       ),
       'last_refresh' => array(
         'description' => "A UNIX timestamp marking the date Twitter statuses were last fetched on.",
@@ -269,7 +296,14 @@ function twitter_schema() {
         'default' => 0,
       ),
       'is_global' => array(
-        'description' => "Boolean flag indicating if this account is available for global use",
+        'description' => "Boolean flag indicating if this account is available for global use.",
+        'type' => 'int',
+        'unsigned' => TRUE,
+        'not null' => TRUE,
+        'default' => 0,
+      ),
+      'uid' => array(
+        'description' => "The uid of the user who added this Twitter account.",
         'type' => 'int',
         'unsigned' => TRUE,
         'not null' => TRUE,
@@ -306,9 +340,7 @@ function twitter_uninstall() {
   variable_del('twitter_expire');
   variable_del('twitter_consumer_key');
   variable_del('twitter_consumer_secret');
-  variable_del('twitter_post_types');
   variable_del('twitter_host');
-  variable_del('twitter_post_default_format');
   variable_del('twitter_signin_button');
   variable_del('twitter_signin_register');
   variable_del('twitter_host');
@@ -318,11 +350,80 @@ function twitter_uninstall() {
 }
 
 /**
- * Implements hook_update_N().
- *
  * Removes password field
  */
 function twitter_update_7300() {
   db_drop_field('twitter_account', 'password');
   return t('Password field was removed from Twitter accounts.');
 }
+
+/**
+ * Removes include_retweets field
+ */
+function twitter_update_7301() {
+  if (db_field_exists('twitter_account', 'include_retweets')) {
+    db_drop_field('twitter_account', 'include_retweets');
+    return t('Include Retweets field was removed from Twitter accounts.');
+  }
+}
+
+/**
+ * Adds field mentions to twitter_account table.
+ */
+function twitter_update_7400() {
+  $data = array(
+    'description' => "Boolean flag indicating whether the {twitter_user}'s mentions should be pulled in by the site.",
+    'type' => 'int',
+    'unsigned' => TRUE,
+    'not null' => TRUE,
+    'default' => 0,
+  );
+  db_add_field('twitter_account', 'mentions', $data);
+}
+
+/**
+ * Drops field uid at twitter_account.
+ *
+ * This update has been set empty afterwards as the field is actually needed.
+ */
+function twitter_update_7401() {}
+
+/**
+ * Makes the import field not enabled by default at twitter_account table.
+ */
+function twitter_update_7402() {
+  $spec = array(
+    'description' => "Boolean flag indicating whether the {twitter_user}'s posts should be pulled in by the site.",
+    'type' => 'int',
+    'unsigned' => TRUE,
+    'not null' => TRUE,
+    'default' => 0,
+  );
+
+  db_change_field('twitter_account', 'import', 'import', $spec);
+}
+
+/**
+ * Adds field added_by_uid to twitter_account table.
+ *
+ * This update has been set empty afterwards as the existing field twitter_account.uid is used.
+ */
+function twitter_update_7403() {}
+
+/**
+ * Renames twitter_account.added_by_uid to twitter_account.uid.
+ *
+ * This reverts update 7403.
+ */
+function twitter_update_7500() {
+  if (db_field_exists('twitter_account', 'added_by_uid')) {
+    $spec = array(
+      'description' => "The uid of the user who added this Twitter account.",
+      'type' => 'int',
+      'unsigned' => TRUE,
+      'not null' => TRUE,
+      'default' => 0,
+    );
+    db_change_field('twitter_account', 'added_by_uid', 'uid', $spec);
+  }
+}
diff --git a/kolab.org/www/drupal-7.18/sites/all/modules/twitter/twitter.lib.php b/kolab.org/www/drupal-7.18/sites/all/modules/twitter/twitter.lib.php
index a852b57..b58a1d7 100644
--- a/kolab.org/www/drupal-7.18/sites/all/modules/twitter/twitter.lib.php
+++ b/kolab.org/www/drupal-7.18/sites/all/modules/twitter/twitter.lib.php
@@ -1,349 +1,1240 @@
 <?php
 /**
  * @file
- * Classes to implement the full Twitter API
+ * Integration layer to communicate with the Twitter REST API 1.1.
+ * https://dev.twitter.com/docs/api/1.1
+ *
+ * Original work my James Walker (@walkah).
+ * Upgraded to 1.1 by Juampy (@juampy72).
  */
 
 /**
  * Exception handling class.
  */
-class TwitterException extends Exception {
+class TwitterException extends Exception {}
+
+/**
+ * Primary Twitter API implementation class
+ */
+class Twitter {
+  /**
+   * @var $source the twitter api 'source'
+   */
+  protected $source = 'drupal';
+
+  protected $signature_method;
+
+  protected $consumer;
+
+  protected $token;
+
+
+  /********************************************//**
+   * Authentication
+   ***********************************************/
+  /**
+   * Constructor for the Twitter class
+   */
+  public function __construct($consumer_key, $consumer_secret, $oauth_token = NULL,
+                              $oauth_token_secret = NULL) {
+    $this->signature_method = new OAuthSignatureMethod_HMAC_SHA1();
+    $this->consumer = new OAuthConsumer($consumer_key, $consumer_secret);
+    if (!empty($oauth_token) && !empty($oauth_token_secret)) {
+      $this->token = new OAuthConsumer($oauth_token, $oauth_token_secret);
+    }
+  }
+
+  public function get_request_token() {
+    $url = variable_get('twitter_api', TWITTER_API) . '/oauth/request_token';
+    try {
+      $params = array('oauth_callback' => url('twitter/oauth', array('absolute' => TRUE)));
+      $response = $this->auth_request($url, $params);
+    }
+    catch (TwitterException $e) {
+      watchdog('twitter', '!message', array('!message' => $e->__toString()), WATCHDOG_ERROR);
+      return FALSE;
+    }
+    parse_str($response, $token);
+    $this->token = new OAuthConsumer($token['oauth_token'], $token['oauth_token_secret']);
+    return $token;
+  }
+
+  public function get_authorize_url($token) {
+    $url = variable_get('twitter_api', TWITTER_API) . '/oauth/authorize';
+    $url.= '?oauth_token=' . $token['oauth_token'];
+
+    return $url;
+  }
+
+  public function get_authenticate_url($token) {
+    $url = variable_get('twitter_api', TWITTER_API) . '/oauth/authenticate';
+    $url.= '?oauth_token=' . $token['oauth_token'];
+
+    return $url;
+  }
+
+  /**
+   * Request an access token to the Twitter API.
+   * @see https://dev.twitter.com/docs/auth/implementing-sign-twitter
+   *
+   * @param string$oauth_verifier
+   *   String an access token to append to the request or NULL.
+   * @return
+   *   String the access token or FALSE when there was an error.
+   */
+  public function get_access_token($oauth_verifier = NULL) {
+    $url = variable_get('twitter_api', TWITTER_API) . '/oauth/access_token';
+
+    // Adding parameter oauth_verifier to auth_request
+    $parameters = array();
+    if (!empty($oauth_verifier)) {
+      $parameters['oauth_verifier'] = $oauth_verifier;
+    }
+
+    try {
+      $response = $this->auth_request($url, $parameters);
+    }
+    catch (TwitterException $e) {
+      watchdog('twitter', '!message', array('!message' => $e->__toString()), WATCHDOG_ERROR);
+      return FALSE;
+    }
+    parse_str($response, $token);
+    $this->token = new OAuthConsumer($token['oauth_token'], $token['oauth_token_secret']);
+    return $token;
+  }
+
+  /**
+   * Performs an authenticated request.
+   */
+  public function auth_request($url, $params = array(), $method = 'GET') {
+    $request = OAuthRequest::from_consumer_and_token($this->consumer, $this->token, $method, $url, $params);
+    $request->sign_request($this->signature_method, $this->consumer, $this->token);
+    switch ($method) {
+      case 'GET':
+        return $this->request($request->to_url());
+      case 'POST':
+        return $this->request($request->get_normalized_http_url(), $request->get_parameters(), 'POST');
+    }
+  }
+
+  /**
+   * Performs a request.
+   *
+   * @throws TwitterException
+   */
+  protected function request($url, $params = array(), $method = 'GET') {
+    $data = '';
+    if (count($params) > 0) {
+      if ($method == 'GET') {
+        $url .= '?'. http_build_query($params, '', '&');
+      }
+      else {
+        $data = http_build_query($params, '', '&');
+      }
+    }
+
+    $headers = array();
+
+    $headers['Authorization'] = 'Oauth';
+    $headers['Content-type'] = 'application/x-www-form-urlencoded';
+
+    $response = $this->doRequest($url, $headers, $method, $data);
+    if (!isset($response->error)) {
+      return $response->data;
+    }
+    else {
+      $error = $response->error;
+      $data = $this->parse_response($response->data);
+      if (isset($data['error'])) {
+        $error = $data['error'];
+      }
+      throw new TwitterException($error);
+    }
+  }
+
+  /**
+   * Actually performs a request.
+   *
+   * This method can be easily overriden through inheritance.
+   *
+   * @param string $url
+   *   The url of the endpoint.
+   * @param array $headers
+   *   Array of headers.
+   * @param string $method
+   *   The HTTP method to use (normally POST or GET).
+   * @param array $data
+   *   An array of parameters
+   * @return
+   *   stdClass response object.
+   */
+  protected function doRequest($url, $headers, $method, $data) {
+    return drupal_http_request($url, array('headers' => $headers, 'method' => $method, 'data' => $data));
+  }
+
+  protected function parse_response($response) {
+    // http://drupal.org/node/985544 - json_decode large integer issue
+    $length = strlen(PHP_INT_MAX);
+    $response = preg_replace('/"(id|in_reply_to_status_id)":(\d{' . $length . ',})/', '"\1":"\2"', $response);
+    return json_decode($response, TRUE);
+  }
+  /**
+   * Creates an API endpoint URL.
+   *
+   * @param string $path
+   *   The path of the endpoint.
+   * @param string $format
+   *   The format of the endpoint to be appended at the end of the path.
+   * @return
+   *   The complete path to the endpoint.
+   */
+  protected function create_url($path, $format = '.json') {
+    $url =  variable_get('twitter_api', TWITTER_API) .'/1.1/'. $path . $format;
+    return $url;
+  }
+
+  /********************************************//**
+   * Helpers used to convert responses in objects
+   ***********************************************/
+  /**
+   * Get an array of TwitterStatus objects from an API endpoint
+   */
+  protected function get_statuses($path, $params = array()) {
+    $values = $this->call($path, $params, 'GET');
+    // Check on successfull call
+    if ($values) {
+      $statuses = array();
+      foreach ($values as $status) {
+        $statuses[] = new TwitterStatus($status);
+      }
+      return $statuses;
+    }
+    // Call might return FALSE , e.g. on failed authentication
+    else {
+      // As call allready throws an exception, we can return an empty array to
+      // break no code.
+      return array();
+    }
+  }
+
+  /**
+   * Get an array of TwitterUser objects from an API endpoint
+   */
+  protected function get_users($path, $params = array()) {
+    $values = $this->call($path, $params, 'GET');
+    // Check on successfull call
+    if ($values) {
+      $users = array();
+      foreach ($values as $user) {
+        $users[] = new TwitterUser($user);
+      }
+      return $users;
+    }
+    // Call might return FALSE , e.g. on failed authentication
+    else {
+      // As call allready throws an exception, we can return an empty array to
+      // break no code.
+      return array();
+    }
+  }
+
+  /********************************************//**
+   * Timelines
+   ***********************************************/
+  /**
+   * Returns the 20 most recent mentions (tweets containing a users's @screen_name).
+   *
+   * @param array $params
+   *   an array of parameters.
+   *
+   * @see https://dev.twitter.com/docs/api/1.1/get/statuses/mentions_timeline
+   */
+  public function mentions_timeline($params = array()) {
+    return $this->get_statuses('statuses/mentions_timeline', $params);
+  }
+
+  /**
+   * Fetch a user's timeline
+   *
+   * Returns a collection of the most recent Tweets posted by the user indicated
+   * by the screen_name or user_id parameters.
+   *
+   * @param mixed $id
+   *   either a Twitter user_id or a Twitter screen_name.
+   *
+   * @param array $params
+   *   an array of parameters.
+   *
+   * @see https://dev.twitter.com/docs/api/1.1/get/statuses/user_timeline
+   */
+  public function user_timeline($id, $params = array()) {
+    if (is_numeric($id)) {
+      $params['user_id'] = $id;
+    }
+    else {
+      $params['screen_name'] = $id;
+    }
+    return $this->get_statuses('statuses/user_timeline', $params);
+  }
+
+  /**
+   * Returns a collection of the most recent Tweets and retweets posted by
+   * the authenticating user and the users they follow.
+   *
+   * @param array $params
+   *   an array of parameters.
+   *
+   * @see https://dev.twitter.com/docs/api/1.1/get/statuses/home_timeline
+   */
+  public function home_timeline($params = array()) {
+    return $this->get_statuses('statuses/home_timeline', $params);
+  }
+
+  /**
+   * Returns the most recent tweets authored by the authenticating user
+   * that have recently been retweeted by others.
+   *
+   * @param array $params
+   *   an array of parameters.
+   *
+   * @see https://dev.twitter.com/docs/api/1.1/get/statuses/retweets_of_me
+   */
+  public function retweets_of_me($params = array()) {
+    return $this->get_statuses('statuses/retweets_of_me', $params);
+  }
+
+  /********************************************//**
+   * Tweets
+   ***********************************************/
+  /**
+   * Returns up to 100 of the first retweets of a given tweet.
+   *
+   * @param int $id
+   *   The numerical ID of the desired status.
+   * @param array $params
+   *   an array of parameters.
+   *
+   * @see https://dev.twitter.com/docs/api/1.1/get/statuses/retweets
+   */
+  public function statuses_retweets($id, $params = array()) {
+    return $this->get_statuses('statuses/retweets/' . $id, $params);
+  }
+
+  /**
+   * Destroys the status specified by the required ID parameter.
+   *
+   * @param array $params
+   *   an array of parameters.
+   *
+   * @return
+   *   TwitterStatus object if successful or FALSE.
+   * @see https://dev.twitter.com/docs/api/1.1/get/statuses/destroy
+   */
+  public function statuses_destroy($id, $params = array()) {
+    $values = $this->call('statuses/update', $params, 'POST');
+    if ($values) {
+      return new TwitterStatus($values);
+    }
+    else {
+      return FALSE;
+    }
+  }
+
+  /**
+   * Updates the authenticating user's current status, also known as tweeting.
+   *
+   * @param string $status
+   *   The text of the status update (the tweet).
+   * @param array $params
+   *   an array of parameters.
+   *
+   * @see https://dev.twitter.com/docs/api/1.1/post/statuses/update
+   */
+  public function statuses_update($status, $params = array()) {
+    $params['status'] = $status;
+    $values = $this->call('statuses/update', $params, 'POST');
+    return new TwitterStatus($values);
+  }
+
+  /**
+   * Retweets a tweet. Returns the original tweet with retweet details embedded.
+   *
+   * @param int $id
+   *   The numerical ID of the desired status.
+   * @param array $params
+   *   an array of parameters.
+   *
+   * @see https://dev.twitter.com/docs/api/1.1/post/statuses/retweet/%3Aid
+   */
+  public function statuses_retweet($id, $params = array()) {
+    $values = $this->call('statuses/retweet/' . $id, $params, 'POST');
+    return new TwitterStatus($values);
+  }
+
+  /**
+   * Creates a Tweet with a picture attached.
+   *
+   * @param string $status
+   *   The text of the status update (the tweet).
+   * @param array $media
+   *   An array of physical paths of images.
+   * @param array $params
+   *   an array of parameters.
+   *
+   * @see https://dev.twitter.com/docs/api/1.1/post/statuses/update_with_media
+   */
+  public function statuses_update_with_media($status, $media, $params = array()) {
+    $params['status'] = $status;
+    $params['media[]'] = '@{' . implode(',', $media) . '}';
+    $values = $this->call('statuses/statuses/update_with_media', $params, 'POST');
+    // @TODO support media at TwitterStatus class.
+    return new TwitterStatus($values);
+  }
+
+  /**
+   * Returns information allowing the creation of an embedded representation of
+   * a Tweet on third party sites.
+   *
+   * @param mixed $id
+   *   The Tweet/status ID or the URL of the Tweet/status to be embedded.
+   * @param array $params
+   *   an array of parameters.
+   *
+   * @see https://dev.twitter.com/docs/api/1.1/get/statuses/oembed
+   */
+  public function statuses_oembed($id, $params = array()) {
+    if (is_numeric($id)) {
+      $params['id'] = $id;
+    }
+    else {
+      $params['url'] = $id;
+    }
+    return $this->call('statuses/oembed', $params, 'GET');
+  }
+
+  /********************************************//**
+   * Search
+   ***********************************************/
+  /**
+   * Returns a collection of relevant Tweets matching a specified query.
+   *
+   * @param string $query
+   *   A UTF-8, URL-encoded search query of 1,000 characters maximum,
+   *   including operators.
+   * @param array $params
+   *   an array of parameters.
+   * @return
+   *   array of Twitter statuses.
+   *
+   * @see https://dev.twitter.com/docs/api/1.1/get/search/tweets
+   */
+  public function search_tweets($query, $params = array()) {
+    $params['q'] = $query;
+    return $this->get_statuses('statuses/oembed', $params);
+  }
+
+  /********************************************//**
+   * Streaming
+   ***********************************************/
+  /**
+   * Returns public statuses that match one or more filter predicates.
+   *
+   * At least one predicate parameter (follow, locations, or track) must be specified.
+   *
+   * @param string $follow
+   *   A comma separated list of user IDs.
+   * @param string $track
+   *   Keywords to track.
+   * @param string $locations
+   *   Specifies a set of bounding boxes to track.
+   * @param array $params
+   *   an array of parameters.
+   * @return
+   *   array of Twitter statuses.
+   *
+   * @see https://dev.twitter.com/docs/api/1.1/post/statuses/filter
+   */
+  public function statuses_filter($follow = '', $track = '', $locations = '', $params = array()) {
+    if (!empty($follow)) {
+      $params['follow'] = $follow;
+    }
+    if (!empty($track)) {
+      $params['track'] = $track;
+    }
+    if (!empty($locations)) {
+      $params['locations'] = $locations;
+    }
+    return $this->call('statuses/filter', $params, 'POST');
+  }
+
+  /**
+   * Returns a small random sample of all public statuses.
+   *
+   * @param array $params
+   *   an array of parameters.
+   * @return
+   *   array of Twitter statuses.
+   *
+   * @see https://dev.twitter.com/docs/api/1.1/get/statuses/sample
+   */
+  public function statuses_sample($params = array()) {
+    return $this->get_statuses('statuses/sample', $params);
+  }
+
+  /**
+   * Returns all public statuses. Few applications require this level of access.
+   *
+   * @param array $params
+   *   an array of parameters.
+   * @return
+   *   array of Twitter statuses.
+   *
+   * @see https://dev.twitter.com/docs/api/1.1/get/statuses/firehose
+   */
+  public function statuses_firehose($params = array()) {
+    return $this->get_statuses('statuses/firehose', $params);
+  }
+
+  /**
+   * Streams messages for a single user.
+   *
+   * @param array $params
+   *   an array of parameters.
+   * @return
+   *   array of Twitter statuses.
+   *
+   * @see https://dev.twitter.com/docs/api/1.1/get/user
+   */
+  public function user($params = array()) {
+    return $this->get_statuses('user', $params);
+  }
+
+  /**
+   * Streams messages for a set of users.
+   *
+   * @param string $follow
+   *   A comma separated list of user IDs
+   * @param array $params
+   *   an array of parameters.
+   * @return
+   *   array of Twitter statuses.
+   *
+   * @see https://dev.twitter.com/docs/api/1.1/get/site
+   */
+  public function site($follow, $params = array()) {
+    $params['follow'] = $follow;
+    return $this->get_statuses('site', $params);
+  }
+
+  /********************************************//**
+   * Direct Messages
+   ***********************************************/
+  /**
+   * Returns the 20 most recent direct messages sent to the authenticating user.
+   *
+   * This method requires an access token with RWD (read, write & direct message)
+   * permissions
+   *
+   * @param array $params
+   *   an array of parameters.
+   * @return
+   *   array of Twitter statuses.
+   * @see https://dev.twitter.com/docs/api/1.1/get/direct_messages
+   */
+  public function direct_messages($params = array()) {
+    return $this->get_statuses('direct_messages', $params);
+  }
+
+  /**
+   * Returns the 20 most recent direct messages sent by the authenticating user.
+   *
+   * This method requires an access token with RWD (read, write & direct message)
+   * permissions
+   *
+   * @param array $params
+   *   An array of parameters.
+   * @return
+   *   Array of Twitter statuses.
+   * @see https://dev.twitter.com/docs/api/1.1/get/direct_messages/sent
+   */
+  public function direct_messages_sent($params = array()) {
+    return $this->get_statuses('direct_messages/sent', $params);
+  }
+
+  /**
+   * Returns a single direct message, specified by an id parameter.
+   *
+   * This method requires an access token with RWD (read, write & direct message)
+   * permissions
+   *
+   * @param int $id
+   *   The ID of the direct message.
+   * @return
+   *   array of Twitter statuses.
+   * @see https://dev.twitter.com/docs/api/1.1/get/direct_messages/show
+   */
+  public function direct_messages_show($id) {
+    $params = array('id' => $id);
+    return $this->get_statuses('direct_messages/show', $params);
+  }
+
+  /**
+   * Destroys the direct message specified in the required ID parameter.
+   *
+   * This method requires an access token with RWD (read, write & direct message)
+   * permissions
+   *
+   * @param int $id
+   *   The ID of the direct message.
+   * @param array $params
+   *   An array of parameters.
+   * @return
+   *   The deleted direct message
+   * @see https://dev.twitter.com/docs/api/1.1/post/direct_messages/destroy
+   */
+  public function direct_messages_destroy($id, $params = array()) {
+    $params['id'] = $id;
+    return $this->get_statuses('direct_messages/destroy', $params);
+  }
+
+  /**
+   * Sends a new direct message to the specified user from the authenticating user.
+   *
+   * @param mixed $id
+   *   The user ID or the screen name.
+   * @param string $text
+   *   The URL encoded text of the message.
+   * @return
+   *   array of Twitter statuses.
+   *
+   * @see https://dev.twitter.com/docs/api/1.1/post/direct_messages/new
+   */
+  public function direct_messages_new($id, $params = array()) {
+    if (is_numeric($id)) {
+      $params['user_id'] = $id;
+    }
+    else {
+      $params['screen_name'] = $id;
+    }
+    return $this->call('direct_messages/new', $params, 'POST');
+  }
+
+  /********************************************//**
+   * Friends & Followers
+   ***********************************************/
+  /**
+   * Returns a cursored collection of user IDs for every user the specified user
+   * is following.
+   *
+   * @param mixed $id
+   *   The user ID or the screen name.
+   * @return
+   *   An array of user IDS.
+   *
+   * @see https://dev.twitter.com/docs/api/1.1/get/friends/ids
+   */
+  public function friends_ids($id, $params = array()) {
+    if (is_numeric($id)) {
+      $params['user_id'] = $id;
+    }
+    else {
+      $params['screen_name'] = $id;
+    }
+    return $this->call('friends/ids', $params, 'GET');
+  }
+
+  /**
+   * Returns a cursored collection of user IDs for every user following the
+   * specified user.
+   *
+   * @param mixed $id
+   *   The user ID or the screen name.
+   * @return
+   *   An array of user IDS.
+   *
+   * @see https://dev.twitter.com/docs/api/1.1/get/followers/ids
+   */
+  public function followers_ids($id, $params = array()) {
+    if (is_numeric($id)) {
+      $params['user_id'] = $id;
+    }
+    else {
+      $params['screen_name'] = $id;
+    }
+    return $this->call('followers/ids', $params, 'GET');
+  }
+
+  /**
+   * Returns the relationships of the authenticating user to the
+   * comma-separated list of up to 100 screen_names or user_ids provided.
+   *
+   * @param string $screen_name
+   *   A comma separated list of screen names.
+   * @param string $user_id
+   *   A comma separated list of user IDs.
+   * @return
+   *   An array of user IDs and relationships.
+   *
+   * @see https://dev.twitter.com/docs/api/1.1/get/friendships/lookup
+   */
+  public function friendships_lookup($screen_name = '', $user_id = '') {
+    if (!empty($screen_name)) {
+      $params['screen_name'] = $screen_name;
+    }
+    if (!empty($user_id)) {
+      $params['user_id'] = $user_id;
+    }
+    return $this->call('friendships/lookup', $params, 'GET');
+  }
+
+  /**
+   * Returns a collection of numeric IDs for every user who has a pending
+   * request to follow the authenticating user.
+   *
+   * @param array $params
+   *   An array of parameters.
+   * @return
+   *   An array of numeric user IDs.
+   *
+   * @see https://dev.twitter.com/docs/api/1.1/get/friendships/incoming
+   */
+  public function friendships_incoming($params = array()) {
+    return $this->call('friendships/incoming', $params, 'GET');
+  }
+
+  /**
+   * Returns a collection of numeric IDs for every protected user for whom
+   * the authenticating user has a pending follow request.
+   *
+   * @param array $params
+   *   An array of parameters.
+   * @return
+   *   An array of numeric user IDs.
+   *
+   * @see https://dev.twitter.com/docs/api/1.1/get/friendships/outgoing
+   */
+  public function friendships_outgoing($params = array()) {
+    return $this->call('friendships/outgoing', $params, 'GET');
+  }
+
+  /**
+   * Allows the authenticating users to follow the user specified in the
+   * ID parameter.
+   *
+   * @param mixed $id
+   *   The user ID or the screen name.
+   * @param bool $follow
+   *   Wether to enable notifications for the target user.
+   * @return
+   *   The befriended user in the requested format when successful, or a
+   *   string describing the failure condition when unsuccessful.
+   *
+   * @see https://dev.twitter.com/docs/api/1.1/post/friendships/create
+   */
+  public function friendships_create($id, $follow = NULL) {
+    if (is_numeric($id)) {
+      $params['user_id'] = $id;
+    }
+    else {
+      $params['screen_name'] = $id;
+    }
+    if ($follow !== NULL) {
+      $params['follow'] = $id;
+    }
+    return $this->call('friendships/create', $params, 'POST');
+  }
+
+  /**
+   * Allows the authenticating user to unfollow the user specified in the
+   * ID parameter.
+   *
+   * @param mixed $id
+   *   The user ID or the screen name.
+   * @return
+   *   The unfollowed user in the requested format when successful, or a
+   *   string describing the failure condition when unsuccessful.
+   *
+   * @see https://dev.twitter.com/docs/api/1.1/post/friendships/destroy
+   */
+  public function friendships_destroy($id) {
+    $params = array();
+    if (is_numeric($id)) {
+      $params['user_id'] = $id;
+    }
+    else {
+      $params['screen_name'] = $id;
+    }
+    return $this->call('friendships/destroy', $params, 'POST');
+  }
+
   /**
-   * Overrides constructor to log the error.
+   * Allows one to enable or disable retweets and device notifications
+   * from the specified user.
+   *
+   * @param mixed $id
+   *   The user ID or the screen name.
+   * @param bool $device
+   *   Whether to enable/disable device notifications from the target user.
+   * @param bool $retweets
+   *   Whether to enable/disable retweets from the target user.
+   *
+   * @see https://dev.twitter.com/docs/api/1.1/post/friendships/update
    */
-  public function __construct($message = NULL, $code = 0, Exception $previous = NULL) {
-    watchdog('twitter', 'Unexpected error: @message', array(
-      '@message' => $message,
-    ), WATCHDOG_ERROR);
-    parent::__construct($message, $code, $previous);
+  public function friendships_update($id, $device = NULL, $retweets = NULL) {
+    $params = array();
+    if (is_numeric($id)) {
+      $params['user_id'] = $id;
+    }
+    else {
+      $params['screen_name'] = $id;
+    }
+    if ($device !== NULL) {
+      $params['device'] = $device;
+    }
+    if ($retweets!== NULL) {
+      $params['retweets'] = $retweets;
+    }
+    return $this->call('friendships/update', $params, 'POST');
   }
-}
-/**
- * Primary Twitter API implementation class
- * Supports the full REST API for twitter.
- */
-class Twitter {
 
   /**
-   * @var $format API format to use: can be json or xml
+   * Returns detailed information about the relationship between two arbitrary
+   * users.
+   *
+   * @param mixed $source_id
+   *   The user ID or the screen name of the subject user.
+   * @param mixed $target_id
+   *   The user ID or the screen name of the target user.
+   * @return
+   *   An array of numeric user IDs.
+   *
+   * @see https://dev.twitter.com/docs/api/1.1/get/friendships/show
    */
-  protected $format = 'json';
+  public function friendships_show($source_id, $target_id) {
+    if (is_numeric($source_id)) {
+      $params['source_id'] = $source_id;
+    }
+    else {
+      $params['source_screen_name'] = $source_id;
+    }
+    if (is_numeric($target_id)) {
+      $params['target_id'] = $target_id;
+    }
+    else {
+      $params['target_screen_name'] = $target_id;
+    }
+    return $this->call('friendships/show', $params, 'GET');
+  }
 
+  /********************************************//**
+   * Users
+   ***********************************************/
   /**
-   * @var $source the twitter api 'source'
+   * Returns settings (including current trend, geo and sleep time
+   * information) for the authenticating user.
+   *
+   * @return
+   *   An array of settings.
+   *
+   * @see https://dev.twitter.com/docs/api/1.1/get/account/settings
    */
-  protected $source = 'drupal';
+  public function account_settings() {
+    return $this->call('account/settings', $params, 'GET');
+  }
 
   /**
-   * @var $username Twitter username to use for authenticated requests
+   * Returns an HTTP 200 OK response code and a representation of the
+   * requesting user if authentication was successful; returns a 401
+   * status code and an error message if not.
+   *
+   * @param array $params
+   *   An array of parameters.
+   * @return
+   *   A TwitterUser object or FALSE.
+   * @see https://dev.twitter.com/docs/api/1.1/get/account/verify_credentials
    */
-  protected $username;
+  public function verify_credentials($params = array()) {
+    $values = $this->call('account/verify_credentials', $params, 'GET');
+    if (!$values) {
+      return FALSE;
+    }
+    return new TwitterUser($values);
+  }
 
   /**
-   * @var $password Twitter password to use for authenticated requests
+   * Updates the authenticating user's settings.
+   *
+   * @param array $params
+   *   An array of parameters.
+   *
+   * @see https://dev.twitter.com/docs/api/1.1/post/account/settings
    */
-  protected $password;
+  public function account_settings_update($params = array()) {
+    return $this->call('account/settings', $params, 'POST');
+  }
 
   /**
-   * Constructor for the Twitter class
+   * Sets which device Twitter delivers updates to for the authenticating user.
+   *
+   * @param string $device
+   *   A string which must be one of: sms, none.
+   * @param bool $include_entities
+   *   Whether tweets should include entities or not.
+   *
+   * @see https://dev.twitter.com/docs/api/1.1/post/account/update_delivery_device
    */
-  public function __construct($username = NULL, $password = NULL) {
-    if (!empty($username) && !empty($password)) {
-      $this->set_auth($username, $password);
+  public function account_update_delivery_device($device, $include_entities = NULL) {
+    $params = array('device' => $device);
+    if ($include_entities !== NULL) {
+      $params['include_entities'] = $include_entities;
     }
+    return $this->call('account/settings', $params, 'POST');
   }
 
   /**
-   * Set the username and password
+   * Sets values that users are able to set under the "Account" tab of their
+   * settings page.
+   *
+   * @param array $params
+   *   An array of parameters.
+   *
+   * @see https://dev.twitter.com/docs/api/1.1/post/account/update_profile
    */
-  public function set_auth($username, $password) {
-    $this->username = $username;
-    $this->password = $password;
+  public function account_update_profile($params = array()) {
+    return $this->call('account/update_profile', $params, 'POST');
   }
 
   /**
-   * Get an array of TwitterStatus objects from an API endpoint
+   * Updates the authenticating user's profile background image.
+   *
+   * This method can also be used to enable or disable the profile
+   * background image.
+   * At least one of image, tile or use must be provided when making this
+   * request.
+   *
+   * @param string $image
+   *   A base64-encoded. Must be a valid GIF, JPG, or PNG image of less
+   *   than 800 kilobytes in size.
+   * @param bool $tile
+   *   Whether or not to tile the background image.
+   * @param bool $use
+   *   Whether or not to use the background image.
+   * @param array $params
+   *   An array of parameters.
+   *
+   * @see https://dev.twitter.com/docs/api/1.1/post/account/update_profile_background_image
    */
-  protected function get_statuses($path, $params = array(), $use_auth = FALSE) {
-    $values = $this->call($path, $params, 'GET', $use_auth);
-    // Check on successfull call
-    if ($values) {
-      $statuses = array();
-      foreach ($values as $status) {
-        $statuses[] = new TwitterStatus($status);
-      }
-      return $statuses;
+  public function account_update_profile_background_image($image = NULL, $tile = NULL,
+                                                          $use = NULL, $params = array()) {
+    if ($image !== NULL) {
+      $params['image'] = $image;
     }
-    // Call might return FALSE , e.g. on failed authentication
-    else {
-      // As call allready throws an exception, we can return an empty array to
-      // break no code.
-      return array();
+    if ($tile !== NULL) {
+      $params['tile'] = $tile;
+    }
+    if ($use !== NULL) {
+      $params['use'] = $use;
     }
+    return $this->call('account/update_profile_background_image', $params, 'POST');
   }
 
   /**
-   * Fetch a user's timeline
+   * Sets one or more hex values that control the color scheme of the
+   * authenticating user's profile page on twitter.com.
+   *
+   * @param array $params
+   *   An array of parameters.
    *
-   * @see http://apiwiki.twitter.com/Twitter-REST-API-Method%3A-statuses-user_timeline
+   * @see https://dev.twitter.com/docs/api/1.1/post/account/update_profile_colors
    */
-  public function user_timeline($id, $params = array(), $use_auth = FALSE) {
-    if (is_numeric($id)) {
-      $params['user_id'] = $id;
-    }
-    else {
-      $params['screen_name'] = $id;
-    }
-    return $this->get_statuses('statuses/user_timeline', $params, $use_auth);
+  public function account_update_profile_colors($params = array()) {
+    return $this->call('account_update_profile_colors', $params, 'POST');
   }
 
   /**
+   * Updates the authenticating user's profile image.
    *
-   * @see http://apiwiki.twitter.com/Twitter-REST-API-Method%3A-statuses-mentions
+   * @param string $image
+   *   The avatar image for the profile, base64-encoded. Must be a valid
+   *   GIF, JPG, or PNG
+   * @param array $params
+   *   An array of parameters.
+   *
+   * @see https://dev.twitter.com/docs/api/1.1/post/account/update_profile_image
    */
-  public function mentions($params = array()) {
-    return $this->get_statuses('statuses/mentions', $params, TRUE);
+  public function account_update_profile_image($image, $params = array()) {
+    $params['image'] = $image;
+    return $this->call('account_update_profile_image', $params, 'POST');
   }
 
   /**
+   * Returns a collection of user objects that the authenticating user is
+   * blocking.
    *
-   * @see http://apiwiki.twitter.com/Twitter-REST-API-Method%3A-statuses%C2%A0update
+   * @param array $params
+   *   An array of parameters.
+   * @return
+   *   A TwitterUser object or FALSE.
+   * @see https://dev.twitter.com/docs/api/1.1/get/blocks/list
    */
-  public function status_update($status, $params = array()) {
-    $params['status'] = $status;
-    if ($this->source) {
-      $params['source'] = $this->source;
+  public function blocks_list($params = array()) {
+    $values = $this->call('blocks/list', $params, 'GET');
+    if (!$values) {
+      return FALSE;
     }
-    $values = $this->call('statuses/update', $params, 'POST', TRUE);
-    return new TwitterStatus($values);
+    return new TwitterUser($values);
   }
 
   /**
-   * Returns profile information about a user.
-   * @see http://apiwiki.twitter.com/Twitter-REST-API-Method%3A-users%C2%A0show
+   * Returns an array of numeric user ids the authenticating user is blocking.
+   *
+   * @param array $params
+   *   An array of parameters.
+   * @return
+   *   A TwitterUser object or FALSE.
+   * @see https://dev.twitter.com/docs/api/1.1/get/blocks/ids
    */
-  public function users_show($id, $use_auth = TRUE) {
-    $params = array();
+  public function blocks_ids($params = array()) {
+    return $this->call('blocks/ids', $params, 'GET');
+  }
+
+  /**
+   * Blocks the specified user from following the authenticating user.
+   *
+   * @param mixed $id
+   *   The numeric id or screen name of a Twitter user.
+   * @param array $params
+   *   An array of parameters.
+   * @see https://dev.twitter.com/docs/api/1.1/post/blocks/create
+   */
+  public function blocks_create($id, $params = array()) {
     if (is_numeric($id)) {
       $params['user_id'] = $id;
     }
     else {
       $params['screen_name'] = $id;
     }
-
-    $values = $this->call('users/show', $params, 'GET', $use_auth);
-    return new TwitterUser($values);
+    $params['image'] = $image;
+    return $this->call('blocks/create', $params, 'POST');
   }
 
   /**
+   * Un-blocks the user specified in the ID parameter for the authenticating
+   * user.
    *
-   * @see http://apiwiki.twitter.com/Twitter-REST-API-Method%3A-account%C2%A0verify_credentials
+   * @param mixed $id
+   *   The numeric id or screen name of a Twitter user.
+   * @param array $params
+   *   An array of parameters.
+   * @see https://dev.twitter.com/docs/api/1.1/post/blocks/destroy
    */
-  public function verify_credentials() {
-    $values = $this->call('account/verify_credentials', array(), 'GET', TRUE);
-    if (!$values) {
-      return FALSE;
+  public function blocks_destroy($id, $params = array()) {
+    if (is_numeric($id)) {
+      $params['user_id'] = $id;
     }
-    return new TwitterUser($values);
+    else {
+      $params['screen_name'] = $id;
+    }
+    $params['image'] = $image;
+    return $this->call('blocks/destroy', $params, 'POST');
   }
 
-
   /**
-   * Method for calling any twitter api resource
+   * Returns fully-hydrated user objects for up to 100 users per request,
+   * as specified by comma-separated values passed to the user_id and/or
+   * screen_name parameters.
+   *
+   * @param string $screen_name
+   *   A comma separated list of screen names.
+   * @param string user_id
+   *   A comma separated list of user IDs.
+   * @param bool $include_entities
+   *   Whether to include entities or not.
+   * @see https://dev.twitter.com/docs/api/1.1/get/users/lookup
    */
-  public function call($path, $params = array(), $method = 'GET', $use_auth = FALSE) {
-    $url = $this->create_url($path);
-
-    if ($use_auth) {
-      $response = $this->auth_request($url, $params, $method);
+  protected function users_lookup($screen_name = NULL, $user_id = NULL,
+                                  $include_entities = NULL) {
+    if ($screen_name !== NULL) {
+      $params['screen_name'] = $screen_name;
     }
-    else {
-      $response = $this->request($url, $params, $method);
+    if ($user_id !== NULL) {
+      $params['user_id'] = $user_id;
     }
-
-    if (!$response) {
-      return FALSE;
+    if ($include_entities !== NULL) {
+      $params['include_entities'] = $include_entities;
     }
-
-    return $this->parse_response($response);
+    return $this->get_users('users/lookup', $params);
   }
 
   /**
-   * Perform an authentication required request.
+   * Returns a variety of information about the user specified by the
+   * required user_id or screen_name parameter.
+   *
+   * @param mixed $id
+   *   The numeric id or screen name of a Twitter user.
+   * @param bool $include_entities
+   *   Whether to include entities or not.
+   * @see https://dev.twitter.com/docs/api/1.1/get/users/show
    */
-  protected function auth_request($path, $params = array(), $method = 'GET') {
-    if (empty($this->username) || empty($this->password)) {
-      return false;
+  public function users_show($id, $include_entities = NULL) {
+    $params = array();
+    if (is_numeric($id)) {
+      $params['user_id'] = $id;
     }
-
-    return $this->request($path, $params, $method, TRUE);
+    else {
+      $params['screen_name'] = $id;
+    }
+    if ($include_entities !== NULL) {
+      $params['include_entities'] = $include_entities;
+    }
+    $values = $this->call('users/show', $params, 'GET');
+    return new TwitterUser($values);
   }
 
   /**
-   * Perform a request
+   * Provides a simple, relevance-based search interface to public user
+   * accounts on Twitter.
    *
-   * @throws TwitterException
+   * @param string $query
+   *   The search query to run against people search.
+   * @param array $params
+   *   an array of parameters.
+   * @return
+   *   array of TwitterUser objects.
+   *
+   * @see https://dev.twitter.com/docs/api/1.1/get/users/search
    */
-  protected function request($url, $params = array(), $method = 'GET', $use_auth = FALSE) {
-    $data = '';
-    if (count($params) > 0) {
-      if ($method == 'GET') {
-        $url .= '?'. http_build_query($params, '', '&');
-      }
-      else {
-        $data = http_build_query($params, '', '&');
-      }
-    }
-
-    $headers = array();
-
-    if ($use_auth) {
-      $headers['Authorization'] = 'Basic '. base64_encode($this->username .':'. $this->password);
-      $headers['Content-type'] = 'application/x-www-form-urlencoded';
-    }
+  public function users_search($query, $params = array()) {
+    $params['q'] = $query;
+    return $this->get_users('users/search', $params);
+  }
 
-    $response = drupal_http_request($url, array('headers' => $headers, 'method' => $method, 'data' => $data));
-    if (!isset($response->error)) {
-      return $response->data;
+  /**
+   * Returns a collection of users that the specified user can "contribute" to.
+   *
+   * @param mixed $id
+   *   The numeric id or screen name of a Twitter user.
+   * @param array $params
+   *   an array of parameters.
+   * @see https://dev.twitter.com/docs/api/1.1/get/users/contributees
+   */
+  public function users_contributees($id, $params = array()) {
+    if (is_numeric($id)) {
+      $params['user_id'] = $id;
     }
     else {
-      $error = $response->error;
-      // Check if Twitter returned an error in the response data.
-      if (isset($response->data)) {
-        $data = $this->parse_response($response->data);
-        if (isset($data['errors'])) {
-          $error = $data['errors'][0]['message'];
-        }
-        elseif (isset($data['error'])) {
-          $error = $data['error'];
-        }
-      }
-      throw new TwitterException($error);
+      $params['screen_name'] = $id;
     }
+    return $this->get_users('users/contributees', $params);
   }
 
-  protected function parse_response($response, $format = NULL) {
-    if (empty($format)) {
-      $format = $this->format;
+  /**
+   * Returns a collection of users who can contribute to the specified account.
+   *
+   * @param mixed $id
+   *   The numeric id or screen name of a Twitter user.
+   * @param array $params
+   *   an array of parameters.
+   * @see https://dev.twitter.com/docs/api/1.1/get/users/contributors
+   */
+  public function users_contributors($id, $params = array()) {
+    if (is_numeric($id)) {
+      $params['user_id'] = $id;
     }
-
-    switch ($format) {
-      case 'json':
-        // http://drupal.org/node/985544 - json_decode large integer issue
-        $length = strlen(PHP_INT_MAX);
-        $response = preg_replace('/"(id|in_reply_to_status_id)":(\d{' . $length . ',})/', '"\1":"\2"', $response);
-        return json_decode($response, TRUE);
+    else {
+      $params['screen_name'] = $id;
     }
+    return $this->get_users('users/contributors', $params);
   }
 
-  protected function create_url($path, $format = NULL) {
-    if (is_null($format)) {
-      $format = $this->format;
-    }
-    $url =  variable_get('twitter_api', TWITTER_API) .'/1/'. $path;
-    if (!empty($format)) {
-      $url .= '.'. $this->format;
-    }
-    return $url;
+  /**
+   * Removes the uploaded profile banner for the authenticating user.
+   *
+   * @see https://dev.twitter.com/docs/api/1.1/post/account/remove_profile_banner
+   */
+  public function account_remove_profile_banner() {
+    return $this->call('account/remove_profile_banner', array(), 'POST');
   }
-}
-
-/**
- * A class to provide OAuth enabled access to the twitter API
- */
-class TwitterOAuth extends Twitter {
-
-  protected $signature_method;
-
-  protected $consumer;
-
-  protected $token;
 
-  public function __construct($consumer_key, $consumer_secret, $oauth_token = NULL, $oauth_token_secret = NULL) {
-    $this->signature_method = new OAuthSignatureMethod_HMAC_SHA1();
-    $this->consumer = new OAuthConsumer($consumer_key, $consumer_secret);
-    if (!empty($oauth_token) && !empty($oauth_token_secret)) {
-      $this->token = new OAuthConsumer($oauth_token, $oauth_token_secret);
-    }
+  /**
+   * Uploads a profile banner on behalf of the authenticating user.
+   *
+   * @param string $banner
+   *   The Base64-encoded or raw image data being uploaded as the user's new
+   *   profile banner.
+   * @param array $params
+   *   An array of parameters.
+   * @see https://dev.twitter.com/docs/api/1.1/post/account/update_profile_banner
+   */
+  public function account_update_profile_banner($banner, $params = array()) {
+    $params['banner'] = $banner;
+    return $this->call('account/update_profile_banner', $params, 'POST');
   }
 
   /**
-   * Builds a full URL to perform an OAuth operation
+   * Returns a map of the available size variations of the specified user's
+   * profile banner.
    *
-   * @param $path
-   *   string the path of the operation
-   * @param $format
-   *   string a valid format
-   * @return
-   *   string full URL
+   * @param mixed $id
+   *   The numeric id or screen name of a Twitter user.
+   * @see https://dev.twitter.com/docs/api/1.1/get/users/profile_banner
    */
-  protected function create_url($path, $format = NULL) {
-    if (is_null($format)) {
-      $format = $this->format;
+  public function account_profile_banner($id) {
+    if (is_numeric($id)) {
+      $params['user_id'] = $id;
     }
-    $url =  variable_get('twitter_api', TWITTER_API) .'/'. $path;
-    if (!empty($format)) {
-      $url .= '.'. $this->format;
+    else {
+      $params['screen_name'] = $id;
     }
-    return $url;
+    return $this->call('account/profile_banner', $params, 'GET');
   }
 
-  public function get_request_token() {
-    $url = $this->create_url('oauth/request_token', '');
-    try {
-      $response = $this->auth_request($url);
+  /********************************************//**
+   * Favorites
+   ***********************************************/
+  /**
+   * Returns the 20 most recent favorited tweets for a user.
+   *
+   * @param mixed $id
+   *   The numeric id or screen name of a Twitter user.
+   * @param array $params
+   *   an array of parameters.
+   *
+   * @see https://dev.twitter.com/docs/api/1.1/get/favorites/list
+   */
+  public function favorites_list($id, $params = array()) {
+    if (is_numeric($id)) {
+      $params['user_id'] = $id;
     }
-    catch (TwitterException $e) {
+    else {
+      $params['screen_name'] = $id;
     }
-    parse_str($response, $token);
-    $this->token = new OAuthConsumer($token['oauth_token'], $token['oauth_token_secret']);
-    return $token;
-  }
-
-  public function get_authorize_url($token) {
-    $url = $this->create_url('oauth/authorize', '');
-    $url.= '?oauth_token=' . $token['oauth_token'];
-
-    return $url;
+    return $this->get_statuses('favorites/list', $params);
   }
 
-  public function get_authenticate_url($token) {
-    $url = $this->create_url('oauth/authenticate', '');
-    $url.= '?oauth_token=' . $token['oauth_token'];
-
-    return $url;
-  }
+  /********************************************//**
+   * Utilities
+   ***********************************************/
+  /**
+   * Calls a Twitter API endpoint.
+   */
+  public function call($path, $params = array(), $method = 'GET') {
+    $url = $this->create_url($path);
 
-  public function get_access_token() {
-    $url = $this->create_url('oauth/access_token', '');
     try {
-      $response = $this->auth_request($url);
+      $response = $this->auth_request($url, $params, $method);
     }
     catch (TwitterException $e) {
+      watchdog('twitter', '!message', array('!message' => $e->__toString()), WATCHDOG_ERROR);
+      return FALSE;
     }
-    parse_str($response, $token);
-    $this->token = new OAuthConsumer($token['oauth_token'], $token['oauth_token_secret']);
-    return $token;
-  }
 
-  public function auth_request($url, $params = array(), $method = 'GET') {
-    $request = OAuthRequest::from_consumer_and_token($this->consumer, $this->token, $method, $url, $params);
-    $request->sign_request($this->signature_method, $this->consumer, $this->token);
-    switch ($method) {
-      case 'GET':
-        return $this->request($request->to_url());
-      case 'POST':
-        return $this->request($request->get_normalized_http_url(), $request->get_parameters(), 'POST');
+    if (!$response) {
+      return FALSE;
     }
-  }
-
-}
 
-/**
-  * Twitter search is not used in this module yet
-  */
-class TwitterSearch extends Twitter {
-  public function search($params = array()) {}
+    return $this->parse_response($response);
+  }
 }
 
 /**
@@ -390,7 +1281,6 @@ class TwitterStatus {
       $this->user = new TwitterUser($values['user']);
     }
   }
-
 }
 
 class TwitterUser {
@@ -443,8 +1333,6 @@ class TwitterUser {
 
   public $status;
 
-  protected $password;
-
   protected $oauth_token;
 
   protected $oauth_token_secret;
@@ -471,7 +1359,10 @@ class TwitterUser {
     $this->profile_background_tile = $values['profile_background_tile'];
     $this->verified = $values['verified'];
     $this->created_at = $values['created_at'];
-    if ($values['created_at'] && $created_time = strtotime($values['created_at'])) {
+    if (!empty($values['uid'])) {
+      $this->uid = $values['uid'];
+    }
+    if (!empty($values['created_at']) && $created_time = strtotime($values['created_at'])) {
       $this->created_time = $created_time;
     }
     $this->utc_offset = $values['utc_offset']?$values['utc_offset']:0;
@@ -481,12 +1372,34 @@ class TwitterUser {
     }
   }
 
+  /**
+   * Returns an array with the authentication tokens.
+   *
+   * @return
+   *   array with the oauth token key and secret.
+   */
   public function get_auth() {
-    return array('password' => $this->password, 'oauth_token' => $this->oauth_token, 'oauth_token_secret' => $this->oauth_token_secret);
+    return array('oauth_token' => $this->oauth_token, 'oauth_token_secret' => $this->oauth_token_secret);
   }
 
+  /**
+   * Sets the authentication tokens to a user.
+   *
+   * @param array $values
+   *   Array with 'oauth_token' and 'oauth_token_secret' keys.
+   */
   public function set_auth($values) {
     $this->oauth_token = isset($values['oauth_token'])?$values['oauth_token']:NULL;
     $this->oauth_token_secret = isset($values['oauth_token_secret'])?$values['oauth_token_secret']:NULL;
   }
+
+  /**
+   * Checks whether the account is authenticated or not.
+   *
+   * @return
+   *   boolean TRUE when the account is authenticated.
+   */
+  public function is_auth() {
+    return !empty($this->oauth_token) && !empty($this->oauth_token_secret);
+  }
 }
diff --git a/kolab.org/www/drupal-7.18/sites/all/modules/twitter/twitter.module b/kolab.org/www/drupal-7.18/sites/all/modules/twitter/twitter.module
index b50ed9c..9b91bec 100644
--- a/kolab.org/www/drupal-7.18/sites/all/modules/twitter/twitter.module
+++ b/kolab.org/www/drupal-7.18/sites/all/modules/twitter/twitter.module
@@ -14,7 +14,7 @@ define ('TWITTER_TINYURL',      'http://tinyurl.com');
  */
 function twitter_menu() {
   $items['twitter/oauth'] = array(
-    'title' => 'Twitter',
+    'title' => 'Twitter OAuth',
     'access callback' => TRUE,
     'page callback' => 'drupal_get_form',
     'page arguments' => array('twitter_oauth_callback'),
@@ -24,10 +24,9 @@ function twitter_menu() {
 
   $items['admin/config/services/twitter'] = array(
     'title' => 'Twitter',
-    'description' => 'Configure integration with Twitter (and compatible) API services.',
-    'page callback' => 'drupal_get_form',
-    'page arguments' => array('twitter_admin_form'),
-    'access arguments' => array('administer site configuration'),
+    'description' => 'Twitter accounts and settings.',
+    'page callback' => 'twitter_user_settings',
+    'access arguments' => array('administer twitter accounts'),
     'file' => 'twitter.pages.inc',
   );
 
@@ -36,37 +35,30 @@ function twitter_menu() {
     'type' => MENU_DEFAULT_LOCAL_TASK,
   );
 
-  $items['user/%user_category/edit/twitter'] = array(
-    'title' => 'Twitter accounts',
-    'page callback' => 'twitter_user_settings',
-    'page arguments' => array(1),
-    'access callback' => 'twitter_edit_access',
-    'access arguments' => array(1),
-    'load arguments' => array('%map', '%index'),
-    'weight' => 10,
+  $items['admin/config/services/twitter/settings'] = array(
+    'title' => 'Settings',
+    'description' => 'Twitter settings.',
+    'page callback' => 'drupal_get_form',
+    'page arguments' => array('twitter_admin_form'),
+    'access arguments' => array('administer site configuration'),
     'file' => 'twitter.pages.inc',
     'type' => MENU_LOCAL_TASK,
   );
 
-  $items['user/%user/edit/twitter/global/%'] = array(
+  $items['user/%user/edit/twitter'] = array(
     'title' => 'Twitter accounts',
-    'page callback' => 'drupal_get_form',
-    'page arguments' => array('twitter_user_make_global', 1, 5),
-    'access arguments' => array('make twitter accounts global'),
+    'page callback' => 'twitter_user_settings',
+    'page arguments' => array(1),
+    'access callback' => 'twitter_account_access',
+    'weight' => 10,
     'file' => 'twitter.pages.inc',
+    'type' => MENU_LOCAL_TASK,
   );
 
   return $items;
 }
 
 /**
- * Access callback for twitter account editing.
- */
-function twitter_edit_access($account) {
-  return user_edit_access($account) && user_access('add twitter accounts');
-}
-
-/**
  * Implements hook_permission().
  */
 function twitter_permission() {
@@ -74,29 +66,23 @@ function twitter_permission() {
     'add twitter accounts' => array(
       'title' => t('Add Twitter accounts'),
     ),
-    'use global twitter account' => array(
-      'title' => t('Use the site global Twitter account'),
+    'add authenticated twitter accounts' => array(
+      'title' => t('Add authenticated Twitter accounts'),
     ),
-    'make twitter accounts global' => array(
-      'title' => t('Assign a Twitter account as the site global account.'),
-    ),
-    'import own tweets' => array(
-      'title' => t('Import own tweets to the site.'),
+    'administer twitter accounts' => array(
+      'title' => t('Administer Twitter accounts'),
     ),
   );
 }
 
 /**
- * Implements hook_user_categories().
+ * Access callback for the Twitter accounts page.
+ *
+ * @return
+ *   Boolean TRUE if the current user has access.
  */
-function twitter_user_categories() {
-  return array(
-    array(
-      'name' => 'twitter',
-      'title' => t('Twitter accounts'),
-      'weight' => 3,
-    ),
-  );
+function twitter_account_access() {
+  return user_access('add twitter accounts') || user_access('add authenticated twitter accounts');
 }
 
 /**
@@ -107,10 +93,45 @@ function twitter_theme() {
     'twitter_account_list_form' => array(
       'render element' => 'form',
     ),
+    'twitter_status' => array(
+      'variables' => array(
+        'status' => NULL,
+        'author' => NULL,
+      ),
+      'template' => 'tweet',
+      'path' => drupal_get_path('module', 'twitter'),
+    ),
+    'twitter_user_accounts' => array(
+      'variables' => array(
+        'accounts' => array(),
+      ),
+    ),
   );
 }
 
 /**
+ * Default callback for theme('twitter_user_accounts');
+ *
+ * Renders a list of Twitter accounts for the user profile page.
+ */
+function theme_twitter_user_accounts($variables) {
+  module_load_include('inc', 'twitter');
+  $accounts = $variables['accounts'];
+  $items = array();
+  foreach ($accounts as $twitter_account) {
+    $tweets = twitter_tweets($twitter_account->screen_name);
+    // If we have tweets for this Twitter account, link to the View. If not, link to Twitter.
+    if (count($tweets)) {
+      $items[] = l('@' . $twitter_account->screen_name, 'tweets/' . $twitter_account->screen_name);
+    }
+    else {
+      $items[] = _twitter_user_profile($twitter_account->screen_name);
+    }
+  }
+  return theme('item_list', array('items' => $items));
+}
+
+/**
  * Very lightweight helper function to generate a TinyURL for a given post.
  */
 function twitter_shorten_url($url) {
@@ -137,15 +158,36 @@ function twitter_cron() {
   if (!variable_get('twitter_import', TRUE)) {
     return;
   }
+  // Check if we can connect to Twitter before proceeding.
+  module_load_include('inc', 'twitter');
+  $twitter = twitter_connect();
+  if (!$twitter) {
+    return;
+  }
 
   // Pull up a list of Twitter accounts that are flagged for updating,
   // sorted by how long it's been since we last updated them. This ensures
   // that the most out-of-date accounts get updated first.
-  module_load_include('inc', 'twitter');
-  $result = db_query_range("SELECT twitter_uid FROM {twitter_account} WHERE import = :import ORDER BY last_refresh ASC", 0, 20, array(':import' => 1));
+  $result = db_query_range("SELECT twitter_uid
+                            FROM {twitter_account}
+                            WHERE uid <> 0 AND import = 1
+                            ORDER BY last_refresh ASC",
+                            0, 20);
   try {
     foreach ($result as $account) {
+      // Fetch tweets and mentions.
       twitter_fetch_user_timeline($account->twitter_uid);
+      $twitter_account = twitter_account_load($account->twitter_uid);
+      if ($twitter_account->is_auth() && $twitter_account->mentions) {
+        twitter_fetch_mentions_timeline($twitter_account->id);
+      }
+      // Mark the time this account was updated.
+      db_update('twitter_account')
+        ->fields(array(
+          'last_refresh' => REQUEST_TIME,
+        ))
+        ->condition('twitter_uid', $twitter_account->id)
+        ->execute();
     }
   } catch (TwitterException $e) {
     // The exception has already been logged so we do not need to do anything here apart from catching it.
@@ -160,7 +202,7 @@ function twitter_cron() {
 }
 
 /**
- * Implements hook_filter_info().
+ * Implements hook_filter_info()
  */
 function twitter_filter_info() {
   $filters['twitter_username'] = array(
@@ -175,18 +217,26 @@ function twitter_filter_info() {
     'process callback' => '_twitter_filter_hashtag',
     'tips callback' => '_twitter_filter_tip_hashtag',
   );
+  $filters['twitter_links'] = array(
+    'title' => t('Twitter link converter'),
+    'description' => t('Makes links in Twitter messages to be opened in new windows and adds ' .
+                       'rel="nofollow" so these links do not penalize SEO.'),
+    'process callback' => '_twitter_filter_link',
+    'tips callback' => '_twitter_filter_tip_link',
+  );
+
   return $filters;
 }
 
 /**
- * Filter tips callback function for $filters[0] in hook_filter_info().
+ * Filter tips callback function for Twitter usernames.
  */
 function _twitter_filter_tip_username($filter, $format, $long = FALSE) {
   return t('Twitter-style @usernames are linked to their Twitter account pages.');
 }
 
 /**
- * Filter tips callback function for $filters[1] in hook_filter_info().
+ * Filter tips callback function for Twitter hashtags.
  */
 function _twitter_filter_tip_hashtag($format, $long = FALSE) {
   return t('Twitter-style #hashtags are linked to !url.', array(
@@ -195,7 +245,14 @@ function _twitter_filter_tip_hashtag($format, $long = FALSE) {
 }
 
 /**
- * Callback for twitter @username converter
+ * Filter tips callback function for Twitter links.
+ */
+function _twitter_filter_tip_link($filter, $format, $long = FALSE) {
+  return t('Twitter message links are opened in new windows and rel="nofollow" is added.');
+}
+
+/**
+ * Callback for twitter @username converter.
  */
 function _twitter_filter_username($text, $filter) {
   $prefix = '@';
@@ -204,7 +261,7 @@ function _twitter_filter_username($text, $filter) {
 }
 
 /**
- * Callback for twitter #hashtag converter
+ * Callback for twitter #hashtag converter.
  */
 function _twitter_filter_hashtag($text, $filter) {
   $prefix = '#';
@@ -218,19 +275,33 @@ function _twitter_filter_hashtag($text, $filter) {
  */
 function _twitter_filter_text($text, $prefix, $destination) {
   $matches = array(
-    '/\>' . $prefix . '([a-z0-9_]+)/i',
-    '/^' . $prefix . '([a-z0-9_]+)/i',
-    '/(\s+)' . $prefix . '([a-z0-9_]+)/i',
+    '/\>' . $prefix . '(\w+)/ui',
+    '/^' . $prefix . '(\w+)/ui',
+    '/(\s+)' . $prefix . '(\w+)/ui',
   );
   $replacements = array(
-    '><a href="' . $destination . '${1}" target="_blank">' . $prefix . '${1}</a>',
-    '<a href="' . $destination . '${1}" target="_blank">' . $prefix . '${1}</a>',
-    '${1}<a href="' . $destination . '${2}" target="_blank">' . $prefix . '${2}</a>',
+    '><a href="' . $destination . '${1}">' . $prefix . '${1}</a>',
+    '<a href="' . $destination . '${1}">' . $prefix . '${1}</a>',
+    '${1}<a href="' . $destination . '${2}">' . $prefix . '${2}</a>',
   );
   return preg_replace($matches, $replacements, $text);
 }
 
 /**
+ * Callback for twitter link converter.
+ */
+function _twitter_filter_link($text, $filter) {
+  return str_replace("<a ", '<a target="_blank" rel="nofollow" ', $text);
+}
+
+/**
+ * Implements hook_views_api().
+ */
+function twitter_views_api() {
+  return array('api' => 2);
+}
+
+/**
  * Implements hook_user_load().
  */
 function twitter_user_load($accounts) {
@@ -240,10 +311,7 @@ function twitter_user_load($accounts) {
 }
 
 /**
- * An implementation of hook_twitter_accounts. We want to move this into a
- * separate module eventually, but sticking the code here and using a hook
- * lets other modules solve the 'what accounts can a user post with' problem
- * in cleaner ways.
+ * Implements hook_twitter_accounts().
  *
  * @return
  *   array with Twitter accounts
@@ -252,16 +320,8 @@ function twitter_twitter_accounts($account) {
   module_load_include('inc', 'twitter');
 
   $query = db_select('twitter_account', 'ta')
-    ->fields('ta', array('twitter_uid'));
-  if (user_access('use global twitter account', $account)) {
-    $or = db_or();
-    $or->condition('ta.uid', $account->uid);
-    $or->condition('ta.is_global', 1);
-    $query->condition($or);
-  }
-  else {
-    $query->condition('ta.uid', $account->uid);
-  }
+    ->fields('ta', array('twitter_uid'))
+    ->condition('ta.uid', $account->uid);
 
   $twitter_accounts = array();
   foreach ($query->execute()->fetchCol() as $twitter_uid) {
@@ -271,36 +331,49 @@ function twitter_twitter_accounts($account) {
 }
 
 /**
- * Detect whether we should use oauth. This can probably just go now :)
+ * Implements hook_user_delete().
+ *
+ * Removes user's Twitter accounts and tweets
  */
-function _twitter_use_oauth() {
-  return module_exists('oauth_common') && variable_get('twitter_consumer_key', '') &&
-         variable_get('twitter_consumer_secret', '');
+function twitter_user_delete($account) {
+  module_load_include('inc', 'twitter');
+  foreach ($account->twitter_accounts as $twitter_account) {
+    twitter_account_delete($twitter_account->id);
+  }
 }
 
 /**
- * Implements hook_views_api().
+ * Implements hook_user_view_alter()
+ *
+ * Adds Twitter account information to the user profile.
  */
-function twitter_views_api() {
-  return array('api' => 2);
+function twitter_user_view_alter(&$build) {
+  $user = $build['#account'];
+  if (!empty($user->twitter_accounts)) {
+    $build['twitter'] = array(
+      '#type' => 'user_profile_item',
+      '#title' => t('Twitter accounts'),
+      '#markup' => theme('twitter_user_accounts', array('accounts' => $user->twitter_accounts)),
+      '#weight' => 10,
+    );
+  }
 }
 
 /**
- * Implements hook_admin_paths_alter().
+ * Checks if the Twitter Application keys are set.
  *
- * OAuth callback disagrees with overlay.
+ * @return
+ *   boolean TRUE if both the Twitter Application key and secret are set.
  */
-function twitter_admin_paths_alter(&$paths) {
-  $paths['user/*/edit/twitter'] = FALSE;
+function twitter_api_keys() {
+  $key = variable_get('twitter_consumer_key');
+  $secret = variable_get('twitter_consumer_secret');
+  return !(empty($key) && empty($secret));
 }
 
 /**
- * Implements hook_user_delete().
- *
- * Removes user's Twitter accounts and tweets
+ * Helper to build a Twitter profile URL
  */
-function twitter_user_delete($account) {
-  foreach ($account->twitter_accounts as $twitter_account) {
-    twitter_account_delete($twitter_account->id);
-  }
+function _twitter_user_profile($screen_name) {
+  return l('@' . $screen_name, TWITTER_HOST . '/' . $screen_name);
 }
diff --git a/kolab.org/www/drupal-7.18/sites/all/modules/twitter/twitter.pages.inc b/kolab.org/www/drupal-7.18/sites/all/modules/twitter/twitter.pages.inc
index d17fff7..caaa940 100644
--- a/kolab.org/www/drupal-7.18/sites/all/modules/twitter/twitter.pages.inc
+++ b/kolab.org/www/drupal-7.18/sites/all/modules/twitter/twitter.pages.inc
@@ -5,7 +5,7 @@
  */
 
 /**
- * Form builder; Twitter settings form.
+ * Twitter settings form.
  */
 function twitter_admin_form($form, &$form_state) {
   $form['twitter_import'] = array(
@@ -79,24 +79,69 @@ function twitter_admin_form($form, &$form_state) {
 }
 
 /**
- * @todo Please document this function.
- * @see http://drupal.org/node/1354
+ * Form builder that lists Twitter accounts.
+ *
+ * @param object $account
+ *   Optional user account.
+ * @return
+ *   A list of Twitter accounts and a form to add more.
  */
-function twitter_user_settings($account) {
-  module_load_include('inc', 'twitter');
+function twitter_user_settings($account = NULL) {
+  // Verify OAuth keys.
+  if (!twitter_api_keys()) {
+    $variables = array('@twitter-settings' => url('admin/config/services/twitter/settings'));
+    $output = '<p>' . t('You need to authenticate at least one Twitter account in order to use the Twitter API. Please fill out the OAuth fields at <a href="@twitter-settings">Twitter Settings</a> and then return here.', $variables) . '</p>';
+  }
+  else {
+    module_load_include('inc', 'twitter');
+    if (!$account) {
+      $twitter_accounts = twitter_account_load_all();
+    }
+    else {
+      $twitter_accounts = twitter_twitter_accounts($account);
+    }
+
+    $output = array();
+    if (count($twitter_accounts)) {
+      // List Twitter accounts.
+      $output['header']['#markup'] = '<p>';
+      if (user_access('administer site configuration')) {
+        $variables = array('@run-cron' => url('admin/reports/status/run-cron', array('query' => array('destination' => 'admin/config/services/twitter'))));
+        $output['header']['#markup'] .= t('Tweets are pulled from Twitter by <a href="@run-cron">running cron</a>.', $variables) . ' ';
+      }
+      $variables = array('@tweets' => url('tweets'));
+      $output['header']['#markup'] .= t('You can view the full list of tweets at the <a href="@tweets">Tweets</a> view.', $variables);
+      $output['header']['#markup'] .= '</p>';
+      $output['list_form'] = drupal_get_form('twitter_account_list_form', $twitter_accounts);
+    }
+    else {
+      // No accounts added. Inform about how to add one.
+      $output['header'] = array(
+        '#markup' => '<p>' . t('No Twitter accounts have been added yet. Click on the following button to add one.') . '</p>',
+      );
+    }
+
+    $output['add_account'] = array(
+      '#type' => 'fieldset',
+      '#title' => t('Add Twitter accounts'),
+      '#weight' => 5,
+      '#collapsible' => TRUE,
+      '#collapsed' => FALSE,
+    );
 
-  $output = array();
-  if (!empty($account->twitter_accounts)) {
-    $output['list_form'] = drupal_get_form('twitter_account_list_form', $account->twitter_accounts);
+    if (user_access('add authenticated twitter accounts')) {
+      $output['add_account']['form'] = drupal_get_form('twitter_auth_account_form');
+    }
+    if (twitter_connect()) {
+      $output['add_account']['non_auth'] = drupal_get_form('twitter_non_auth_account_form');
+    }
   }
-  $output['form'] = drupal_get_form('twitter_account_form', $account);
 
   return $output;
 }
 
 /**
- * @todo Please document this function.
- * @see http://drupal.org/node/1354
+ * Formats each Twitter account as a row within a form.
  */
 function twitter_account_list_form($form, $form_state, $twitter_accounts = array()) {
   $form['#tree'] = TRUE;
@@ -116,6 +161,9 @@ function twitter_account_list_form($form, $form_state, $twitter_accounts = array
   return $form;
 }
 
+/**
+ * Returns the form fields to manage a Twitter account.
+ */
 function _twitter_account_list_row($account) {
   $form['#account'] = $account;
 
@@ -124,11 +172,6 @@ function _twitter_account_list_row($account) {
     '#value' => $account->id,
   );
 
-  $form['uid'] = array(
-    '#type' => 'value',
-    '#value' => $account->uid,
-  );
-
   $form['screen_name'] = array(
     '#type' => 'value',
     '#value' => $account->screen_name,
@@ -139,25 +182,45 @@ function _twitter_account_list_row($account) {
   );
 
   $form['visible_name'] = array(
-    '#markup' => l($account->screen_name, 'http://www.twitter.com/' . $account->screen_name),
+    '#markup' => _twitter_user_profile($account->screen_name),
   );
 
   $form['description'] = array(
     '#markup' => filter_xss($account->description),
   );
 
+  if (user_access('administer twitter accounts')) {
+    $user = user_load($account->uid);
+    $form['user'] = array(
+      '#markup' => l($user->name, 'user/' . $account->uid),
+    );
+  }
+
+  $form['auth'] = array(
+    '#markup' => $account->is_auth() ? t('Yes') : t('No'),
+  );
+
   $form['protected'] = array(
     '#markup' => empty($account->protected) ? t('No') : t('Yes'),
   );
 
-  // Here we use user_access('import own tweets') to check permission
-  // instead of user_access('import own tweets', $account->uid)
-  // because we allow roles with sufficient permission to overwrite
-  // the user's import settings.
-  if (variable_get('twitter_import', TRUE) && user_access('import own tweets')) {
-    $form['import'] = array(
+  $form['import'] = array(
+    '#type' => 'checkbox',
+    '#default_value' => $account->import ? $account->import : '',
+  );
+  if ($account->import == TRUE) {
+    $form['import']['#suffix'] = l('View', 'tweets/' . $account->screen_name, array('attributes' => array('target' => '_blank')));
+  }
+
+  if ($account->is_auth()) {
+    $form['mentions'] = array(
       '#type' => 'checkbox',
-      '#default_value' => user_access('import own tweets') ? $account->import : '',
+      '#default_value' => $account->mentions ? $account->mentions : '',
+    );
+  }
+  else {
+    $form['mentions'] = array(
+      '#markup' => '',
     );
   }
 
@@ -169,52 +232,46 @@ function _twitter_account_list_row($account) {
 }
 
 /**
- * @todo Please document this function.
- * @see http://drupal.org/node/1354
+ * Themes the list of Twitter accounts.
  */
 function theme_twitter_account_list_form($variables) {
   $form = $variables['form'];
 
-  if (variable_get('twitter_import', TRUE) && user_access('import own tweets')) {
-    $header = array('', t('Name'), t('Description'), t('Private'), t('Import'), t('Delete'));
-  } 
-  else {
-    $header = array('', t('Name'), t('Description'), t('Private'), t('Delete'));
-  }
-
-  if (user_access('make twitter accounts global')) {
-    $header[] = '';
+  $header = array(
+    '',
+    t('Name'),
+    t('Description'),
+  );
+  if (user_access('administer twitter accounts')) {
+    $header[] = t('Added by');
   }
+  $header = array_merge($header, array(
+    t('Auth'),
+    t('Private'),
+    t('Tweets'),
+    t('Mentions'),
+    t('Delete'),
+  ));
 
   $rows = array();
-
   foreach (element_children($form['accounts']) as $key) {
     $element = &$form['accounts'][$key];
-    if (variable_get('twitter_import', TRUE) && user_access('import own tweets')) {
-      $row = array(
-        drupal_render($element['image']),
-        drupal_render($element['id']) . drupal_render($element['screen_name']) . drupal_render($element['visible_name']),
-        drupal_render($element['description']),
-        drupal_render($element['protected']),
-        drupal_render($element['import']),
-        drupal_render($element['delete']),
-      );
-    }
-    else {
-      $row = array(
-        drupal_render($element['image']),
-        drupal_render($element['id']) . drupal_render($element['screen_name']) . drupal_render($element['visible_name']),
-        drupal_render($element['description']),
-        drupal_render($element['protected']),
-        drupal_render($element['delete']),
-      );
-    }
-
-    if (user_access('make twitter accounts global')) {
-      $label = ($element['#account']->is_global) ? t('remove global') : t('make global');
-      $row[] = l($label, 'user/' . $element['#account']->uid . '/edit/twitter/global/' . $element['#account']->id);
+    $row = array(
+      drupal_render($element['image']),
+      drupal_render($element['id']) . drupal_render($element['screen_name']) .
+        drupal_render($element['visible_name']),
+      drupal_render($element['description']),
+    );
+    if (user_access('administer twitter accounts')) {
+      $row[] = drupal_render($element['user']);
     }
-
+    $row = array_merge($row, array(
+      drupal_render($element['auth']),
+      drupal_render($element['protected']),
+      drupal_render($element['import']),
+      drupal_render($element['mentions']),
+      drupal_render($element['delete']),
+    ));
     $rows[] = $row;
   }
 
@@ -224,165 +281,95 @@ function theme_twitter_account_list_form($variables) {
 }
 
 /**
- * @todo Please document this function.
- * @see http://drupal.org/node/1354
+ * Form submit handler for altering the list of Twitter accounts.
  */
 function twitter_account_list_form_submit($form, &$form_state) {
   $accounts = $form_state['values']['accounts'];
   foreach ($accounts as $account) {
     if (empty($account['delete'])) {
       twitter_account_save($account);
-      drupal_set_message(t('The Twitter account settings were updated.'));
     }
     else {
+      $twitter_account = twitter_account_load($account['id']);
       twitter_account_delete($account['id']);
-      drupal_set_message(t('The Twitter account was deleted.'));
+      drupal_set_message(t('The Twitter account <em>!account</em> was deleted.',
+        array('!account' => $twitter_account->screen_name)));
     }
   }
+  drupal_set_message(t('The Twitter account settings were updated.'));
 }
 
 /**
- * @todo Please document this function.
- * @see http://drupal.org/node/1354
- */
-function twitter_user_make_global($form, $form_state, $account, $twitter_uid) {
-  module_load_include('inc', 'twitter');
-
-  $twitter_account = twitter_account_load($twitter_uid);
-
-  $form = array();
-
-  $form['uid'] = array(
-    '#type' => 'value',
-    '#value' => $account->uid,
-  );
-
-  $form['twitter_uid'] = array(
-    '#type' => 'value',
-    '#value' => $twitter_uid,
-  );
-
-  if ($twitter_account->is_global) {
-    $text = t('Are you sure you want to remove %screen_name from the global accounts?', array('%screen_name' => $twitter_account->screen_name));
-    $description = t('This means other users will no longer be allowed to post using this account.');
-  }
-  else {
-    $text = t('Are you sure you want to allow other users to access the %screen_name account?', array('%screen_name' => $twitter_account->screen_name));
-    $description = t('This will allow other users to post using this account.');
-  }
-
-  return confirm_form($form, $text, 'user/' . $account->uid . '/edit/twitter', $description);
-}
-
-/**
- * @todo Please document this function.
- * @see http://drupal.org/node/1354
+ * Form to add an authenticated Twitter account.
  */
-function twitter_user_make_global_submit($form, &$form_state) {
-  db_update('twitter_account')
-    ->expression('is_global', '(1 - is_global)')
-    ->condition('twitter_uid', $form_state['values']['twitter_uid'])
-    ->execute();
-
-  $form_state['redirect'] = 'user/' . $form_state['values']['uid'] . '/edit/twitter';
-}
-
-/**
- * Form to add a Twitter account
- *
- * If OAuth is not enabled, a text field lets users to add their
- * Twitter screen name. If it is, a submit button redirects to
- * Twitter.com asking for authorisation.
- */
-function twitter_account_form($form, $form_state, $account = NULL) {
-  if (empty($account)) {
-    global $user;
-    $account = $user;
-  }
-
-  $form['uid'] = array(
-    '#type' => 'value',
-    '#value' => $account->uid,
-  );
-
-  if (_twitter_use_oauth()) {
-    $form['#validate'] = array('twitter_account_oauth_validate');
-  }
-  else {
-    $form['screen_name'] = array(
-      '#type' => 'textfield',
-      '#required' => TRUE,
-      '#title' => t('Twitter user name'),
-    );
-
-    $form['import'] = array(
-      '#type' => 'checkbox',
-      '#title' => t('Import statuses from this account'),
-      '#default_value' => TRUE,
-      '#access' => FALSE,
-    );
-  }
-
+function twitter_auth_account_form($form, $form_state) {
   $form['submit'] = array(
     '#type' => 'submit',
-    '#value' => t('Add account'),
+    '#value' => t('Go to Twitter to add an authenticated account'),
+    '#prefix' => t('Authenticated accounts can post, sign in and pull mentions. ' .
+                   'At least one authenticated account is needed for Twitter ' .
+                   'module to work.</br>'),
   );
 
   return $form;
 }
 
 /**
- * Implements hook_FORM_ID_submit()
- *
- * Loads Twitter account details and adds them to the user account
+ * Form validation for adding a new Twitter account.
  */
-function twitter_account_form_submit($form, &$form_state) {
-  module_load_include('lib.php', 'twitter');
-  module_load_include('inc', 'twitter');
-
-  $name = $form_state['values']['screen_name'];
-  $twitter = new Twitter($name);
-  try {
-    $account = $twitter->users_show($name, FALSE);
-  } catch (TwitterException $e) {
-    form_set_error('screen_name', t('Request failed: @message.', array('@message' => $e->getMessage())));
-    return;
+function twitter_auth_account_form_validate($form, &$form_state) {
+  $key = variable_get('twitter_consumer_key', '');
+  $secret = variable_get('twitter_consumer_secret', '');
+  if ($key == '' || $secret == '') {
+    form_set_error('', t('Please configure your consumer key and secret key at ' .
+      '<a href="!url">Twitter settings</a>.', array( '!url' => url('admin/config/services/twitter'),
+    )));
   }
-  twitter_account_save($account, TRUE, user_load($form_state['values']['uid']));
 }
 
 /**
- * If OAuth is enabled, intercept submission of 'Add Account' form on
- * user/%/edit/twitter page and redirect to Twitter for auth.
+ * Form submit handler for adding a Twiter account.
+ *
+ * Loads Twitter account details and adds them to the user account
  */
-function twitter_account_oauth_validate($form, &$form_state) {
-  module_load_include('lib.php', 'oauth_common');
-  module_load_include('lib.php', 'twitter');
-
+function twitter_auth_account_form_submit($form, &$form_state) {
   $key = variable_get('twitter_consumer_key', '');
   $secret = variable_get('twitter_consumer_secret', '');
-  if ($key == '' || $secret == '') {
-    form_set_error('', t('Please configure your Twitter consumer key and secret.'));
-  }
-
-  $twitter = new TwitterOAuth($key, $secret);
+  $twitter = new Twitter($key, $secret);
   $token = $twitter->get_request_token();
-
-  $_SESSION['twitter_oauth']['account'] = user_load($form['uid']['#value']);  
-  $_SESSION['twitter_oauth']['token'] = $token;
-  $_SESSION['twitter_oauth']['destination'] = $_GET['q'];
-  drupal_goto($twitter->get_authorize_url($token));
+  if ($token) {
+    $_SESSION['twitter_oauth']['token'] = $token;
+    $_SESSION['twitter_oauth']['destination'] = $_GET['q'];
+    // Check for the overlay.
+    if (module_exists('overlay') && overlay_get_mode() == 'child') {
+      overlay_close_dialog($twitter->get_authorize_url($token), array('external' => TRUE));
+      overlay_deliver_empty_page();
+    }
+    else {
+      drupal_goto($twitter->get_authorize_url($token));
+    }
+  }
+  else {
+    drupal_set_message(t('Could not obtain a valid token from the Twitter API. Please review the configuration.'),
+      'error');
+  }
 }
 
 /**
- * @TODO This code should probably be reviewed.
- *
  * Wrapper to call drupal_form_submit() which wasn't required in D6.
  */
 function twitter_oauth_callback() {
   if (isset($_GET['denied']) || empty($_GET['oauth_token'])) {
     drupal_set_message(t('The connection to Twitter failed. Please try again.'), 'error');
-    drupal_goto('<front>');
+    global $user;
+    if ($user->uid) {
+      // User is logged in, was attempting to OAuth a Twitter account.
+      drupal_goto('admin/config/services/twitter');
+    }
+    else {
+      // Anonymous user, redirect to front page.
+      drupal_goto('<front>');
+    }
   }
   $form_state['values']['oauth_token'] = $_GET['oauth_token'];
   drupal_form_submit('twitter_oauth_callback_form', $form_state);
@@ -410,10 +397,6 @@ function twitter_oauth_callback_form_validate($form, &$form_state) {
   $key = variable_get('twitter_consumer_key', '');
   $secret = variable_get('twitter_consumer_secret', '');
 
-  if ($key == '' || $secret == '') {
-    form_set_error('', t('Please configure your Twitter consumer key and secret.'));
-  }
-
   if (isset($_SESSION['twitter_oauth'])) {
     $form_state['twitter_oauth'] = $_SESSION['twitter_oauth'];
     unset($_SESSION['twitter_oauth']);
@@ -435,12 +418,11 @@ function twitter_oauth_callback_form_validate($form, &$form_state) {
     form_set_error('oauth_token', t('Invalid Twitter OAuth request'));
   }
 
-  module_load_include('lib.php', 'oauth_common');
-  module_load_include('lib.php', 'twitter');
   module_load_include('inc', 'twitter');
 
-  if ($twitter = new TwitterOAuth($key, $secret, $token['oauth_token'], $token['oauth_token_secret'])) {
-    if ($response = $twitter->get_access_token()) {
+  if ($twitter = new Twitter($key, $secret, $token['oauth_token'], $token['oauth_token_secret'])) {
+    //Collect oauth_verifier from url
+    if ($response = $twitter->get_access_token($_GET['oauth_verifier'])) {
       $form_state['twitter_oauth']['response'] = $response;
     }
     else {
@@ -454,24 +436,76 @@ function twitter_oauth_callback_form_validate($form, &$form_state) {
 
 /**
  * Handle a Twitter OAuth return request and store the account creds
- * in the DB. Redirects to user/%/edit/twitter
+ * in the DB.
  */
 function twitter_oauth_callback_form_submit($form, &$form_state) {
   $key = variable_get('twitter_consumer_key', '');
   $secret = variable_get('twitter_consumer_secret', '');
   $response = $form_state['twitter_oauth']['response'];
 
-  $twitter = new TwitterOAuth($key, $secret, $response['oauth_token'], $response['oauth_token_secret']);
+  $twitter = new Twitter($key, $secret, $response['oauth_token'], $response['oauth_token_secret']);
   try {
     $twitter_account = $twitter->users_show($response['screen_name']);
   } catch (TwitterException $e) {
     form_set_error('screen_name', t('Request failed: @message.', array('@message' => $e->getMessage())));
     return;
   }
+  // Save the new Twitter account and set the user's uid who added it.
   $twitter_account->set_auth($response);
-  $account = $form_state['twitter_oauth']['account'];
-  twitter_account_save($twitter_account, TRUE, $account);
+  global $user;
+  $twitter_account->uid = $user->uid;
+  twitter_account_save($twitter_account, TRUE);
 
   $form_state['programmed'] = FALSE;
   $form_state['redirect'] = $form_state['twitter_oauth']['destination'];
 }
+
+/**
+ * Form to add a non-authenticated Twitter account.
+ */
+function twitter_non_auth_account_form($form, $form_state) {
+  $form['screen_name'] = array(
+    '#type' => 'textfield',
+    '#required' => TRUE,
+    '#title' => t('Twitter account name'),
+    '#prefix' => t('If you simply want to pull tweets from additional Twitter accounts, ' .
+                   'enter the Twitter account name below and click on the following button.</br>'),
+  );
+
+  $form['submit_non_auth'] = array(
+    '#type' => 'submit',
+    '#value' => t('Add a non-authenticated account'),
+  );
+
+  return $form;
+}
+
+/**
+ * Form validation for adding a new non-authenticated Twitter account.
+ */
+function twitter_non_auth_account_form_validate($form, &$form_state) {
+  $screen_name = $form_state['values']['screen_name'];
+  if (twitter_account_load($screen_name)) {
+    form_set_error('screen_name', t('The Twitter account <em>@screen_name</em> has been added already.',
+      array('@screen_name' => $screen_name)));
+  }
+}
+
+/**
+ * Submit form handler to add a non-authenticated Twitter account.
+ */
+function twitter_non_auth_account_form_submit($form, &$form_state) {
+  $name = $form_state['values']['screen_name'];
+  $twitter = twitter_connect();
+  $twitter_account = $twitter->users_show($name, FALSE);
+  if (!isset($twitter_account->id)) {
+    form_set_error('screen_name', t('Could not add the Twitter account <em>@name</em>. ' .
+      'Check the recent messages log.', array('@name' => $name)));
+  }
+  else {
+    global $user;
+    $twitter_account->uid = $user->uid;
+    twitter_account_save($twitter_account, FALSE);
+    drupal_set_message(t('Twitter account added successfully'));
+  }
+}
diff --git a/kolab.org/www/drupal-7.18/sites/all/modules/twitter/twitter.views.inc b/kolab.org/www/drupal-7.18/sites/all/modules/twitter/twitter.views.inc
index bf45437..6e319f1 100644
--- a/kolab.org/www/drupal-7.18/sites/all/modules/twitter/twitter.views.inc
+++ b/kolab.org/www/drupal-7.18/sites/all/modules/twitter/twitter.views.inc
@@ -5,19 +5,10 @@
  */
 
 /**
- * @defgroup views_twitter_module twitter.module handlers
- *
- * Includes the ability to create views of just the twitter table.
- * @{
- */
-
-
-/**
  * Implements hook_views_data().
  */
 function twitter_views_data() {
   // Basic table information.
-
   $data['twitter']['table']['group']  = t('Twitter');
 
   // Advertise this table as a possible base table
@@ -87,7 +78,7 @@ function twitter_views_data() {
   // Twitter screen name
   $data['twitter']['screen_name'] = array(
     'title' => t('Login name'),
-    'help' => t('The login account of the Twitter user.'),
+    'help' => t('The screen name of the author of the tweet.'),
     'field' => array(
       'handler' => 'views_handler_field',
       'click sortable' => TRUE,
@@ -148,11 +139,35 @@ function twitter_views_data() {
     ),
   );
 
+  // Twitter Web Intents.
+  $data['twitter']['web_intents'] = array(
+    'title' => t('Web Intents'),
+    'help' => t('Links to Reply, Retweet and Favorite a tweet.'),
+    'field' => array(
+      'handler' => 'twitter_views_handler_field_web_intents',
+    ),
+  );
 
+  // Follow link
+  $data['twitter']['follow'] = array(
+    'title' => t('Follow'),
+    'help' => t('Link to Follow a Twitter user.'),
+    'field' => array(
+      'handler' => 'twitter_views_handler_field_follow',
+    ),
+  );
 
+  // Renders a formatted tweet.
+  $data['twitter']['formatted_tweet'] = array(
+    'title' => t('Formatted tweet'),
+    'help' => t('Renders a tweet as it is shown at Twitter.com.'),
+    'field' => array(
+      'handler' => 'twitter_views_handler_field_formatted_tweet',
+    ),
+  );
 
+  // Table twitter_account.
   $data['twitter_account']['table']['group']  = t('Twitter');
-
   $data['twitter_account']['table']['join'] = array(
     'twitter' => array(
       'left_field' => 'screen_name',
@@ -315,7 +330,8 @@ function twitter_views_data() {
     'title' => t('User ID'),
     'help' => t('The UID of the Twitter account.'),
   );
-  // Twitter account protected
+
+  // Twitter account protected.
   $data['twitter_account']['import'] = array(
     'title' => t('Import status'),
     'help' => t('Whether posts from this Twitter account should be imported automatically.'),
@@ -351,7 +367,3 @@ function twitter_views_data_alter(&$data) {
     'field' => 'uid',
   );
 }
-
-/**
- * @}
- */
diff --git a/kolab.org/www/drupal-7.18/sites/all/modules/twitter/twitter.views_default.inc b/kolab.org/www/drupal-7.18/sites/all/modules/twitter/twitter.views_default.inc
index 1205e1d..6883bca 100644
--- a/kolab.org/www/drupal-7.18/sites/all/modules/twitter/twitter.views_default.inc
+++ b/kolab.org/www/drupal-7.18/sites/all/modules/twitter/twitter.views_default.inc
@@ -4,18 +4,19 @@
  * Default views for Twitter module.
  */
 function twitter_views_default_views() {
-  $view = new view;
+  $view = new view();
   $view->name = 'tweets';
   $view->description = 'Displays Twitter.com status messages for users who have associated Twitter accounts.';
   $view->tag = '';
   $view->base_table = 'twitter';
-  $view->human_name = '';
+  $view->human_name = 'Tweets';
   $view->core = 0;
   $view->api_version = '3.0';
   $view->disabled = FALSE; /* Edit this to true to make a default view disabled initially */
 
   /* Display: Tweets */
   $handler = $view->new_display('default', 'Tweets', 'default');
+  $handler->display->display_options['use_more_always'] = FALSE;
   $handler->display->display_options['access']['type'] = 'none';
   $handler->display->display_options['cache']['type'] = 'none';
   $handler->display->display_options['query']['type'] = 'views_query';
@@ -44,8 +45,6 @@ function twitter_views_default_views() {
       'separator' => '',
     ),
   );
-  $handler->display->display_options['style_options']['override'] = 1;
-  $handler->display->display_options['style_options']['sticky'] = 0;
   /* Field: Twitter: Profile image */
   $handler->display->display_options['fields']['profile_image_url']['id'] = 'profile_image_url';
   $handler->display->display_options['fields']['profile_image_url']['table'] = 'twitter_account';
@@ -56,24 +55,7 @@ function twitter_views_default_views() {
   $handler->display->display_options['fields']['text']['table'] = 'twitter';
   $handler->display->display_options['fields']['text']['field'] = 'text';
   $handler->display->display_options['fields']['text']['label'] = '';
-  $handler->display->display_options['fields']['text']['alter']['alter_text'] = 0;
-  $handler->display->display_options['fields']['text']['alter']['make_link'] = 0;
-  $handler->display->display_options['fields']['text']['alter']['absolute'] = 0;
-  $handler->display->display_options['fields']['text']['alter']['external'] = 0;
-  $handler->display->display_options['fields']['text']['alter']['replace_spaces'] = 0;
-  $handler->display->display_options['fields']['text']['alter']['trim_whitespace'] = 0;
-  $handler->display->display_options['fields']['text']['alter']['nl2br'] = 0;
-  $handler->display->display_options['fields']['text']['alter']['word_boundary'] = 1;
-  $handler->display->display_options['fields']['text']['alter']['ellipsis'] = 1;
-  $handler->display->display_options['fields']['text']['alter']['more_link'] = 0;
-  $handler->display->display_options['fields']['text']['alter']['strip_tags'] = 0;
-  $handler->display->display_options['fields']['text']['alter']['trim'] = 0;
-  $handler->display->display_options['fields']['text']['alter']['html'] = 0;
   $handler->display->display_options['fields']['text']['element_label_colon'] = FALSE;
-  $handler->display->display_options['fields']['text']['element_default_classes'] = 1;
-  $handler->display->display_options['fields']['text']['hide_empty'] = 0;
-  $handler->display->display_options['fields']['text']['empty_zero'] = 0;
-  $handler->display->display_options['fields']['text']['hide_alter_empty'] = 1;
   $handler->display->display_options['fields']['text']['link_urls'] = 1;
   $handler->display->display_options['fields']['text']['link_usernames'] = 1;
   $handler->display->display_options['fields']['text']['link_hashtags'] = 1;
@@ -88,33 +70,6 @@ function twitter_views_default_views() {
   $handler->display->display_options['sorts']['created_time']['table'] = 'twitter';
   $handler->display->display_options['sorts']['created_time']['field'] = 'created_time';
   $handler->display->display_options['sorts']['created_time']['order'] = 'DESC';
-  /* Contextual filter: User: Uid */
-  $handler->display->display_options['arguments']['uid']['id'] = 'uid';
-  $handler->display->display_options['arguments']['uid']['table'] = 'users';
-  $handler->display->display_options['arguments']['uid']['field'] = 'uid';
-  $handler->display->display_options['arguments']['uid']['default_action'] = 'default';
-  $handler->display->display_options['arguments']['uid']['exception']['title_enable'] = 1;
-  $handler->display->display_options['arguments']['uid']['exception']['title'] = 'All author';
-  $handler->display->display_options['arguments']['uid']['title_enable'] = 1;
-  $handler->display->display_options['arguments']['uid']['title'] = '%1\'s tweets';
-  $handler->display->display_options['arguments']['uid']['default_argument_type'] = 'user';
-  $handler->display->display_options['arguments']['uid']['default_argument_options']['user'] = TRUE;
-  $handler->display->display_options['arguments']['uid']['summary']['format'] = 'default_summary';
-  $handler->display->display_options['arguments']['uid']['specify_validation'] = 1;
-  $handler->display->display_options['arguments']['uid']['validate']['type'] = 'php';
-  $handler->display->display_options['arguments']['uid']['validate_options']['code'] = '$uid = arg(1);
-                       if (!empty($uid)) {
-                         $account = user_load($uid);
-                         $twitter_accounts = twitter_twitter_accounts($account);
-                         if(count($twitter_accounts)) {
-                           return TRUE;
-                         }
-                         else {
-                           return FALSE;
-                         }
-                       }';
-  $handler->display->display_options['arguments']['uid']['break_phrase'] = 0;
-  $handler->display->display_options['arguments']['uid']['not'] = 0;
   /* Filter criterion: Twitter: Protected status */
   $handler->display->display_options['filters']['protected']['id'] = 'protected';
   $handler->display->display_options['filters']['protected']['table'] = 'twitter_account';
@@ -123,15 +78,166 @@ function twitter_views_default_views() {
   $handler->display->display_options['filters']['protected']['group'] = '0';
   $handler->display->display_options['filters']['protected']['expose']['operator'] = FALSE;
 
-  /* Display: Page */
-  $handler = $view->new_display('page', 'Page', 'page');
-  $handler->display->display_options['path'] = 'user/%/tweets';
-  $handler->display->display_options['menu']['type'] = 'tab';
-  $handler->display->display_options['menu']['title'] = 'Twitter';
-  $handler->display->display_options['menu']['weight'] = '1';
+  /* Display: Tweets Raw */
+  $handler = $view->new_display('page', 'Tweets Raw', 'page_all_tweets');
+  $handler->display->display_options['defaults']['title'] = FALSE;
+  $handler->display->display_options['title'] = 'Tweets';
+  $handler->display->display_options['display_description'] = 'Lists tweets within a table.';
+  $handler->display->display_options['defaults']['hide_admin_links'] = FALSE;
+  $handler->display->display_options['defaults']['pager'] = FALSE;
+  $handler->display->display_options['pager']['type'] = 'full';
+  $handler->display->display_options['pager']['options']['items_per_page'] = '20';
+  $handler->display->display_options['pager']['options']['offset'] = '0';
+  $handler->display->display_options['pager']['options']['id'] = '0';
+  $handler->display->display_options['pager']['options']['quantity'] = '9';
+  $handler->display->display_options['defaults']['style_plugin'] = FALSE;
+  $handler->display->display_options['style_plugin'] = 'table';
+  $handler->display->display_options['style_options']['columns'] = array(
+    'twitter_id' => 'twitter_id',
+    'profile_image_url' => 'profile_image_url',
+    'name' => 'name',
+    'screen_name' => 'screen_name',
+    'follow' => 'follow',
+    'text' => 'text',
+    'created_time' => 'created_time',
+    'web_intents' => 'web_intents',
+  );
+  $handler->display->display_options['style_options']['default'] = '-1';
+  $handler->display->display_options['style_options']['info'] = array(
+    'twitter_id' => array(
+      'sortable' => 0,
+      'default_sort_order' => 'asc',
+      'align' => '',
+      'separator' => '',
+      'empty_column' => 0,
+    ),
+    'profile_image_url' => array(
+      'sortable' => 0,
+      'default_sort_order' => 'asc',
+      'align' => '',
+      'separator' => '',
+      'empty_column' => 0,
+    ),
+    'name' => array(
+      'sortable' => 0,
+      'default_sort_order' => 'asc',
+      'align' => '',
+      'separator' => '',
+      'empty_column' => 0,
+    ),
+    'screen_name' => array(
+      'sortable' => 0,
+      'default_sort_order' => 'asc',
+      'align' => '',
+      'separator' => '',
+      'empty_column' => 0,
+    ),
+    'follow' => array(
+      'align' => '',
+      'separator' => '',
+      'empty_column' => 0,
+    ),
+    'text' => array(
+      'sortable' => 0,
+      'default_sort_order' => 'asc',
+      'align' => '',
+      'separator' => '',
+      'empty_column' => 0,
+    ),
+    'created_time' => array(
+      'sortable' => 0,
+      'default_sort_order' => 'asc',
+      'align' => '',
+      'separator' => '',
+      'empty_column' => 0,
+    ),
+    'web_intents' => array(
+      'align' => '',
+      'separator' => '',
+      'empty_column' => 0,
+    ),
+  );
+  $handler->display->display_options['defaults']['style_options'] = FALSE;
+  $handler->display->display_options['defaults']['row_plugin'] = FALSE;
+  $handler->display->display_options['defaults']['row_options'] = FALSE;
+  $handler->display->display_options['defaults']['fields'] = FALSE;
+  /* Field: Twitter: Twitter status message ID */
+  $handler->display->display_options['fields']['twitter_id']['id'] = 'twitter_id';
+  $handler->display->display_options['fields']['twitter_id']['table'] = 'twitter';
+  $handler->display->display_options['fields']['twitter_id']['field'] = 'twitter_id';
+  $handler->display->display_options['fields']['twitter_id']['label'] = '';
+  $handler->display->display_options['fields']['twitter_id']['exclude'] = TRUE;
+  $handler->display->display_options['fields']['twitter_id']['element_label_colon'] = FALSE;
+  /* Field: Twitter: Profile image */
+  $handler->display->display_options['fields']['profile_image_url']['id'] = 'profile_image_url';
+  $handler->display->display_options['fields']['profile_image_url']['table'] = 'twitter_account';
+  $handler->display->display_options['fields']['profile_image_url']['field'] = 'profile_image_url';
+  $handler->display->display_options['fields']['profile_image_url']['label'] = '';
+  /* Field: Twitter: Full name */
+  $handler->display->display_options['fields']['name']['id'] = 'name';
+  $handler->display->display_options['fields']['name']['table'] = 'twitter_account';
+  $handler->display->display_options['fields']['name']['field'] = 'name';
+  $handler->display->display_options['fields']['name']['label'] = '';
+  $handler->display->display_options['fields']['name']['alter']['make_link'] = TRUE;
+  $handler->display->display_options['fields']['name']['alter']['path'] = 'https://twitter.com/[screen_name]';
+  $handler->display->display_options['fields']['name']['alter']['absolute'] = TRUE;
+  $handler->display->display_options['fields']['name']['alter']['target'] = '_blank';
+  $handler->display->display_options['fields']['name']['element_label_colon'] = FALSE;
+  /* Field: Twitter: Login name */
+  $handler->display->display_options['fields']['screen_name']['id'] = 'screen_name';
+  $handler->display->display_options['fields']['screen_name']['table'] = 'twitter';
+  $handler->display->display_options['fields']['screen_name']['field'] = 'screen_name';
+  $handler->display->display_options['fields']['screen_name']['label'] = '';
+  $handler->display->display_options['fields']['screen_name']['alter']['make_link'] = TRUE;
+  $handler->display->display_options['fields']['screen_name']['alter']['path'] = 'https://twitter.com/[screen_name] ';
+  $handler->display->display_options['fields']['screen_name']['alter']['absolute'] = TRUE;
+  $handler->display->display_options['fields']['screen_name']['alter']['prefix'] = '@';
+  $handler->display->display_options['fields']['screen_name']['alter']['target'] = '_blank';
+  $handler->display->display_options['fields']['screen_name']['element_label_colon'] = FALSE;
+  /* Field: Twitter: Follow */
+  $handler->display->display_options['fields']['follow']['id'] = 'follow';
+  $handler->display->display_options['fields']['follow']['table'] = 'twitter';
+  $handler->display->display_options['fields']['follow']['field'] = 'follow';
+  $handler->display->display_options['fields']['follow']['label'] = '';
+  $handler->display->display_options['fields']['follow']['element_label_colon'] = FALSE;
+  /* Field: Twitter: Message text */
+  $handler->display->display_options['fields']['text']['id'] = 'text';
+  $handler->display->display_options['fields']['text']['table'] = 'twitter';
+  $handler->display->display_options['fields']['text']['field'] = 'text';
+  $handler->display->display_options['fields']['text']['label'] = '';
+  $handler->display->display_options['fields']['text']['element_label_colon'] = FALSE;
+  $handler->display->display_options['fields']['text']['link_urls'] = 1;
+  $handler->display->display_options['fields']['text']['link_usernames'] = 1;
+  $handler->display->display_options['fields']['text']['link_hashtags'] = 1;
+  /* Field: Twitter: Created time */
+  $handler->display->display_options['fields']['created_time']['id'] = 'created_time';
+  $handler->display->display_options['fields']['created_time']['table'] = 'twitter';
+  $handler->display->display_options['fields']['created_time']['field'] = 'created_time';
+  $handler->display->display_options['fields']['created_time']['label'] = '';
+  $handler->display->display_options['fields']['created_time']['date_format'] = 'time ago';
+  /* Field: Twitter: Web Intents */
+  $handler->display->display_options['fields']['web_intents']['id'] = 'web_intents';
+  $handler->display->display_options['fields']['web_intents']['table'] = 'twitter';
+  $handler->display->display_options['fields']['web_intents']['field'] = 'web_intents';
+  $handler->display->display_options['fields']['web_intents']['label'] = '';
+  $handler->display->display_options['fields']['web_intents']['element_label_colon'] = FALSE;
+  $handler->display->display_options['defaults']['arguments'] = FALSE;
+  /* Contextual filter: Twitter: Login name */
+  $handler->display->display_options['arguments']['screen_name']['id'] = 'screen_name';
+  $handler->display->display_options['arguments']['screen_name']['table'] = 'twitter';
+  $handler->display->display_options['arguments']['screen_name']['field'] = 'screen_name';
+  $handler->display->display_options['arguments']['screen_name']['title_enable'] = TRUE;
+  $handler->display->display_options['arguments']['screen_name']['title'] = 'Tweets by @%1';
+  $handler->display->display_options['arguments']['screen_name']['default_argument_type'] = 'fixed';
+  $handler->display->display_options['arguments']['screen_name']['summary']['number_of_records'] = '0';
+  $handler->display->display_options['arguments']['screen_name']['summary']['format'] = 'default_summary';
+  $handler->display->display_options['arguments']['screen_name']['summary_options']['items_per_page'] = '25';
+  $handler->display->display_options['arguments']['screen_name']['limit'] = '0';
+  $handler->display->display_options['path'] = 'tweets-raw';
 
   /* Display: Block */
   $handler = $view->new_display('block', 'Block', 'block');
+  $handler->display->display_options['defaults']['hide_admin_links'] = FALSE;
   $handler->display->display_options['defaults']['use_more'] = FALSE;
   $handler->display->display_options['use_more'] = TRUE;
   $handler->display->display_options['defaults']['style_plugin'] = FALSE;
@@ -152,27 +258,11 @@ function twitter_views_default_views() {
   $handler->display->display_options['fields']['text']['table'] = 'twitter';
   $handler->display->display_options['fields']['text']['field'] = 'text';
   $handler->display->display_options['fields']['text']['label'] = '';
-  $handler->display->display_options['fields']['text']['alter']['alter_text'] = 0;
-  $handler->display->display_options['fields']['text']['alter']['make_link'] = 0;
-  $handler->display->display_options['fields']['text']['alter']['absolute'] = 0;
-  $handler->display->display_options['fields']['text']['alter']['external'] = 0;
-  $handler->display->display_options['fields']['text']['alter']['replace_spaces'] = 0;
-  $handler->display->display_options['fields']['text']['alter']['trim_whitespace'] = 0;
-  $handler->display->display_options['fields']['text']['alter']['nl2br'] = 0;
-  $handler->display->display_options['fields']['text']['alter']['word_boundary'] = 1;
-  $handler->display->display_options['fields']['text']['alter']['ellipsis'] = 1;
-  $handler->display->display_options['fields']['text']['alter']['more_link'] = 0;
-  $handler->display->display_options['fields']['text']['alter']['strip_tags'] = 0;
-  $handler->display->display_options['fields']['text']['alter']['trim'] = 0;
-  $handler->display->display_options['fields']['text']['alter']['html'] = 0;
   $handler->display->display_options['fields']['text']['element_label_colon'] = FALSE;
-  $handler->display->display_options['fields']['text']['element_default_classes'] = 1;
-  $handler->display->display_options['fields']['text']['hide_empty'] = 0;
-  $handler->display->display_options['fields']['text']['empty_zero'] = 0;
-  $handler->display->display_options['fields']['text']['hide_alter_empty'] = 1;
   $handler->display->display_options['fields']['text']['link_urls'] = 1;
   $handler->display->display_options['fields']['text']['link_usernames'] = 1;
   $handler->display->display_options['fields']['text']['link_hashtags'] = 1;
+  $handler->display->display_options['fields']['text']['link_attributes'] = 1;
   /* Field: Twitter: Created time */
   $handler->display->display_options['fields']['created_time']['id'] = 'created_time';
   $handler->display->display_options['fields']['created_time']['table'] = 'twitter';
@@ -180,7 +270,53 @@ function twitter_views_default_views() {
   $handler->display->display_options['fields']['created_time']['label'] = '';
   $handler->display->display_options['fields']['created_time']['date_format'] = 'time ago';
   $handler->display->display_options['defaults']['arguments'] = FALSE;
-  $handler->display->display_options['block_description'] = 'User Tweets';
+  $handler->display->display_options['block_description'] = 'Tweets';
+
+  /* Display: Tweets */
+  $handler = $view->new_display('page', 'Tweets', 'page_2');
+  $handler->display->display_options['defaults']['title'] = FALSE;
+  $handler->display->display_options['title'] = 'Tweets';
+  $handler->display->display_options['display_description'] = 'Displays a formatted list of tweets';
+  $handler->display->display_options['defaults']['hide_admin_links'] = FALSE;
+  $handler->display->display_options['defaults']['pager'] = FALSE;
+  $handler->display->display_options['pager']['type'] = 'full';
+  $handler->display->display_options['pager']['options']['items_per_page'] = '20';
+  $handler->display->display_options['pager']['options']['offset'] = '0';
+  $handler->display->display_options['pager']['options']['id'] = '0';
+  $handler->display->display_options['pager']['options']['quantity'] = '9';
+  $handler->display->display_options['defaults']['style_plugin'] = FALSE;
+  $handler->display->display_options['style_plugin'] = 'list';
+  $handler->display->display_options['defaults']['style_options'] = FALSE;
+  $handler->display->display_options['defaults']['row_plugin'] = FALSE;
+  $handler->display->display_options['row_plugin'] = 'fields';
+  $handler->display->display_options['defaults']['row_options'] = FALSE;
+  $handler->display->display_options['defaults']['fields'] = FALSE;
+  /* Field: Twitter: Twitter status message ID */
+  $handler->display->display_options['fields']['twitter_id']['id'] = 'twitter_id';
+  $handler->display->display_options['fields']['twitter_id']['table'] = 'twitter';
+  $handler->display->display_options['fields']['twitter_id']['field'] = 'twitter_id';
+  $handler->display->display_options['fields']['twitter_id']['label'] = '';
+  $handler->display->display_options['fields']['twitter_id']['exclude'] = TRUE;
+  $handler->display->display_options['fields']['twitter_id']['element_label_colon'] = FALSE;
+  /* Field: Twitter: Formatted tweet */
+  $handler->display->display_options['fields']['formatted_tweet']['id'] = 'formatted_tweet';
+  $handler->display->display_options['fields']['formatted_tweet']['table'] = 'twitter';
+  $handler->display->display_options['fields']['formatted_tweet']['field'] = 'formatted_tweet';
+  $handler->display->display_options['fields']['formatted_tweet']['label'] = '';
+  $handler->display->display_options['fields']['formatted_tweet']['element_label_colon'] = FALSE;
+  $handler->display->display_options['defaults']['arguments'] = FALSE;
+  /* Contextual filter: Twitter: Login name */
+  $handler->display->display_options['arguments']['screen_name']['id'] = 'screen_name';
+  $handler->display->display_options['arguments']['screen_name']['table'] = 'twitter';
+  $handler->display->display_options['arguments']['screen_name']['field'] = 'screen_name';
+  $handler->display->display_options['arguments']['screen_name']['title_enable'] = TRUE;
+  $handler->display->display_options['arguments']['screen_name']['title'] = 'Tweets by @%1';
+  $handler->display->display_options['arguments']['screen_name']['default_argument_type'] = 'fixed';
+  $handler->display->display_options['arguments']['screen_name']['summary']['number_of_records'] = '0';
+  $handler->display->display_options['arguments']['screen_name']['summary']['format'] = 'default_summary';
+  $handler->display->display_options['arguments']['screen_name']['summary_options']['items_per_page'] = '25';
+  $handler->display->display_options['arguments']['screen_name']['limit'] = '0';
+  $handler->display->display_options['path'] = 'tweets';
 
   return array('tweets' => $view);
 }
diff --git a/kolab.org/www/drupal-7.18/sites/all/modules/twitter/twitter_actions/twitter_actions.info b/kolab.org/www/drupal-7.18/sites/all/modules/twitter/twitter_actions/twitter_actions.info
index 4d2b6d3..66c6cea 100644
--- a/kolab.org/www/drupal-7.18/sites/all/modules/twitter/twitter_actions/twitter_actions.info
+++ b/kolab.org/www/drupal-7.18/sites/all/modules/twitter/twitter_actions/twitter_actions.info
@@ -2,11 +2,10 @@ name = Twitter actions
 description = Exposes Drupal actions to send Twitter messages.
 core = 7.x
 dependencies[] = twitter
-dependencies[] = oauth_common
 
-; Information added by drupal.org packaging script on 2012-08-11
-version = "7.x-3.2"
+; Information added by drupal.org packaging script on 2013-06-03
+version = "7.x-5.8"
 core = "7.x"
 project = "twitter"
-datestamp = "1344714197"
+datestamp = "1370303463"
 
diff --git a/kolab.org/www/drupal-7.18/sites/all/modules/twitter/twitter_actions/twitter_actions.module b/kolab.org/www/drupal-7.18/sites/all/modules/twitter/twitter_actions/twitter_actions.module
index 25d1fd5..7a36541 100644
--- a/kolab.org/www/drupal-7.18/sites/all/modules/twitter/twitter_actions/twitter_actions.module
+++ b/kolab.org/www/drupal-7.18/sites/all/modules/twitter/twitter_actions/twitter_actions.module
@@ -36,31 +36,25 @@ function twitter_actions_action_info() {
 /**
  * Returns a form definition so the Twitter action can be configured.
  *
- * @param $context
+ * @param array $context
  *   Default values (if we are editing an existing action instance).
  * @return
  *   Form definition.
  */
 function twitter_actions_set_status_action_form($context) {
-  $options = array();
-  $results = db_query("SELECT screen_name FROM {twitter_account}");
-  foreach ($results as $result) {
-    $options[$result->screen_name] = $result->screen_name;
-  }
+  $options = twitter_actions_account_options();
   // Set default values for form.
   $form['screen_name'] = array(
     '#type' => 'select',
     '#title' => t('Twitter account name'),
-    '#options'       => $options,
+    '#description' => t('Twitter account which will be used. ' .
+      'By selecting [current user] the rule will check if the user ' .
+      'has authenticated a Twitter account to use.'),
+    '#options' => $options,
     '#default_value' => isset($context['screen_name']) ? $context['screen_name'] : '',
     '#required' => TRUE,
   );
 
-  if (!count($options)) {
-    $form['screen_name']['#description'] = t('You first need to add a Twitter account to one of ' .
-                                             'your users with rights for posting to Twitter.');
-  }
-
   $form['message'] = array(
     '#type' => 'textarea',
     '#title' => t('Message'),
@@ -77,14 +71,21 @@ function twitter_actions_set_status_action_form($context) {
 }
 
 /**
- * Verifies if Oauth module has been setup and also checks the Twitter
- * authentication against the provided screen name.
+ * Returns a list of authenticated Twitter accounts to be used as options.
+ *
+ * @return
+ *   array of screen_name => screen_name entries.
  */
-function twitter_actions_set_status_action_validate($form, $form_state) {
-  if (!_twitter_use_oauth()) {
-    form_set_error('screen_name', t('Oauth has not been setup yet. Please go to !link and follow steps.',
-                                     array('!link' => l(t('Twitter settings'), 'admin/settings/twitter'))));
+function twitter_actions_account_options() {
+  module_load_include('inc', 'twitter');
+  $twitter_accounts = twitter_load_authenticated_accounts();
+  $options = array();
+  foreach ($twitter_accounts as $twitter_account) {
+    $options[$twitter_account->screen_name] = '@' . $twitter_account->screen_name;
   }
+  // Extra token to use current user's account.
+  $options['[current user]'] = '[current user]';
+  return $options;
 }
 
 /**
@@ -93,11 +94,9 @@ function twitter_actions_set_status_action_validate($form, $form_state) {
  */
 function twitter_actions_set_status_action_submit($form, $form_state) {
   $form_values = $form_state['values'];
-  $twitter_uid = db_query("SELECT twitter_uid FROM {twitter_account} WHERE screen_name = :screen_name", array(':screen_name' => $form_values['screen_name']))->fetchField();
   // Process the HTML form to store configuration. The keyed array that
   // we return will be serialized to the database.
   $params = array(
-    'twitter_uid' => $twitter_uid,
     'screen_name' => $form_values['screen_name'],
     'message' => $form_values['message'],
   );
@@ -106,7 +105,39 @@ function twitter_actions_set_status_action_submit($form, $form_state) {
 }
 
 /**
- * Implementation of a configurable Twitter actions.
+ * Validates the Twitter account to use to send a Tweet.
+ *
+ * If it is a Twitter account, it will check it still exists.
+ * If it is [current user], it will see if the current user has an
+ * authenticated Twitter account to use.
+ *
+ * @param string $screen_name
+ *   The selected value that represents a Twitter account to use.
+ * @return
+ *   Integer the Twitter ID of the account to use or NULL.
+ */
+function _twitter_actions_get_twitter_id($screen_name) {
+  $twitter_uid = NULL;
+  // Find out the Twitter ID to use.
+  if ($screen_name == '[current user]') {
+    // Check if this user has an authenticated account.
+    global $user;
+    $account = user_load($user->uid);
+    foreach ($account->twitter_accounts as $twitter_account) {
+      if ($twitter_account->is_auth()) {
+        $twitter_uid = $twitter_account->id;
+      }
+    }
+  }
+  else {
+    $twitter_uid = db_query("SELECT twitter_uid FROM {twitter_account} WHERE screen_name = :screen_name",
+      array(':screen_name' => $screen_name))->fetchField();
+  }
+  return $twitter_uid;
+}
+
+/**
+ * Implementation of a configurable Twitter action.
  * @todo Implementation for language negotiation for the body and sumary. Also
  * need implementation for bodies with multiple values. Right now it is hard
  * coded and it will only get body and summary for 'und' language and only
@@ -114,75 +145,86 @@ function twitter_actions_set_status_action_submit($form, $form_state) {
  * If the final message is over 140 chars, there is no feedback to the user.
  */
 function twitter_actions_set_status_action($object, $context) {
-  global $user;
-  $variables['%site_name'] = variable_get('site_name', 'Drupal');
-  // Seting variables array depending on action's group
-  switch ($context['group']) {
-    case 'node':
-      $node = $context['node'];
-      if (isset($node)) {
-        $variables = array_merge($variables, array(
-          '%uid' => $node->uid,
-          '%username' => $node->name,
-          '%node_url' => url('node/' . $node->nid, array('absolute' => TRUE)),
-          '%node_type' => node_type_get_name($node),
-          '%title' => $node->title,
-          '%summary' => isset($node->body['und'][0]['value']) ? $node->body['und'][0]['summary'] : '',
-          '%body' => isset($node->body['und'][0]['value']) ? $node->body['und'][0]['value'] : '',
-          )
-        );
-      }
-      break;
+  $twitter_uid = _twitter_actions_get_twitter_id($context['screen_name']);
+  if ($twitter_uid) {
+    global $user;
+    $variables['%site_name'] = variable_get('site_name', 'Drupal');
+    // Seting variables array depending on action's group
+    switch ($context['group']) {
+      case 'node':
+        $node = $context['node'];
+        if (isset($node)) {
+          $variables = array_merge($variables, array(
+            '%uid' => $node->uid,
+            '%username' => $node->name,
+            '%node_url' => url('node/' . $node->nid, array('absolute' => TRUE)),
+            '%node_type' => node_type_get_name($node),
+            '%title' => $node->title,
+            '%summary' => isset($node->body['und'][0]['value']) ? $node->body['und'][0]['summary'] : '',
+            '%body' => isset($node->body['und'][0]['value']) ? $node->body['und'][0]['value'] : '',
+            )
+          );
+        }
+        break;
 
-    case 'comment':
-      $node = node_load($context['comment']->nid);
-      if (isset($node)) {
-        $variables = array_merge($variables, array(
-          '%uid' => $context['comment']->uid,
-          '%username' => $context['comment']->name,
-          '%node_url' => url('node/' . $node->nid, array('absolute' => TRUE)),
-          '%node_type' => node_type_get_name($node),
-          '%title' => $node->title,
-          '%summary' => isset($node->body['und'][0]['value']) ? $node->body['und'][0]['summary'] : '',
-          '%body' => isset($node->body['und'][0]['value']) ? $node->body['und'][0]['value'] : '',
-          )
-        );
-      }
-      break;
+      case 'comment':
+        $node = node_load($context['comment']->nid);
+        if (isset($node)) {
+          $variables = array_merge($variables, array(
+            '%uid' => $context['comment']->uid,
+            '%username' => $context['comment']->name,
+            '%node_url' => url('node/' . $node->nid, array('absolute' => TRUE)),
+            '%node_type' => node_type_get_name($node),
+            '%title' => $node->title,
+            '%summary' => isset($node->body[LANGUAGE_NONE][0]['value']) ? $node->body['und'][0]['summary'] : '',
+            '%body' => isset($node->body[LANGUAGE_NONE][0]['value']) ? $node->body['und'][0]['value'] : '',
+            )
+          );
+        }
+        break;
 
-    case 'user':
-      $variables['%username'] = $context['user']->name;
-      break;
+      case 'user':
+        $variables['%username'] = $context['user']->name;
+        break;
 
-    case 'cron':
-      break;
+      case 'cron':
+        break;
 
-    default:
-      // We are being called directly.
-      $node = $object;
-      if (isset($node)  && is_object($node)) {
-        $variables = array_merge($variables, array(
-          '%uid' => $node->uid,
-          '%username' => $node->name,
-          '%node_url' => url('node/' . $node->nid, array('absolute' => TRUE)),
-          '%node_type' => node_type_get_name($node),
-          '%title' => $node->title,
-          '%summary' => isset($node->body['und'][0]['value']) ? $node->body['und'][0]['summary'] : '',
-          '%body' => isset($node->body['und'][0]['value']) ? $node->body['und'][0]['value'] : '',
-          )
-        );
-      }
-  }
+      default:
+        // We are being called directly.
+        $node = $object;
+        if (isset($node)  && is_object($node)) {
+          $variables = array_merge($variables, array(
+            '%uid' => $node->uid,
+            '%username' => $node->name,
+            '%node_url' => url('node/' . $node->nid, array('absolute' => TRUE)),
+            '%node_type' => node_type_get_name($node),
+            '%title' => $node->title,
+            '%summary' => isset($node->body[LANGUAGE_NONE][0]['value']) ? $node->body['und'][0]['summary'] : '',
+            '%body' => isset($node->body[LANGUAGE_NONE][0]['value']) ? $node->body['und'][0]['value'] : '',
+            )
+          );
+        }
+    }
 
-  // Only make a tinyurl if it was asked for.
-  if (strstr($context['message'], '%tinyurl') !== FALSE) {
-    $variables = array_merge($variables, array(
-      '%tinyurl' => twitter_shorten_url(url('node/' . $node->nid, array('absolute' => TRUE))),
-    ));
-  }
+    // Only make a tinyurl if it was asked for.
+    if (strstr($context['message'], '%tinyurl') !== FALSE) {
+      $variables = array_merge($variables, array(
+        '%tinyurl' => twitter_shorten_url(url('node/' . $node->nid, array('absolute' => TRUE))),
+      ));
+    }
 
-  $message = strtr($context['message'], $variables);
-  module_load_include('inc', 'twitter');
-  $twitter_account = twitter_account_load($context['twitter_uid']);
-  twitter_set_status($twitter_account, $message);
+    // Send the tweet.
+    $message = strtr($context['message'], $variables);
+    module_load_include('inc', 'twitter');
+    try {
+      $twitter_account = twitter_account_load($twitter_uid);
+      twitter_set_status($twitter_account, $message);
+      drupal_set_message(t('Successfully posted to Twitter'));
+    }
+    catch (TwitterException $e) {
+      drupal_set_message(t('An error occurred when posting to Twitter: @message',
+                           array('@message' => $e->getMessage())), 'warning');
+    }
+  }
 }
diff --git a/kolab.org/www/drupal-7.18/sites/all/modules/twitter/twitter_actions/twitter_actions.rules.inc b/kolab.org/www/drupal-7.18/sites/all/modules/twitter/twitter_actions/twitter_actions.rules.inc
index 1efac55..7b77043 100644
--- a/kolab.org/www/drupal-7.18/sites/all/modules/twitter/twitter_actions/twitter_actions.rules.inc
+++ b/kolab.org/www/drupal-7.18/sites/all/modules/twitter/twitter_actions/twitter_actions.rules.inc
@@ -1,11 +1,11 @@
 <?php
 /**
  * @file
- * Provide better intergration into the rules module
+ * Provides Rules integration in order to post tweets.
  */
 
 /**
- * Implements hook_rules_action_info() on behalf of the twitter module.
+ * Implements hook_rules_action_info() on behalf of the Twitter module.
  */
 function twitter_actions_rules_action_info() {
   return array(
@@ -19,9 +19,14 @@ function twitter_actions_rules_action_info() {
           'description' => t("The content of the tweet."),
         ),
         'sender' => array(
-          'type' => 'user',
+          'type' => 'text',
           'label' => t('Sender'),
-          'description' => t("User whose Twitter account will be used."),
+          'description' => t('Twitter account which will be used. ' .
+            'By selecting [current user] the rule will check if the user ' .
+            'has authenticated a Twitter account to use.'),
+          'options list' => '_twitter_rules_account_options',
+          'restriction' => 'input',
+          'default mode' => 'input',
         ),
       ),
       'base' => 'twitter_actions_set_status',
@@ -31,7 +36,18 @@ function twitter_actions_rules_action_info() {
 }
 
 /**
- * Fetches Twitter account info and submits with the message to the Twitter API
+ * Returns the list of Twitter accounts to be used for posting.
+ *
+ * @return
+ *   an array of Twitter accounts.
+ * @see twitter_actions_account_options()
+ */
+function _twitter_rules_account_options(RulesPlugin $element, $param_name) {
+  return twitter_actions_account_options();
+}
+
+/**
+ * Fetches Twitter account info and submits with the message to the Twitter API.
  *
  * @param $message
  *   The message to post
@@ -39,21 +55,22 @@ function twitter_actions_rules_action_info() {
  *   The Drupal user that has a Twitter account
  */
 function twitter_actions_set_status($message, $sender) {
-  if ($twitter_uid = db_query("SELECT twitter_uid FROM {twitter_account} WHERE uid = :uid", array(':uid' => $sender->uid))->fetchField()) {
-    module_load_include('inc', 'twitter');
-    $twitter_account = twitter_account_load($twitter_uid);
-    try {
-      twitter_set_status($twitter_account, $message);
-      drupal_set_message(t('Successfully posted to Twitter'));
-    }
-    catch (TwitterException $e) {
-      drupal_set_message(t('An error occurred when posting to Twitter: @message',
-                           array('@message' => $e->getMessage())), 'warning');
-    }
+  $twitter_uid = _twitter_actions_get_twitter_id($sender);
+  if (!isset($twitter_uid)) {
+    // No Twitter authenticated account found.
+    return;
+  }
+
+  // Send tweet.
+  module_load_include('inc', 'twitter');
+  $twitter_account = twitter_account_load($twitter_uid);
+  try {
+    twitter_set_status($twitter_account, $message);
+    drupal_set_message(t('Successfully posted to Twitter'));
   }
-  else {
-    watchdog('twitter', 'Could not find the Twitter account to be used for posting. Please review the rule settings.',
-             array(), WATCHDOG_ERROR);
+  catch (TwitterException $e) {
+    drupal_set_message(t('An error occurred when posting to Twitter: @message',
+                         array('@message' => $e->getMessage())), 'warning');
   }
 }
 
@@ -61,23 +78,7 @@ function twitter_actions_set_status($message, $sender) {
  * Implements hook_rules_condition_info().
  */
 function twitter_actions_rules_condition_info() {
-  $defaults = array(
-    'group' => t('Twitter'),
-    'parameter' => array(
-      'user' => array(
-        'type' => 'user',
-        'label' => t('User'),
-        'description' => t('The user to be checked for.'),
-      ),
-    ),
-    'named parameter' => TRUE,
-    'access callback' => 'rules_twitter_actions_access_callback',
-  );
-  $items['rules_core_twitter_conditions_user_has_linked'] = $defaults + array(
-    'label' => t('User has linked Twitter account'),
-    'help' => t('Evaluates to TRUE in case there is a record in the twitter_account for the provided user.'),
-    'base' => 'twitter_actions_has_linked',
-  );
+  $items = array();
 
   $items['rules_core_twitter_conditions_text_is_under_140'] = array(
     'group' => t('Twitter'),
@@ -99,20 +100,9 @@ function twitter_actions_rules_condition_info() {
 /**
  * The callback function for the Rules condition
  * @param $element
- *   $element['user']: The user to be checked for.
- * @return
- *   TRUE if the user has linked his/her Twitter account.
- */
-function twitter_actions_has_linked($element) {
-  return db_query("SELECT twitter_uid FROM {twitter_account} WHERE uid = :uid", array(':uid' => $element['user']->uid))->fetchField() ? TRUE : FALSE;
-}
-
-/**
- * The callback function for the Rules condition
- * @param $element
- *   $element['user']: The user to be checked for.
+ *   $element['text']: The text of the message.
  * @return
- *   TRUE if the user has linked his/her Twitter account.
+ *   TRUE if the message length is less than 141 characters.
  */
 function twitter_actions_less_140($element) {
   return strlen($element['text']) < 141;
diff --git a/kolab.org/www/drupal-7.18/sites/all/modules/twitter/twitter_post/twitter_post.info b/kolab.org/www/drupal-7.18/sites/all/modules/twitter/twitter_post/twitter_post.info
index f515a7f..2ff82ce 100644
--- a/kolab.org/www/drupal-7.18/sites/all/modules/twitter/twitter_post/twitter_post.info
+++ b/kolab.org/www/drupal-7.18/sites/all/modules/twitter/twitter_post/twitter_post.info
@@ -2,11 +2,11 @@ name = Twitter Post
 description = Enables posting to twitter
 core = 7.x
 dependencies[] = twitter
-dependencies[] = oauth_common
+configure = admin/config/services/twitter/post
 
-; Information added by drupal.org packaging script on 2012-08-11
-version = "7.x-3.2"
+; Information added by drupal.org packaging script on 2013-06-03
+version = "7.x-5.8"
 core = "7.x"
 project = "twitter"
-datestamp = "1344714197"
+datestamp = "1370303463"
 
diff --git a/kolab.org/www/drupal-7.18/sites/all/modules/twitter/twitter_post/twitter_post.install b/kolab.org/www/drupal-7.18/sites/all/modules/twitter/twitter_post/twitter_post.install
new file mode 100644
index 0000000..af71041
--- /dev/null
+++ b/kolab.org/www/drupal-7.18/sites/all/modules/twitter/twitter_post/twitter_post.install
@@ -0,0 +1,40 @@
+<?php
+/**
+ * @file
+ * Install, update and uninstall functions for the twitter module.
+ *
+ */
+
+/**
+ * Implements hook_uninstall().
+ */
+function twitter_post_uninstall() {
+  variable_del('twitter_post_types');
+  variable_del('twitter_post_default_format');
+  variable_del('twitter_post_default_value');
+}
+
+/**
+ * Don't default to TinyURL any more.
+ */
+function twitter_post_update_7300() {
+  if (variable_get('twitter_post_default_format', NULL) == 'New post: !title !tinyurl') {
+    variable_set('twitter_post_default_format', "New post: !title !url-alias");
+  }
+  return t('Twitter Post now defaults to using aliases, not TinyURL.');
+}
+
+/**
+ * Implements hook_update_N().
+ *
+ * Increase module weight to run after pathauto.
+ */
+function twitter_post_update_7301() {
+  db_update('system')
+    ->fields(array(
+      'weight' => 10,
+    ))
+    ->condition('name', 'twitter_post', '=')
+    ->execute();
+  return t('Updated system weight to 10 for Twitter Post.');
+}
diff --git a/kolab.org/www/drupal-7.18/sites/all/modules/twitter/twitter_post/twitter_post.module b/kolab.org/www/drupal-7.18/sites/all/modules/twitter/twitter_post/twitter_post.module
index b1debd3..0effd13 100644
--- a/kolab.org/www/drupal-7.18/sites/all/modules/twitter/twitter_post/twitter_post.module
+++ b/kolab.org/www/drupal-7.18/sites/all/modules/twitter/twitter_post/twitter_post.module
@@ -1,8 +1,7 @@
 <?php
-
 /**
  * @file
- * Main hooks for twitter post module
+ * Hook implementations for twitter_post module.
  */
 
 /**
@@ -39,16 +38,14 @@ function twitter_post_permission() {
 function twitter_post_form_alter(&$form, $form_state, $form_id) {
   // Alter any node forms.
   if (isset($form['#node']) && $form['#node']->type . '_node_form' == $form_id) {
-    // If we haven't enabled Twitter posting on this node type, nothing to do
-    // here.
+    // If we haven't enabled Twitter posting on this node type, nothing to do here.
     $type = $form['#node']->type;
-    $allowed_types = variable_get('twitter_post_types', array('story' => 'story', 'blog' => 'blog'));
+    $allowed_types = variable_get('twitter_post_types', array());
     if (empty($allowed_types[$type])) {
       return;
     }
 
     module_load_include('inc', 'twitter');
-
     $twitter_form = twitter_post_form();
     if (!$twitter_form) {
       return;
@@ -64,11 +61,11 @@ function twitter_post_form_alter(&$form, $form_state, $form_id) {
     $form['twitter']['post'] = array(
       '#type' => 'checkbox',
       '#title' => t('Announce this post on Twitter'),
-      '#default_value' => (empty($form['nid']['#value'])),
+      '#default_value' => variable_get('twitter_post_default_value', 0),
       '#id' => 'twitter-toggle',
     );
     $form['twitter'] += $twitter_form;
-    $form['twitter']['status']['#default_value'] = variable_get('twitter_post_default_format', 'New post: !title !tinyurl');
+    $form['twitter']['status']['#default_value'] = variable_get('twitter_post_default_format', 'New post: !title !url-alias');
     $form['twitter']['status']['#description'] = t('The given text will be posted to twitter.com. You can use !url, !url-alias, !tinyurl, !title and !user as replacement text.');
     $form['twitter']['status']['#maxlength'] = 150;
   }
@@ -77,7 +74,7 @@ function twitter_post_form_alter(&$form, $form_state, $form_id) {
 /**
  * Implementation of hook_node_insert().
  *
- * Intercepts newly published nodes and posts noticed to Twitter.
+ * Intercepts newly published nodes and posts notices to Twitter.
  */
 function twitter_post_node_insert($node) {
   if (!empty($node->status) && !empty($node->twitter) && !empty($node->twitter['post'])) {
@@ -98,14 +95,9 @@ function twitter_post_node_insert($node) {
     }
 
     $status = strtr($node->twitter['status'], $replacements);
-    try {
-      twitter_set_status($twitter_account, $status);
+    if (twitter_set_status($twitter_account, $status)) {
       drupal_set_message(t('Successfully posted to Twitter'));
     }
-    catch (TwitterException $e) {
-      drupal_set_message(t('An error occurred when posting to Twitter: @message',
-                           array('@message' => $e->getMessage())), 'warning');
-    }
   }
 }
 
@@ -123,8 +115,6 @@ function twitter_post_node_update($node) {
  *   A Drupal user object.
  */
 function twitter_post_form($account = NULL) {
-  drupal_add_js(drupal_get_path('module', 'twitter_post') . '/twitter_post.js');
-
   if (empty($account)) {
     $account = user_load($GLOBALS['user']->uid);
   }
@@ -133,8 +123,10 @@ function twitter_post_form($account = NULL) {
     return;
   }
 
+  drupal_add_js(drupal_get_path('module', 'twitter_post') . '/twitter_post.js');
   $options = array();
-  foreach ($account->twitter_accounts as $twitter_account) {
+  $twitter_accounts = twitter_load_authenticated_accounts();
+  foreach ($twitter_accounts as $twitter_account) {
     $options[$twitter_account->id] = $twitter_account->screen_name;
   }
 
diff --git a/kolab.org/www/drupal-7.18/sites/all/modules/twitter/twitter_post/twitter_post.pages.inc b/kolab.org/www/drupal-7.18/sites/all/modules/twitter/twitter_post/twitter_post.pages.inc
index 5fe9b89..a0bf4cb 100644
--- a/kolab.org/www/drupal-7.18/sites/all/modules/twitter/twitter_post/twitter_post.pages.inc
+++ b/kolab.org/www/drupal-7.18/sites/all/modules/twitter/twitter_post/twitter_post.pages.inc
@@ -10,19 +10,28 @@
  */
 function twitter_post_admin_settings($form, &$form_state) {
   $form['twitter_post_types'] = array(
-    '#type' => 'checkboxes',
     '#title' => t('Node types'),
+    '#type' => 'checkboxes',
+    '#description' => t('Choose which node types should support posting to Twitter.'),
     '#options' => node_type_get_names(),
-    '#default_value' => variable_get('twitter_post_types', array('story' => 'story', 'blog' => 'blog')),
+    '#default_value' => variable_get('twitter_post_types', NULL),
   );
 
   $form['twitter_post_default_format'] = array(
     '#type' => 'textfield',
     '#title' => t('Default format string'),
     '#maxlength' => 140,
-    '#description' => t('The given text will be posted to twitter.com. You can use !url, !url-alias, !tinyurl, !title, and !user as replacement text.'),
+    '#description' => t('The given text will be used as a template for posting to Twitter.com. ' .
+                        'The following token replacements are available: !url, !url-alias, !tinyurl, !title, and !user'),
     '#default_value' => variable_get('twitter_post_default_format', 'New post: !title !tinyurl'),
   );
 
+  $form['twitter_post_default_value'] = array(
+    '#type' => 'checkbox',
+    '#description' => t('When active, tweets will be automatically published.'),
+    '#title' => t('Post to twitter by default'),
+    '#default_value' => variable_get('twitter_post_default_value', 0),
+  );
+
   return system_settings_form($form);
 }
diff --git a/kolab.org/www/drupal-7.18/sites/all/modules/twitter/twitter_signin/twitter_signin.info b/kolab.org/www/drupal-7.18/sites/all/modules/twitter/twitter_signin/twitter_signin.info
index 1ff530c..af708de 100644
--- a/kolab.org/www/drupal-7.18/sites/all/modules/twitter/twitter_signin/twitter_signin.info
+++ b/kolab.org/www/drupal-7.18/sites/all/modules/twitter/twitter_signin/twitter_signin.info
@@ -2,12 +2,11 @@ name = Twitter Signin
 description = Adds support for "Sign in with Twitter"
 core = 7.x
 dependencies[] = twitter
-dependencies[] = oauth_common
 configure = admin/config/services/twitter/signin
 
-; Information added by drupal.org packaging script on 2012-08-11
-version = "7.x-3.2"
+; Information added by drupal.org packaging script on 2013-06-03
+version = "7.x-5.8"
 core = "7.x"
 project = "twitter"
-datestamp = "1344714197"
+datestamp = "1370303463"
 
diff --git a/kolab.org/www/drupal-7.18/sites/all/modules/twitter/twitter_signin/twitter_signin.module b/kolab.org/www/drupal-7.18/sites/all/modules/twitter/twitter_signin/twitter_signin.module
index a4c1bc3..dfea8ef 100644
--- a/kolab.org/www/drupal-7.18/sites/all/modules/twitter/twitter_signin/twitter_signin.module
+++ b/kolab.org/www/drupal-7.18/sites/all/modules/twitter/twitter_signin/twitter_signin.module
@@ -1,4 +1,8 @@
 <?php
+/**
+ * @file
+ * Hook implementations for Twitter Signin module.
+ */
 
 /**
  * Implements hook_menu().
@@ -38,7 +42,7 @@ function twitter_signin_block_info() {
 function twitter_signin_block_view($delta) {
   global $user;
 
-  if (!$user->uid && _twitter_use_oauth()) {
+  if (!$user->uid) {
     $block['subject'] = t('Sign in with Twitter');
     $block['content'] = twitter_signin_button();
     return $block;
@@ -74,13 +78,11 @@ function theme_twitter_signin_button() {
  * Submit handler for Twitter signin.
  */
 function twitter_signin_redirect() {
-  module_load_include('lib.php', 'oauth');
-  module_load_include('lib.php', 'twitter');
   module_load_include('inc', 'twitter');
 
   $key = variable_get('twitter_consumer_key', '');
   $secret = variable_get('twitter_consumer_secret', '');
-  $twitter = new TwitterOAuth($key, $secret);
+  $twitter = new Twitter($key, $secret);
   $token = $twitter->get_request_token();
 
   $_SESSION['twitter_oauth']['token'] = $token;
@@ -93,11 +95,6 @@ function twitter_signin_redirect() {
  * Implements hook_form_alter().
  */
 function twitter_signin_form_alter(&$form, $form_state, $form_id) {
-  // This only applies if we've got OAuth / signin enabled.
-  if (!_twitter_use_oauth()) {
-    return;
-  }
-
   if ($form_id == 'twitter_oauth_callback_form' && isset($_SESSION['twitter_oauth']['signin'])) {
     $form['#submit'] = array_merge(array('twitter_signin_oauth_callback_submit'), $form['#submit']);
   }
@@ -124,7 +121,6 @@ function twitter_signin_form_alter(&$form, $form_state, $form_id) {
  */
 function twitter_signin_oauth_callback_submit(&$form, &$form_state) {
   global $user;
-
   $success = FALSE;
 
   $key = variable_get('twitter_consumer_key', '');
@@ -164,15 +160,20 @@ function twitter_signin_oauth_callback_submit(&$form, &$form_state) {
         $user = $account;
         $form_state['twitter_oauth']['account'] = $account;
         drupal_set_message(t('You have been automatically registered with the password !password. ' .
-          'Copy it to <a href="!link">set your account settings</a>.', array(
+          'Copy it to !link.', array(
             '!password' => $password,
-            '!link' => 'user/' . $account->uid . '/edit',
+            '!link' => l('set your account settings', 'user/' . $account->uid . '/edit'),
           )
         ));
         $success = TRUE;
       }
       else {
-        drupal_set_message(t('The nickname %name is already in use on this site, please register below with a new nick name, or @login to continue.', array('%name' => $response['screen_name'], '@login' => url('user/login'))), 'warning');
+        drupal_set_message(t('The nickname %name is already in use on this site, please register below with a new nick name, or @login to continue.',
+          array(
+            '%name' => $response['screen_name'],
+            '@login' => url('user/login')
+          )
+        ), 'warning');
       }
     }
     else {
@@ -182,7 +183,10 @@ function twitter_signin_oauth_callback_submit(&$form, &$form_state) {
 
   if (!$success) {
     $_SESSION['twitter']['values'] = $response;
-    drupal_goto('user/register');
+    drupal_goto('user/login');
+  }
+  else {
+    user_login_finalize();
   }
 }
 
@@ -193,17 +197,17 @@ function twitter_signin_oauth_callback_submit(&$form, &$form_state) {
  * signed in with Twitter but did not have an account in the site yet.
  */
 function twitter_signin_user_insert(&$edit, $account, $category) {
-  _twitter_signin_add_account($account);
+  _twitter_signin_add_account($edit, $account);
 }
 
 /**
- * Implements hook_user_insert().
- * 
+ * Implements hook_user_login().
+ *
  * Relates a Twitter account with an existing user account if the user
  * signed in with Twitter.
  */
 function twitter_signin_user_login(&$edit, $account) {
-  _twitter_signin_add_account($account);
+  _twitter_signin_add_account($edit, $account);
 }
 
 /**
@@ -212,15 +216,14 @@ function twitter_signin_user_login(&$edit, $account) {
  * @param $account
  *   The Drupal user account.
  */
-function _twitter_signin_add_account($account) {
+function _twitter_signin_add_account($edit, $account) {
   if (isset($_SESSION['twitter']['values'])) {
-    module_load_include('lib.php', 'twitter');
     module_load_include('inc', 'twitter');
     $key = variable_get('twitter_consumer_key', '');
     $secret = variable_get('twitter_consumer_secret', '');
     $response = $_SESSION['twitter']['values'];
 
-    $twitter = new TwitterOAuth($key, $secret, $response['oauth_token'], $response['oauth_token_secret']);
+    $twitter = new Twitter($key, $secret, $response['oauth_token'], $response['oauth_token_secret']);
     try {
       $twitter_account = $twitter->users_show($response['screen_name']);
     } catch (TwitterException $e) {
@@ -228,7 +231,8 @@ function _twitter_signin_add_account($account) {
       return;
     }
     $twitter_account->set_auth($response);
-    twitter_account_save($twitter_account, TRUE, $account);
+    $twitter_account->uid = $account->uid;
+    twitter_account_save($twitter_account, TRUE);
 
     unset($_SESSION['twitter']);
     drupal_set_message(t('You have related a Twitter account with your user. Next time you can sign in with Twitter.'));
diff --git a/kolab.org/www/drupal-7.18/sites/all/modules/twitter/twitter_signin/twitter_signin.pages.inc b/kolab.org/www/drupal-7.18/sites/all/modules/twitter/twitter_signin/twitter_signin.pages.inc
index 19b3299..4f7963e 100644
--- a/kolab.org/www/drupal-7.18/sites/all/modules/twitter/twitter_signin/twitter_signin.pages.inc
+++ b/kolab.org/www/drupal-7.18/sites/all/modules/twitter/twitter_signin/twitter_signin.pages.inc
@@ -19,7 +19,7 @@ function twitter_signin_admin_settings($form, &$form_state) {
   }
 
   $form['twitter_signin_button'] = array(
-    '#type' =>  'radios',
+    '#type' => 'radios',
     '#title' => t('Select sign-in button'),
     '#options' => $options,
     '#default_value' => variable_get('twitter_signin_button', 'Sign-in-with-Twitter-lighter-small.png'),
diff --git a/kolab.org/www/drupal-7.18/sites/all/modules/twitter/twitter_views_field_handlers.inc b/kolab.org/www/drupal-7.18/sites/all/modules/twitter/twitter_views_field_handlers.inc
index ebd6de2..567dd37 100644
--- a/kolab.org/www/drupal-7.18/sites/all/modules/twitter/twitter_views_field_handlers.inc
+++ b/kolab.org/www/drupal-7.18/sites/all/modules/twitter/twitter_views_field_handlers.inc
@@ -14,6 +14,7 @@ class twitter_views_handler_field_xss extends views_handler_field {
     $options['link_usernames'] = array('default' => TRUE);
     $options['link_hashtags'] = array('default' => FALSE);
     $options['hashtags_url'] = array('default' => variable_get('twitter_search', TWITTER_SEARCH) . '/search?q=%23');
+    $options['link_attributes'] = array('default' => TRUE);
     return $options;
   }
 
@@ -40,8 +41,16 @@ class twitter_views_handler_field_xss extends views_handler_field {
       '#process' => array('ctools_dependent_process'),
       '#dependency' => array('edit-options-link-hashtags' => array(TRUE)),
     );
+    $form['link_attributes'] = array(
+      '#title' => t('Open links in new windows/tabs and add rel="nofollow" attributes.'),
+      '#type' => 'checkbox',
+      '#default_value' => !empty($this->options['link_attributes']),
+    );
   }
 
+  /**
+   * Processes the message through the selected options.
+   */
   function render($values) {
     $value = $values->{$this->field_alias};
     if (!empty($this->options['link_urls'])) {
@@ -51,11 +60,19 @@ class twitter_views_handler_field_xss extends views_handler_field {
       );
      $value = _filter_url($value, $filter);
     }
+    // Link usernames with their profiles.
     if (!empty($this->options['link_usernames'])) {
       $value = _twitter_filter_text($value, '@', variable_get('twitter_host', TWITTER_HOST) . '/');
-    } if (!empty($this->options['link_hashtags']) && valid_url($this->options['hashtags_url'])) {
+    }
+    // Link hashtags.
+    if (!empty($this->options['link_hashtags']) && valid_url($this->options['hashtags_url'])) {
       $value = _twitter_filter_text($value, '#', url($this->options['hashtags_url']));
     }
+    // Add extra attributes to links.
+    if (!empty($this->options['link_attributes'])) {
+      $value = _twitter_filter_link($value, NULL);
+    }
+    // Avoid XSS within the message.
     return filter_xss($value);
   }
 }
@@ -69,3 +86,80 @@ class twitter_views_handler_field_profile_image extends views_handler_field {
     return theme('image', array('path' => $value));
   }
 }
+
+/**
+ * Adds Twitter Intents links.
+ *
+ * @see https://dev.twitter.com/docs/intents
+ */
+class twitter_views_handler_field_web_intents extends views_handler_field {
+  /**
+   * Add twitter_id field, which is needed during rendering.
+   */
+  function construct() {
+    parent::construct();
+    $this->additional_fields['twitter_id'] = 'twitter_id';
+  }
+
+  function query() {
+    $this->ensure_my_table();
+    $this->add_additional_fields();
+  }
+
+  function render($values) {
+    drupal_add_js('//platform.twitter.com/widgets.js', 'external');
+    return '<span><a href="https://twitter.com/intent/tweet?in_reply_to=' . $values->twitter_id . '">Reply</a></span> ' .
+      '<span><a href="https://twitter.com/intent/retweet?tweet_id=' . $values->twitter_id . '">Retweet</a></span> ' .
+      '<span><a href="https://twitter.com/intent/favorite?tweet_id=' . $values->twitter_id . '">Favorite</a></span>';
+  }
+}
+
+/**
+ * Adds Twitter Follow link.
+ *
+ * @see https://dev.twitter.com/docs/intents#follow-intent
+ */
+class twitter_views_handler_field_follow extends views_handler_field {
+  function query() {}
+
+  function render($values) {
+    drupal_add_js('//platform.twitter.com/widgets.js', 'external');
+    return '<span><a href="https://twitter.com/intent/user?screen_name=' . $values->twitter_screen_name . '">Follow</a></span>';
+  }
+}
+
+/**
+ * Renders a tweet as it is presented at Twitter.com.
+ *
+ * @see https://dev.twitter.com/terms/display-requirements
+ */
+class twitter_views_handler_field_formatted_tweet extends views_handler_field {
+  function query() {}
+
+  function render($values) {
+    drupal_add_js('//platform.twitter.com/widgets.js', 'external');
+    drupal_add_css(drupal_get_path('module', 'twitter') . '/twitter.css');
+    module_load_include('inc', 'twitter');
+
+    // Load tweet and author.
+    $status = twitter_status_load($values->twitter_id);
+    $author = twitter_account_load($status->screen_name);
+
+    // Format the timestamp.
+    $time_diff = REQUEST_TIME - $values->twitter_created_time;
+
+    // Format the message.
+    $status->time_ago = t('%time ago', array('%time' => format_interval($time_diff, 2)));
+    $filter = new stdClass;
+      $filter->settings = array(
+        'filter_url_length' => 496,
+      );
+    $status->text = _filter_url($status->text, $filter);
+
+    // Render the tweet.
+    return theme('twitter_status', array(
+      'status' => $status,
+      'author' => $author,
+    ));
+  }
+}






More information about the commits mailing list