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

Last change on this file since 19703 was 19703, checked in by plg, 11 years ago

update Piwigo headers to 2013 (the end of the world didn't occur as expected on r12922)

  • Property svn:eol-style set to LF
File size: 55.0 KB
Line 
1<?php
2// +-----------------------------------------------------------------------+
3// | Piwigo - a PHP based photo gallery                                    |
4// +-----------------------------------------------------------------------+
5// | Copyright(C) 2008-2013 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(pwg_get_db_version(), '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.