Core  3.2
PHP API documentation
 All Data Structures Namespaces Files Functions Variables Pages
ExportAccounts.php
Go to the documentation of this file.
1 <?php
2 /*
3  * @author Anakeen
4  * @package FDL
5 */
6 
7 namespace Dcp\Core;
8 
10 {
11  /**
12  * @var \SearchAccount
13  */
14  protected $search = null;
15  /**
16  * @var \DOMElement
17  */
18  private $rootNode;
19  /**
20  * @var \DOMElement
21  */
22  private $roleRootNode;
23  /**
24  * @var \DOMElement
25  */
26  private $groupRootNode;
27  /**
28  * @var \DOMElement
29  */
30  private $userRootNode;
31  /**
32  * @var \DOMDocument
33  */
34  protected $xml;
35  /**
36  * @var \Account
37  */
38  private $workAccount;
39 
40  protected $sessionKey = '';
41  protected $exportCryptedPassword = false;
42  /**
43  * @var \DocFam[]
44  */
45  private $families = array();
46 
47  const ABORTORDER = "::ABORT::";
48  const XSDDIR = "XSD";
49  protected $exportGroupParent = false;
50  protected $exportRoleParent = false;
51  protected $exportDocument = false;
52  protected $exportSchemaDirectory = "";
53  protected $aborted = false;
54 
55  protected $documentInfo = array();
56  private $addedIds = array();
57  private $schemaWritted = array();
58  /**
59  * Define accounts to exports
60  * @param \SearchAccount $s
61  */
62  public function setSearchAccount(\SearchAccount $s)
63  {
64  $this->search = $s;
65  }
66  /**
67  * Return account exportation based on SearchAccount object
68  * @return string the XML result
69  * @throws Exception
70  */
71  public function export()
72  {
73  if (!$this->search) {
74  throw new Exception("ACCT0101");
75  }
76  $this->workAccount = new \Account();
77  $this->initXml();
78  $this->setSessionMessage(___("Retrieve Account data", "fuserexport"));
79 
80  try {
81  $accounts = $this->search->search();
82 
83  $count = $accounts->length;
84  $k = 0;
85  foreach ($accounts as $account) {
86  $k++;
87  $this->setSessionMessage(sprintf(___("Export accounts (%d/%d)", "fuserexport") , $k, $count));
88  switch ($account->accounttype) {
89  case \Account::USER_TYPE:
90  $this->addUserAccount($account);
91  break;
92 
93  case \Account::GROUP_TYPE:
94  $this->addGroupAccount($account);
95  break;
96 
97  case \Account::ROLE_TYPE:
98  $this->addRoleAccount($account);
99  break;
100 
101  default:
102  throw new Exception("ACCT0100", $account->login, $account->id);
103  }
104  }
105  $this->addDocumentNodes();
106  $this->clearInfo();
107  $this->reorderGroups();
108  }
109  catch(Exception $e) {
110  if ($e->getDcpCode() === "ACCT0205") {
111  $this->aborted = true;
112  $this->rootNode->setAttribute("aborted", "true");
113  $this->setSessionMessage(___("Export Aborted", "fuserexport"));
114  } else {
115  $this->setSessionMessage("::END::");
116  throw $e;
117  }
118  }
119  $this->setSessionMessage("::END::");
120  return $this->xml->saveXML();
121  }
122  /**
123  * @param string $exportSchemaDirectory
124  * @throws Exception
125  */
127  {
128  if (!is_dir($exportSchemaDirectory)) {
129  throw new Exception("ACCT0207", $exportSchemaDirectory);
130  }
131  if (!is_writable($exportSchemaDirectory)) {
132  throw new Exception("ACCT0208", $exportSchemaDirectory);
133  }
134  $this->exportSchemaDirectory = $exportSchemaDirectory . "/" . self::XSDDIR;
135 
136  if (!is_dir($this->exportSchemaDirectory)) {
137  mkdir($this->exportSchemaDirectory);
138  }
139  }
140  /**
141  * @param string $sessionKey
142  */
143  public function setSessionKey($sessionKey)
144  {
145  $this->sessionKey = $sessionKey;
146  }
147  /**
148  * Abort current export session
149  */
150  public function abortSession()
151  {
152  if ($this->sessionKey) {
153  global $action;
154  $action->session->register($this->sessionKey . "::ABORT", self::ABORTORDER);
155  }
156  }
157  /**
158  * @return boolean
159  */
160  public function isAborted()
161  {
162  return $this->aborted;
163  }
164  protected function setSessionMessage($text)
165  {
166  if ($this->sessionKey) {
167  global $action;
168  $action->session->register($this->sessionKey, $text);
169  $msg = $action->session->read($this->sessionKey . "::ABORT");
170  if ($msg === self::ABORTORDER) {
171  $action->session->register($this->sessionKey . "::ABORT", "CATCHED");
172  throw new Exception("ACCT0205");
173  }
174  }
175  }
176 
177  public function getSessionMessage()
178  {
179  if ($this->sessionKey) {
180  global $action;
181  return $action->session->read($this->sessionKey);
182  }
183  return null;
184  }
185  /**
186  * Reorder group bu depth to avoid unreferenced groups
187  * @throws \Dcp\Db\Exception
188  */
189  protected function reorderGroups()
190  {
191  $xpath = new \DOMXpath($this->xml);
192  $groups = $xpath->query('//accounts/groups/group/reference');
193 
194  $groupLogins = array();
195  /**
196  * @var \DOMElement $loginNode
197  */
198  foreach ($groups as $loginNode) {
199  $groupLogins[] = pg_escape_string($loginNode->nodeValue);
200  }
201  if (count($groupLogins) > 0) {
202  // Get all members of every exported groups
203  $sql = sprintf("select memberof,id from users where login in (%s)", implode(array_map(function ($s)
204  {
205  return pg_escape_literal($s);
206  }
207  , $groupLogins) , ", "));
208  simpleQuery("", $sql, $members);
209  $searchGroups = array();
210  foreach ($members as $parents) {
211  $memberOf = explode(',', substr($parents["memberof"], 1, -1));
212  $memberOf[] = $parents["id"];
213  $searchGroups = array_merge($searchGroups, $memberOf);
214  }
215  $searchGroups = array_unique($searchGroups);
216  $searchGroups = array_filter($searchGroups, function ($x)
217  {
218  return !empty($x);
219  });
220  if ($searchGroups) {
221  // Get tree group information
222  $sql = sprintf("select groups.iduser as groupid, groups.idgroup as parentid, users.login as grouplogin from groups, users where groups.iduser in (%s) and groups.iduser=users.id and users.accounttype='G'", implode(array_map(function ($s)
223  {
224  return pg_escape_literal($s);
225  }
226  , $searchGroups) , ", "));
227  simpleQuery("", $sql, $groupTree);
228 
229  if ($groupTree) {
230  foreach ($groupTree as & $groupItem) {
231  $groupItem["groupid"] = intval($groupItem["groupid"]);
232  $groupItem["parentid"] = intval($groupItem["parentid"]);
233  }
234  // Compute level depth for each group
235  $groupOrdered = array();
236  foreach ($groupLogins as $groupRef) {
237  $groupOrdered[] = array(
238  "reference" => $groupRef,
239  "order" => $this->getDepthLevel($groupRef, $groupTree)
240  );
241  }
242  // sort by order
243  usort($groupOrdered, function ($a, $b)
244  {
245  if ($a["order"] > $b["order"]) return +1;
246  elseif ($a["order"] < $b["order"]) return -1;
247  return 0;
248  });
249 
250  foreach ($groupOrdered as $group) {
251  $reference = $group["reference"];
252  /**
253  * @var \DOMElement $groups
254  */
255  $groups = $xpath->query(sprintf('//accounts/groups/group/reference[text()=%s]/..', self::xpathLiteral($reference)))->item(0);
256  // $groups->setAttribute("level", $group["order"]);
257  $groups->parentNode->appendChild($groups);
258  }
259  }
260  }
261  }
262  }
263 
264  protected function getDepthLevel($groupIdentifier, $tree)
265  {
266  $parentLevel = 0;
267  foreach ($tree as $row) {
268  if ($row["grouplogin"] === $groupIdentifier || $row["groupid"] === $groupIdentifier) {
269  $parentLevel = max($parentLevel, $this->getDepthLevel($row["parentid"], $tree));
270  }
271  }
272  return $parentLevel + 1;
273  }
274  /**
275  * Remove empty tags
276  */
277  protected function clearInfo()
278  {
279  $xpath = new \DOMXpath($this->xml);
280  foreach ($xpath->query('//*[not(node())][not(@*)]') as $node) {
281  $node->parentNode->removeChild($node);
282  }
283  foreach ($xpath->query('//*[not(node())][not(@*)]') as $node) {
284  $node->parentNode->removeChild($node);
285  }
286  }
287  /**
288  * Set to true to export crypted password (default is false)
289  * @param bool $exportCryptedPassword
290  */
292  {
293  $this->exportCryptedPassword = $exportCryptedPassword;
294  }
295  /**
296  * Set to true to export parent group definition for user accounts (default is false)
297  * @param bool $exportGroupParent
298  */
300  {
301  $this->exportGroupParent = $exportGroupParent;
302  }
303  /**
304  * Set to true to export document information about account (default is false)
305  * @param bool $exportDocument
306  */
308  {
309  $this->exportDocument = $exportDocument;
310  }
311  /**
312  * Set to true to export relative roles for group and user accounts (default is false)
313  * @param bool $exportRoleParent
314  */
316  {
317  $this->exportRoleParent = $exportRoleParent;
318  }
319  protected function initXml()
320  {
321  $this->xml = new \DOMDocument("1.0", "utf-8");
322  $this->rootNode = $this->xml->createElement("accounts");
323  $this->rootNode->setAttribute("date", date("Y-m-d\\TH:i:s"));
324  $this->xml->appendChild($this->rootNode);
325  $this->roleRootNode = $this->xml->createElement("roles");
326  $this->rootNode->appendChild($this->roleRootNode);
327  $this->groupRootNode = $this->xml->createElement("groups");
328  $this->rootNode->appendChild($this->groupRootNode);
329  $this->userRootNode = $this->xml->createElement("users");
330  $this->userRootNode = $this->xml->createElement("users");
331  $this->rootNode->appendChild($this->userRootNode);
332 
333  $this->xml->preserveWhiteSpace = false;
334  $this->xml->formatOutput = true;
335  }
336  /**
337  * Record document to add
338  * These record are processed in one time at the end of export
339  * @see addDocumentNodes
340  * @param \Account $user
341  */
342  protected function memoDocumentInfo(\Account $user)
343  {
344  $this->documentInfo[$user->id] = $user->fid;
345  }
346  /**
347  * Add document node for each recorded account
348  * @throws \Dcp\Exception
349  */
350  protected function addDocumentNodes()
351  {
352  $s = new \DocumentList();
353  $s->addDocumentIdentifiers($this->documentInfo);
354  if (!$this->exportDocument) {
355  $search = $s->getSearchDocument();
356  $search->returnsOnly(array(
357  "id",
358  "fromid"
359  ));
360  }
361 
362  $export = new \Dcp\ExportXmlDocument();
363  $export->setStructureAttributes(true);
364  $export->setIncludeSchemaReference(false);
365 
366  $docXml = new \DOMDocument("1.0", "utf-8");
367  $docXml->preserveWhiteSpace = false;
368  $xpath = new \DOMXpath($this->xml);
369  $count = $s->count();
370  $k = 0;
371  foreach ($s as $doc) {
372  $k++;
373  $this->setSessionMessage(sprintf(___("Export relative document (%d/%d)", "fuserexport") , $k, $count));
374 
375  $documentNode = $this->xml->createElement("document");
376  $documentNode->setAttribute("family", $doc->fromname);
377  $uid = array_search($doc->id, $this->documentInfo);
378  $nodes = $xpath->query(sprintf('//*[@id="%d"]', $uid));
379 
380  $accountNode = $nodes->item(0);
381  $accountNode->appendChild($documentNode);
382  if ($this->exportDocument) {
383  $export->setDocument($doc);
384 
385  $export->setAttributeToExport(array(
386  $doc->fromid => $this->filterAttribute($doc)
387  ));
388  $xml = $export->getXml();
389  $docXml->loadXML($xml);
390  //$docXml->documentElement->removeAttribute("xsi:noNamespaceSchemaLocation");
391  //$docXml->documentElement->removeAttribute("xmlns:xsi");
392  $docNode = $this->xml->importNode($docXml->documentElement, true);
393 
394  $documentNode->appendChild($docNode);
395  if ($this->exportSchemaDirectory) {
396  $this->writeFamilySchema($doc->fromname);
397  }
398  }
399  }
400  if ($this->exportSchemaDirectory) {
401  $this->writeCommonSchema();
402  }
403  }
404 
405  protected function writeCommonSchema()
406  {
407  copy(sprintf("%s/FDL/Layout/fdl.xsd", DEFAULT_PUBDIR) , sprintf("%s/fdl.xsd", $this->exportSchemaDirectory));
408  copy(sprintf("%s/FDL/Layout/fdloptions.xsd", DEFAULT_PUBDIR) , sprintf("%s/fdloptions.xsd", $this->exportSchemaDirectory));
409  $this->xml->documentElement->setAttribute("xmlns:xsi", "http://www.w3.org/2001/XMLSchema-instance");
410  $this->xml->documentElement->setAttribute("xsi:noNamespaceSchemaLocation", self::XSDDIR . "/accounts.xsd");
411 
412  $xsd = new \DOMDocument();
413  $xsd->load("USERCARD/Layout/accounts.xsd");
414  $xpath = new \DOMXPath($xsd);
415  $documentTypeNode = $xpath->query('//xs:complexType[@name="documentType"]/xs:sequence')->item(0);
416 
417  $this->writeFamilySchema("IUSER");
418  $this->writeFamilySchema("IGROUP");
419  $this->writeFamilySchema("ROLE");
420 
421  $family = new_doc("", "IUSER");
422  $subFams = $family->getChildFam();
423  foreach ($subFams as $subFam) {
424  $this->writeFamilySchema($subFam["name"]);
425  }
426  $family = new_doc("", "IGROUP");
427  $subFams = $family->getChildFam();
428  foreach ($subFams as $subFam) {
429  $this->writeFamilySchema($subFam["name"]);
430  }
431  $family = new_doc("", "ROLE");
432  $subFams = $family->getChildFam();
433  foreach ($subFams as $subFam) {
434  $this->writeFamilySchema($subFam["name"]);
435  }
436 
437  foreach ($this->schemaWritted as $familyName => $true) {
438  $familyName = strtolower($familyName);
439  $node = $xsd->createElement("xs:element");
440  $node->setAttribute("type", sprintf("family-%s-type", $familyName));
441  $node->setAttribute("name", $familyName);
442  $node->setAttribute("minOccurs", "0");
443  $documentTypeNode->appendChild($node);
444 
445  $node = $xsd->createElement("xs:include");
446  $node->setAttribute("schemaLocation", sprintf("%s.xsd", $familyName));
447 
448  $firstElement = $xpath->query('//xs:element')->item(0);
449  $firstElement->parentNode->insertBefore($node, $firstElement);
450  }
451  file_put_contents(sprintf("%s/accounts.xsd", $this->exportSchemaDirectory) , $xsd->saveXML());
452  }
453 
454  protected function writeFamilySchema($familyName)
455  {
456  if (empty($this->schemaWritted[$familyName])) {
457  /**
458  * @var \DocFam $fam
459  */
460  $fam = new_doc("", $familyName);
461  $output = sprintf("%s/%s.xsd", $this->exportSchemaDirectory, strtolower($fam->name));
462 
463  file_put_contents($output, $fam->getXmlSchema(true));
464  $this->schemaWritted[$familyName] = true;
465  }
466  }
467  /**
468  * Return only specific document attribute.
469  * System attributes are no exported in document node
470  * @param \Doc $doc
471  * @return array
472  */
473  private function filterAttribute(\Doc $doc)
474  {
475  $filter = array();
476  $excludeFilters = array();
477  if (is_a($doc, "\\Dcp\\Family\\Iuser")) {
478  if (!isset($this->families["IUSER"])) {
479  $this->families["IUSER"] = new_doc("", "IUSER");
480  }
481  $excludeFilters = array(
482  "us_lname",
483  "us_meid",
484  "us_login",
485  "us_extmail",
486  "us_fname",
487  "us_rolegorigin",
488  "us_expiresd",
489  "us_mail",
490  "us_groups",
491  "us_t_roles",
492  "us_whatid",
493  "us_roles",
494  "us_rolesorigin",
495  "us_group",
496  "us_idgroup",
497  "us_expires",
498  "us_daydelay",
499  "us_expirest",
500  "us_passdelay",
501  "us_ldapdn",
502  "us_substitute",
503  "us_incumbents",
504  "us_passwd1",
505  "us_passwd2",
506  "us_status",
507  "us_loginfailure"
508  );
509  }
510  if (is_a($doc, "\\Dcp\\Family\\Igroup")) {
511  if (!isset($this->families["IGROUP"])) {
512  $this->families["IGROUP"] = new_doc("", "IGROUP");
513  }
514  $excludeFilters = array(
515  "us_login",
516  "us_meid",
517  "grp_roles",
518  "us_whatid",
519  "grp_idpgroup",
520  "grp_isrefreshed",
521  "grp_name",
522  "grp_idgroup",
523  "grp_mail",
524  "grp_hasmail",
525  "ba_title",
526  "ba_desc",
527  "gui_color",
528  "gui_isrss",
529  "gui_sysrss",
530  "fld_allbut",
531  "fld_tfam",
532  "fld_fam",
533  "fld_famids",
534  "fld_subfam",
535  "fld_pdoc",
536  "fld_pdocid",
537  "fld_pdir",
538  "fld_pdirid"
539  );
540  }
541  if (is_a($doc, "\\Dcp\\Family\\Role")) {
542  if (!isset($this->families["ROLE"])) {
543  $this->families["ROLE"] = new_doc("", "ROLE");
544  }
545  $excludeFilters = array(
546  "role_login",
547  "role_name",
548  "us_whatid"
549  );
550  }
551 
552  $attributes = $doc->getAttributes();
553  foreach ($attributes as $oattr) {
554  if ($oattr->usefor !== "Q" && !in_array($oattr->id, $excludeFilters) && (!$oattr->isNormal || $oattr->type === "array" || $doc->getRawValue($oattr->id) !== "")) {
555  $filter[] = $oattr->id;
556  }
557  }
558  return $filter;
559  }
560  /**
561  * Add nodes for group and role related accounts
562  * @param \Account $user
563  * @param \DOMElement $node
564  */
565  private function addParentInfo(\Account $user, \DOMElement $node)
566  {
567 
568  $roles = $user->getRoles();
569  $groups = $user->getGroupsId();
570 
571  if (count($roles) > 0) {
572  $roleNode = $this->xml->createElement("associatedRoles");
573  $roleNode->setAttribute("reset", "false");
574  $node->appendChild($roleNode);
575  foreach ($roles as $role) {
576  $this->workAccount->select($role);
577  $roleRef = $this->workAccount->login;
578  if ($this->exportRoleParent) {
579  $this->addRoleAccount($this->workAccount);
580  }
581  $nodeInfo = $this->xml->createElement("associatedRole");
582  $nodeInfo->setAttribute("reference", $roleRef);
583  $roleNode->appendChild($nodeInfo);
584  }
585  }
586 
587  if (count($groups) > 0) {
588  $roleNode = $this->xml->createElement("parentGroups");
589  $roleNode->setAttribute("reset", "false");
590  $node->appendChild($roleNode);
591  foreach ($groups as $group) {
592  $this->workAccount->select($group);
593  $groupRef = $this->workAccount->login;
594  if ($this->exportGroupParent) {
595  $this->addGroupAccount($this->workAccount);
596  }
597  $nodeInfo = $this->xml->createElement("parentGroup");
598  $nodeInfo->setAttribute("reference", $groupRef);
599  $roleNode->appendChild($nodeInfo);
600  }
601  }
602  }
603  /**
604  * Add User node info
605  * @param \Account $user
606  * @throws \Dcp\Db\Exception
607  */
608  protected function addUserAccount(\Account $user)
609  {
610  $node = $this->xml->createElement("user");
611 
612  $infos = array(
613  "login",
614  "firstname",
615  "lastname",
616  "mail"
617  );
618  foreach ($infos as $info) {
619  $infoValue = $user->$info;
620  if ($infoValue) {
621  $nodeInfo = $this->xml->createElement($info, htmlspecialchars($infoValue));
622  $node->appendChild($nodeInfo);
623  }
624  }
625  $nodeInfo = $this->xml->createElement("status");
626  $nodeInfo->setAttribute("activated", $user->status === "D" ? "false" : "true");
627  $node->appendChild($nodeInfo);
628 
629  if ($user->substitute) {
630  simpleQuery("", sprintf("select login from users where id = %d", $user->substitute) , $substituteLogin, true, true);
631  if ($substituteLogin) {
632  $nodeInfo = $this->xml->createElement("substitute");
633  $nodeInfo->setAttribute("reference", $substituteLogin);
634  $node->appendChild($nodeInfo);
635  }
636  }
637  if ($this->exportCryptedPassword) {
638  $nodeInfo = $this->xml->createElement("password", htmlspecialchars($user->password));
639  $nodeInfo->setAttribute("crypted", "true");
640  $node->appendChild($nodeInfo);
641  }
642 
643  $node->setAttribute("id", $user->id);
644  $this->addParentInfo($user, $node);
645 
646  $this->memoDocumentInfo($user);
647 
648  $this->userRootNode->appendChild($node);
649  }
650  /**
651  * Add group node info
652  * @param \Account $group
653  */
654  protected function addGroupAccount(\Account $group)
655  {
656  if (empty($this->addedIds[$group->id])) {
657  $node = $this->xml->createElement("group");
658  $nodeInfo = $this->xml->createElement("reference", htmlspecialchars($group->login));
659  $node->appendChild($nodeInfo);
660  $nodeInfo = $this->xml->createElement("displayName", htmlspecialchars($group->getAccountName()));
661  $node->appendChild($nodeInfo);
662 
663  $node->setAttribute("id", $group->id);
664 
665  $this->memoDocumentInfo($group);
666  $this->groupRootNode->appendChild($node);
667  $this->addedIds[$group->id] = true;
668  $this->addParentInfo($group, $node);
669  }
670  }
671  /**
672  * Add role node info
673  * @param \Account $role
674  */
675  protected function addRoleAccount(\Account $role)
676  {
677  if (empty($this->addedIds[$role->id])) {
678  $node = $this->xml->createElement("role");
679  $nodeInfo = $this->xml->createElement("reference", htmlspecialchars($role->login));
680  $node->appendChild($nodeInfo);
681  $nodeInfo = $this->xml->createElement("displayName", htmlspecialchars($role->getAccountName()));
682  $node->appendChild($nodeInfo);
683  $node->setAttribute("id", $role->id);
684  $this->memoDocumentInfo($role);
685 
686  $this->roleRootNode->appendChild($node);
687  $this->addedIds[$role->id] = true;
688  }
689  }
690  /**
691  * Convert a string to an XPath literal
692  *
693  * If the string contains an apostrophe, then a concat() is used
694  * to construct the string literal expression.
695  *
696  * If no apostrophe is found, then quote the string with apostrophes.
697  *
698  * @param $str
699  * @return string
700  */
701  protected static function xpathLiteral($str)
702  {
703  if (strpos($str, "'") === false) {
704  return "'" . $str . "'";
705  } else {
706  return "concat(" . str_replace(array(
707  "'',",
708  ",''"
709  ) , "", "'" . implode("',\"'\",'", explode("'", $str)) . "'") . ")";
710  }
711  }
712 }
global $action
print< H1 > Check Database< i > $dbaccess</i ></H1 > $a
Definition: checklist.php:45
setExportGroupParent($exportGroupParent)
$memberOf
getDepthLevel($groupIdentifier, $tree)
search(Action &$action)
Definition: search.php:30
setSearchAccount(\SearchAccount $s)
setExportRoleParent($exportRoleParent)
if($famId) $s
if(!function_exists('pgettext')) ___($message, $context="")
Definition: Lib.Common.php:46
addRoleAccount(\Account $role)
const DEFAULT_PUBDIR
Definition: Lib.Prefix.php:28
getRoles($useSystemId=true)
setExportCryptedPassword($exportCryptedPassword)
setExportSchemaDirectory($exportSchemaDirectory)
& getAttributes($useMask=true)
Definition: Class.Doc.php:2174
setExportDocument($exportDocument)
memoDocumentInfo(\Account $user)
$account
Definition: guest.php:36
addUserAccount(\Account $user)
$info
Definition: geticon.php:30
addGroupAccount(\Account $group)
simpleQuery($dbaccess, $query, &$result=array(), $singlecolumn=false, $singleresult=false, $useStrict=null)
Definition: Lib.Common.php:484
getRawValue($idAttr, $def="")
Definition: Class.Doc.php:3117
$export
← centre documentaire © anakeen