source: branches/2.4/admin/include/functions.php @ 17766

Last change on this file since 17766 was 17766, checked in by rvelices, 12 years ago

merge-r17765 from trunk to branch 2.4 feature 2737: improve tag administration screen
show for every tag

  • the number of photos
  • link to public index page
  • link to batch manager edit

add an event for extended description multi language strings (used for autocompletion and shown in the tag admin screen) instead of hard coded in the core [lang=..

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