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

Last change on this file since 12855 was 12855, checked in by rvelices, 9 years ago

feature 2548 multisize - improved picture.php display (original...) + code cleanup

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