ILIAS  release_6 Revision v6.24-5-g0c8bfefb3b8
class.ilRbacAdmin.php
Go to the documentation of this file.
1 <?php
2 /* Copyright (c) 1998-2010 ILIAS open source, Extended GPL, see docs/LICENSE */
3 
4 
19 {
24  public function __construct()
25  {
26  global $DIC;
27 
28  $ilDB = $DIC['ilDB'];
29  $ilErr = $DIC['ilErr'];
30  $ilias = $DIC['ilias'];
31 
32  // set db & error handler
33  (isset($ilDB)) ? $this->ilDB = &$ilDB : $this->ilDB = &$ilias->db;
34 
35  if (!isset($ilErr)) {
36  $ilErr = new ilErrorHandling();
37  $ilErr->setErrorHandling(PEAR_ERROR_CALLBACK, array($ilErr,'errorHandler'));
38  } else {
39  $this->ilErr = &$ilErr;
40  }
41  }
42 
49  public function setBlockedStatus($a_role_id, $a_ref_id, $a_blocked_status)
50  {
51  global $DIC;
52 
53  $ilDB = $DIC['ilDB'];
54 
55  ilLoggerFactory::getLogger('crs')->logStack();
56  $query = 'UPDATE rbac_fa set blocked = ' . $ilDB->quote($a_blocked_status, 'integer') . ' ' .
57  'WHERE rol_id = ' . $ilDB->quote($a_role_id, 'integer') . ' ' .
58  'AND parent = ' . $ilDB->quote($a_ref_id, 'integer');
59  $ilDB->manipulate($query);
60  }
61 
69  public function removeUser($a_usr_id)
70  {
71  global $DIC;
72 
73  $ilDB = $DIC->database();
74  $review = $DIC->rbac()->review();
75 
76  if (!isset($a_usr_id)) {
77  $message = get_class($this) . "::removeUser(): No usr_id given!";
78  $this->ilErr->raiseError($message, $this->ilErr->WARNING);
79  }
80 
81  foreach ($review->assignedRoles($a_usr_id) as $role_id) {
82  $this->deassignUser($role_id, $a_usr_id);
83  }
84 
85  $query = "DELETE FROM rbac_ua WHERE usr_id = " . $ilDB->quote($a_usr_id, 'integer');
86  $res = $ilDB->manipulate($query);
87 
88  return true;
89  }
90 
98  public function deleteRole($a_rol_id, $a_ref_id)
99  {
100  global $DIC;
101 
102  $lng = $DIC['lng'];
103  $ilDB = $DIC['ilDB'];
104 
105  if (!isset($a_rol_id) or !isset($a_ref_id)) {
106  $message = get_class($this) . "::deleteRole(): Missing parameter! role_id: " . $a_rol_id . " ref_id of role folder: " . $a_ref_id;
107  $this->ilErr->raiseError($message, $this->ilErr->WARNING);
108  }
109 
110  // exclude system role from rbac
111  if ($a_rol_id == SYSTEM_ROLE_ID) {
112  $this->ilErr->raiseError($lng->txt("msg_sysrole_not_deletable"), $this->ilErr->MESSAGE);
113  }
114 
115  include_once('Services/LDAP/classes/class.ilLDAPRoleGroupMapping.php');
117  $mapping->deleteRole($a_rol_id);
118 
119 
120  // TODO: check assigned users before deletion
121  // This is done in ilObjRole. Should be better moved to this place?
122 
123  // delete user assignements
124  $query = "DELETE FROM rbac_ua " .
125  "WHERE rol_id = " . $ilDB->quote($a_rol_id, 'integer');
126  $res = $ilDB->manipulate($query);
127 
128  // delete permission assignments
129  $query = "DELETE FROM rbac_pa " .
130  "WHERE rol_id = " . $ilDB->quote($a_rol_id, 'integer') . " ";
131  $res = $ilDB->manipulate($query);
132 
133  //delete rbac_templates and rbac_fa
134  $this->deleteLocalRole($a_rol_id);
135 
136  return true;
137  }
138 
145  public function deleteTemplate($a_obj_id)
146  {
147  global $DIC;
148 
149  $ilDB = $DIC['ilDB'];
150 
151  if (!isset($a_obj_id)) {
152  $message = get_class($this) . "::deleteTemplate(): No obj_id given!";
153  $this->ilErr->raiseError($message, $this->ilErr->WARNING);
154  }
155 
156  $query = 'DELETE FROM rbac_templates ' .
157  'WHERE rol_id = ' . $ilDB->quote($a_obj_id, 'integer');
158  $res = $ilDB->manipulate($query);
159 
160  $query = 'DELETE FROM rbac_fa ' .
161  'WHERE rol_id = ' . $ilDB->quote($a_obj_id, 'integer');
162  $res = $ilDB->manipulate($query);
163 
164  return true;
165  }
166 
174  public function deleteLocalRole($a_rol_id, $a_ref_id = 0)
175  {
176  global $DIC;
177 
178  $ilDB = $DIC['ilDB'];
179 
180  if (!isset($a_rol_id)) {
181  $message = get_class($this) . "::deleteLocalRole(): Missing parameter! role_id: '" . $a_rol_id . "'";
182  $this->ilErr->raiseError($message, $this->ilErr->WARNING);
183  }
184 
185  // exclude system role from rbac
186  if ($a_rol_id == SYSTEM_ROLE_ID) {
187  return true;
188  }
189 
190  if ($a_ref_id != 0) {
191  $clause = 'AND parent = ' . $ilDB->quote($a_ref_id, 'integer') . ' ';
192  }
193 
194  $query = 'DELETE FROM rbac_fa ' .
195  'WHERE rol_id = ' . $ilDB->quote($a_rol_id, 'integer') . ' ' .
196  $clause;
197  $res = $ilDB->manipulate($query);
198 
199  $query = 'DELETE FROM rbac_templates ' .
200  'WHERE rol_id = ' . $ilDB->quote($a_rol_id, 'integer') . ' ' .
201  $clause;
202  $res = $ilDB->manipulate($query);
203  return true;
204  }
205 
212  public function assignUserLimited($a_role_id, $a_usr_id, $a_limit, $a_limited_roles = array())
213  {
214  global $DIC;
215 
216  $ilDB = $DIC['ilDB'];
217 
218  $ilAtomQuery = $ilDB->buildAtomQuery();
219  $ilAtomQuery->addTableLock('rbac_ua');
220 
221  $ilAtomQuery->addQueryCallable(
222  function (ilDBInterface $ilDB) use (&$ret, $a_role_id, $a_usr_id,$a_limit, $a_limited_roles) {
223  $ret = true;
224  $limit_query = 'SELECT COUNT(*) num FROM rbac_ua ' .
225  'WHERE ' . $ilDB->in('rol_id', (array) $a_limited_roles, false, 'integer');
226  $res = $ilDB->query($limit_query);
227  $row = $res->fetchRow(ilDBConstants::FETCHMODE_OBJECT);
228  if ($row->num >= $a_limit) {
229  $ret = false;
230  return;
231  }
232 
233  $query = "INSERT INTO rbac_ua (usr_id, rol_id) " .
234  "VALUES (" .
235  $ilDB->quote($a_usr_id, 'integer') . "," . $ilDB->quote($a_role_id, 'integer') .
236  ")";
237  $res = $ilDB->manipulate($query);
238  }
239  );
240 
241  $ilAtomQuery->run();
242 
243  if (!$ret) {
244  return false;
245  }
246 
247  $GLOBALS['DIC']['rbacreview']->setAssignedCacheEntry($a_role_id, $a_usr_id, true);
248 
249  include_once('Services/LDAP/classes/class.ilLDAPRoleGroupMapping.php');
251  $mapping->assign($a_role_id, $a_usr_id);
252  return true;
253  }
254 
255 
264  public function assignUser($a_rol_id, $a_usr_id)
265  {
266  global $DIC;
267 
268  $ilDB = $DIC['ilDB'];
269  $rbacreview = $DIC['rbacreview'];
270 
271  if (!isset($a_rol_id) or !isset($a_usr_id)) {
272  $message = get_class($this) . "::assignUser(): Missing parameter! role_id: " . $a_rol_id . " usr_id: " . $a_usr_id;
273  #$this->ilErr->raiseError($message,$this->ilErr->WARNING);
274  }
275 
276  // check if already assigned user id and role_id
277  $alreadyAssigned = $rbacreview->isAssigned($a_usr_id, $a_rol_id);
278 
279  // enhanced: only if we haven't had this role for this user
280  if (!$alreadyAssigned) {
281  $query = "INSERT INTO rbac_ua (usr_id, rol_id) " .
282  "VALUES (" . $ilDB->quote($a_usr_id, 'integer') . "," . $ilDB->quote($a_rol_id, 'integer') . ")";
283  $res = $ilDB->manipulate($query);
284 
285  $rbacreview->setAssignedCacheEntry($a_rol_id, $a_usr_id, true);
286  }
287 
288  include_once('Services/LDAP/classes/class.ilLDAPRoleGroupMapping.php');
290  $mapping->assign($a_rol_id, $a_usr_id);
291 
292 
293  $ref_id = $GLOBALS['DIC']['rbacreview']->getObjectReferenceOfRole($a_rol_id);
294  $obj_id = ilObject::_lookupObjId($ref_id);
295  $type = ilObject::_lookupType($obj_id);
296 
297  if (!$alreadyAssigned) {
298  ilLoggerFactory::getInstance()->getLogger('ac')->debug('Raise event assign user');
299  $GLOBALS['DIC']['ilAppEventHandler']->raise(
300  'Services/AccessControl',
301  'assignUser',
302  array(
303  'obj_id' => $obj_id,
304  'usr_id' => $a_usr_id,
305  'role_id' => $a_rol_id,
306  'type' => $type
307  )
308  );
309  }
310  return true;
311  }
312 
313 
322  public function deassignUser($a_rol_id, $a_usr_id)
323  {
324  global $DIC;
325 
326  $ilDB = $DIC['ilDB'];
327  $rbacreview = $DIC->rbac()->review();
328 
329  if (!isset($a_rol_id) or !isset($a_usr_id)) {
330  $message = get_class($this) . "::deassignUser(): Missing parameter! role_id: " . $a_rol_id . " usr_id: " . $a_usr_id;
331  $this->ilErr->raiseError($message, $this->ilErr->WARNING);
332  }
333 
334  $query = "DELETE FROM rbac_ua " .
335  "WHERE usr_id = " . $ilDB->quote($a_usr_id, 'integer') . " " .
336  "AND rol_id = " . $ilDB->quote($a_rol_id, 'integer') . " ";
337  $res = $ilDB->manipulate($query);
338 
339  $rbacreview->setAssignedCacheEntry($a_rol_id, $a_usr_id, false);
340 
341  include_once('Services/LDAP/classes/class.ilLDAPRoleGroupMapping.php');
343  $mapping->deassign($a_rol_id, $a_usr_id);
344 
345  if ($res) {
346  $ref_id = $GLOBALS['DIC']['rbacreview']->getObjectReferenceOfRole($a_rol_id);
347  $obj_id = ilObject::_lookupObjId($ref_id);
348  $type = ilObject::_lookupType($obj_id);
349 
350  ilLoggerFactory::getInstance()->getLogger('ac')->debug('Raise event deassign user');
351  $GLOBALS['DIC']['ilAppEventHandler']->raise('Services/AccessControl', 'deassignUser', array(
352  'obj_id' => $obj_id,
353  'usr_id' => $a_usr_id,
354  'role_id' => $a_rol_id,
355  'type' => $type,
356  ));
357  }
358 
359  return true;
360  }
361 
370  public function grantPermission($a_rol_id, $a_ops, $a_ref_id)
371  {
372  global $DIC;
373 
374  $ilDB = $DIC['ilDB'];
375 
376  if (!isset($a_rol_id) or !isset($a_ops) or !isset($a_ref_id)) {
377  $this->ilErr->raiseError(get_class($this) . "::grantPermission(): Missing parameter! " .
378  "role_id: " . $a_rol_id . " ref_id: " . $a_ref_id . " operations: ", $this->ilErr->WARNING);
379  }
380 
381  if (!is_array($a_ops)) {
382  $this->ilErr->raiseError(
383  get_class($this) . "::grantPermission(): Wrong datatype for operations!",
384  $this->ilErr->WARNING
385  );
386  }
387 
388  /*
389  if (count($a_ops) == 0)
390  {
391  return false;
392  }
393  */
394  // exclude system role from rbac
395  if ($a_rol_id == SYSTEM_ROLE_ID) {
396  return true;
397  }
398 
399  // convert all values to integer
400  foreach ($a_ops as $key => $operation) {
401  $a_ops[$key] = (int) $operation;
402  }
403 
404  // Serialization des ops_id Arrays
405  $ops_ids = serialize($a_ops);
406 
407  $query = 'DELETE FROM rbac_pa ' .
408  'WHERE rol_id = %s ' .
409  'AND ref_id = %s';
410  $res = $ilDB->queryF(
411  $query,
412  array('integer','integer'),
413  array($a_rol_id,$a_ref_id)
414  );
415 
416  if (!count($a_ops)) {
417  return false;
418  }
419 
420  $query = "INSERT INTO rbac_pa (rol_id,ops_id,ref_id) " .
421  "VALUES " .
422  "(" . $ilDB->quote($a_rol_id, 'integer') . "," . $ilDB->quote($ops_ids, 'text') . "," . $ilDB->quote($a_ref_id, 'integer') . ")";
423  $res = $ilDB->manipulate($query);
424 
425  return true;
426  }
427 
437  public function revokePermission($a_ref_id, $a_rol_id = 0, $a_keep_protected = true)
438  {
439  global $DIC;
440 
441  $rbacreview = $DIC['rbacreview'];
442  $log = $DIC['log'];
443  $ilDB = $DIC['ilDB'];
444  $ilLog = $DIC['ilLog'];
445 
446  if (!isset($a_ref_id)) {
447  $ilLog->logStack();
448  $message = get_class($this) . "::revokePermission(): Missing parameter! ref_id: " . $a_ref_id;
449  $this->ilErr->raiseError($message, $this->ilErr->WARNING);
450  }
451  #$log->write("ilRBACadmin::revokePermission(), 0");
452 
453  // bypass protected status of roles
454  if ($a_keep_protected != true) {
455  // exclude system role from rbac
456  if ($a_rol_id == SYSTEM_ROLE_ID) {
457  return true;
458  }
459 
460  if ($a_rol_id) {
461  $and1 = " AND rol_id = " . $ilDB->quote($a_rol_id, 'integer') . " ";
462  } else {
463  $and1 = "";
464  }
465 
466  $query = "DELETE FROM rbac_pa " .
467  "WHERE ref_id = " . $ilDB->quote($a_ref_id, 'integer') .
468  $and1;
469 
470  $res = $ilDB->manipulate($query);
471 
472  return true;
473  }
474 
475  // consider protected status of roles
476 
477  // in any case, get all roles in scope first
478  $roles_in_scope = $rbacreview->getParentRoleIds($a_ref_id);
479 
480  if (!$a_rol_id) {
481  #$log->write("ilRBACadmin::revokePermission(), 1");
482 
483  $role_ids = array();
484 
485  foreach ($roles_in_scope as $role) {
486  if ($role['protected'] == true) {
487  continue;
488  }
489 
490  $role_ids[] = $role['obj_id'];
491  }
492 
493  // return if no role in array
494  if (!$role_ids) {
495  return true;
496  }
497 
498  $query = 'DELETE FROM rbac_pa ' .
499  'WHERE ' . $ilDB->in('rol_id', $role_ids, false, 'integer') . ' ' .
500  'AND ref_id = ' . $ilDB->quote($a_ref_id, 'integer');
501  $res = $ilDB->manipulate($query);
502  } else {
503  #$log->write("ilRBACadmin::revokePermission(), 2");
504  // exclude system role from rbac
505  if ($a_rol_id == SYSTEM_ROLE_ID) {
506  return true;
507  }
508 
509  // exclude protected permission settings from revoking
510  if ($roles_in_scope[$a_rol_id]['protected'] == true) {
511  return true;
512  }
513 
514  $query = "DELETE FROM rbac_pa " .
515  "WHERE ref_id = " . $ilDB->quote($a_ref_id, 'integer') . " " .
516  "AND rol_id = " . $ilDB->quote($a_rol_id, 'integer') . " ";
517  $res = $ilDB->manipulate($query);
518  }
519 
520  return true;
521  }
522 
529  public function revokeSubtreePermissions($a_ref_id, $a_role_id)
530  {
531  global $DIC;
532 
533  $ilDB = $DIC['ilDB'];
534 
535  $query = 'DELETE FROM rbac_pa ' .
536  'WHERE ref_id IN ' .
537  '( ' . $GLOBALS['DIC']['tree']->getSubTreeQuery($a_ref_id, array('child')) . ' ) ' .
538  'AND rol_id = ' . $ilDB->quote($a_role_id, 'integer');
539 
540  $ilDB->manipulate($query);
541  return true;
542  }
543 
550  public function deleteSubtreeTemplates($a_ref_id, $a_rol_id)
551  {
552  global $DIC;
553 
554  $ilDB = $DIC['ilDB'];
555 
556  $query = 'DELETE FROM rbac_templates ' .
557  'WHERE parent IN ( ' .
558  $GLOBALS['DIC']['tree']->getSubTreeQuery($a_ref_id, array('child')) . ' ) ' .
559  'AND rol_id = ' . $ilDB->quote($a_rol_id, 'integer');
560 
561  $ilDB->manipulate($query);
562 
563  $query = 'DELETE FROM rbac_fa ' .
564  'WHERE parent IN ( ' .
565  $GLOBALS['DIC']['tree']->getSubTreeQuery($a_ref_id, array('child')) . ' ) ' .
566  'AND rol_id = ' . $ilDB->quote($a_rol_id, 'integer');
567 
568  $ilDB->manipulate($query);
569 
570  return true;
571  }
572 
580  public function revokePermissionList($a_ref_ids, $a_rol_id)
581  {
582  global $DIC;
583 
584  $ilDB = $DIC['ilDB'];
585 
586  if (!isset($a_ref_ids) or !is_array($a_ref_ids)) {
587  $message = get_class($this) . "::revokePermissionList(): Missing parameter or parameter is not an array! reference_list: " . var_dump($a_ref_ids);
588  $this->ilErr->raiseError($message, $this->ilErr->WARNING);
589  }
590 
591  if (!isset($a_rol_id)) {
592  $message = get_class($this) . "::revokePermissionList(): Missing parameter! rol_id: " . $a_rol_id;
593  $this->ilErr->raiseError($message, $this->ilErr->WARNING);
594  }
595 
596  // exclude system role from rbac
597  if ($a_rol_id == SYSTEM_ROLE_ID) {
598  return true;
599  }
600 
601  $query = "DELETE FROM rbac_pa " .
602  "WHERE " . $ilDB->in('ref_id', $a_ref_ids, false, 'integer') . ' ' .
603  "AND rol_id = " . $ilDB->quote($a_rol_id, 'integer');
604  $res = $ilDB->manipulate($query);
605 
606  return true;
607  }
608 
619  public function copyRolePermissions($a_source_id, $a_source_parent, $a_dest_parent, $a_dest_id, $a_consider_protected = true)
620  {
621  global $DIC;
622 
623  $tree = $DIC['tree'];
624  $rbacreview = $DIC['rbacreview'];
625 
626  // Copy template permissions
627  $this->copyRoleTemplatePermissions($a_source_id, $a_source_parent, $a_dest_parent, $a_dest_id, $a_consider_protected);
628 
629  $ops = $rbacreview->getRoleOperationsOnObject($a_source_id, $a_source_parent);
630 
631  $this->revokePermission($a_dest_parent, $a_dest_id);
632  $this->grantPermission($a_dest_id, $ops, $a_dest_parent);
633  return true;
634  }
635 
646  public function copyRoleTemplatePermissions($a_source_id, $a_source_parent, $a_dest_parent, $a_dest_id, $a_consider_protected = true)
647  {
648  global $DIC;
649 
650  $rbacreview = $DIC['rbacreview'];
651  $ilDB = $DIC['ilDB'];
652 
653  if (!isset($a_source_id) or !isset($a_source_parent) or !isset($a_dest_id) or !isset($a_dest_parent)) {
654  $message = __METHOD__ . ": Missing parameter! source_id: " . $a_source_id .
655  " source_parent_id: " . $a_source_parent .
656  " dest_id : " . $a_dest_id .
657  " dest_parent_id: " . $a_dest_parent;
658  $this->ilErr->raiseError($message, $this->ilErr->WARNING);
659  }
660 
661  // exclude system role from rbac
662  if ($a_dest_id == SYSTEM_ROLE_ID) {
663  return true;
664  }
665 
666  // Read operations
667  $query = 'SELECT * FROM rbac_templates ' .
668  'WHERE rol_id = ' . $ilDB->quote($a_source_id, 'integer') . ' ' .
669  'AND parent = ' . $ilDB->quote($a_source_parent, 'integer');
670  $res = $ilDB->query($query);
671  $operations = array();
672  $rownum = 0;
673  while ($row = $ilDB->fetchObject($res)) {
674  $operations[$rownum]['type'] = $row->type;
675  $operations[$rownum]['ops_id'] = $row->ops_id;
676  $rownum++;
677  }
678 
679  // Delete target permissions
680  $query = 'DELETE FROM rbac_templates WHERE rol_id = ' . $ilDB->quote($a_dest_id, 'integer') . ' ' .
681  'AND parent = ' . $ilDB->quote($a_dest_parent, 'integer');
682  $res = $ilDB->manipulate($query);
683 
684  foreach ($operations as $row => $op) {
685  $query = 'INSERT INTO rbac_templates (rol_id,type,ops_id,parent) ' .
686  'VALUES (' .
687  $ilDB->quote($a_dest_id, 'integer') . "," .
688  $ilDB->quote($op['type'], 'text') . "," .
689  $ilDB->quote($op['ops_id'], 'integer') . "," .
690  $ilDB->quote($a_dest_parent, 'integer') . ")";
691  $ilDB->manipulate($query);
692  }
693 
694  // copy also protection status if applicable
695  if ($a_consider_protected == true) {
696  if ($rbacreview->isProtected($a_source_parent, $a_source_id)) {
697  $this->setProtected($a_dest_parent, $a_dest_id, 'y');
698  }
699  }
700 
701  return true;
702  }
716  public function copyRolePermissionIntersection($a_source1_id, $a_source1_parent, $a_source2_id, $a_source2_parent, $a_dest_parent, $a_dest_id)
717  {
718  global $DIC;
719 
720  $rbacreview = $DIC['rbacreview'];
721  $ilDB = $DIC['ilDB'];
722 
723  if (!isset($a_source1_id) or !isset($a_source1_parent)
724  or !isset($a_source2_id) or !isset($a_source2_parent)
725  or !isset($a_dest_id) or !isset($a_dest_parent)) {
726  $message = get_class($this) . "::copyRolePermissionIntersection(): Missing parameter! source1_id: " . $a_source1_id .
727  " source1_parent: " . $a_source1_parent .
728  " source2_id: " . $a_source2_id .
729  " source2_parent: " . $a_source2_parent .
730  " dest_id: " . $a_dest_id .
731  " dest_parent_id: " . $a_dest_parent;
732  $this->ilErr->raiseError($message, $this->ilErr->WARNING);
733  }
734 
735  // exclude system role from rbac
736  if ($a_dest_id == SYSTEM_ROLE_ID) {
737  ilLoggerFactory::getLogger('ac')->debug('Ignoring system role.');
738  return true;
739  }
740 
741  if ($rbacreview->isProtected($a_source2_parent, $a_source2_id)) {
742  $GLOBALS['DIC']['ilLog']->write(__METHOD__ . ': Role is protected');
743  return true;
744  }
745 
746  $query = "SELECT s1.type, s1.ops_id " .
747  "FROM rbac_templates s1, rbac_templates s2 " .
748  "WHERE s1.rol_id = " . $ilDB->quote($a_source1_id, 'integer') . " " .
749  "AND s1.parent = " . $ilDB->quote($a_source1_parent, 'integer') . " " .
750  "AND s2.rol_id = " . $ilDB->quote($a_source2_id, 'integer') . " " .
751  "AND s2.parent = " . $ilDB->quote($a_source2_parent, 'integer') . " " .
752  "AND s1.type = s2.type " .
753  "AND s1.ops_id = s2.ops_id";
754 
755  ilLoggerFactory::getLogger('ac')->dump($query);
756 
757  $res = $ilDB->query($query);
758  $operations = array();
759  $rowNum = 0;
760  while ($row = $res->fetchRow(ilDBConstants::FETCHMODE_OBJECT)) {
761  $operations[$rowNum]['type'] = $row->type;
762  $operations[$rowNum]['ops_id'] = $row->ops_id;
763 
764  $rowNum++;
765  }
766 
767  // Delete template permissions of target
768  $query = 'DELETE FROM rbac_templates WHERE rol_id = ' . $ilDB->quote($a_dest_id, 'integer') . ' ' .
769  'AND parent = ' . $ilDB->quote($a_dest_parent, 'integer');
770  $res = $ilDB->manipulate($query);
771 
772  $query = 'INSERT INTO rbac_templates (rol_id,type,ops_id,parent) ' .
773  'VALUES (?,?,?,?)';
774  $sta = $ilDB->prepareManip($query, array('integer','text','integer','integer'));
775  foreach ($operations as $key => $set) {
776  $ilDB->execute($sta, array(
777  $a_dest_id,
778  $set['type'],
779  $set['ops_id'],
780  $a_dest_parent));
781  }
782  return true;
783  }
784 
796  public function copyRolePermissionUnion(
797  $a_source1_id,
798  $a_source1_parent,
799  $a_source2_id,
800  $a_source2_parent,
801  $a_dest_id,
802  $a_dest_parent
803  ) {
804  global $DIC;
805 
806  $ilDB = $DIC['ilDB'];
807  $rbacreview = $DIC['rbacreview'];
808 
809 
810  $s1_ops = $rbacreview->getAllOperationsOfRole($a_source1_id, $a_source1_parent);
811  $s2_ops = $rbacreview->getAlloperationsOfRole($a_source2_id, $a_source2_parent);
812 
813  $this->deleteRolePermission($a_dest_id, $a_dest_parent);
814 
815  $GLOBALS['DIC']['ilLog']->write(__METHOD__ . ': ' . print_r($s1_ops, true));
816  $GLOBALS['DIC']['ilLog']->write(__METHOD__ . ': ' . print_r($s2_ops, true));
817 
818  foreach ($s1_ops as $type => $ops) {
819  foreach ($ops as $op) {
820  // insert all permission of source 1
821  // #15469
822  $query = 'INSERT INTO rbac_templates (rol_id,type,ops_id,parent) ' .
823  'VALUES( ' .
824  $ilDB->quote($a_dest_id, 'integer') . ', ' .
825  $ilDB->quote($type, 'text') . ', ' .
826  $ilDB->quote($op, 'integer') . ', ' .
827  $ilDB->quote($a_dest_parent, 'integer') . ' ' .
828  ')';
829  $ilDB->manipulate($query);
830  }
831  }
832 
833  // and the other direction...
834  foreach ($s2_ops as $type => $ops) {
835  foreach ($ops as $op) {
836  if (!isset($s1_ops[$type]) or !in_array($op, $s1_ops[$type])) {
837  $query = 'INSERT INTO rbac_templates (rol_id,type,ops_id,parent) ' .
838  'VALUES( ' .
839  $ilDB->quote($a_dest_id, 'integer') . ', ' .
840  $ilDB->quote($type, 'text') . ', ' .
841  $ilDB->quote($op, 'integer') . ', ' .
842  $ilDB->quote($a_dest_parent, 'integer') . ' ' .
843  ')';
844  $ilDB->manipulate($query);
845  }
846  }
847  }
848 
849  return true;
850  }
851 
859  public function copyRolePermissionSubtract($a_source_id, $a_source_parent, $a_dest_id, $a_dest_parent)
860  {
861  global $DIC;
862 
863  $rbacreview = $DIC['rbacreview'];
864  $ilDB = $DIC['ilDB'];
865 
866  $s1_ops = $rbacreview->getAllOperationsOfRole($a_source_id, $a_source_parent);
867  $d_ops = $rbacreview->getAllOperationsOfRole($a_dest_id, $a_dest_parent);
868 
869  foreach ($s1_ops as $type => $ops) {
870  foreach ($ops as $op) {
871  if (isset($d_ops[$type]) and in_array($op, $d_ops[$type])) {
872  $query = 'DELETE FROM rbac_templates ' .
873  'WHERE rol_id = ' . $ilDB->quote($a_dest_id, 'integer') . ' ' .
874  'AND type = ' . $ilDB->quote($type, 'text') . ' ' .
875  'AND ops_id = ' . $ilDB->quote($op, 'integer') . ' ' .
876  'AND parent = ' . $ilDB->quote($a_dest_parent, 'integer');
877  $ilDB->manipulate($query);
878  }
879  }
880  }
881  return true;
882  }
883 
884 
895  public function deleteRolePermission($a_rol_id, $a_ref_id, $a_type = false)
896  {
897  global $DIC;
898 
899  $ilDB = $DIC['ilDB'];
900 
901  if (!isset($a_rol_id) or !isset($a_ref_id)) {
902  $message = get_class($this) . "::deleteRolePermission(): Missing parameter! role_id: " . $a_rol_id . " ref_id: " . $a_ref_id;
903  $this->ilErr->raiseError($message, $this->ilErr->WARNING);
904  }
905 
906  // exclude system role from rbac
907  if ($a_rol_id == SYSTEM_ROLE_ID) {
908  return true;
909  }
910 
911  if ($a_type !== false) {
912  $and_type = " AND type=" . $ilDB->quote($a_type, 'text') . " ";
913  }
914 
915  $query = 'DELETE FROM rbac_templates ' .
916  'WHERE rol_id = ' . $ilDB->quote($a_rol_id, 'integer') . ' ' .
917  'AND parent = ' . $ilDB->quote($a_ref_id, 'integer') . ' ' .
918  $and_type;
919 
920  $res = $ilDB->manipulate($query);
921 
922  return true;
923  }
924 
935  public function setRolePermission($a_rol_id, $a_type, $a_ops, $a_ref_id)
936  {
937  global $DIC;
938 
939  $ilDB = $DIC['ilDB'];
940 
941  if (!isset($a_rol_id) or !isset($a_type) or !isset($a_ops) or !isset($a_ref_id)) {
942  $message = get_class($this) . "::setRolePermission(): Missing parameter!" .
943  " role_id: " . $a_rol_id .
944  " type: " . $a_type .
945  " operations: " . $a_ops .
946  " ref_id: " . $a_ref_id;
947  $this->ilErr->raiseError($message, $this->ilErr->WARNING);
948  }
949 
950  if (!is_string($a_type) or empty($a_type)) {
951  $message = get_class($this) . "::setRolePermission(): a_type is no string or empty!";
952  $this->ilErr->raiseError($message, $this->ilErr->WARNING);
953  }
954 
955  if (!is_array($a_ops) or empty($a_ops)) {
956  $message = get_class($this) . "::setRolePermission(): a_ops is no array or empty!";
957  $this->ilErr->raiseError($message, $this->ilErr->WARNING);
958  }
959 
960  // exclude system role from rbac
961  if ($a_rol_id == SYSTEM_ROLE_ID) {
962  return true;
963  }
964 
965  foreach ($a_ops as $op) {
966  $ilDB->replace(
967  'rbac_templates',
968  [
969  'rol_id' => ['integer', $a_rol_id],
970  'type' => ['text', $a_type],
971  'ops_id' => ['integer', $op],
972  'parent' => ['integer', $a_ref_id]
973  ],
974  []
975  );
976  }
977  return true;
978  }
979 
993  public function assignRoleToFolder($a_rol_id, $a_parent, $a_assign = "y")
994  {
995  global $DIC;
996 
997  $ilDB = $DIC['ilDB'];
998  $rbacreview = $DIC['rbacreview'];
999 
1000  if (!isset($a_rol_id) or !isset($a_parent)) {
1001  $message = get_class($this) . "::assignRoleToFolder(): Missing Parameter!" .
1002  " role_id: " . $a_rol_id .
1003  " parent_id: " . $a_parent .
1004  " assign: " . $a_assign;
1005  $this->ilErr->raiseError($message, $this->ilErr->WARNING);
1006  }
1007 
1008  // exclude system role from rbac
1009  if ($a_rol_id == SYSTEM_ROLE_ID) {
1010  return true;
1011  }
1012 
1013  // if a wrong value is passed, always set assign to "n"
1014  if ($a_assign != "y") {
1015  $a_assign = "n";
1016  }
1017 
1018  // check if already assigned
1019  $query = 'SELECT rol_id FROM rbac_fa ' .
1020  'WHERE rol_id = ' . $ilDB->quote($a_rol_id, 'integer') . ' ' .
1021  'AND parent = ' . $ilDB->quote($a_parent, 'integer');
1022  $res = $ilDB->query($query);
1023  if ($res->numRows()) {
1024  ilLoggerFactory::getLogger('ac')->info('Role already assigned to object');
1025  return false;
1026  }
1027 
1028  $query = sprintf(
1029  'INSERT INTO rbac_fa (rol_id, parent, assign, protected) ' .
1030  'VALUES (%s,%s,%s,%s)',
1031  $ilDB->quote($a_rol_id, 'integer'),
1032  $ilDB->quote($a_parent, 'integer'),
1033  $ilDB->quote($a_assign, 'text'),
1034  $ilDB->quote('n', 'text')
1035  );
1036  $res = $ilDB->manipulate($query);
1037 
1038  return true;
1039  }
1040 
1049  public function assignOperationToObject($a_type_id, $a_ops_id)
1050  {
1051  global $DIC;
1052 
1053  $ilDB = $DIC['ilDB'];
1054 
1055  if (!isset($a_type_id) or !isset($a_ops_id)) {
1056  $message = get_class($this) . "::assignOperationToObject(): Missing parameter!" .
1057  "type_id: " . $a_type_id .
1058  "ops_id: " . $a_ops_id;
1059  $this->ilErr->raiseError($message, $this->ilErr->WARNING);
1060  }
1061 
1062  $query = "INSERT INTO rbac_ta (typ_id, ops_id) " .
1063  "VALUES(" . $ilDB->quote($a_type_id, 'integer') . "," . $ilDB->quote($a_ops_id, 'integer') . ")";
1064  $res = $ilDB->manipulate($query);
1065  return true;
1066  }
1067 
1076  public function deassignOperationFromObject($a_type_id, $a_ops_id)
1077  {
1078  global $DIC;
1079 
1080  $ilDB = $DIC['ilDB'];
1081 
1082  if (!isset($a_type_id) or !isset($a_ops_id)) {
1083  $message = get_class($this) . "::deassignPermissionFromObject(): Missing parameter!" .
1084  "type_id: " . $a_type_id .
1085  "ops_id: " . $a_ops_id;
1086  $this->ilErr->raiseError($message, $this->ilErr->WARNING);
1087  }
1088 
1089  $query = "DELETE FROM rbac_ta " .
1090  "WHERE typ_id = " . $ilDB->quote($a_type_id, 'integer') . " " .
1091  "AND ops_id = " . $ilDB->quote($a_ops_id, 'integer');
1092  $res = $ilDB->manipulate($query);
1093 
1094  return true;
1095  }
1096 
1105  public function setProtected($a_ref_id, $a_role_id, $a_value)
1106  {
1107  global $DIC;
1108 
1109  $ilDB = $DIC['ilDB'];
1110 
1111  // ref_id not used yet. protected permission acts 'global' for each role,
1112  // regardless of any broken inheritance before
1113  $query = 'UPDATE rbac_fa ' .
1114  'SET protected = ' . $ilDB->quote($a_value, 'text') . ' ' .
1115  'WHERE rol_id = ' . $ilDB->quote($a_role_id, 'integer');
1116  $res = $ilDB->manipulate($query);
1117  return true;
1118  }
1119 
1130  public function copyLocalRoles($a_source_id, $a_target_id)
1131  {
1132  global $DIC;
1133 
1134  $rbacreview = $DIC['rbacreview'];
1135  $ilLog = $DIC['ilLog'];
1136  $ilObjDataCache = $DIC['ilObjDataCache'];
1137 
1138  $real_local = array();
1139  foreach ($rbacreview->getRolesOfRoleFolder($a_source_id, false) as $role_data) {
1140  $title = $ilObjDataCache->lookupTitle($role_data);
1141  if (substr($title, 0, 3) == 'il_') {
1142  continue;
1143  }
1144  $real_local[] = $role_data;
1145  }
1146  if (!count($real_local)) {
1147  return true;
1148  }
1149  // Create role folder
1150  foreach ($real_local as $role) {
1151  include_once("./Services/AccessControl/classes/class.ilObjRole.php");
1152  $orig = new ilObjRole($role);
1153  $orig->read();
1154 
1155  $ilLog->write(__METHOD__ . ': Start copying of role ' . $orig->getTitle());
1156  $roleObj = new ilObjRole();
1157  $roleObj->setTitle($orig->getTitle());
1158  $roleObj->setDescription($orig->getDescription());
1159  $roleObj->setImportId($orig->getImportId());
1160  $roleObj->create();
1161 
1162  $this->assignRoleToFolder($roleObj->getId(), $a_target_id, "y");
1163  $this->copyRolePermissions($role, $a_source_id, $a_target_id, $roleObj->getId(), true);
1164  $ilLog->write(__METHOD__ . ': Added new local role, id ' . $roleObj->getId());
1165  }
1166  }
1167 
1178  public function initIntersectionPermissions($a_ref_id, $a_role_id, $a_role_parent, $a_template_id, $a_template_parent)
1179  {
1180  global $DIC;
1181 
1182  $rbacreview = $DIC['rbacreview'];
1183 
1184  if ($rbacreview->isProtected($a_role_parent, $a_role_id)) {
1185  // Assign object permissions
1186  $new_ops = $rbacreview->getOperationsOfRole(
1187  $a_role_id,
1188  ilObject::_lookupType($a_ref_id, true),
1189  $a_role_parent
1190  );
1191 
1192  // set new permissions for object
1193  $this->grantPermission(
1194  $a_role_id,
1195  (array) $new_ops,
1196  $a_ref_id
1197  );
1198  return;
1199  }
1200  if (!$a_template_id) {
1201  ilLoggerFactory::getLogger('ac')->info('No template id given. Aborting.');
1202  return;
1203  }
1204  // create template permission intersection
1206  $a_template_id,
1207  $a_template_parent,
1208  $a_role_id,
1209  $a_role_parent,
1210  $a_ref_id,
1211  $a_role_id
1212  );
1213 
1214  // assign role to folder
1215  $this->assignRoleToFolder(
1216  $a_role_id,
1217  $a_ref_id,
1218  'n'
1219  );
1220 
1221  // Assign object permissions
1222  $new_ops = $rbacreview->getOperationsOfRole(
1223  $a_role_id,
1224  ilObject::_lookupType($a_ref_id, true),
1225  $a_ref_id
1226  );
1227 
1228  // revoke existing permissions
1229  $this->revokePermission($a_ref_id, $a_role_id);
1230 
1231  // set new permissions for object
1232  $this->grantPermission(
1233  $a_role_id,
1234  (array) $new_ops,
1235  $a_ref_id
1236  );
1237 
1238  return;
1239  }
1240 
1248  protected function applyMovedObjectDidacticTemplates($a_ref_id, $a_old_parent)
1249  {
1250  include_once './Services/DidacticTemplate/classes/class.ilDidacticTemplateObjSettings.php';
1252  if (!$tpl_id) {
1253  return;
1254  }
1255  include_once './Services/DidacticTemplate/classes/class.ilDidacticTemplateActionFactory.php';
1256  foreach (ilDidacticTemplateActionFactory::getActionsByTemplateId($tpl_id) as $action) {
1257  if ($action instanceof ilDidacticTemplateLocalRoleAction) {
1258  continue;
1259  }
1260  $action->setRefId($a_ref_id);
1261  $action->apply();
1262  }
1263  return;
1264  }
1265 
1266 
1278  public function adjustMovedObjectPermissions($a_ref_id, $a_old_parent)
1279  {
1280  global $DIC;
1281 
1282  $rbacreview = $DIC['rbacreview'];
1283  $tree = $DIC['tree'];
1284  $ilLog = $DIC['ilLog'];
1285 
1286  $new_parent = $tree->getParentId($a_ref_id);
1287  $old_context_roles = $rbacreview->getParentRoleIds($a_old_parent, false);
1288  $new_context_roles = $rbacreview->getParentRoleIds($new_parent, false);
1289 
1290  $for_addition = $for_deletion = array();
1291  foreach ($new_context_roles as $new_role_id => $new_role) {
1292  if (!isset($old_context_roles[$new_role_id])) {
1293  $for_addition[$new_role_id] = $new_role;
1294  } elseif ($new_role['parent'] != $old_context_roles[$new_role_id]['parent']) {
1295  // handle stopped inheritance
1296  $for_deletion[$new_role_id] = $new_role;
1297  $for_addition[$new_role_id] = $new_role;
1298  }
1299  }
1300  foreach ($old_context_roles as $old_role_id => $old_role) {
1301  if (!isset($new_context_roles[$old_role_id])) {
1302  $for_deletion[$old_role_id] = $old_role;
1303  }
1304  }
1305 
1306  if (!count($for_deletion) and !count($for_addition)) {
1307  $this->applyMovedObjectDidacticTemplates($a_ref_id, $a_old_parent);
1308  return true;
1309  }
1310 
1311  include_once "Services/AccessControl/classes/class.ilRbacLog.php";
1312  $rbac_log_active = ilRbacLog::isActive();
1313  if ($rbac_log_active) {
1314  $role_ids = array_unique(array_merge(array_keys($for_deletion), array_keys($for_addition)));
1315  }
1316 
1317  foreach ($nodes = $tree->getSubTree($tree->getNodeData($a_ref_id), true) as $node_data) {
1318  $node_id = $node_data['child'];
1319 
1320  if ($rbac_log_active) {
1321  $log_old = ilRbacLog::gatherFaPa($node_id, $role_ids);
1322  }
1323 
1324  // If $node_data['type'] is not set, this means there is a tree entry without
1325  // object_reference and/or object_data entry
1326  // Continue in this case
1327  if (!$node_data['type']) {
1328  $ilLog->write(__METHOD__ . ': No type give. Choosing next tree entry.');
1329  continue;
1330  }
1331 
1332  if (!$node_id) {
1333  $ilLog->write(__METHOD__ . ': Missing subtree node_id');
1334  continue;
1335  }
1336 
1337  foreach ($for_deletion as $role_id => $role_data) {
1338  $this->deleteLocalRole($role_id, $node_id);
1339  $this->revokePermission($node_id, $role_id, false);
1340  //var_dump("<pre>",'REVOKE',$role_id,$node_id,$rolf_id,"</pre>");
1341  }
1342  foreach ($for_addition as $role_id => $role_data) {
1343  switch ($node_data['type']) {
1344  case 'grp':
1345  include_once './Modules/Group/classes/class.ilObjGroup.php';
1346  $tpl_id = ilObjGroup::lookupGroupStatusTemplateId($node_data['obj_id']);
1348  $node_data['child'],
1349  $role_id,
1350  $role_data['parent'],
1351  $tpl_id,
1352  ROLE_FOLDER_ID
1353  );
1354  break;
1355 
1356  case 'crs':
1357  include_once './Modules/Course/classes/class.ilObjCourse.php';
1360  $node_data['child'],
1361  $role_id,
1362  $role_data['parent'],
1363  $tpl_id,
1364  ROLE_FOLDER_ID
1365  );
1366  break;
1367 
1368 
1369  default:
1370  $this->grantPermission(
1371  $role_id,
1372  $ops = $rbacreview->getOperationsOfRole($role_id, $node_data['type'], $role_data['parent']),
1373  $node_id
1374  );
1375  break;
1376 
1377 
1378  }
1379 
1380 
1381  //var_dump("<pre>",'GRANT',$role_id,$ops,$role_id,$node_data['type'],$role_data['parent'],"</pre>");
1382  }
1383 
1384  if ($rbac_log_active) {
1385  $log_new = ilRbacLog::gatherFaPa($node_id, $role_ids);
1386  $log = ilRbacLog::diffFaPa($log_old, $log_new);
1388  }
1389  }
1390 
1391  $this->applyMovedObjectDidacticTemplates($a_ref_id, $a_old_parent);
1392  }
1393 } // END class.ilRbacAdmin
static lookupTemplateId($a_ref_id)
Lookup template id ilDB $ilDB.
static lookupGroupStatusTemplateId($a_obj_id)
$ilDB $ilDB
Class ilObjRole.
applyMovedObjectDidacticTemplates($a_ref_id, $a_old_parent)
Apply didactic templates after object movement.
removeUser($a_usr_id)
deletes a user from rbac_ua all user <-> role relations are deleted public
deleteRolePermission($a_rol_id, $a_ref_id, $a_type=false)
Deletes all entries of a template.
const PEAR_ERROR_CALLBACK
Definition: PEAR.php:35
$type
deassignOperationFromObject($a_type_id, $a_ops_id)
Deassign an existing operation from an object Update of rbac_ta public.
static lookupCourseNonMemberTemplatesId()
Lookup course non member id.
revokeSubtreePermissions($a_ref_id, $a_role_id)
Revoke subtree permissions.
setBlockedStatus($a_role_id, $a_ref_id, $a_blocked_status)
Set blocked status.
adjustMovedObjectPermissions($a_ref_id, $a_old_parent)
Adjust permissions of moved objects.
in($field, $values, $negate=false, $type="")
copyRolePermissionUnion( $a_source1_id, $a_source1_parent, $a_source2_id, $a_source2_parent, $a_dest_id, $a_dest_parent)
<type> $ilDB
static isActive()
deleteSubtreeTemplates($a_ref_id, $a_rol_id)
Delete all template permissions of subtree nodes.
assignUser($a_rol_id, $a_usr_id)
Assigns an user to a role.
setRolePermission($a_rol_id, $a_type, $a_ops, $a_ref_id)
Inserts template permissions in rbac_templates for an specific object type.
copyRoleTemplatePermissions($a_source_id, $a_source_parent, $a_dest_parent, $a_dest_id, $a_consider_protected=true)
Copies template permissions of one role to another.
static gatherFaPa($a_ref_id, array $a_role_ids, $a_add_action=false)
static diffFaPa(array $a_old, array $a_new)
$ilErr
Definition: raiseError.php:18
deassignUser($a_rol_id, $a_usr_id)
Deassigns a user from a role.
deleteLocalRole($a_rol_id, $a_ref_id=0)
Deletes a local role and entries in rbac_fa and rbac_templates public.
static _getInstance()
Get singleton instance of this class.
Interface ilDBInterface.
$a_type
Definition: workflow.php:92
initIntersectionPermissions($a_ref_id, $a_role_id, $a_role_parent, $a_template_id, $a_template_parent)
Init intersection permissions.
quote($value, $type)
assignUserLimited($a_role_id, $a_usr_id, $a_limit, $a_limited_roles=array())
Assign user limited.
if(!file_exists(getcwd() . '/ilias.ini.php'))
registration confirmation script for ilias
Definition: confirmReg.php:12
foreach($_POST as $key=> $value) $res
$lng
$log
Definition: result.php:15
setProtected($a_ref_id, $a_role_id, $a_value)
Set protected $ilDB.
copyLocalRoles($a_source_id, $a_target_id)
Copy local roles This method creates a copy of all local role.
static _lookupObjId($a_id)
if(!defined('PATH_SEPARATOR')) $GLOBALS['_PEAR_default_error_mode']
Definition: PEAR.php:64
copyRolePermissionIntersection($a_source1_id, $a_source1_parent, $a_source2_id, $a_source2_parent, $a_dest_parent, $a_dest_id)
Copies the intersection of the template permissions of two roles to a third role. ...
$query
grantPermission($a_rol_id, $a_ops, $a_ref_id)
Grants a permission to an object and a specific role.
revokePermissionList($a_ref_ids, $a_rol_id)
Revokes permissions of a LIST of objects of ONE role.
static _lookupType($a_id, $a_reference=false)
lookup object type
copyRolePermissionSubtract($a_source_id, $a_source_parent, $a_dest_id, $a_dest_parent)
Subtract role permissions.
__construct()
Constructor public.
copyRolePermissions($a_source_id, $a_source_parent, $a_dest_parent, $a_dest_id, $a_consider_protected=true)
Copies template permissions and permission of one role to another.
deleteRole($a_rol_id, $a_ref_id)
Deletes a role and deletes entries in object_data, rbac_pa, rbac_templates, rbac_ua, rbac_fa public.
represents a creation of local roles action
assignOperationToObject($a_type_id, $a_ops_id)
Assign an existing operation to an object Update of rbac_ta.
static add($a_action, $a_ref_id, array $a_diff, $a_source_ref_id=false)
revokePermission($a_ref_id, $a_rol_id=0, $a_keep_protected=true)
Revokes permissions of an object of one role.
global $ilDB
$ret
Definition: parser.php:6
query($query)
Run a (read-only) Query on the database.
$DIC
Definition: xapitoken.php:46
deleteTemplate($a_obj_id)
Deletes a template from role folder and deletes all entries in rbac_templates, rbac_fa public...
$message
Definition: xapiexit.php:14
const MOVE_OBJECT
static getLogger($a_component_id)
Get component logger.
Class ilRbacAdmin Core functions for role based access control.
manipulate($query)
Run a (write) Query on the database.
assignRoleToFolder($a_rol_id, $a_parent, $a_assign="y")
Assigns a role to an role folder A role folder is an object to store roles.
static getActionsByTemplateId($a_tpl_id)
Get actions of one template.