source: trunk/admin/include/functions.php @ 18628

Revision 18628, 55.0 KB checked in by rvelices, 7 years ago (diff)
  • remove unsed code; shorten existing code and improve readability ...
  • Property svn:eol-style set to LF
Line 
1<?php
2// +-----------------------------------------------------------------------+
3// | Piwigo - a PHP based photo gallery                                    |
4// +-----------------------------------------------------------------------+
5// | Copyright(C) 2008-2012 Piwigo Team                  http://piwigo.org |
6// | Copyright(C) 2003-2008 PhpWebGallery Team    http://phpwebgallery.net |
7// | Copyright(C) 2002-2003 Pierrick LE GALL   http://le-gall.net/pierrick |
8// +-----------------------------------------------------------------------+
9// | This program is free software; you can redistribute it and/or modify  |
10// | it under the terms of the GNU General Public License as published by  |
11// | the Free Software Foundation                                          |
12// |                                                                       |
13// | This program is distributed in the hope that it will be useful, but   |
14// | WITHOUT ANY WARRANTY; without even the implied warranty of            |
15// | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU      |
16// | General Public License for more details.                              |
17// |                                                                       |
18// | You should have received a copy of the GNU General Public License     |
19// | along with this program; if not, write to the Free Software           |
20// | Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, |
21// | USA.                                                                  |
22// +-----------------------------------------------------------------------+
23
24include(PHPWG_ROOT_PATH.'admin/include/functions_metadata.php');
25
26// The function delete_site deletes a site and call the function
27// delete_categories for each primary category of the site
28function delete_site( $id )
29{
30  // destruction of the categories of the site
31  $query = '
32SELECT id
33  FROM '.CATEGORIES_TABLE.'
34  WHERE site_id = '.$id.'
35;';
36  $category_ids = array_from_query($query, 'id');
37  delete_categories($category_ids);
38
39  // destruction of the site
40  $query = '
41DELETE FROM '.SITES_TABLE.'
42  WHERE id = '.$id.'
43;';
44  pwg_query($query);
45}
46
47
48// The function delete_categories deletes the categories identified by the
49// (numeric) key of the array $ids. It also deletes (in the database) :
50//    - all the elements physically linked to the category (delete_elements, see further)
51//    - all the links between elements and this category
52//    - all the restrictions linked to the category
53// The function works recursively.
54//
55// the $photo_deletion_mode is for photos virtually linked to the categorty
56//   * no_delete : delete no photo, may create orphans
57//   * delete_orphans : delete photos that are no longer linked to any category
58//   * force_delete : delete photos even if they are linked to another category
59function delete_categories($ids, $photo_deletion_mode='no_delete')
60{
61  if (count($ids) == 0)
62  {
63    return;
64  }
65
66  // add sub-category ids to the given ids : if a category is deleted, all
67  // sub-categories must be so
68  $ids = get_subcat_ids($ids);
69
70  // destruction of all photos physically linked to the category
71  $query = '
72SELECT id
73  FROM '.IMAGES_TABLE.'
74  WHERE storage_category_id IN (
75'.wordwrap(implode(', ', $ids), 80, "\n").')
76;';
77  $element_ids = array_from_query($query, 'id');
78  delete_elements($element_ids);
79
80  // now, should we delete photos that are virtually linked to the category?
81  if ('delete_orphans' == $photo_deletion_mode or 'force_delete' == $photo_deletion_mode)
82  {
83    $query = '
84SELECT
85    DISTINCT(image_id)
86  FROM '.IMAGE_CATEGORY_TABLE.'
87  WHERE category_id IN ('.implode(',', $ids).')
88;';
89    $image_ids_linked = array_from_query($query, 'image_id');
90
91    if (count($image_ids_linked) > 0)
92    {
93      if ('delete_orphans' == $photo_deletion_mode)
94      {
95        $query = '
96SELECT
97    DISTINCT(image_id)
98  FROM '.IMAGE_CATEGORY_TABLE.'
99  WHERE image_id IN ('.implode(',', $image_ids_linked).')
100    AND category_id NOT IN ('.implode(',', $ids).')
101;';
102        $image_ids_not_orphans = array_from_query($query, 'image_id');
103        $image_ids_to_delete = array_diff($image_ids_linked, $image_ids_not_orphans);
104      }
105
106      if ('force_delete' == $photo_deletion_mode)
107      {
108        $image_ids_to_delete = $image_ids_linked;
109      }
110
111      delete_elements($image_ids_to_delete, true);
112    }
113  }
114
115  // destruction of the links between images and this category
116  $query = '
117DELETE FROM '.IMAGE_CATEGORY_TABLE.'
118  WHERE category_id IN (
119'.wordwrap(implode(', ', $ids), 80, "\n").')
120;';
121  pwg_query($query);
122
123  // destruction of the access linked to the category
124  $query = '
125DELETE FROM '.USER_ACCESS_TABLE.'
126  WHERE cat_id IN (
127'.wordwrap(implode(', ', $ids), 80, "\n").')
128;';
129  pwg_query($query);
130
131  $query = '
132DELETE FROM '.GROUP_ACCESS_TABLE.'
133  WHERE cat_id IN (
134'.wordwrap(implode(', ', $ids), 80, "\n").')
135;';
136  pwg_query($query);
137
138  // destruction of the category
139  $query = '
140DELETE FROM '.CATEGORIES_TABLE.'
141  WHERE id IN (
142'.wordwrap(implode(', ', $ids), 80, "\n").')
143;';
144  pwg_query($query);
145
146  $query='
147DELETE FROM '.OLD_PERMALINKS_TABLE.'
148  WHERE cat_id IN ('.implode(',',$ids).')';
149  pwg_query($query);
150
151  $query='
152DELETE FROM '.USER_CACHE_CATEGORIES_TABLE.'
153  WHERE cat_id IN ('.implode(',',$ids).')';
154  pwg_query($query);
155
156  trigger_action('delete_categories', $ids);
157}
158
159// Deletes all files (on disk) related to given image ids
160// @return image ids where files are deleted successfully
161function delete_element_files($ids)
162{
163  global $conf;
164  if (count($ids) == 0)
165  {
166    return 0;
167  }
168
169  $new_ids = array();
170
171  $query = '
172SELECT
173    id,
174    path,
175    representative_ext
176  FROM '.IMAGES_TABLE.'
177  WHERE id IN ('.implode(',', $ids).')
178;';
179  $result = pwg_query($query);
180  while ($row = pwg_db_fetch_assoc($result))
181  {
182    if (url_is_remote($row['path']))
183    {
184      continue;
185    }
186
187    $files = array();
188    $files[] = get_element_path($row);
189
190    if (!empty($row['representative_ext']))
191    {
192      $files[] = original_to_representative( $files[0], $row['representative_ext']);
193    }
194
195    $ok = true;
196    if (!isset($conf['never_delete_originals']))
197    {
198      foreach ($files as $path)
199      {
200        if (is_file($path) and !unlink($path))
201        {
202          $ok = false;
203          trigger_error('"'.$path.'" cannot be removed', E_USER_WARNING);
204          break;
205        }
206      }
207    }
208
209    if ($ok)
210    {
211      delete_element_derivatives($row);
212      $new_ids[] = $row['id'];
213    }
214    else
215    {
216      break;
217    }
218  }
219  return $new_ids;
220}
221
222// The function delete_elements deletes the elements identified by the
223// (numeric) values of the array $ids. It also deletes (in the database) :
224//    - all the comments related to elements
225//    - all the links between categories and elements
226//    - all the favorites associated to elements
227// @return number of deleted elements
228function delete_elements($ids, $physical_deletion=false)
229{
230  if (count($ids) == 0)
231  {
232    return 0;
233  }
234  trigger_action('begin_delete_elements', $ids);
235
236  if ($physical_deletion)
237  {
238    $ids = delete_element_files($ids);
239    if (count($ids)==0)
240    {
241      return 0;
242    }
243  }
244
245  // destruction of the comments on the image
246  $query = '
247DELETE FROM '.COMMENTS_TABLE.'
248  WHERE image_id IN (
249'.wordwrap(implode(', ', $ids), 80, "\n").')
250;';
251  pwg_query($query);
252
253  // destruction of the links between images and this category
254  $query = '
255DELETE FROM '.IMAGE_CATEGORY_TABLE.'
256  WHERE image_id IN (
257'.wordwrap(implode(', ', $ids), 80, "\n").')
258;';
259  pwg_query($query);
260
261  // destruction of the links between images and tags
262  $query = '
263DELETE FROM '.IMAGE_TAG_TABLE.'
264  WHERE image_id IN (
265'.wordwrap(implode(', ', $ids), 80, "\n").')
266;';
267  pwg_query($query);
268
269  // destruction of the favorites associated with the picture
270  $query = '
271DELETE FROM '.FAVORITES_TABLE.'
272  WHERE image_id IN (
273'.wordwrap(implode(', ', $ids), 80, "\n").')
274;';
275  pwg_query($query);
276
277  // destruction of the rates associated to this element
278  $query = '
279DELETE FROM '.RATE_TABLE.'
280  WHERE element_id IN (
281'.wordwrap(implode(', ', $ids), 80, "\n").')
282;';
283  pwg_query($query);
284
285  // destruction of the rates associated to this element
286  $query = '
287DELETE FROM '.CADDIE_TABLE.'
288  WHERE element_id IN (
289'.wordwrap(implode(', ', $ids), 80, "\n").')
290;';
291  pwg_query($query);
292
293  // destruction of the image
294  $query = '
295DELETE FROM '.IMAGES_TABLE.'
296  WHERE id IN (
297'.wordwrap(implode(', ', $ids), 80, "\n").')
298;';
299  pwg_query($query);
300
301  // are the photo used as category representant?
302  $query = '
303SELECT
304    id
305  FROM '.CATEGORIES_TABLE.'
306  WHERE representative_picture_id IN (
307'.wordwrap(implode(', ', $ids), 80, "\n").')
308;';
309  $category_ids = array_from_query($query, 'id');
310  if (count($category_ids) > 0)
311  {
312    update_category($category_ids);
313  }
314
315  trigger_action('delete_elements', $ids);
316  return count($ids);
317}
318
319// The delete_user function delete a user identified by the $user_id
320// It also deletes :
321//     - all the access linked to this user
322//     - all the links to any group
323//     - all the favorites linked to this user
324//     - calculated permissions linked to the user
325//     - all datas about notifications for the user
326function delete_user($user_id)
327{
328  global $conf;
329  $tables = array(
330    // destruction of the access linked to the user
331    USER_ACCESS_TABLE,
332    // destruction of data notification by mail for this user
333    USER_MAIL_NOTIFICATION_TABLE,
334    // destruction of data RSS notification for this user
335    USER_FEED_TABLE,
336    // deletion of calculated permissions linked to the user
337    USER_CACHE_TABLE,
338    // deletion of computed cache data linked to the user
339    USER_CACHE_CATEGORIES_TABLE,
340    // destruction of the group links for this user
341    USER_GROUP_TABLE,
342    // destruction of the favorites associated with the user
343    FAVORITES_TABLE,
344    // destruction of the caddie associated with the user
345    CADDIE_TABLE,
346    // deletion of piwigo specific informations
347    USER_INFOS_TABLE,
348    );
349
350  foreach ($tables as $table)
351  {
352    $query = '
353DELETE FROM '.$table.'
354  WHERE user_id = '.$user_id.'
355;';
356    pwg_query($query);
357  }
358
359  // destruction of the user
360  $query = '
361DELETE FROM '.SESSIONS_TABLE.'
362  WHERE data LIKE \'pwg_uid|i:'.(int)$user_id.';%\'
363;';
364  pwg_query($query);
365
366  // destruction of the user
367  $query = '
368DELETE FROM '.USERS_TABLE.'
369  WHERE '.$conf['user_fields']['id'].' = '.$user_id.'
370;';
371  pwg_query($query);
372
373  trigger_action('delete_user', $user_id);
374}
375
376/**
377 * Deletes all tags linked to no photo
378 */
379function delete_orphan_tags()
380{
381  $orphan_tags = get_orphan_tags();
382
383  if (count($orphan_tags) > 0)
384  {
385    $orphan_tag_ids = array();
386    foreach ($orphan_tags as $tag)
387    {
388      $orphan_tag_ids[] = $tag['id'];
389    }
390
391    $query = '
392DELETE
393  FROM '.TAGS_TABLE.'
394  WHERE id IN ('.implode(',', $orphan_tag_ids).')
395;';
396    pwg_query($query);
397  }
398}
399
400/**
401 * Get all tags (id + name) linked to no photo
402 */
403function get_orphan_tags()
404{
405  $query = '
406SELECT
407    id,
408    name
409  FROM '.TAGS_TABLE.'
410    LEFT JOIN '.IMAGE_TAG_TABLE.' ON id = tag_id
411  WHERE tag_id IS NULL
412;';
413  return array_from_query($query);
414}
415
416/**
417 * Verifies that the representative picture really exists in the db and
418 * picks up a random represantive if possible and based on config.
419 *
420 * @param mixed category id
421 * @returns void
422 */
423function update_category($ids = 'all')
424{
425  global $conf;
426
427  if ($ids=='all')
428  {
429    $where_cats = '1=1';
430  }
431  elseif ( !is_array($ids) )
432  {
433    $where_cats = '%s='.$ids;
434  }
435  else
436  {
437    if (count($ids) == 0)
438    {
439      return false;
440    }
441    $where_cats = '%s IN('.wordwrap(implode(', ', $ids), 120, "\n").')';
442  }
443
444  // find all categories where the setted representative is not possible :
445  // the picture does not exist
446  $query = '
447SELECT DISTINCT c.id
448  FROM '.CATEGORIES_TABLE.' AS c LEFT JOIN '.IMAGES_TABLE.' AS i
449    ON c.representative_picture_id = i.id
450  WHERE representative_picture_id IS NOT NULL
451    AND '.sprintf($where_cats, 'c.id').'
452    AND i.id IS NULL
453;';
454  $wrong_representant = array_from_query($query, 'id');
455
456  if (count($wrong_representant) > 0)
457  {
458    $query = '
459UPDATE '.CATEGORIES_TABLE.'
460  SET representative_picture_id = NULL
461  WHERE id IN ('.wordwrap(implode(', ', $wrong_representant), 120, "\n").')
462;';
463    pwg_query($query);
464  }
465
466  if (!$conf['allow_random_representative'])
467  {
468    // If the random representant is not allowed, we need to find
469    // categories with elements and with no representant. Those categories
470    // must be added to the list of categories to set to a random
471    // representant.
472    $query = '
473SELECT DISTINCT id
474  FROM '.CATEGORIES_TABLE.' INNER JOIN '.IMAGE_CATEGORY_TABLE.'
475    ON id = category_id
476  WHERE representative_picture_id IS NULL
477    AND '.sprintf($where_cats, 'category_id').'
478;';
479    $to_rand = array_from_query($query, 'id');
480    if (count($to_rand) > 0)
481    {
482      set_random_representant($to_rand);
483    }
484  }
485}
486
487/**
488 * returns an array containing sub-directories which can be a category,
489 * recursive by default
490 *
491 * directories nammed "thumbnail", "pwg_high" or "pwg_representative" are
492 * omitted
493 *
494 * @param string $basedir
495 * @return array
496 */
497function get_fs_directories($path, $recursive = true)
498{
499  $dirs = array();
500
501  if (is_dir($path))
502  {
503    if ($contents = opendir($path))
504    {
505      while (($node = readdir($contents)) !== false)
506      {
507        if ($node != '.'
508            and $node != '..'
509            and $node != '.svn'
510            and $node != 'thumbnail'
511            and $node != 'pwg_high'
512            and $node != 'pwg_representative'
513            and is_dir($path.'/'.$node))
514        {
515          array_push($dirs, $path.'/'.$node);
516          if ($recursive)
517          {
518            $dirs = array_merge($dirs, get_fs_directories($path.'/'.$node));
519          }
520        }
521      }
522      closedir($contents);
523    }
524  }
525
526  return $dirs;
527}
528
529/**
530 * order categories (update categories.rank and global_rank database fields)
531 * so that rank field are consecutive integers starting at 1 for each child
532 * @return void
533 */
534function update_global_rank()
535{
536  $query = '
537SELECT id, id_uppercat, uppercats, rank, global_rank
538  FROM '.CATEGORIES_TABLE.'
539  ORDER BY id_uppercat,rank,name';
540
541  $cat_map = array();
542
543  $current_rank = 0;
544  $current_uppercat = '';
545
546  $result = pwg_query($query);
547  while ($row = pwg_db_fetch_assoc($result))
548  {
549    if ($row['id_uppercat'] != $current_uppercat)
550    {
551      $current_rank = 0;
552      $current_uppercat = $row['id_uppercat'];
553    }
554    ++$current_rank;
555    $cat =
556      array(
557        'rank' =>        $current_rank,
558        'rank_changed' =>$current_rank!=$row['rank'],
559        'global_rank' => $row['global_rank'],
560        'uppercats' =>   $row['uppercats'],
561        );
562    $cat_map[ $row['id'] ] = $cat;
563  }
564
565  $datas = array();
566
567  foreach( $cat_map as $id=>$cat )
568  {
569    $new_global_rank = preg_replace(
570          '/(\d+)/e',
571          "\$cat_map['$1']['rank']",
572          str_replace(',', '.', $cat['uppercats'] )
573          );
574    if ( $cat['rank_changed']
575      or $new_global_rank!=$cat['global_rank']
576      )
577    {
578      $datas[] = array(
579          'id' => $id,
580          'rank' => $cat['rank'],
581          'global_rank' => $new_global_rank,
582        );
583    }
584  }
585
586  mass_updates(
587    CATEGORIES_TABLE,
588    array(
589      'primary' => array('id'),
590      'update'  => array('rank', 'global_rank')
591      ),
592    $datas
593    );
594  return count($datas);
595}
596
597/**
598 * change the visible property on a set of categories
599 *
600 * @param array categories
601 * @param string value
602 * @return void
603 */
604function set_cat_visible($categories, $value)
605{
606  if (!in_array($value, array('true', 'false')))
607  {
608    trigger_error("set_cat_visible invalid param $value", E_USER_WARNING);
609    return false;
610  }
611
612  // unlocking a category => all its parent categories become unlocked
613  if ($value == 'true')
614  {
615    $uppercats = get_uppercat_ids($categories);
616    $query = '
617UPDATE '.CATEGORIES_TABLE.'
618  SET visible = \'true\'
619  WHERE id IN ('.implode(',', $uppercats).')';
620    pwg_query($query);
621  }
622  // locking a category   => all its child categories become locked
623  if ($value == 'false')
624  {
625    $subcats = get_subcat_ids($categories);
626    $query = '
627UPDATE '.CATEGORIES_TABLE.'
628  SET visible = \'false\'
629  WHERE id IN ('.implode(',', $subcats).')';
630    pwg_query($query);
631  }
632}
633
634/**
635 * change the status property on a set of categories : private or public
636 *
637 * @param array categories
638 * @param string value
639 * @return void
640 */
641function set_cat_status($categories, $value)
642{
643  if (!in_array($value, array('public', 'private')))
644  {
645    trigger_error("set_cat_status invalid param $value", E_USER_WARNING);
646    return false;
647  }
648
649  // make public a category => all its parent categories become public
650  if ($value == 'public')
651  {
652    $uppercats = get_uppercat_ids($categories);
653    $query = '
654UPDATE '.CATEGORIES_TABLE.'
655  SET status = \'public\'
656  WHERE id IN ('.implode(',', $uppercats).')
657;';
658    pwg_query($query);
659  }
660  // make a category private => all its child categories become private
661  if ($value == 'private')
662  {
663    $subcats = get_subcat_ids($categories);
664    $query = '
665UPDATE '.CATEGORIES_TABLE.'
666  SET status = \'private\'
667  WHERE id IN ('.implode(',', $subcats).')';
668    pwg_query($query);
669  }
670}
671
672/**
673 * returns all uppercats category ids of the given category ids
674 *
675 * @param array cat_ids
676 * @return array
677 */
678function get_uppercat_ids($cat_ids)
679{
680  if (!is_array($cat_ids) or count($cat_ids) < 1)
681  {
682    return array();
683  }
684
685  $uppercats = array();
686
687  $query = '
688SELECT uppercats
689  FROM '.CATEGORIES_TABLE.'
690  WHERE id IN ('.implode(',', $cat_ids).')
691;';
692  $result = pwg_query($query);
693  while ($row = pwg_db_fetch_assoc($result))
694  {
695    $uppercats = array_merge($uppercats,
696                             explode(',', $row['uppercats']));
697  }
698  $uppercats = array_unique($uppercats);
699
700  return $uppercats;
701}
702
703/**
704 * set a new random representant to the categories
705 *
706 * @param array categories
707 */
708function set_random_representant($categories)
709{
710  $datas = array();
711  foreach ($categories as $category_id)
712  {
713    $query = '
714SELECT image_id
715  FROM '.IMAGE_CATEGORY_TABLE.'
716  WHERE category_id = '.$category_id.'
717  ORDER BY '.DB_RANDOM_FUNCTION.'()
718  LIMIT 1
719;';
720    list($representative) = pwg_db_fetch_row(pwg_query($query));
721
722    array_push(
723      $datas,
724      array(
725        'id' => $category_id,
726        'representative_picture_id' => $representative,
727        )
728      );
729  }
730
731  mass_updates(
732    CATEGORIES_TABLE,
733    array(
734      'primary' => array('id'),
735      'update' => array('representative_picture_id')
736      ),
737    $datas
738    );
739}
740
741/**
742 * returns the fulldir for each given category id
743 *
744 * @param array cat_ids
745 * @return array
746 */
747function get_fulldirs($cat_ids)
748{
749  if (count($cat_ids) == 0)
750  {
751    return array();
752  }
753
754  // caching directories of existing categories
755  $query = '
756SELECT id, dir
757  FROM '.CATEGORIES_TABLE.'
758  WHERE dir IS NOT NULL
759;';
760  $cat_dirs = simple_hash_from_query($query, 'id', 'dir');
761
762  // caching galleries_url
763  $query = '
764SELECT id, galleries_url
765  FROM '.SITES_TABLE.'
766;';
767  $galleries_url = simple_hash_from_query($query, 'id', 'galleries_url');
768
769  // categories : id, site_id, uppercats
770  $query = '
771SELECT id, uppercats, site_id
772  FROM '.CATEGORIES_TABLE.'
773  WHERE dir IS NOT NULL
774    AND id IN (
775'.wordwrap(implode(', ', $cat_ids), 80, "\n").')
776;';
777  $categories = array_from_query($query);
778
779  // filling $cat_fulldirs
780  $cat_fulldirs = array();
781  foreach ($categories as $category)
782  {
783    $uppercats = str_replace(',', '/', $category['uppercats']);
784    $cat_fulldirs[$category['id']] = $galleries_url[$category['site_id']];
785    $cat_fulldirs[$category['id']].= preg_replace('/(\d+)/e',
786                                                  "\$cat_dirs['$1']",
787                                                  $uppercats);
788  }
789
790  return $cat_fulldirs;
791}
792
793/**
794 * returns an array with all file system files according to
795 * $conf['file_ext']
796 *
797 * @param string $path
798 * @param bool recursive
799 * @return array
800 */
801function get_fs($path, $recursive = true)
802{
803  global $conf;
804
805  // because isset is faster than in_array...
806  if (!isset($conf['flip_picture_ext']))
807  {
808    $conf['flip_picture_ext'] = array_flip($conf['picture_ext']);
809  }
810  if (!isset($conf['flip_file_ext']))
811  {
812    $conf['flip_file_ext'] = array_flip($conf['file_ext']);
813  }
814
815  $fs['elements'] = array();
816  $fs['thumbnails'] = array();
817  $fs['representatives'] = array();
818  $subdirs = array();
819
820  if (is_dir($path))
821  {
822    if ($contents = opendir($path))
823    {
824      while (($node = readdir($contents)) !== false)
825      {
826        if ($node == '.' or $node == '..') continue;
827
828        if (is_file($path.'/'.$node))
829        {
830          $extension = get_extension($node);
831
832//          if (in_array($extension, $conf['picture_ext']))
833          if (isset($conf['flip_picture_ext'][$extension]))
834          {
835            if (basename($path) == 'thumbnail')
836            {
837              array_push($fs['thumbnails'], $path.'/'.$node);
838            }
839            else if (basename($path) == 'pwg_representative')
840            {
841              array_push($fs['representatives'], $path.'/'.$node);
842            }
843            else
844            {
845              array_push($fs['elements'], $path.'/'.$node);
846            }
847          }
848//          else if (in_array($extension, $conf['file_ext']))
849          else if (isset($conf['flip_file_ext'][$extension]))
850          {
851            array_push($fs['elements'], $path.'/'.$node);
852          }
853        }
854        else if (is_dir($path.'/'.$node) and $node != 'pwg_high' and $recursive)
855        {
856          array_push($subdirs, $node);
857        }
858      }
859    }
860    closedir($contents);
861
862    foreach ($subdirs as $subdir)
863    {
864      $tmp_fs = get_fs($path.'/'.$subdir);
865
866      $fs['elements']        = array_merge($fs['elements'],
867                                           $tmp_fs['elements']);
868
869      $fs['thumbnails']      = array_merge($fs['thumbnails'],
870                                           $tmp_fs['thumbnails']);
871
872      $fs['representatives'] = array_merge($fs['representatives'],
873                                           $tmp_fs['representatives']);
874    }
875  }
876  return $fs;
877}
878
879/**
880 * synchronize base users list and related users list
881 *
882 * compares and synchronizes base users table (USERS_TABLE) with its child
883 * tables (USER_INFOS_TABLE, USER_ACCESS, USER_CACHE, USER_GROUP) : each
884 * base user must be present in child tables, users in child tables not
885 * present in base table must be deleted.
886 *
887 * @return void
888 */
889function sync_users()
890{
891  global $conf;
892
893  $query = '
894SELECT '.$conf['user_fields']['id'].' AS id
895  FROM '.USERS_TABLE.'
896;';
897  $base_users = array_from_query($query, 'id');
898
899  $query = '
900SELECT user_id
901  FROM '.USER_INFOS_TABLE.'
902;';
903  $infos_users = array_from_query($query, 'user_id');
904
905  // users present in $base_users and not in $infos_users must be added
906  $to_create = array_diff($base_users, $infos_users);
907
908  if (count($to_create) > 0)
909  {
910    create_user_infos($to_create);
911  }
912
913  // users present in user related tables must be present in the base user
914  // table
915  $tables = array(
916    USER_MAIL_NOTIFICATION_TABLE,
917    USER_FEED_TABLE,
918    USER_INFOS_TABLE,
919    USER_ACCESS_TABLE,
920    USER_CACHE_TABLE,
921    USER_CACHE_CATEGORIES_TABLE,
922    USER_GROUP_TABLE
923    );
924
925  foreach ($tables as $table)
926  {
927    $query = '
928SELECT DISTINCT user_id
929  FROM '.$table.'
930;';
931    $to_delete = array_diff(
932      array_from_query($query, 'user_id'),
933      $base_users
934      );
935
936    if (count($to_delete) > 0)
937    {
938      $query = '
939DELETE
940  FROM '.$table.'
941  WHERE user_id in ('.implode(',', $to_delete).')
942;';
943      pwg_query($query);
944    }
945  }
946}
947
948/**
949 * updates categories.uppercats field based on categories.id +
950 * categories.id_uppercat
951 *
952 * @return void
953 */
954function update_uppercats()
955{
956  $query = '
957SELECT id, id_uppercat, uppercats
958  FROM '.CATEGORIES_TABLE.'
959;';
960  $cat_map = hash_from_query($query, 'id');
961
962  $datas = array();
963  foreach ($cat_map as $id => $cat)
964  {
965    $upper_list = array();
966
967    $uppercat = $id;
968    while ($uppercat)
969    {
970      array_push($upper_list, $uppercat);
971      $uppercat = $cat_map[$uppercat]['id_uppercat'];
972    }
973
974    $new_uppercats = implode(',', array_reverse($upper_list));
975    if ($new_uppercats != $cat['uppercats'])
976    {
977      array_push(
978        $datas,
979        array(
980          'id' => $id,
981          'uppercats' => $new_uppercats
982          )
983        );
984    }
985  }
986  $fields = array('primary' => array('id'), 'update' => array('uppercats'));
987  mass_updates(CATEGORIES_TABLE, $fields, $datas);
988}
989
990/**
991 * update images.path field
992 *
993 * @return void
994 */
995function update_path()
996{
997  $query = '
998SELECT DISTINCT(storage_category_id)
999  FROM '.IMAGES_TABLE.'
1000  WHERE storage_category_id IS NOT NULL
1001;';
1002  $cat_ids = array_from_query($query, 'storage_category_id');
1003  $fulldirs = get_fulldirs($cat_ids);
1004
1005  foreach ($cat_ids as $cat_id)
1006  {
1007    $query = '
1008UPDATE '.IMAGES_TABLE.'
1009  SET path = '.pwg_db_concat(array("'".$fulldirs[$cat_id]."/'",'file')).'
1010  WHERE storage_category_id = '.$cat_id.'
1011;';
1012    pwg_query($query);
1013  }
1014}
1015
1016/**
1017 * change the parent category of the given categories. The categories are
1018 * supposed virtual.
1019 *
1020 * @param array category identifiers
1021 * @param int parent category identifier
1022 * @return void
1023 */
1024function move_categories($category_ids, $new_parent = -1)
1025{
1026  global $page;
1027
1028  if (count($category_ids) == 0)
1029  {
1030    return;
1031  }
1032
1033  $new_parent = $new_parent < 1 ? 'NULL' : $new_parent;
1034
1035  $categories = array();
1036
1037  $query = '
1038SELECT id, id_uppercat, status, uppercats
1039  FROM '.CATEGORIES_TABLE.'
1040  WHERE id IN ('.implode(',', $category_ids).')
1041;';
1042  $result = pwg_query($query);
1043  while ($row = pwg_db_fetch_assoc($result))
1044  {
1045    $categories[$row['id']] =
1046      array(
1047        'parent' => empty($row['id_uppercat']) ? 'NULL' : $row['id_uppercat'],
1048        'status' => $row['status'],
1049        'uppercats' => $row['uppercats']
1050        );
1051  }
1052
1053  // is the movement possible? The movement is impossible if you try to move
1054  // a category in a sub-category or itself
1055  if ('NULL' != $new_parent)
1056  {
1057    $query = '
1058SELECT uppercats
1059  FROM '.CATEGORIES_TABLE.'
1060  WHERE id = '.$new_parent.'
1061;';
1062    list($new_parent_uppercats) = pwg_db_fetch_row(pwg_query($query));
1063
1064    foreach ($categories as $category)
1065    {
1066      // technically, you can't move a category with uppercats 12,125,13,14
1067      // into a new parent category with uppercats 12,125,13,14,24
1068      if (preg_match('/^'.$category['uppercats'].'(,|$)/', $new_parent_uppercats))
1069      {
1070        array_push(
1071          $page['errors'],
1072          l10n('You cannot move an album in its own sub album')
1073          );
1074        return;
1075      }
1076    }
1077  }
1078
1079  $tables =
1080    array(
1081      USER_ACCESS_TABLE => 'user_id',
1082      GROUP_ACCESS_TABLE => 'group_id'
1083      );
1084
1085  $query = '
1086UPDATE '.CATEGORIES_TABLE.'
1087  SET id_uppercat = '.$new_parent.'
1088  WHERE id IN ('.implode(',', $category_ids).')
1089;';
1090  pwg_query($query);
1091
1092  update_uppercats();
1093  update_global_rank();
1094
1095  // status and related permissions management
1096  if ('NULL' == $new_parent)
1097  {
1098    $parent_status = 'public';
1099  }
1100  else
1101  {
1102    $query = '
1103SELECT status
1104  FROM '.CATEGORIES_TABLE.'
1105  WHERE id = '.$new_parent.'
1106;';
1107    list($parent_status) = pwg_db_fetch_row(pwg_query($query));
1108  }
1109
1110  if ('private' == $parent_status)
1111  {
1112    foreach ($categories as $cat_id => $category)
1113    {
1114      switch ($category['status'])
1115      {
1116        case 'public' :
1117        {
1118          set_cat_status(array($cat_id), 'private');
1119          break;
1120        }
1121        case 'private' :
1122        {
1123          $subcats = get_subcat_ids(array($cat_id));
1124
1125          foreach ($tables as $table => $field)
1126          {
1127            $query = '
1128SELECT '.$field.'
1129  FROM '.$table.'
1130  WHERE cat_id = '.$cat_id.'
1131;';
1132            $category_access = array_from_query($query, $field);
1133
1134            $query = '
1135SELECT '.$field.'
1136  FROM '.$table.'
1137  WHERE cat_id = '.$new_parent.'
1138;';
1139            $parent_access = array_from_query($query, $field);
1140
1141            $to_delete = array_diff($parent_access, $category_access);
1142
1143            if (count($to_delete) > 0)
1144            {
1145              $query = '
1146DELETE FROM '.$table.'
1147  WHERE '.$field.' IN ('.implode(',', $to_delete).')
1148    AND cat_id IN ('.implode(',', $subcats).')
1149;';
1150              pwg_query($query);
1151            }
1152          }
1153          break;
1154        }
1155      }
1156    }
1157  }
1158
1159  array_push(
1160    $page['infos'],
1161    l10n_dec(
1162      '%d album moved', '%d albums moved',
1163      count($categories)
1164      )
1165    );
1166}
1167
1168/**
1169 * create a virtual category
1170 *
1171 * @param string category name
1172 * @param int parent category id
1173 * @return array with ('info' and 'id') or ('error') key
1174 */
1175function create_virtual_category($category_name, $parent_id=null, $options=array())
1176{
1177  global $conf, $user;
1178
1179  // is the given category name only containing blank spaces ?
1180  if (preg_match('/^\s*$/', $category_name))
1181  {
1182    return array('error' => l10n('The name of an album must not be empty'));
1183  }
1184
1185  $insert = array(
1186    'name' => $category_name,
1187    'rank' => 0,
1188    'global_rank' => 0,
1189    );
1190
1191  // is the album commentable?
1192  if (isset($options['commentable']) and is_bool($options['commentable']))
1193  {
1194    $insert['commentable'] = $options['commentable'];
1195  }
1196  else
1197  {
1198    $insert['commentable'] = $conf['newcat_default_commentable'];
1199  }
1200  $insert['commentable'] = boolean_to_string($insert['commentable']);
1201
1202  // is the album temporarily locked? (only visible by administrators,
1203  // whatever permissions) (may be overwritten if parent album is not
1204  // visible)
1205  if (isset($options['visible']) and is_bool($options['visible']))
1206  {
1207    $insert['visible'] = $options['visible'];
1208  }
1209  else
1210  {
1211    $insert['visible'] = $conf['newcat_default_visible'];
1212  }
1213  $insert['visible'] = boolean_to_string($insert['visible']);
1214
1215  // is the album private? (may be overwritten if parent album is private)
1216  if (isset($options['status']) and 'private' == $options['status'])
1217  {
1218    $insert['status'] = 'private';
1219  }
1220  else
1221  {
1222    $insert['status'] = $conf['newcat_default_status'];
1223  }
1224
1225  // any description for this album?
1226  if (isset($options['comment']))
1227  {
1228    $insert['comment'] = strip_tags($options['comment']);
1229  }
1230
1231  if (!empty($parent_id) and is_numeric($parent_id))
1232  {
1233    $query = '
1234SELECT id, uppercats, global_rank, visible, status
1235  FROM '.CATEGORIES_TABLE.'
1236  WHERE id = '.$parent_id.'
1237;';
1238    $parent = pwg_db_fetch_assoc(pwg_query($query));
1239
1240    $insert['id_uppercat'] = $parent['id'];
1241    $insert['global_rank'] = $parent['global_rank'].'.'.$insert['rank'];
1242
1243    // at creation, must a category be visible or not ? Warning : if the
1244    // parent category is invisible, the category is automatically create
1245    // invisible. (invisible = locked)
1246    if ('false' == $parent['visible'])
1247    {
1248      $insert['visible'] = 'false';
1249    }
1250
1251    // at creation, must a category be public or private ? Warning : if the
1252    // parent category is private, the category is automatically create
1253    // private.
1254    if ('private' == $parent['status'])
1255    {
1256      $insert['status'] = 'private';
1257    }
1258
1259    $uppercats_prefix = $parent['uppercats'].',';
1260  }
1261  else
1262  {
1263    $uppercats_prefix = '';
1264  }
1265
1266  // we have then to add the virtual category
1267  single_insert(CATEGORIES_TABLE, $insert);
1268  $inserted_id = pwg_db_insert_id(CATEGORIES_TABLE);
1269
1270  single_update(
1271    CATEGORIES_TABLE,
1272    array('uppercats' => $uppercats_prefix.$inserted_id),
1273    array('id' => $inserted_id)
1274    );
1275
1276  update_global_rank();
1277
1278  if ('private' == $insert['status'])
1279  {
1280    add_permission_on_category($inserted_id, array_unique(array_merge(get_admins(), array($user['id']))));
1281  }
1282
1283  return array(
1284    'info' => l10n('Virtual album added'),
1285    'id'   => $inserted_id,
1286    );
1287}
1288
1289/**
1290 * Set tags to an image. Warning: given tags are all tags associated to the
1291 * image, not additionnal tags.
1292 *
1293 * @param array tag ids
1294 * @param int image id
1295 * @return void
1296 */
1297function set_tags($tags, $image_id)
1298{
1299  set_tags_of( array($image_id=>$tags) );
1300}
1301
1302/**
1303 * Add new tags to a set of images.
1304 *
1305 * @param array tag ids
1306 * @param array image ids
1307 * @return void
1308 */
1309function add_tags($tags, $images)
1310{
1311  if (count($tags) == 0 or count($images) == 0)
1312  {
1313    return;
1314  }
1315
1316  // we can't insert twice the same {image_id,tag_id} so we must first
1317  // delete lines we'll insert later
1318  $query = '
1319DELETE
1320  FROM '.IMAGE_TAG_TABLE.'
1321  WHERE image_id IN ('.implode(',', $images).')
1322    AND tag_id IN ('.implode(',', $tags).')
1323;';
1324  pwg_query($query);
1325
1326  $inserts = array();
1327  foreach ($images as $image_id)
1328  {
1329    foreach ( array_unique($tags) as $tag_id)
1330    {
1331      $inserts[] = array(
1332          'image_id' => $image_id,
1333          'tag_id' => $tag_id,
1334        );
1335    }
1336  }
1337  mass_inserts(
1338    IMAGE_TAG_TABLE,
1339    array_keys($inserts[0]),
1340    $inserts
1341    );
1342}
1343
1344/**
1345 *
1346 */
1347function delete_tags($tag_ids)
1348{
1349  if (is_numeric($tag_ids))
1350  {
1351    $tag_ids = array($tag_ids);
1352  }
1353
1354  if (!is_array($tag_ids))
1355  {
1356    return false;
1357  }
1358
1359  $query = '
1360DELETE
1361  FROM '.IMAGE_TAG_TABLE.'
1362  WHERE tag_id IN ('.implode(',', $tag_ids).')
1363;';
1364  pwg_query($query);
1365
1366  $query = '
1367DELETE
1368  FROM '.TAGS_TABLE.'
1369  WHERE id IN ('.implode(',', $tag_ids).')
1370;';
1371  pwg_query($query);
1372}
1373
1374function tag_id_from_tag_name($tag_name)
1375{
1376  global $page;
1377
1378  $tag_name = trim($tag_name);
1379  if (isset($page['tag_id_from_tag_name_cache'][$tag_name]))
1380  {
1381    return $page['tag_id_from_tag_name_cache'][$tag_name];
1382  }
1383
1384  // search existing by exact name
1385  $query = '
1386SELECT id
1387  FROM '.TAGS_TABLE.'
1388  WHERE name = \''.$tag_name.'\'
1389;';
1390  if (count($existing_tags = array_from_query($query, 'id')) == 0)
1391  {
1392    // search existing by case insensitive name
1393    $query = '
1394SELECT id
1395  FROM '.TAGS_TABLE.'
1396  WHERE CONVERT(name, CHAR) = \''.$tag_name.'\'
1397;';
1398    if (count($existing_tags = array_from_query($query, 'id')) == 0)
1399    {
1400      $url_name = trigger_event('render_tag_url', $tag_name);
1401      // search existing by url name
1402      $query = '
1403SELECT id
1404  FROM '.TAGS_TABLE.'
1405  WHERE url_name = \''.$url_name.'\'
1406;';
1407      if (count($existing_tags = array_from_query($query, 'id')) == 0)
1408      {
1409        mass_inserts(
1410          TAGS_TABLE,
1411          array('name', 'url_name'),
1412          array(
1413            array(
1414              'name' => $tag_name,
1415              'url_name' => $url_name,
1416              )
1417            )
1418          );
1419        $page['tag_id_from_tag_name_cache'][$tag_name] = pwg_db_insert_id(TAGS_TABLE);
1420        return $page['tag_id_from_tag_name_cache'][$tag_name];
1421      }
1422    }
1423  }
1424
1425  $page['tag_id_from_tag_name_cache'][$tag_name] = $existing_tags[0];
1426  return $page['tag_id_from_tag_name_cache'][$tag_name];
1427}
1428
1429function set_tags_of($tags_of)
1430{
1431  if (count($tags_of) > 0)
1432  {
1433    $query = '
1434DELETE
1435  FROM '.IMAGE_TAG_TABLE.'
1436  WHERE image_id IN ('.implode(',', array_keys($tags_of)).')
1437;';
1438    pwg_query($query);
1439
1440    $inserts = array();
1441
1442    foreach ($tags_of as $image_id => $tag_ids)
1443    {
1444      foreach (array_unique($tag_ids) as $tag_id)
1445      {
1446        $inserts[] = array(
1447            'image_id' => $image_id,
1448            'tag_id' => $tag_id,
1449          );
1450      }
1451    }
1452
1453    if (count($inserts))
1454    {
1455      mass_inserts(
1456        IMAGE_TAG_TABLE,
1457        array_keys($inserts[0]),
1458        $inserts
1459        );
1460    }
1461  }
1462}
1463
1464/**
1465 * Associate a list of images to a list of categories.
1466 *
1467 * The function will not duplicate links and will preserve ranks
1468 *
1469 * @param array images
1470 * @param array categories
1471 * @return void
1472 */
1473function associate_images_to_categories($images, $categories)
1474{
1475  if (count($images) == 0
1476      or count($categories) == 0)
1477  {
1478    return false;
1479  }
1480
1481  // get existing associations
1482  $query = '
1483SELECT
1484    image_id,
1485    category_id
1486  FROM '.IMAGE_CATEGORY_TABLE.'
1487  WHERE image_id IN ('.implode(',', $images).')
1488    AND category_id IN ('.implode(',', $categories).')
1489;';
1490  $result = pwg_query($query);
1491
1492  $existing = array();
1493  while ($row = pwg_db_fetch_assoc($result))
1494  {
1495    $existing[ $row['category_id'] ][] = $row['image_id'];
1496  }
1497
1498  // get max rank of each categories
1499  $query = '
1500SELECT
1501    category_id,
1502    MAX(rank) AS max_rank
1503  FROM '.IMAGE_CATEGORY_TABLE.'
1504  WHERE rank IS NOT NULL
1505    AND category_id IN ('.implode(',', $categories).')
1506  GROUP BY category_id
1507;';
1508
1509  $current_rank_of = simple_hash_from_query(
1510    $query,
1511    'category_id',
1512    'max_rank'
1513    );
1514
1515  // associate only not already associated images
1516  $inserts = array();
1517  foreach ($categories as $category_id)
1518  {
1519    if (!isset($current_rank_of[$category_id]))
1520    {
1521      $current_rank_of[$category_id] = 0;
1522    }
1523    if (!isset($existing[$category_id]))
1524    {
1525      $existing[$category_id] = array();
1526    }
1527
1528    foreach ($images as $image_id)
1529    {
1530      if (!in_array($image_id, $existing[$category_id]))
1531      {
1532        $rank = ++$current_rank_of[$category_id];
1533
1534        array_push(
1535          $inserts,
1536          array(
1537            'image_id' => $image_id,
1538            'category_id' => $category_id,
1539            'rank' => $rank,
1540            )
1541          );
1542      }
1543    }
1544  }
1545
1546  if (count($inserts))
1547  {
1548    mass_inserts(
1549      IMAGE_CATEGORY_TABLE,
1550      array_keys($inserts[0]),
1551      $inserts
1552      );
1553
1554    update_category($categories);
1555  }
1556}
1557
1558/**
1559 * Dissociate images from all old categories except their storage category and
1560 * associate to new categories.
1561 *
1562 * This function will preserve ranks
1563 *
1564 * @param array images
1565 * @param array categories
1566 * @return void
1567 */
1568function move_images_to_categories($images, $categories)
1569{
1570  if (count($images) == 0)
1571  {
1572    return false;
1573  }
1574
1575  // let's first break links with all old albums but their "storage album"
1576  $query = '
1577DELETE '.IMAGE_CATEGORY_TABLE.'.*
1578  FROM '.IMAGE_CATEGORY_TABLE.'
1579    JOIN '.IMAGES_TABLE.' ON image_id=id
1580  WHERE id IN ('.implode(',', $images).')
1581';
1582
1583  if (is_array($categories) and count($categories) > 0)
1584  {
1585    $query.= '
1586    AND category_id NOT IN ('.implode(',', $categories).')
1587';
1588  }
1589
1590  $query.= '
1591    AND (storage_category_id IS NULL OR storage_category_id != category_id)
1592;';
1593  pwg_query($query);
1594
1595  if (is_array($categories) and count($categories) > 0)
1596  {
1597    associate_images_to_categories($images, $categories);
1598  }
1599}
1600
1601/**
1602 * Associate images associated to a list of source categories to a list of
1603 * destination categories.
1604 *
1605 * @param array sources
1606 * @param array destinations
1607 * @return void
1608 */
1609function associate_categories_to_categories($sources, $destinations)
1610{
1611  if (count($sources) == 0)
1612  {
1613    return false;
1614  }
1615
1616  $query = '
1617SELECT image_id
1618  FROM '.IMAGE_CATEGORY_TABLE.'
1619  WHERE category_id IN ('.implode(',', $sources).')
1620;';
1621  $images = array_from_query($query, 'image_id');
1622
1623  associate_images_to_categories($images, $destinations);
1624}
1625
1626/**
1627 * Refer main Piwigo URLs (currently PHPWG_DOMAIN domain)
1628 *
1629 * @param void
1630 * @return array like $conf['links']
1631 */
1632function pwg_URL()
1633{
1634  $urls = array(
1635    'HOME'       => PHPWG_URL,
1636    'WIKI'       => PHPWG_URL.'/doc',
1637    'DEMO'       => PHPWG_URL.'/demo',
1638    'FORUM'      => PHPWG_URL.'/forum',
1639    'BUGS'       => PHPWG_URL.'/bugs',
1640    'EXTENSIONS' => PHPWG_URL.'/ext',
1641    );
1642  return $urls;
1643}
1644
1645/**
1646 * Invalidates cahed data (permissions and category counts) for all users.
1647 */
1648function invalidate_user_cache($full = true)
1649{
1650  if ($full)
1651  {
1652    $query = '
1653TRUNCATE TABLE '.USER_CACHE_CATEGORIES_TABLE.';';
1654    pwg_query($query);
1655    $query = '
1656TRUNCATE TABLE '.USER_CACHE_TABLE.';';
1657    pwg_query($query);
1658  }
1659  else
1660  {
1661    $query = '
1662UPDATE '.USER_CACHE_TABLE.'
1663  SET need_update = \'true\';';
1664    pwg_query($query);
1665  }
1666  trigger_action('invalidate_user_cache', $full);
1667}
1668
1669/**
1670 * adds the caracter set to a create table sql query.
1671 * all CREATE TABLE queries must call this function
1672 * @param string query - the sql query
1673 */
1674function create_table_add_character_set($query)
1675{
1676  defined('DB_CHARSET') or fatal_error('create_table_add_character_set DB_CHARSET undefined');
1677  if ('DB_CHARSET'!='')
1678  {
1679    if ( version_compare(mysql_get_server_info(), '4.1.0', '<') )
1680    {
1681      return $query;
1682    }
1683    $charset_collate = " DEFAULT CHARACTER SET ".DB_CHARSET;
1684    if (DB_COLLATE!='')
1685    {
1686      $charset_collate .= " COLLATE ".DB_COLLATE;
1687    }
1688    if ( is_array($query) )
1689    {
1690      foreach( $query as $id=>$q)
1691      {
1692        $q=trim($q);
1693        $q=trim($q, ';');
1694        if (preg_match('/^CREATE\s+TABLE/i',$q))
1695        {
1696          $q.=$charset_collate;
1697        }
1698        $q .= ';';
1699        $query[$id] = $q;
1700      }
1701    }
1702    else
1703    {
1704      $query=trim($query);
1705      $query=trim($query, ';');
1706      if (preg_match('/^CREATE\s+TABLE/i',$query))
1707      {
1708        $query.=$charset_collate;
1709      }
1710      $query .= ';';
1711    }
1712  }
1713  return $query;
1714}
1715
1716/**
1717 * Returns array use on template with html_options method
1718 * @param Min and Max access to use
1719 * @return array of user access level
1720 */
1721function get_user_access_level_html_options($MinLevelAccess = ACCESS_FREE, $MaxLevelAccess = ACCESS_CLOSED)
1722{
1723  $tpl_options = array();
1724  for ($level = $MinLevelAccess; $level <= $MaxLevelAccess; $level++)
1725  {
1726    $tpl_options[$level] = l10n(sprintf('ACCESS_%d', $level));
1727  }
1728  return $tpl_options;
1729}
1730
1731/**
1732 * returns a list of templates currently available in template-extension
1733 * Each .tpl file is extracted from template-extension.
1734 * @return array
1735 */
1736function get_extents($start='')
1737{
1738  if ($start == '') { $start = './template-extension'; }
1739  $dir = opendir($start);
1740  $extents = array();
1741
1742  while (($file = readdir($dir)) !== false)
1743  {
1744    if ( $file == '.' or $file == '..' or $file == '.svn') continue;
1745    $path = $start . '/' . $file;
1746    if (is_dir($path))
1747    {
1748      $extents = array_merge($extents, get_extents($path));
1749    }
1750    elseif ( !is_link($path) and file_exists($path)
1751            and get_extension($path) == 'tpl' )
1752    {
1753      $extents[] = substr($path, 21);
1754    }
1755  }
1756  return $extents;
1757}
1758
1759function create_tag($tag_name)
1760{
1761  // does the tag already exists?
1762  $query = '
1763SELECT id
1764  FROM '.TAGS_TABLE.'
1765  WHERE name = \''.$tag_name.'\'
1766;';
1767  $existing_tags = array_from_query($query, 'id');
1768
1769  if (count($existing_tags) == 0)
1770  {
1771    mass_inserts(
1772      TAGS_TABLE,
1773      array('name', 'url_name'),
1774      array(
1775        array(
1776          'name' => $tag_name,
1777          'url_name' => trigger_event('render_tag_url', $tag_name),
1778          )
1779        )
1780      );
1781
1782    $inserted_id = pwg_db_insert_id(TAGS_TABLE);
1783
1784    return array(
1785      'info' => sprintf(
1786        l10n('Tag "%s" was added'),
1787        stripslashes($tag_name)
1788        ),
1789      'id' => $inserted_id,
1790      );
1791  }
1792  else
1793  {
1794    return array(
1795      'error' => sprintf(
1796        l10n('Tag "%s" already exists'),
1797        stripslashes($tag_name)
1798        )
1799      );
1800  }
1801}
1802
1803/**
1804 * Is the category accessible to the (Admin) user ?
1805 *
1806 * Note : if the user is not authorized to see this category, category jump
1807 * will be replaced by admin cat_modify page
1808 *
1809 * @param int category id to verify
1810 * @return bool
1811 */
1812function cat_admin_access($category_id)
1813{
1814  global $user;
1815
1816  // $filter['visible_categories'] and $filter['visible_images']
1817  // are not used because it's not necessary (filter <> restriction)
1818  if (in_array($category_id, explode(',', $user['forbidden_categories'])))
1819  {
1820    return false;
1821  }
1822  return true;
1823}
1824
1825/**
1826 * Retrieve data from external URL
1827 *
1828 * @param string $src: URL
1829 * @param global $dest: can be a file ressource or string
1830 * @return bool
1831 */
1832function fetchRemote($src, &$dest, $get_data=array(), $post_data=array(), $user_agent='Piwigo', $step=0)
1833{
1834  // Try to retrieve data from local file?
1835  if (!url_is_remote($src))
1836  {
1837    $content = @file_get_contents($src);
1838    if ($content !== false)
1839    {
1840      is_resource($dest) ? @fwrite($dest, $content) : $dest = $content;
1841      return true;
1842    }
1843    else
1844    {
1845      return false;
1846    }
1847  }
1848
1849  // After 3 redirections, return false
1850  if ($step > 3) return false;
1851
1852  // Initialization
1853  $method  = empty($post_data) ? 'GET' : 'POST';
1854  $request = empty($post_data) ? '' : http_build_query($post_data, '', '&');
1855  if (!empty($get_data))
1856  {
1857    $src .= strpos($src, '?') === false ? '?' : '&';
1858    $src .= http_build_query($get_data, '', '&');
1859  }
1860
1861  // Initialize $dest
1862  is_resource($dest) or $dest = '';
1863
1864  // Try curl to read remote file
1865  if (function_exists('curl_init'))
1866  {
1867    $ch = @curl_init();
1868    @curl_setopt($ch, CURLOPT_URL, $src);
1869    @curl_setopt($ch, CURLOPT_HEADER, 1);
1870    @curl_setopt($ch, CURLOPT_USERAGENT, $user_agent);
1871    @curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
1872    if ($method == 'POST')
1873    {
1874      @curl_setopt($ch, CURLOPT_POST, 1);
1875      @curl_setopt($ch, CURLOPT_POSTFIELDS, $request);
1876    }
1877    $content = @curl_exec($ch);
1878    $header_length = @curl_getinfo($ch, CURLINFO_HEADER_SIZE);
1879    $status = @curl_getinfo($ch, CURLINFO_HTTP_CODE);
1880    @curl_close($ch);
1881    if ($content !== false and $status >= 200 and $status < 400)
1882    {
1883      if (preg_match('/Location:\s+?(.+)/', substr($content, 0, $header_length), $m))
1884      {
1885        return fetchRemote($m[1], $dest, array(), array(), $user_agent, $step+1);
1886      }
1887      $content = substr($content, $header_length);
1888      is_resource($dest) ? @fwrite($dest, $content) : $dest = $content;
1889      return true;
1890    }
1891  }
1892
1893  // Try file_get_contents to read remote file
1894  if (ini_get('allow_url_fopen'))
1895  {
1896    $opts = array(
1897      'http' => array(
1898        'method' => $method,
1899        'user_agent' => $user_agent,
1900      )
1901    );
1902    if ($method == 'POST')
1903    {
1904      $opts['http']['content'] = $request;
1905    }
1906    $context = @stream_context_create($opts);
1907    $content = @file_get_contents($src, false, $context);
1908    if ($content !== false)
1909    {
1910      is_resource($dest) ? @fwrite($dest, $content) : $dest = $content;
1911      return true;
1912    }
1913  }
1914
1915  // Try fsockopen to read remote file
1916  $src = parse_url($src);
1917  $host = $src['host'];
1918  $path = isset($src['path']) ? $src['path'] : '/';
1919  $path .= isset($src['query']) ? '?'.$src['query'] : '';
1920
1921  if (($s = @fsockopen($host,80,$errno,$errstr,5)) === false)
1922  {
1923    return false;
1924  }
1925
1926  $http_request  = $method." ".$path." HTTP/1.0\r\n";
1927  $http_request .= "Host: ".$host."\r\n";
1928  if ($method == 'POST')
1929  {
1930    $http_request .= "Content-Type: application/x-www-form-urlencoded;\r\n";
1931    $http_request .= "Content-Length: ".strlen($request)."\r\n";
1932  }
1933  $http_request .= "User-Agent: ".$user_agent."\r\n";
1934  $http_request .= "Accept: */*\r\n";
1935  $http_request .= "\r\n";
1936  $http_request .= $request;
1937
1938  fwrite($s, $http_request);
1939
1940  $i = 0;
1941  $in_content = false;
1942  while (!feof($s))
1943  {
1944    $line = fgets($s);
1945
1946    if (rtrim($line,"\r\n") == '' && !$in_content)
1947    {
1948      $in_content = true;
1949      $i++;
1950      continue;
1951    }
1952    if ($i == 0)
1953    {
1954      if (!preg_match('/HTTP\/(\\d\\.\\d)\\s*(\\d+)\\s*(.*)/',rtrim($line,"\r\n"), $m))
1955      {
1956        fclose($s);
1957        return false;
1958      }
1959      $status = (integer) $m[2];
1960      if ($status < 200 || $status >= 400)
1961      {
1962        fclose($s);
1963        return false;
1964      }
1965    }
1966    if (!$in_content)
1967    {
1968      if (preg_match('/Location:\s+?(.+)$/',rtrim($line,"\r\n"),$m))
1969      {
1970        fclose($s);
1971        return fetchRemote(trim($m[1]),$dest,array(),array(),$user_agent,$step+1);
1972      }
1973      $i++;
1974      continue;
1975    }
1976    is_resource($dest) ? @fwrite($dest, $line) : $dest .= $line;
1977    $i++;
1978  }
1979  fclose($s);
1980  return true;
1981}
1982
1983
1984/**
1985 * returns the groupname corresponding to the given group identifier if
1986 * exists
1987 *
1988 * @param int group_id
1989 * @return mixed
1990 */
1991function get_groupname($group_id)
1992{
1993  $query = '
1994SELECT name
1995  FROM '.GROUPS_TABLE.'
1996  WHERE id = '.intval($group_id).'
1997;';
1998  $result = pwg_query($query);
1999  if (pwg_db_num_rows($result) > 0)
2000  {
2001    list($groupname) = pwg_db_fetch_row($result);
2002  }
2003  else
2004  {
2005    return false;
2006  }
2007
2008  return $groupname;
2009}
2010
2011/**
2012 * returns the username corresponding to the given user identifier if exists
2013 *
2014 * @param int user_id
2015 * @return mixed
2016 */
2017function get_username($user_id)
2018{
2019  global $conf;
2020
2021  $query = '
2022SELECT '.$conf['user_fields']['username'].'
2023  FROM '.USERS_TABLE.'
2024  WHERE '.$conf['user_fields']['id'].' = '.intval($user_id).'
2025;';
2026  $result = pwg_query($query);
2027  if (pwg_db_num_rows($result) > 0)
2028  {
2029    list($username) = pwg_db_fetch_row($result);
2030  }
2031  else
2032  {
2033    return false;
2034  }
2035
2036  return stripslashes($username);
2037}
2038
2039function get_newsletter_subscribe_base_url($language) {
2040  return PHPWG_URL.'/announcement/subscribe/';
2041}
2042
2043/**
2044 * Accordion menus need to know which section to open by default when
2045 * loading the page
2046 */
2047function get_active_menu($menu_page)
2048{
2049  global $page;
2050
2051  if (isset($page['active_menu']))
2052  {
2053    return $page['active_menu'];
2054  }
2055
2056  switch ($menu_page)
2057  {
2058    case 'photo':
2059    case 'photos_add':
2060    case 'rating':
2061    case 'tags':
2062    case 'batch_manager':
2063      return 0;
2064
2065    case 'album':
2066    case 'cat_list':
2067    case 'cat_move':
2068    case 'cat_options':
2069    case 'permalinks':
2070      return 1;
2071
2072    case 'user_list':
2073    case 'user_perm':
2074    case 'group_list':
2075    case 'group_perm':
2076    case 'notification_by_mail':
2077      return 2;
2078
2079    case 'plugins':
2080    case 'plugin':
2081      return 3;
2082
2083    case 'site_manager':
2084    case 'site_update':
2085    case 'stats':
2086    case 'history':
2087    case 'maintenance':
2088    case 'comments':
2089    case 'updates':
2090      return 4;
2091
2092    case 'configuration':
2093    case 'derivatives':
2094    case 'extend_for_templates':
2095    case 'menubar':
2096    case 'themes':
2097    case 'theme':
2098    case 'languages':
2099      return 5;
2100  }
2101  return 0;
2102}
2103
2104function get_taglist($query, $only_user_language=true)
2105{
2106  $result = pwg_query($query);
2107
2108  $taglist = array();
2109  $altlist = array();
2110  while ($row = pwg_db_fetch_assoc($result))
2111  {
2112    $raw_name = $row['name'];
2113    $name = trigger_event('render_tag_name', $raw_name);
2114
2115    $taglist[] =  array(
2116        'name' => $name,
2117        'id' => '~~'.$row['id'].'~~',
2118      );
2119
2120    if (!$only_user_language)
2121    {
2122      $alt_names = trigger_event('get_tag_alt_names', array(), $raw_name);
2123
2124      foreach( array_diff( array_unique($alt_names), array($name) ) as $alt)
2125      {
2126        $altlist[] =  array(
2127            'name' => $alt,
2128            'id' => '~~'.$row['id'].'~~',
2129          );
2130      }
2131    }
2132  }
2133
2134  usort($taglist, 'tag_alpha_compare');
2135  if (count($altlist))
2136  {
2137    usort($altlist, 'tag_alpha_compare');
2138    $taglist = array_merge($taglist, $altlist);
2139  }
2140
2141  return $taglist;
2142}
2143
2144function get_tag_ids($raw_tags, $allow_create=true)
2145{
2146  // In $raw_tags we receive something like array('~~6~~', '~~59~~', 'New
2147  // tag', 'Another new tag') The ~~34~~ means that it is an existing
2148  // tag. I've added the surrounding ~~ to permit creation of tags like "10"
2149  // or "1234" (numeric characters only)
2150
2151  $tag_ids = array();
2152  $raw_tags = explode(',',$raw_tags);
2153
2154  foreach ($raw_tags as $raw_tag)
2155  {
2156    if (preg_match('/^~~(\d+)~~$/', $raw_tag, $matches))
2157    {
2158      $tag_ids[] = $matches[1];
2159    }
2160    elseif ($allow_create)
2161    {
2162      // we have to create a new tag
2163      $tag_ids[] = tag_id_from_tag_name($raw_tag);
2164    }
2165  }
2166
2167  return $tag_ids;
2168}
2169
2170/** returns the argument_ids array with new sequenced keys based on related
2171 * names. Sequence is not case sensitive.
2172 * Warning: By definition, this function breaks original keys
2173 */
2174function order_by_name($element_ids,$name)
2175{
2176  $ordered_element_ids = array();
2177  foreach ($element_ids as $k_id => $element_id)
2178  {
2179    $key = strtolower($name[$element_id]) .'-'. $name[$element_id] .'-'. $k_id;
2180    $ordered_element_ids[$key] = $element_id;
2181  }
2182  ksort($ordered_element_ids);
2183  return $ordered_element_ids;
2184}
2185
2186function add_permission_on_category($category_ids, $user_ids)
2187{
2188  // array-ify categories and users
2189  if (!is_array($category_ids))
2190  {
2191    $category_ids = array($category_ids);
2192  }
2193
2194  if (!is_array($user_ids))
2195  {
2196    $user_ids = array($user_ids);
2197  }
2198
2199  // check for emptiness
2200  if (count($category_ids) == 0 or count($user_ids) == 0)
2201  {
2202    return;
2203  }
2204
2205  // make sure categories are private and select uppercats or subcats
2206  $cat_ids = (isset($_POST['apply_on_sub'])) ? implode(',', get_subcat_ids($category_ids)).",".implode(',', get_uppercat_ids($category_ids)) : implode(',', get_uppercat_ids($category_ids));
2207  $query = '
2208SELECT
2209    id
2210  FROM '.CATEGORIES_TABLE.'
2211  WHERE id IN ('.$cat_ids.')
2212    AND status = \'private\'
2213;';
2214  $private_cats = array_from_query($query, 'id');
2215
2216  if (count($private_cats) == 0)
2217  {
2218    return;
2219  }
2220
2221  // We must not reinsert already existing lines in user_access table
2222  $granteds = array();
2223  foreach ($private_cats as $cat_id)
2224  {
2225    $granteds[$cat_id] = array();
2226  }
2227
2228  $query = '
2229SELECT
2230    user_id,
2231    cat_id
2232  FROM '.USER_ACCESS_TABLE.'
2233  WHERE cat_id IN ('.implode(',', $private_cats).')
2234    AND user_id IN ('.implode(',', $user_ids).')
2235;';
2236  $result = pwg_query($query);
2237  while ($row = pwg_db_fetch_assoc($result))
2238  {
2239    array_push($granteds[$row['cat_id']], $row['user_id']);
2240  }
2241
2242  $inserts = array();
2243
2244  foreach ($private_cats as $cat_id)
2245  {
2246    $grant_to_users = array_diff($user_ids, $granteds[$cat_id]);
2247
2248    foreach ($grant_to_users as $user_id)
2249    {
2250      array_push(
2251        $inserts,
2252        array(
2253          'user_id' => $user_id,
2254          'cat_id' => $cat_id
2255          )
2256        );
2257    }
2258  }
2259
2260  if (count($inserts) > 0)
2261  {
2262    mass_inserts(USER_ACCESS_TABLE, array_keys($inserts[0]), $inserts);
2263  }
2264}
2265
2266
2267function get_admins($include_webmaster=true)
2268{
2269  $status_list = array('admin');
2270
2271  if ($include_webmaster)
2272  {
2273    $status_list[] = 'webmaster';
2274  }
2275
2276  $query = '
2277SELECT
2278    user_id
2279  FROM '.USER_INFOS_TABLE.'
2280  WHERE status in (\''.implode("','", $status_list).'\')
2281;';
2282
2283  return array_from_query($query, 'user_id');
2284}
2285
2286/** delete all derivative files for one or several types */
2287function clear_derivative_cache($types='all')
2288{
2289  if ($types === 'all')
2290  {
2291    $types = ImageStdParams::get_all_types();
2292    $types[] = IMG_CUSTOM;
2293  }
2294  elseif (!is_array($types))
2295  {
2296    $types = array($types);
2297  }
2298
2299  for ($i=0; $i<count($types); $i++)
2300  {
2301    $type = $types[$i];
2302    if ($type == IMG_CUSTOM)
2303    {
2304      $type = derivative_to_url($type).'[a-zA-Z0-9]+';
2305    }
2306    elseif (in_array($type, ImageStdParams::get_all_types()))
2307    {
2308      $type = derivative_to_url($type);
2309    }
2310    else
2311    {//assume a custom type
2312      $type = derivative_to_url(IMG_CUSTOM).'_'.$type;
2313    }
2314    $types[$i] = $type;
2315  }
2316
2317  $pattern='#.*-';
2318  if (count($types)>1)
2319  {
2320    $pattern .= '(' . implode('|',$types) . ')';
2321  }
2322  else
2323  {
2324    $pattern .= $types[0];
2325  }
2326  $pattern.='\.[a-zA-Z0-9]{3,4}$#';
2327
2328  if ($contents = @opendir(PHPWG_ROOT_PATH.PWG_DERIVATIVE_DIR))
2329  {
2330    while (($node = readdir($contents)) !== false)
2331    {
2332      if ($node != '.'
2333          and $node != '..'
2334          and is_dir(PHPWG_ROOT_PATH.PWG_DERIVATIVE_DIR.$node))
2335      {
2336        clear_derivative_cache_rec(PHPWG_ROOT_PATH.PWG_DERIVATIVE_DIR.$node, $pattern);
2337      }
2338    }
2339    closedir($contents);
2340  }
2341}
2342
2343function clear_derivative_cache_rec($path, $pattern)
2344{
2345  $rmdir = true;
2346  $rm_index = false;
2347
2348  if ($contents = opendir($path))
2349  {
2350    while (($node = readdir($contents)) !== false)
2351    {
2352      if ($node == '.' or $node == '..')
2353        continue;
2354      if (is_dir($path.'/'.$node))
2355      {
2356        $rmdir &= clear_derivative_cache_rec($path.'/'.$node, $pattern);
2357      }
2358      else
2359      {
2360        if (preg_match($pattern, $node))
2361        {
2362          unlink($path.'/'.$node);
2363        }
2364        elseif ($node=='index.htm')
2365        {
2366          $rm_index = true;
2367        }
2368        else
2369        {
2370          $rmdir = false;
2371        }
2372      }
2373    }
2374    closedir($contents);
2375
2376    if ($rmdir)
2377    {
2378      if ($rm_index)
2379      {
2380        unlink($path.'/index.htm');
2381      }
2382      clearstatcache();
2383      @rmdir($path);
2384    }
2385    return $rmdir;
2386  }
2387}
2388
2389function delete_element_derivatives($infos, $type='all')
2390{
2391  $path = $infos['path'];
2392  if (!empty($infos['representative_ext']))
2393  {
2394    $path = original_to_representative( $path, $infos['representative_ext']);
2395  }
2396  if (substr_compare($path, '../', 0, 3)==0)
2397  {
2398    $path = substr($path, 3);
2399  }
2400  $dot = strrpos($path, '.');
2401  if ($type=='all')
2402  {
2403    $pattern = '-*';
2404  }
2405  else
2406  {
2407    $pattern = '-'.derivative_to_url($type).'*';
2408  }
2409  $path = substr_replace($path, $pattern, $dot, 0);
2410  if ( ($glob=glob(PHPWG_ROOT_PATH.PWG_DERIVATIVE_DIR.$path)) !== false)
2411  {
2412    foreach( $glob as $file)
2413    {
2414      @unlink($file);
2415    }
2416  }
2417}
2418?>
Note: See TracBrowser for help on using the repository browser.