]> git.agnieray.net Git - galette.git/commitdiff
Rework groups managers capabilities; closes #499
authorJohan Cwiklinski <johan@x-tnd.be>
Thu, 11 Nov 2021 12:51:48 +0000 (13:51 +0100)
committerJohan Cwiklinski <johan@x-tnd.be>
Sat, 20 Nov 2021 08:11:25 +0000 (09:11 +0100)
14 files changed:
galette/docs/CHANGES
galette/includes/core_acls.php
galette/lib/Galette/Controllers/Crud/GroupsController.php
galette/lib/Galette/Controllers/Crud/MembersController.php
galette/lib/Galette/Core/Preferences.php
galette/lib/Galette/Entity/Adherent.php
galette/templates/default/gestion_adherents.tpl
galette/templates/default/gestion_groupes.tpl
galette/templates/default/group.tpl
galette/templates/default/page.tpl
galette/templates/default/preferences.tpl
galette/webroot/themes/default/galette.css
tests/Galette/Entity/tests/units/Adherent.php
tests/Galette/Middleware/tests/unit/CheckAcls.php

index f9fd06c2727313de9d3c12db2f35edd5c5bfdbff..0c95354b91a4ef3f187ce3d6393da2f4b42ffc5e 100644 (file)
@@ -7,6 +7,7 @@ Changes
 - Export contributions as CSV
 - Drop group name uniqueness at same level
 - Add information to display for dynamic fields
+- Add preferences for groups manager to edit, create members, edit groups, send mailing and perform exports
 - Fix various XSS issues
 - Fix possible SQL injection
 - Add CSRF protection
index 2090faec527ec7a432479faf42118a83de9590c6..66c8cecfc73c579a97cfd46789ebf7fbaa955854 100644 (file)
@@ -53,6 +53,10 @@ $core_acls = [
     'charts'                            => 'staff',
     '/(.+)?plugin(.+)?/i'               => 'admin',
     '/(.+)?mailing(.+)?/i'              => 'staff',
+    'mailing'                           => 'groupmanager',
+    'doMailing'                         => 'groupmanager',
+    'mailingPreview'                    => 'groupmanager',
+    'mailingRecipients'                 => 'groupmanager',
     '/(.+)?history(.+)?/i'              => 'staff',
     '/(.+)?import(.+)?/i'               => 'staff',
     '/(.+)?export(.+)?/i'               => 'staff',
@@ -73,7 +77,6 @@ $core_acls = [
     '/(.+)?member(.+)?/i'               => 'groupmanager',
     'ajaxGroupMembers'                  => 'staff',
     'duplicateMember'                   => 'staff',
-    'csv-memberslist'                   => 'staff',
     'payments_filter'                   => 'member',
     'adhesionForm'                      => 'member',
     'getDynamicFile'                    => 'member',
@@ -81,7 +84,6 @@ $core_acls = [
     // /Members rules
     // Groups rules
     '/(.+)?group(.+)?/i'                => 'groupmanager',
-    'ajax_groups_reorder'               => 'staff', //groups ordering is limited to staff
     'add_group'                         => 'staff', //adding group is for staff only
     // /Groups rules
 
@@ -91,7 +93,7 @@ $core_acls = [
     '/(.+)?reminder(.+)?/i'             => 'staff',
     '/(.+)?paymentType(.+)?/i'          => 'staff',
     '/(.+)?dynamicTranslation(.+)?/i'   => 'staff',
-    'previewAttachment'                 => 'staff',
+    'previewAttachment'                 => 'groupmanager',
     'getCsv'                            => 'staff',
     'pdfModels'                         => 'staff',
     'attendance_sheet_details'          => 'groupmanager',
index 96744c92851e29c8a1ec18b1721b430cfb240995..48a0ad0bb054704ab2a962dcafd1a32a8968b10b 100644 (file)
@@ -7,7 +7,7 @@
  *
  * PHP version 5
  *
- * Copyright © 2020 The Galette Team
+ * Copyright © 2020-2021 The Galette Team
  *
  * This file is part of Galette (http://galette.tuxfamily.org).
  *
@@ -28,7 +28,7 @@
  * @package   Galette
  *
  * @author    Johan Cwiklinski <johan@x-tnd.be>
- * @copyright 2020 The Galette Team
+ * @copyright 2020-2021 The Galette Team
  * @license   http://www.gnu.org/licenses/gpl-3.0.html GPL License 3.0 or (at your option) any later version
  * @link      http://galette.tuxfamily.org
  * @since     Available since 0.9.4dev - 2020-05-06
@@ -50,10 +50,10 @@ use Analog\Analog;
  * Galette groups controller
  *
  * @category  Controllers
- * @name      PaymentTypeController
+ * @name      GroupsController
  * @package   Galette
  * @author    Johan Cwiklinski <johan@x-tnd.be>
- * @copyright 2020 The Galette Team
+ * @copyright 2020-2021 The Galette Team
  * @license   http://www.gnu.org/licenses/gpl-3.0.html GPL License 3.0 or (at your option) any later version
  * @link      http://galette.tuxfamily.org
  * @since     Available since 0.9.4dev - 2020-05-06
@@ -74,6 +74,7 @@ class GroupsController extends CrudController
     public function add(Request $request, Response $response): Response
     {
         //no new page (included on list), just to satisfy inheritance
+        return $response;
     }
 
     /**
@@ -393,6 +394,14 @@ class GroupsController extends CrudController
      */
     public function reorder(Request $request, Response $response): Response
     {
+        if (
+            !$this->login->isAdmin()
+            && !$this->login->isStaff()
+            && !($this->login->isGroupManager() && $this->preferences->pref_bool_groupsmanagers_edit_groups)
+        ) {
+            throw new \RuntimeException('Trying to reorder groups without appropriate permissions');
+        }
+
         $post = $request->getParsedBody();
         if (!isset($post['to']) || !isset($post['id_group']) || $post['id_group'] == '') {
             Analog::log(
index 0639530c6b56f0584b0f98d488217818c21f3e21..eb7328896c8b3de782ce23155154c165a90ca2b9 100644 (file)
@@ -1652,11 +1652,8 @@ class MembersController extends CrudController
                     }
 
                     if ($this->login->isGroupManager()) {
-                        //store requested groups
-                        $groups_adh = $post['groups_adh'] ?? null;
-                        $managed_groups_adh = $post['groups_managed_adh'] ?? null;
-
                         //add/remove user from groups
+                        $groups_adh = $post['groups_adh'] ?? null;
                         $add_groups = Groups::addMemberToGroups(
                             $member,
                             $groups_adh
@@ -1665,8 +1662,10 @@ class MembersController extends CrudController
                         if ($add_groups === false) {
                             $error_detected[] = _T("An error occurred adding member to its groups.");
                         }
-
+                    }
+                    if ($this->login->isSuperAdmin() || $this->login->isAdmin() || $this->login->isStaff()) {
                         //add/remove manager from groups
+                        $managed_groups_adh = $post['groups_managed_adh'] ?? null;
                         $add_groups = Groups::addMemberToGroups(
                             $member,
                             $managed_groups_adh,
index e488c4c8ce656dabfb25e583c528066db648893d..111128fba8130b8f66b6dcba9a6504e243e60f38 100644 (file)
@@ -139,6 +139,11 @@ use Galette\Repository\Members;
  * @property integer $pref_password_strength
  * @property integer $pref_default_paymenttype
  * @property boolean $pref_bool_create_member
+ * @property boolean $pref_bool_groupsmanagers_create_member
+ * @property boolean $pref_bool_groupsmanagers_edit_member
+ * @property boolean $pref_bool_groupsmanagers_edit_groups
+ * @property boolean $pref_bool_groupsmanagers_mailings
+ * @property boolean $pref_bool_groupsmanagers_exports
  * @property-read string $vpref_email_newadh Comma separated list of mail senders
  */
 class Preferences
@@ -271,7 +276,12 @@ class Preferences
         'pref_password_blacklist' => false,
         'pref_password_strength' => self::PWD_NONE,
         'pref_default_paymenttype' => PaymentType::CHECK,
-        'pref_bool_create_member' => false
+        'pref_bool_create_member' => false,
+        'pref_bool_groupsmanagers_create_member' => false,
+        'pref_bool_groupsmanagers_edit_member' => false,
+        'pref_bool_groupsmanagers_edit_groups' => false,
+        'pref_bool_groupsmanagers_mailings' => false,
+        'pref_bool_groupsmanagers_exports' => true
     );
 
     /** @var Social[] */
index c3853cfbe041fb96542ba7077494fb4d6df1235b..16d945384dc71b1ef77dee32caad6ea4450fe777 100644 (file)
@@ -1040,6 +1040,8 @@ class Adherent
      */
     public function check(array $values, array $required, array $disabled)
     {
+        global $login;
+
         $this->errors = array();
 
         //Sanitize
@@ -1178,6 +1180,19 @@ class Adherent
             $this->_parent = null;
         }
 
+        if ($login->isGroupManager() && !$login->isAdmin() && !$login->isStaff()) {
+            if (!isset($values['groups_adh'])) {
+                $this->errors[] = _T('You have to select a group you own!');
+            } else {
+                foreach ($values['groups_adh'] as $group) {
+                    list($gid) = explode('|', $group);
+                    if (!$login->isGroupManager($gid)) {
+                        $this->errors[] = _T('You have to select a group you own!');
+                    }
+                }
+            }
+        }
+
         $this->dynamicsCheck($values, $required, $disabled);
         $this->checkSocials($values);
 
@@ -1313,7 +1328,7 @@ class Adherent
                         }
                     } catch (Throwable $e) {
                         Analog::log(
-                            'An error occurred checking member email unicity.',
+                            'An error occurred checking member email uniqueness.',
                             Analog::ERROR
                         );
                         $this->errors[] = _T("An error has occurred while looking if login already exists.");
@@ -2092,6 +2107,10 @@ class Adherent
             return true;
         }
 
+        if ($preferences->pref_bool_groupsmanagers_create_member && $login->isGroupManager()) {
+            return true;
+        }
+
         if ($preferences->pref_bool_create_member && $login->isLogged()) {
             return true;
         }
@@ -2108,6 +2127,8 @@ class Adherent
      */
     public function canEdit(Login $login): bool
     {
+        global $preferences;
+
         //admin and staff users can edit, as well as member itself
         if ($this->id && $login->id == $this->id || $login->isAdmin() || $login->isStaff()) {
             return true;
@@ -2118,8 +2139,8 @@ class Adherent
             return true;
         }
 
-        //group managers can edit members of groups they manage
-        if ($login->isGroupManager()) {
+        //group managers can edit members of groups they manage when pref is on
+        if ($preferences->pref_bool_groupsmanagers_edit_member && $login->isGroupManager()) {
             foreach ($this->getGroups() as $g) {
                 if ($login->isGroupManager($g->getId())) {
                     return true;
@@ -2139,6 +2160,15 @@ class Adherent
      */
     public function canShow(Login $login): bool
     {
+        //group managers can show members of groups they manage
+        if ($login->isGroupManager()) {
+            foreach ($this->getGroups() as $g) {
+                if ($login->isGroupManager($g->getId())) {
+                    return true;
+                }
+            }
+        }
+
         return $this->canEdit($login);
     }
 
index 92c9a99b126dae20c09f136e3674d25526db5121..6141179478456e3d5edfe8840c7abbdc3bbc3afa 100644 (file)
@@ -297,7 +297,7 @@ We have to use a template file, so Smarty will do its work (like replacing varia
 {/foreach}
             </tbody>
         </table>
-{if $nb_members != 0}
+{if $nb_members != 0 && ($login->isGroupManager() && $preferences->pref_bool_groupsmanagers_exports || $login->isAdmin() || $login->isStaff())}
         <div class="center cright">
             {_T string="Pages:"}<br/>
             <ul class="pages">{$pagination}</ul>
@@ -320,6 +320,8 @@ We have to use a template file, so Smarty will do its work (like replacing varia
                     <i class="fas fa-cookie-bite fa-fw"></i> {_T string="Mass add contributions"}
                 </button>
             </li>
+    {/if}
+    {if $login->isAdmin() or $login->isStaff() or $login->isGroupManager() and $preferences->pref_bool_groupsmanagers_mailings}
         {if $pref_mail_method neq constant('Galette\Core\GaletteMail::METHOD_DISABLED')}
             <li>
                 <button type="submit" id="sendmail" name="mailing">
@@ -328,6 +330,8 @@ We have to use a template file, so Smarty will do its work (like replacing varia
             </li>
         {/if}
     {/if}
+
+    {if $login->isGroupManager() && $preferences->pref_bool_groupsmanagers_exports || $login->isAdmin() || $login->isStaff()}
             <li>
                 <button type="submit" id="attendance_sheet" name="attendance_sheet">
                     <i class="fas fa-file-alt fa-fw"></i> {_T string="Attendance sheet"}
@@ -343,7 +347,6 @@ We have to use a template file, so Smarty will do its work (like replacing varia
                     <i class="fas fa-id-badge fa-fw"></i> {_T string="Generate Member Cards"}
                 </button>
             </li>
-    {if $login->isAdmin() or $login->isStaff()}
             <li>
                 <button type="submit" id="csv" name="csv">
                     <i class="fas fa-file-csv fa-fw"></i> {_T string="Export as CSV"}
index abd1c402a70b98868715fa8abfba848e407b41f9..cbe5c2e3689e37aaab2ac25dc6e09a275d0397a0 100644 (file)
     </section>
 </div>
 <div class="button-container">
+{if $login->isGroupManager() && $preferences->pref_bool_groupsmanagers_exports || $login->isAdmin() || $login->isStaff()}
     <a href="{path_for name="pdf_groups"}" class="button tooltip" title="{_T string="Export all groups and their members as PDF"}">
         <i class="fas fa-file-pdf"></i> {_T string="All groups PDF"}
     </a>
+{/if}
 </div>
 {/block}
 
index 522692f50b5301e3ed8758131a1ef689695bbe30..81a3af74892183c4a2f92207f9efe4c0c0fe6a86 100644 (file)
@@ -1,3 +1,5 @@
+{assign var="canEdit" value=$login->isGroupManager() && $preferences->pref_bool_groupsmanagers_edit_groups || $login->isAdmin() || $login->isStaff()}
+{assign var="canExport" value=$login->isGroupManager() && $preferences->pref_bool_groupsmanagers_exports || $login->isAdmin() || $login->isStaff()}
         <form class="tabbed" action="{path_for name="doEditGroup" data=["id" => $group->getId()]}" method="post" enctype="multipart/form-data" id="group_form">
         <div id="group">
             <ul>
                     </p>
 {/if}
                     <p>
+{if $canEdit}
                         <label for="group_name" class="bline">{_T string="Name:"}</label>
                         <input type="text" name="group_name" id="group_name" value="{$group->getName()}" maxlength="20" required/>
+{else}
+                        <span class="bline">{_T string="Name:"}</span>
+                        {$group->getName()}
+{/if}
                     </p>
+
+
 {if $group->getParentGroup()}
     {assign var='pgroup' value=$group->getParentGroup()}
 {/if}
                     <p>
-{if !$login->isAdmin() && !$login->isStaff()}
+{if $canEdit}
+                        <label for="parent_group" class="bline">{_T string="Parent group:"}</label>
+                        <select name="parent_group" id="parent_group">
+                            <option value="">{_T string="None"}</option>
+    {foreach item=g from=$groups}
+        {if $group->canSetParentGroup($g)}
+                            <option value="{$g->getId()}"{if isset($pgroup) and $pgroup->getId() eq $g->getId()} selected="selected"{/if}>{$g->getIndentName()}</option>
+        {/if}
+    {/foreach}
+                        </select>
+{else}
                         <span class="bline">{_T string="Parent group:"}</span>
                         <span>
     {if isset($pgroup)}
                             {$pgroup->getName()}
                             <input type="hidden" name="parent_group" value="{$pgroup->getId()}"/>
+    {else}
+        -
     {/if}
                         </span>
-{else}
-                        <label for="parent_group" class="bline">{_T string="Parent group:"}</label>
-                        <select name="parent_group" id="parent_group">
-                            <option value="">{_T string="None"}</option>
-{foreach item=g from=$groups}
-    {if $group->canSetParentGroup($g)}
-                            <option value="{$g->getId()}"{if isset($pgroup) and $pgroup->getId() eq $g->getId()} selected="selected"{/if}>{$g->getIndentName()}</option>
-    {/if}
-{/foreach}
-                        </select>
 {/if}
                     </p>
                 </div>
 {/if}
       </div>
         <div class="button-container">
+{if $canEdit}
             <button type="submit" name="valid" class="button action">
                 <i class="fas fa-save fa-fw"></i> {_T string="Save"}
             </button>
+            <input type="hidden" name="id_group" id="id_group" value="{$group->getId()}"/>
+            {include file="forms_types/csrf.tpl"}
+{/if}
 {if $login->isAdmin() or $login->isStaff()}
             <a class="button delete" id="delete" href="{path_for name="removeGroup" data=["id" => $group->getId()]}">
                 <i class="fas fa-trash-alt fa-fw"></i>
                 {_T string="Delete"}
             </a>
 {/if}
+{if $canExport}
             <a href="{path_for name="pdf_groups" data=["id" => $group->getId()]}" class="button tooltip" title="{_T string="Current group (and attached people) as PDF"}">
                 <i class="fas fa-file-pdf" aria-hidden="true"></i>
                 {_T string="Group PDF"}
             </a>
-            <input type="hidden" name="id_group" id="id_group" value="{$group->getId()}"/>
-            {include file="forms_types/csrf.tpl"}
+{/if}
         </div>
+{if $canEdit}
         <p>{_T string="NB : The mandatory fields are in"} <span class="required">{_T string="red"}</span></p>
+{/if}
         </form>
 <script type="text/javascript">
     $(function() {
index b182eabc6d0579f2bf7a409c81f561efa843e659..cedf1e81e3f4b7d161ceb54b7759513bad2de549 100644 (file)
@@ -46,7 +46,11 @@ We have to use a template file, so Smarty will do its work (like replacing varia
   {if $login->isAdmin() or $login->isStaff()}
             <li{if $cur_route eq "contributions" and $cur_subroute eq "contributions"} class="selected"{/if}><a href="{path_for name="contributions" data=["type" => "contributions"]}" title="{_T string="View and filter contributions"}">{_T string="List of contributions"}</a></li>
             <li{if $cur_route eq "contributions" and $cur_subroute eq "transactions"} class="selected"{/if}><a href="{path_for name="contributions" data=["type" => "transactions"]}" title="{_T string="View and filter transactions"}">{_T string="List of transactions"}</a></li>
+  {/if}
+  {if $login->isAdmin() or $login->isStaff() or ($login->isGroupManager() and $preferences->pref_bool_groupsmanagers_create_member)}
             <li{if $cur_route eq "editMember" or $cur_route eq "addMember"} class="selected"{/if}><a href="{path_for name="addMember"}" title="{_T string="Add new member in database"}">{_T string="Add a member"}</a></li>
+  {/if}
+  {if $login->isAdmin() or $login->isStaff()}
             <li{if $cur_route eq "addContribution" and $cur_subroute eq constant('Galette\Entity\Contribution::TYPE_FEE')} class="selected"{/if}><a href="{path_for name="addContribution" data=["type" => constant('Galette\Entity\Contribution::TYPE_FEE')]}" title="{_T string="Add new membership fee in database"}">{_T string="Add a membership fee"}</a></li>
             <li{if $cur_route eq "addContribution" and $cur_subroute eq constant('Galette\Entity\Contribution::TYPE_DONATION')} class="selected"{/if}><a href="{path_for name="addContribution" data=["type" => constant('Galette\Entity\Contribution::TYPE_DONATION')]}" title="{_T string="Add new donation in database"}">{_T string="Add a donation"}</a></li>
             <li{if $cur_route eq "addTransaction" or $cur_route eq "editTransaction"} class="selected"{/if}><a href="{path_for name="addTransaction"}" title="{_T string="Add new transaction in database"}">{_T string="Add a transaction"}</a></li>
@@ -56,11 +60,10 @@ We have to use a template file, so Smarty will do its work (like replacing varia
             <li{if $cur_route eq "export"} class="selected"{/if}><a href="{path_for name="export"}" title="{_T string="Export some data in various formats"}">{_T string="Exports"}</a></li>
             <li{if $cur_route eq "import" or $cur_route eq "importModel"} class="selected"{/if}><a href="{path_for name="import"}" title="{_T string="Import members from CSV files"}">{_T string="Imports"}</a></li>
             <li class="mnu_last{if $cur_route eq "charts"} selected{/if}"><a href="{path_for name="charts"}" title="{_T string="Various charts"}">{_T string="Charts"}</a></li>
-  {else}
-            <li{if $cur_route eq "contributions" and $cur_subroute eq "contributions"} class="selected"{/if}><a href="{path_for name="contributions" data=["type" => "contributions"]}" title="{_T string="View and filter all my contributions"}">{_T string="My contributions"}</a></li>
-            <li{if $cur_route eq "contributions" and $cur_subroute eq "transactions"} class="selected"{/if}><a href="{path_for name="contributions" data=["type" => "transactions"]}" title="{_T string="View and filter all my transactions"}">{_T string="My transactions"}</a></li>
   {/if}
   {if !$login->isSuperAdmin()}
+            <li{if $cur_route eq "contributions" and $cur_subroute eq "contributions"} class="selected"{/if}><a href="{path_for name="contributions" data=["type" => "contributions"]}" title="{_T string="View and filter all my contributions"}">{_T string="My contributions"}</a></li>
+            <li{if $cur_route eq "contributions" and $cur_subroute eq "transactions"} class="selected"{/if}><a href="{path_for name="contributions" data=["type" => "transactions"]}" title="{_T string="View and filter all my transactions"}">{_T string="My transactions"}</a></li>
             <li{if $cur_route eq "me" or $cur_route eq "member"} class="selected"{/if}><a href="{path_for name="me"}" title="{_T string="View my member card"}">{_T string="My information"}</a></li>
   {/if}
         </ul>
index aea003fd84023eeb1375f2331350bfaf25b62b4f..9994d62962cf3204e24be8257fe44625ed9d86fa 100644 (file)
@@ -6,6 +6,7 @@
                 <li><a href="#general">{_T string="General"}</a></li>
                 <li><a href="#social">{_T string="Social networks"}</a></li>
                 <li><a href="#parameters">{_T string="Parameters"}</a></li>
+                <li><a href="#rights">{_T string="Rights"}</a></li>
                 <li><a href="#mail">{_T string="E-Mail"}</a></li>
                 <li><a href="#labels">{_T string="Labels"}</a></li>
                 <li><a href="#cards">{_T string="Cards"}</a></li>
                     </select>
                 </p>
                 <p>
-                    <label for="pref_bool_create_member" class="bline tooltip">{_T string="Can members create child?"}</label>
-                    <span class="tip">{_T string="Any logged in member will be able to create his own child cards"}</span>
-                    <input type="checkbox" name="pref_bool_create_member" id="pref_bool_create_member" value="1" {if $pref.pref_bool_create_member eq 1}checked="checked"{/if}{if isset($required.pref_bool_create_member) and $required.pref_bool_create_member eq 1} required="required"{/if}/>
-                </p>
-                <p>
-
                     <label for="pref_redirect_on_create" class="bline">{_T string="After member creation:"}</label>
                     <select name="pref_redirect_on_create" id="pref_redirect_on_create">
                         <option value="{constant('Galette\Entity\Adherent::AFTER_ADD_DEFAULT')}"{if $pref.pref_redirect_on_create  == constant('Galette\Entity\Adherent::AFTER_ADD_DEFAULT')} selected="selected"{/if}>{_T string="create a new contribution (default action)"}</option>
                 </p>
             </fieldset>
 
+            <fieldset class="cssform" id="rights">
+                <legend>{_T string="Rights"}</legend>
+                                <p>
+                    <label for="pref_bool_create_member" class="bline">{_T string="Can members create child?"}</label>
+                    <input type="checkbox" name="pref_bool_create_member" id="pref_bool_create_member" value="1" {if $pref.pref_bool_create_member eq 1}checked="checked"{/if}{if isset($required.pref_bool_create_member) and $required.pref_bool_create_member eq 1} required="required"{/if}/>
+                </p>
+                <p>
+                    <label for="pref_bool_groupsmanagers_edit_groups" class="bline">{_T string="Can group managers edit their groups?"}</label>
+                    <input type="checkbox" name="pref_bool_groupsmanagers_edit_groups" id="pref_bool_groupsmanagers_edit_groups" value="1" {if $pref.pref_bool_groupsmanagers_edit_groups eq 1}checked="checked"{/if}/>
+                </p>
+                <p>
+                    <label for="pref_bool_groupsmanagers_create_member" class="bline">{_T string="Can group managers create members?"}</label>
+                    <input type="checkbox" name="pref_bool_groupsmanagers_create_member" id="pref_bool_groupsmanagers_create_member" value="1" {if $pref.pref_bool_groupsmanagers_create_member eq 1}checked="checked"{/if}/>
+                </p>
+                <p>
+                    <label for="pref_bool_groupsmanagers_edit_member" class="bline">{_T string="Can group managers edit members?"}</label>
+                    <input type="checkbox" name="pref_bool_groupsmanagers_edit_member" id="pref_bool_groupsmanagers_edit_member" value="1" {if $pref.pref_bool_groupsmanagers_edit_member eq 1}checked="checked"{/if}/>
+                </p>
+                <p>
+                    <label for="pref_bool_groupsmanagers_mailings" class="bline">{_T string="Can group managers send mailings?"}</label>
+                    <input type="checkbox" name="pref_bool_groupsmanagers_mailings" id="pref_bool_groupsmanagers_mailings" value="1" {if $pref.pref_bool_groupsmanagers_mailings eq 1}checked="checked"{/if}/>
+                </p>
+                <p>
+                    <label for="pref_bool_groupsmanagers_exports" class="bline tooltip">{_T string="Can group managers do exports?"}</label>
+                    <span class="tip">{_T string="Groups managers will be allowed to export members as csv, pdf cards, attendence sheetss and groups pdf"}</span>
+                    <input type="checkbox" name="pref_bool_groupsmanagers_exports" id="pref_bool_groupsmanagers_exports" value="1" {if $pref.pref_bool_groupsmanagers_exports eq 1}checked="checked"{/if}/>
+                </p>
+            </fieldset>
+
             <fieldset class="cssform" id="mail">
                 <legend>{_T string="Mail settings"}</legend>
     {if $GALETTE_MODE eq 'DEMO'}
index 4903b92ef373e2dd3d144adfd0786dae3abbe6c9..1c9568a442418d514068c8eadb82a7fefee9da52 100644 (file)
@@ -1831,6 +1831,11 @@ th.bline, td.bline{
     width: 19.9em;
 }
 
+#rights .bline, #rights.galette_form label, #rights.galette_form .bline {
+    margin: 0 0 0 -22em;
+    width: 30em;
+}
+
 .galette_form .radios label,
 .galette_form label.labelalign {
     width: auto;
@@ -2362,7 +2367,7 @@ meter[value="4"]::-moz-meter-bar { background: green ; }
         padding-left: 0;
     }
 
-    .cssform .bline, .galette_form label, .galette_form .bline {
+    .cssform .bline, .galette_form label, .galette_form .bline, #rights .bline {
         margin-left: 0;
         margin-bottom: .2em;
         padding-right: 0;
index 1efb1897d7eec73384f92c466d64f33112901663..455a2bbb0f9840a2e9e1d4353dd574fd529d1af2 100644 (file)
@@ -377,6 +377,34 @@ class Adherent extends GaletteTestCase
         $expected = ['Status #256 does not exists in database.'];
         $check = $adh->check($data, [], []);
         $this->array($check)->isIdenticalTo($expected);
+
+        //tests for group managers
+        $g1 = new \mock\Galette\Entity\Group();
+        $this->calling($g1)->getId = 1;
+        $g2 = new \mock\Galette\Entity\Group();
+        $this->calling($g2)->getId = 2;
+
+        //groups managers must specify a group they manage
+        global $login;
+        $login = new \mock\Galette\Core\Login($this->zdb, $this->i18n);
+
+        $this->calling($login)->isGroupManager = function ($gid) use ($g1) {
+            return $gid === null || $gid == $g1->getId();
+        };
+
+        $data = ['id_statut' => ''];
+        $check = $adh->check($data, [], []);
+        $expected = ['You have to select a group you own!'];
+        $this->array($check)->isIdenticalTo($expected);
+
+        $data = ['groups_adh' => [$g2->getId()]];
+        $check = $adh->check($data, [], []);
+        $expected = ['You have to select a group you own!'];
+        $this->array($check)->isIdenticalTo($expected);
+
+        $data = ['groups_adh' => [$g1->getId()]];
+        $check = $adh->check($data, [], []);
+        $this->boolean($check)->isTrue();
     }
 
     /**
@@ -426,14 +454,25 @@ class Adherent extends GaletteTestCase
         $g1 = new \mock\Galette\Entity\Group();
         $this->calling($g1)->getId = 1;
         $g2 = new \mock\Galette\Entity\Group();
-        $this->calling($g1)->getId = 2;
+        $this->calling($g2)->getId = 2;
 
         $this->calling($adh)->getGroups = [$g1, $g2];
         $login = new \mock\Galette\Core\Login($this->zdb, $this->i18n);
         $this->boolean($adh->canEdit($login))->isFalse();
 
-        $this->calling($login)->isGroupManager = true;
-        $this->boolean($adh->canEdit($login))->isTrue();
+        $this->calling($login)->isGroupManager = function ($gid) use ($g1) {
+            return $gid === null || $gid == $g1->getId();
+        };
+        $this->boolean($adh->canEdit($login))->isFalse();
+
+        $this->preferences->pref_bool_groupsmanagers_edit_member = true;
+        $canEdit = $adh->canEdit($login);
+        $this->preferences->pref_bool_groupsmanagers_edit_member = false; //reset
+        $this->boolean($canEdit)->isTrue();
+
+        //groups managers cannot edit members of the groups they do not own
+        $this->calling($adh)->getGroups = [$g2];
+        $this->boolean($adh->canEdit($login))->isFalse();
     }
 
     /**
@@ -614,6 +653,28 @@ class Adherent extends GaletteTestCase
         //logout
         $this->login->logOut();
         $this->boolean($this->login->isLogged())->isFalse();
+
+        //tests for group managers
+        $adh = new \mock\Galette\Entity\Adherent($this->zdb);
+
+        $g1 = new \mock\Galette\Entity\Group();
+        $this->calling($g1)->getId = 1;
+        $g2 = new \mock\Galette\Entity\Group();
+        $this->calling($g2)->getId = 2;
+
+        //groups managers can show members of the groups they own
+        $this->calling($adh)->getGroups = [$g1, $g2];
+        $login = new \mock\Galette\Core\Login($this->zdb, $this->i18n);
+        $this->boolean($adh->canShow($login))->isFalse();
+
+        $this->calling($login)->isGroupManager = function ($gid) use ($g1) {
+            return $gid === null || $gid == $g1->getId();
+        };
+        $this->boolean($adh->canShow($login))->isTrue();
+
+        //groups managers cannot show members of the groups they do not own
+        $this->calling($adh)->getGroups = [$g2];
+        $this->boolean($adh->canShow($login))->isFalse();
     }
 
     /**
index 43657a2002fdd24ad361e58536749ac37d424e80..86fd2e51b8969ff36e70b00f7ef2786ee74299d0 100644 (file)
@@ -104,12 +104,12 @@ class CheckAcls extends atoum
             ['filter-memberslist', 'groupmanager'],
             ['advanced-search', 'groupmanager'],
             ['batch-memberslist', 'groupmanager'],
-            ['mailing', 'staff'],
-            ['doMailing', 'staff'],
-            ['mailingPreview', 'staff'],
-            ['previewAttachment', 'staff'],
-            ['mailingRecipients', 'staff'],
-            ['csv-memberslist', 'staff'],
+            ['mailing', 'groupmanager'],
+            ['doMailing', 'groupmanager'],
+            ['mailingPreview', 'groupmanager'],
+            ['previewAttachment', 'groupmanager'],
+            ['mailingRecipients', 'groupmanager'],
+            ['csv-memberslist', 'groupmanager'],
             ['groups', 'groupmanager'],
             ['me', 'member'],
             ['member', 'member'],
@@ -174,7 +174,7 @@ class CheckAcls extends atoum
             ['ajax_group', 'groupmanager'],
             ['ajax_groups', 'groupmanager'],
             ['ajax_groupname_unique', 'groupmanager'],
-            ['ajax_groups_reorder', 'staff'],
+            ['ajax_groups_reorder', 'groupmanager'],
             ['add_group', 'staff'],
             ['removeGroup', 'staff'],
             ['doRemoveGroup', 'staff'],