Merge commit 'origin/fusionax' into account
authorFlorent Bruneau <florent.bruneau@polytechnique.org>
Mon, 12 Jan 2009 11:04:48 +0000 (12:04 +0100)
committerFlorent Bruneau <florent.bruneau@polytechnique.org>
Mon, 12 Jan 2009 11:04:48 +0000 (12:04 +0100)
Conflicts:

core
include/rss.inc.php
include/secure_hash.inc.php
modules/profile/general.inc.php
plugins/insert.getName.php

Signed-off-by: Florent Bruneau <florent.bruneau@polytechnique.org>
77 files changed:
1  2 
classes/platalglobals.php.in
classes/platallogger.php
classes/profile.php
classes/user.php
classes/xnetsession.php
classes/xorgsession.php
htdocs/css/base.css
htdocs/css/keynote.css
htdocs/webredirect.php
include/banana/forum.inc.php
include/banana/hooks.inc.php
include/banana/ml.inc.php
include/emails.combobox.inc.php
include/emails.inc.php
include/googleapps.inc.php
include/marketing.inc.php
include/massmailer.inc.php
include/notifs.inc.php
include/user.func.inc.php
include/userset.inc.php
include/validations.inc.php
include/validations/aliases.inc.php
include/validations/homonymes.inc.php
include/validations/listes.inc.php
include/vcard.inc.php
modules/admin.php
modules/admin/homonyms.inc.php
modules/auth.php
modules/auth/auth.inc.php
modules/axletter.php
modules/carnet.php
modules/email.php
modules/events.php
modules/forums.php
modules/fusionax.php
modules/lists.php
modules/lists/lists.inc.php
modules/newsletter.php
modules/platal.php
modules/profile.php
modules/profile/addresses.inc.php
modules/profile/decos.inc.php
modules/profile/general.inc.php
modules/profile/groups.inc.php
modules/profile/jobs.inc.php
modules/profile/mentor.inc.php
modules/profile/page.inc.php
modules/profile/skills.inc.php
modules/register.php
modules/register/register.inc.php
modules/stats.php
modules/xnetgrp.php
modules/xnetlists.php
templates/admin/accounts.tpl
templates/admin/deces_promo.tpl
templates/admin/homonymes.tpl
templates/admin/utilisateurs.tpl
templates/admin/wiki.tpl
templates/axletter/letter.mail.tpl
templates/carnet/index.tpl
templates/carnet/mescontacts.tpl
templates/carnet/panel.tpl
templates/core/password_prompt.tpl
templates/emails/alias.tpl
templates/emails/broken-web.mail.tpl
templates/emails/broken.tpl
templates/emails/send.tpl
templates/events/index.tpl
templates/newsletter/nl.mail.tpl
templates/platal/changeLog.tpl
templates/platal/filrss.tpl
templates/platal/preferences.tpl
templates/platal/webredirect.tpl
templates/profile/general.tpl
templates/xnetgrp/asso.tpl
templates/xnetlists/alias-admin.tpl
templates/xnetlists/sync.tpl

Simple merge
Simple merge
index a49a882,0000000..eae671a
mode 100644,000000..100644
--- /dev/null
@@@ -1,178 -1,0 +1,178 @@@
-  *  Copyright (C) 2003-2008 Polytechnique.org                              *
 +<?php
 +/***************************************************************************
++ *  Copyright (C) 2003-2009 Polytechnique.org                              *
 + *  http://opensource.polytechnique.org/                                   *
 + *                                                                         *
 + *  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.,                                                      *
 + *  59 Temple Place, Suite 330, Boston, MA  02111-1307  USA                *
 + ***************************************************************************/
 +
 +class Profile
 +{
 +    private $pid;
 +    private $hrpid;
 +    private $data = array();
 +
 +    private function __construct($login)
 +    {
 +        if ($login instanceof PlUser) {
 +            $res = XDB::query('SELECT  p.pid, p.hrpid
 +                                 FROM  account_profiles AS ap
 +                           INNER JOIN  profiles AS p ON (p.pid = ap.pid)
 +                                WHERE  ap.uid = {?} AND FIND_IN_SET(\'owner\', ap.perms)',
 +                             $login->id());
 +        } else if (is_numeric($login)) {
 +            $res = XDB::query('SELECT  p.pid, p.hrpid
 +                                 FROM  profiles AS p
 +                                WHERE  p.pid = {?}',
 +                              $login);
 +        } else {
 +            $res = XDB::query('SELECT  p.pid, p.hrpid
 +                                 FROM  profiles AS p
 +                                WHERE  p.hrpid = {?}',
 +                              $login);
 +        }
 +        if ($res->numRows() != 1) {
 +            throw new UserNotFoundException();
 +        }
 +        list($this->pid, $this->hrpid) = $res->fetchOneRow();
 +    }
 +
 +    public function id()
 +    {
 +        return $this->pid;
 +    }
 +
 +    public function hrid()
 +    {
 +        return $this->hrpid;
 +    }
 +
 +    public function promo()
 +    {
 +        return $this->promo;
 +    }
 +
 +    /** Print a name with the given formatting:
 +     * %s = • for women
 +     * %f = firstname
 +     * %l = lastname
 +     * %F = fullname
 +     * %S = shortname
 +     * %p = promo
 +     */
 +    public function name($format)
 +    {
 +        return str_replace(array('%s', '%f', '%l', '%F', '%S', '%p'),
 +                           array($this->isFemale() ? '•' : '',
 +                                 $this->first_name, $this->last_name,
 +                                 $this->full_name, $this->short_name,
 +                                 $this->promo), $format);
 +    }
 +
 +    public function fullName($with_promo = false)
 +    {
 +        if ($with_promo) {
 +            return $this->full_name . ' (' . $this->promo . ')';
 +        }
 +        return $this->full_name;
 +    }
 +
 +    public function shortName($with_promo = false)
 +    {
 +        if ($with_promo) {
 +            return $this->short_name . ' (' . $this->promo . ')';
 +        }
 +        return $this->short_name;
 +    }
 +
 +    public function firstName()
 +    {
 +        return $this->first_name;
 +    }
 +
 +    public function lastName()
 +    {
 +        return $this->last_name;
 +    }
 +
 +    public function isFemale()
 +    {
 +        return $this->sex == PlUser::GENDER_FEMALE;
 +    }
 +
 +    public function data()
 +    {
 +        $this->first_name;
 +        return $this->data;
 +    }
 +
 +    public function __get($name)
 +    {
 +        if (property_exists($this, $name)) {
 +            return $this->$name;
 +        }
 +
 +        if (empty($this->data)) {
 +            // XXX: Temporary, use data from auth_user_md5 (waiting for data from newdirectory
 +            $this->data = XDB::fetchOneAssoc('SELECT  p.*, u.prenom AS first_name,
 +                                                      IF(u.nom_usage != "", u.nom_usage, u.nom) AS last_name,
 +                                                      u.promo AS promo,
 +                                                      CONCAT(u.prenom, " ", u.nom) AS short_name,
 +                                                      IF(u.nom_usage != "",
 +                                                         CONCAT(u.nom_usage, " (", u.nom, "),", u.prenom),
 +                                                         CONCAT(u.nom, ", ", u.prenom)) AS full_name
 +                                                FROM  profiles AS p
 +                                          INNER JOIN  auth_user_md5 AS u ON (u.user_id = p.pid)
 +                                               WHERE  p.pid = {?}',
 +                                             $this->id());
 +        }
 +        if (isset($this->data[$name])) {
 +            return $this->data[$name];
 +        }
 +
 +        return null;
 +    }
 +
 +    public function __isset($name)
 +    {
 +        return property_exists($this, $name) || isset($this->data[$name]);
 +    }
 +
 +
 +    public function owner()
 +    {
 +        return User::getSilent($this);
 +    }
 +
 +    /** Return the profile associated with the given login.
 +     */
 +    public static function get($login) {
 +        try {
 +            return new Profile($login);
 +        } catch (UserNotFoundException $e) {
 +            /* Let say we can identify a profile using the identifiers of its owner.
 +             */
 +            $user = User::getSilent($login);
 +            if ($user && $user->hasProfile()) {
 +                return $user->profile();
 +            }
 +            return null;
 +        }
 +    }
 +}
 +
 +// vim:set et sw=4 sts=4 sws=4 foldmethod=marker enc=utf-8:
 +?>
Simple merge
Simple merge
Simple merge
Simple merge
Simple merge
Simple merge
Simple merge
Simple merge
Simple merge
Simple merge
Simple merge
Simple merge
Simple merge
Simple merge
Simple merge
Simple merge
Simple merge
Simple merge
Simple merge
Simple merge
Simple merge
Simple merge
Simple merge
Simple merge
Simple merge
Simple merge
Simple merge
Simple merge
Simple merge
Simple merge
Simple merge
Simple merge
Simple merge
Simple merge
Simple merge
Simple merge
Simple merge
Simple merge
Simple merge
@@@ -65,26 -64,9 +65,10 @@@ class ProfileNom implements ProfileSett
      }
  }
  
- class ProfileSearchName implements ProfileSetting
- {
-     public function __construct()
-     {
-     }
-     public function value(ProfilePage &$page, $field, $value, &$success)
-     {
-     }
-     public function save(ProfilePage &$page, $field, $new_value)
-     {
-     }
- }
  class ProfileEdu implements ProfileSetting
  {
 -    public function __construct(){}
 +    public function __construct() {
 +    }
  
      static function sortByGradYear($line1, $line2) {
          $a = (int) $line1['grad_year'];
@@@ -257,9 -239,7 +241,7 @@@ class ProfileGeneral extends ProfilePag
      public function __construct(PlWizard &$wiz)
      {
          parent::__construct($wiz);
-         $this->settings['nom']    = $this->settings['prenom']
-                                   = new ProfileNom();
 -        $this->settings['naissance'] = new ProfileDate();
 +        $this->settings['birthdate'] = new ProfileDate();
          $this->settings['freetext_pub']
                                    = $this->settings['photo_pub']
                                    = new ProfilePub();
          $this->settings['networking'] = new ProfileNetworking();
          $this->settings['tels']   = new ProfilePhones('user', 0);
          $this->settings['edus']   = new ProfileEdu();
-         $this->watched= array('nom' => true, 'freetext' => true, 'tels' => true,
+         $this->watched= array('freetext' => true, 'tels' => true,
                                'networking' => true, 'edus' => true,
 -                              'nationalite' => true, 'nationalite2' => true,
 -                              'nationalite3' => true);
 +                              'nationality1' => true, 'nationality2' => true,
 +                              'nationality3' => true, 'nick' => true);
      }
  
      protected function _fetchData()
          // Retreive photo informations
          $res = XDB::query("SELECT  pub
                               FROM  photo
 -                            WHERE  uid = {?}", S::v('uid'));
 +                            WHERE  uid = {?}", $this->pid());
          $this->values['photo_pub'] = $res->fetchOneCell();
  
 -        $res = XDB::query("SELECT  COUNT(*)
 -                             FROM  requests
 -                            WHERE  type='photo' AND user_id = {?}",
 -                          S::v('uid'));
 -        $this->values['nouvellephoto'] = $res->fetchOneCell();
 +        if ($this->owner) {
 +            $res = XDB::query("SELECT  COUNT(*)
 +                                 FROM  requests
 +                                WHERE  type='photo' AND user_id = {?}",
 +                              $this->owner->id());
 +            $this->values['nouvellephoto'] = $res->fetchOneCell();
 +        } else {
 +            $this->values['nouvellephoto'] = 0;
 +        }
  
          // Retreive search names info
-         $this->values['search_names'] = XDB::iterator("
-                               SELECT  sn.search_name, sn.name_type, sn.pub, sn.sn_id
-                                 FROM  profile_names_search AS sn
-                                WHERE  sn.user_id = {?}
-                             ORDER BY  sn.name_type, search_score, search_name",
-                           $this->pid());
+         $sn_all = XDB::iterator("SELECT  IF(sn.particle = '', sn.name, CONCAT(sn.particle, ' ', sn.name)) AS name,
+                                          sn.particle, sn.typeid, e.name AS type,
+                                          FIND_IN_SET('has_particle', e.flags) AS has_particle,
+                                          FIND_IN_SET('always_displayed', e.flags) AS always_displayed,
+                                          FIND_IN_SET('public', e.flags) AS pub
+                                    FROM  profile_name_search      AS sn
+                              INNER JOIN  profile_name_search_enum AS e  ON (e.id = sn.typeid)
+                                   WHERE  sn.pid = {?} AND NOT FIND_IN_SET('not_displayed', e.flags)
+                                ORDER BY  NOT FIND_IN_SET('always_displayed', e.flags), e.id, sn.name",
 -                                S::v('uid'));
++                                $this->pid());
+         $sn_types = XDB::iterator("SELECT  id, name, FIND_IN_SET('has_particle', flags) AS has_particle
+                                      FROM  profile_name_search_enum
+                                     WHERE  NOT FIND_IN_SET('not_displayed', flags)
+                                            AND FIND_IN_SET('always_displayed', flags)
+                                  ORDER BY  id");
+         $this->values['search_names'] = array();
+         $sn = $sn_all->next();
+         while ($sn_type = $sn_types->next()) {
+             if ($sn_type['id'] == $sn['typeid']) {
+                 $this->values['search_names'][] = $sn;
+                 $sn = $sn_all->next();
+             } else {
+                 $this->values['search_names'][] = array('typeid' => $sn_type['id'],
+                                                         'type'   => $sn_type['name'],
+                                                         'pub'    => 1,
+                                                         'has_particle'     => $sn_type['has_particle'],
+                                                         'always_displayed' => 1);
+             }
+         }
+         do {
+             $this->values['search_names'][] = $sn;
+         } while ($sn = $sn_all->next());
  
          // Proposes choice for promo_display
          if ($this->values['entry_year'] != $this->values['grad_year'] - 3) {
  
      protected function _saveData()
      {
 -        if ($this->changed['nationalite'] || $this->changed['nationalite2'] || $this->changed['nationalite3']
 -            || $this->changed['naissance']) {
 -            if ($this->values['nationalite3'] == "") {
 -                $this->values['nationalite3'] = NULL;
 +        if ($this->changed['nationality1'] || $this->changed['nationality2'] || $this->changed['nationality3']
-             || $this->changed['nom'] || $this->changed['prenom'] || $this->changed['naissance']
-             || $this->changed['freetext'] || $this->changed['freetext_pub']) {
++            || $this->changed['birthdate'] || $this->changed['freetext'] || $this->changed['freetext_pub']) {
 +            if ($this->values['nationality3'] == "") {
 +                $this->values['nationality3'] = NULL;
              }
 -            if ($this->values['nationalite2'] == "") {
 -                $this->values['nationalite2'] = $this->values['nationalite3'];
 -                $this->values['nationalite3'] = NULL;
 +            if ($this->values['nationality2'] == "") {
 +                $this->values['nationality2'] = $this->values['nationality3'];
 +                $this->values['nationality3'] = NULL;
              }
 -            if ($this->values['nationalite'] == "") {
 -                $this->values['nationalite']  = $this->values['nationalite2'];
 -                $this->values['nationalite2'] = $this->values['nationalite3'];
 -                $this->values['nationalite3'] = NULL;
 +            if ($this->values['nationality1'] == "") {
 +                $this->values['nationality1']  = $this->values['nationality2'];
 +                $this->values['nationality2'] = $this->values['nationality3'];
 +                $this->values['nationality3'] = NULL;
              }
  
 -            XDB::execute("UPDATE  auth_user_md5
 -                             SET  nationalite = {?}, nationalite2 = {?}, nationalite3 = {?}, naissance={?}
 -                           WHERE  user_id = {?}",
 -                         $this->values['nationalite'], $this->values['nationalite2'], $this->values['nationalite3'],
 -                         preg_replace('@(\d{2})/(\d{2})/(\d{4})@', '\3-\2-\1', $this->values['naissance']),
 -                         S::v('uid'));
 -        }
 -        if ($this->changed['freetext'] || $this->changed['freetext_pub'] || $this->changed['synchro_ax']) {
 -            XDB::execute("UPDATE  auth_user_quick
 -                             SET  profile_freetext={?}, profile_freetext_pub={?}, profile_from_ax = {?}
 +            XDB::execute("UPDATE  profiles
 +                             SET  nationality1 = {?}, nationality2 = {?}, nationality3 = {?}, birthdate = {?},
 +                                  freetext = {?}, freetext_pub = {?}
                             WHERE  user_id = {?}",
 -                         $this->values['freetext'], $this->values['freetext_pub'],
 -                         $this->values['synchro_ax'], S::v('uid'));
 +                          $this->values['nationality1'], $this->values['nationality2'], $this->values['nationality3'],
 +                          preg_replace('@(\d{2})/(\d{2})/(\d{4})@', '\3-\2-\1', $this->values['birthdate']),
 +                          $this->values['freetext'], $this->values['freetext_pub'],
 +                          $this->pid());
          }
          if ($this->changed['email_directory']) {
              $new_email = ($this->values['email_directory'] == "new@example.org") ?
              }
              XDB::execute("REPLACE INTO  profile_directory (uid, email_directory)
                                  VALUES  ({?}, {?})",
 -                         S::v('uid'), $new_email);
 +                         $this->pid(), $new_email);
          }
-         if ($this->changed['nick']) {
-             require_once('user.func.inc.php');
-             user_reindex(S::v('uid'));
-         }
          if ($this->changed['photo_pub']) {
              XDB::execute("UPDATE  photo
                               SET  pub = {?}
                             WHERE  uid = {?}",
 -                         $this->values['photo_pub'], S::v('uid'));
 +                         $this->values['photo_pub'], $this->pid());
          }
-         if ($this->changed['yourself'] || $this->changed['sort_name'] ||
-             $this-> changed['display_name'] || $this->changed['tooltip_name']) {
-             XDB::execute("UPDATE  profile_names_display AS n
-                              SET  n.yourself = {?},
-                                   n.sort = {?}, ". // SET
-                                  "n.display = {?}, ". // SET
-                                  "n.tooltip = {?} ". // SET
-                           "WHERE  n.user_id = {?}",
-                          $this->values['yourself'],
-                          $this->values['sort_name'],
-                          $this->values['display_name'],
-                          $this->values['tooltip_name'],
-                          $this->pid());
 -       if ($this->changed['yourself'] || $this->changed['search_names']) {
++        if ($this->changed['yourself'] || $this->changed['search_names']) {
+             if ($this->changed['search_names']) {
+                 XDB::execute("DELETE FROM  s
+                                     USING  profile_name_search      AS s
+                                INNER JOIN  profile_name_search_enum AS e ON (s.typeid = e.id)
+                                     WHERE  s.pid = {?} AND NOT FIND_IN_SET('not_displayed', e.flags)",
 -                             S::i('uid'));
++                             $this->pid());
+                 $search_names = array();
+                 foreach ($this->values['search_names'] as $sn) {
+                     if ($sn['name'] != '') {
+                         if ($sn['particle']) {
+                             list($particle, $name) = explode(' ', $sn['name'], 2);
+                             if (!$name) {
+                                 list($particle, $name) = explode('\'', $sn['name'], 2);
+                             }
+                         } else {
+                             $particle = '';
+                             $name     = $sn['name'];
+                         }
+                         $particle   = trim($particle);
+                         $name       = trim($name);
+                         $sn['name'] = trim($sn['name']);
+                         XDB::execute("INSERT INTO  profile_name_search (particle, name, typeid, pid)
+                                            VALUES  ({?}, {?}, {?}, {?})",
 -                                     $particle, $name, $sn['typeid'], S::i('uid'));
++                                     $particle, $name, $sn['typeid'], $this->pid());
+                         if (!isset($search_names[$sn['typeid']])) {
+                             $search_names[$sn['typeid']] = array($sn['name'], $name);
+                         } else {
+                             $search_names[$sn['typeid']] = array_merge($search_names[$sn['typeid']], array($name));
+                         }
+                     }
+                 }
+                 require_once 'name.func.inc.php';
+                 $sn_types_public  = build_types('public');
+                 $sn_types_private = build_types('private');
+                 $full_name      = build_full_name($search_names, $sn_types_public);
+                 $directory_name = build_directory_name($search_names, $sn_types_public, $full_name);
+                 $short_name     = short_name($search_names, $sn_types_public);
+                 $sort_name      = short_name($search_names, $sn_types_public);
+                 $this->values['public_name']  = build_public_name($search_names, $sn_types_public, $full_name);
+                 $this->values['private_name'] = $public_name . build_private_name($search_names, $sn_types_private);
+                 XDB::execute("UPDATE  profile_display
+                                  SET  yourself = {?}, public_name = {?}, private_name = {?},
+                                       directory_name = {?}, short_name = {?}, sort_name = {?}
+                                WHERE  pid = {?}",
+                              $this->values['yourself'], $this->values['public_name'],
+                              $this->values['private_name'], $directory_name, $short_name,
 -                             $sort_name, S::v('uid'));
++                             $sort_name, $this->pid());
+                 /*if ($this->changed['search_names']) {
+                     require_once('user.func.inc.php');
+                     user_reindex(S::v('uid'));
+                 }*/
+             } else {
+                 XDB::execute("UPDATE  profile_display
+                                  SET  yourself = {?}
+                                WHERE  pid = {?}",
 -                             $this->values['yourself'], S::v('uid'));
++                             $this->values['yourself'], $this->pid());
+             }
          }
          if ($this->changed['promo_display']) {
              XDB::execute("UPDATE  profile_display
Simple merge
Simple merge
Simple merge
Simple merge
Simple merge
Simple merge
Simple merge
Simple merge
Simple merge
Simple merge
Simple merge
Simple merge
Simple merge
Simple merge
Simple merge
Simple merge
Simple merge
Simple merge
Simple merge
Simple merge
Simple merge
index ceab44c,0000000..c47674d
mode 100644,000000..100644
--- /dev/null
@@@ -1,47 -1,0 +1,47 @@@
- {*  Copyright (C) 2003-2008 Polytechnique.org                             *}
 +{**************************************************************************}
 +{*                                                                        *}
++{*  Copyright (C) 2003-2009 Polytechnique.org                             *}
 +{*  http://opensource.polytechnique.org/                                  *}
 +{*                                                                        *}
 +{*  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.,                                                     *}
 +{*  59 Temple Place, Suite 330, Boston, MA  02111-1307  USA               *}
 +{*                                                                        *}
 +{**************************************************************************}
 +
 +{config_load file="mails.conf" section="emails_broken"}
 +{if $mail_part eq 'head'}
 +{from full=#from#}
 +{subject text=#subject#}
 +{elseif $mail_part eq 'wiki'}
 +Bonjour !
 +
 +Cet email a été généré automatiquement par le service de patte cassée de
 +Polytechnique.org car un autre utilisateur, {$request->fullName()},
 +nous a signalé qu'en t'envoyant un email, il avait reçu un message d'erreur
 +indiquant que ton adresse de redirection {$email}
 +ne fonctionnait plus !
 +
 +Nous te suggérons de vérifier cette adresse, et le cas échéant de mettre
 +à jour tes adresses de redirection [[{$globals->baseurl}/emails|sur le site]].
 +
 +Pour plus de renseignements sur le service de patte cassée, n'hésite pas à
 +consulter [[{$globals->baseurl}/emails/broken|la documentation sur le site]].
 +
 +
 +À bientôt sur Polytechnique.org !\\
 +[[support@{$globals->mail->domain}|L'équipe d'administration]]
 +{/if}
 +
 +{* vim:set et sw=2 sts=2 sws=2 enc=utf-8: *}
Simple merge
Simple merge
Simple merge
Simple merge
Simple merge
Simple merge
Simple merge
Simple merge
Simple merge
Simple merge
Simple merge
Simple merge