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

Last change on this file since 8802 was 8802, checked in by plg, 10 years ago

bug 937 fixed: makes sure a user won't see the thumbnail of a photo that has a
higher privacy level than user privacy level.

For an acceptable solution at performance level, I have implemented a cache:
for a given user, each album has a representative_picture_id. This cache also
avoids to perform numerous "order by rand()" SQL queries which is the case
when $confallow_random_representative = true;

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