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

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

feature 2548 multisize

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