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

Last change on this file since 8762 was 8762, checked in by plg, 13 years ago

feature 1289 added: easy "delete orphan tags" function. On the "tags"
administration page, a warning message is displayed if you have at least
one orphan tag + direct action to delete them.

  • Property svn:eol-style set to LF
File size: 49.3 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 * stupidly returns the current microsecond since Unix epoch
887 */
888function micro_seconds()
889{
890  $t1 = explode(' ', microtime());
891  $t2 = explode('.', $t1[0]);
892  $t2 = $t1[1].substr($t2[1], 0, 6);
893  return $t2;
894}
895
896/**
897 * synchronize base users list and related users list
898 *
899 * compares and synchronizes base users table (USERS_TABLE) with its child
900 * tables (USER_INFOS_TABLE, USER_ACCESS, USER_CACHE, USER_GROUP) : each
901 * base user must be present in child tables, users in child tables not
902 * present in base table must be deleted.
903 *
904 * @return void
905 */
906function sync_users()
907{
908  global $conf;
909
910  $query = '
911SELECT '.$conf['user_fields']['id'].' AS id
912  FROM '.USERS_TABLE.'
913;';
914  $base_users = array_from_query($query, 'id');
915
916  $query = '
917SELECT user_id
918  FROM '.USER_INFOS_TABLE.'
919;';
920  $infos_users = array_from_query($query, 'user_id');
921
922  // users present in $base_users and not in $infos_users must be added
923  $to_create = array_diff($base_users, $infos_users);
924
925  if (count($to_create) > 0)
926  {
927    create_user_infos($to_create);
928  }
929
930  // users present in user related tables must be present in the base user
931  // table
932  $tables = array(
933    USER_MAIL_NOTIFICATION_TABLE,
934    USER_FEED_TABLE,
935    USER_INFOS_TABLE,
936    USER_ACCESS_TABLE,
937    USER_CACHE_TABLE,
938    USER_CACHE_CATEGORIES_TABLE,
939    USER_GROUP_TABLE
940    );
941
942  foreach ($tables as $table)
943  {
944    $query = '
945SELECT DISTINCT user_id
946  FROM '.$table.'
947;';
948    $to_delete = array_diff(
949      array_from_query($query, 'user_id'),
950      $base_users
951      );
952
953    if (count($to_delete) > 0)
954    {
955      $query = '
956DELETE
957  FROM '.$table.'
958  WHERE user_id in ('.implode(',', $to_delete).')
959;';
960      pwg_query($query);
961    }
962  }
963}
964
965/**
966 * updates categories.uppercats field based on categories.id +
967 * categories.id_uppercat
968 *
969 * @return void
970 */
971function update_uppercats()
972{
973  $query = '
974SELECT id, id_uppercat, uppercats
975  FROM '.CATEGORIES_TABLE.'
976;';
977  $cat_map = hash_from_query($query, 'id');
978
979  $datas = array();
980  foreach ($cat_map as $id => $cat)
981  {
982    $upper_list = array();
983
984    $uppercat = $id;
985    while ($uppercat)
986    {
987      array_push($upper_list, $uppercat);
988      $uppercat = $cat_map[$uppercat]['id_uppercat'];
989    }
990
991    $new_uppercats = implode(',', array_reverse($upper_list));
992    if ($new_uppercats != $cat['uppercats'])
993    {
994      array_push(
995        $datas,
996        array(
997          'id' => $id,
998          'uppercats' => $new_uppercats
999          )
1000        );
1001    }
1002  }
1003  $fields = array('primary' => array('id'), 'update' => array('uppercats'));
1004  mass_updates(CATEGORIES_TABLE, $fields, $datas);
1005}
1006
1007/**
1008 * update images.path field
1009 *
1010 * @return void
1011 */
1012function update_path()
1013{
1014  $query = '
1015SELECT DISTINCT(storage_category_id)
1016  FROM '.IMAGES_TABLE.'
1017  WHERE storage_category_id IS NOT NULL
1018;';
1019  $cat_ids = array_from_query($query, 'storage_category_id');
1020  $fulldirs = get_fulldirs($cat_ids);
1021
1022  foreach ($cat_ids as $cat_id)
1023  {
1024    $query = '
1025UPDATE '.IMAGES_TABLE.'
1026  SET path = '.pwg_db_concat(array("'".$fulldirs[$cat_id]."/'",'file')).'
1027  WHERE storage_category_id = '.$cat_id.'
1028;';
1029    pwg_query($query);
1030  }
1031}
1032
1033/**
1034 * update images.average_rate field
1035 * param int $element_id optional, otherwise applies to all
1036 * @return void
1037 */
1038function update_average_rate( $element_id=-1 )
1039{
1040  $query = '
1041SELECT element_id,
1042       ROUND(AVG(rate),2) AS average_rate
1043  FROM '.RATE_TABLE;
1044  if ( $element_id != -1 )
1045  {
1046    $query .= ' WHERE element_id=' . $element_id;
1047  }
1048  $query .= ' GROUP BY element_id;';
1049
1050  $result = pwg_query($query);
1051
1052  $datas = array();
1053
1054  while ($row = pwg_db_fetch_assoc($result))
1055  {
1056    array_push(
1057      $datas,
1058      array(
1059        'id' => $row['element_id'],
1060        'average_rate' => $row['average_rate']
1061        )
1062      );
1063  }
1064
1065  mass_updates(
1066    IMAGES_TABLE,
1067    array(
1068      'primary' => array('id'),
1069      'update' => array('average_rate')
1070      ),
1071    $datas
1072    );
1073
1074  $query='
1075SELECT id FROM '.IMAGES_TABLE .'
1076  LEFT JOIN '.RATE_TABLE.' ON id=element_id
1077  WHERE element_id IS NULL AND average_rate IS NOT NULL';
1078  if ( $element_id != -1 )
1079  {
1080    $query .= ' AND id=' . $element_id;
1081  }
1082  $to_update = array_from_query( $query, 'id');
1083
1084  if ( !empty($to_update) )
1085  {
1086    $query='
1087UPDATE '.IMAGES_TABLE .'
1088  SET average_rate=NULL
1089  WHERE id IN (' . implode(',',$to_update) . ')';
1090    pwg_query($query);
1091  }
1092}
1093
1094/**
1095 * change the parent category of the given categories. The categories are
1096 * supposed virtual.
1097 *
1098 * @param array category identifiers
1099 * @param int parent category identifier
1100 * @return void
1101 */
1102function move_categories($category_ids, $new_parent = -1)
1103{
1104  global $page;
1105
1106  if (count($category_ids) == 0)
1107  {
1108    return;
1109  }
1110
1111  $new_parent = $new_parent < 1 ? 'NULL' : $new_parent;
1112
1113  $categories = array();
1114
1115  $query = '
1116SELECT id, id_uppercat, status, uppercats
1117  FROM '.CATEGORIES_TABLE.'
1118  WHERE id IN ('.implode(',', $category_ids).')
1119;';
1120  $result = pwg_query($query);
1121  while ($row = pwg_db_fetch_assoc($result))
1122  {
1123    $categories[$row['id']] =
1124      array(
1125        'parent' => empty($row['id_uppercat']) ? 'NULL' : $row['id_uppercat'],
1126        'status' => $row['status'],
1127        'uppercats' => $row['uppercats']
1128        );
1129  }
1130
1131  // is the movement possible? The movement is impossible if you try to move
1132  // a category in a sub-category or itself
1133  if ('NULL' != $new_parent)
1134  {
1135    $query = '
1136SELECT uppercats
1137  FROM '.CATEGORIES_TABLE.'
1138  WHERE id = '.$new_parent.'
1139;';
1140    list($new_parent_uppercats) = pwg_db_fetch_row(pwg_query($query));
1141
1142    foreach ($categories as $category)
1143    {
1144      // technically, you can't move a category with uppercats 12,125,13,14
1145      // into a new parent category with uppercats 12,125,13,14,24
1146      if (preg_match('/^'.$category['uppercats'].',/', $new_parent_uppercats))
1147      {
1148        array_push(
1149          $page['errors'],
1150          l10n('You cannot move an album in its own sub album')
1151          );
1152        return;
1153      }
1154    }
1155  }
1156
1157  $tables =
1158    array(
1159      USER_ACCESS_TABLE => 'user_id',
1160      GROUP_ACCESS_TABLE => 'group_id'
1161      );
1162
1163  $query = '
1164UPDATE '.CATEGORIES_TABLE.'
1165  SET id_uppercat = '.$new_parent.'
1166  WHERE id IN ('.implode(',', $category_ids).')
1167;';
1168  pwg_query($query);
1169
1170  update_uppercats();
1171  update_global_rank();
1172
1173  // status and related permissions management
1174  if ('NULL' == $new_parent)
1175  {
1176    $parent_status = 'public';
1177  }
1178  else
1179  {
1180    $query = '
1181SELECT status
1182  FROM '.CATEGORIES_TABLE.'
1183  WHERE id = '.$new_parent.'
1184;';
1185    list($parent_status) = pwg_db_fetch_row(pwg_query($query));
1186  }
1187
1188  if ('private' == $parent_status)
1189  {
1190    foreach ($categories as $cat_id => $category)
1191    {
1192      switch ($category['status'])
1193      {
1194        case 'public' :
1195        {
1196          set_cat_status(array($cat_id), 'private');
1197          break;
1198        }
1199        case 'private' :
1200        {
1201          $subcats = get_subcat_ids(array($cat_id));
1202
1203          foreach ($tables as $table => $field)
1204          {
1205            $query = '
1206SELECT '.$field.'
1207  FROM '.$table.'
1208  WHERE cat_id = '.$cat_id.'
1209;';
1210            $category_access = array_from_query($query, $field);
1211
1212            $query = '
1213SELECT '.$field.'
1214  FROM '.$table.'
1215  WHERE cat_id = '.$new_parent.'
1216;';
1217            $parent_access = array_from_query($query, $field);
1218
1219            $to_delete = array_diff($parent_access, $category_access);
1220
1221            if (count($to_delete) > 0)
1222            {
1223              $query = '
1224DELETE FROM '.$table.'
1225  WHERE '.$field.' IN ('.implode(',', $to_delete).')
1226    AND cat_id IN ('.implode(',', $subcats).')
1227;';
1228              pwg_query($query);
1229            }
1230          }
1231          break;
1232        }
1233      }
1234    }
1235  }
1236
1237  array_push(
1238    $page['infos'],
1239    l10n_dec(
1240      '%d album moved', '%d albums moved',
1241      count($categories)
1242      )
1243    );
1244}
1245
1246/**
1247 * create a virtual category
1248 *
1249 * @param string category name
1250 * @param int parent category id
1251 * @return array with ('info' and 'id') or ('error') key
1252 */
1253function create_virtual_category($category_name, $parent_id=null)
1254{
1255  global $conf;
1256
1257  // is the given category name only containing blank spaces ?
1258  if (preg_match('/^\s*$/', $category_name))
1259  {
1260    return array('error' => l10n('The name of an album must not be empty'));
1261  }
1262
1263  $parent_id = !empty($parent_id) ? $parent_id : 'NULL';
1264
1265  $query = '
1266SELECT MAX(rank)
1267  FROM '.CATEGORIES_TABLE.'
1268  WHERE id_uppercat '.(is_numeric($parent_id) ? '= '.$parent_id : 'IS NULL').'
1269;';
1270  list($current_rank) = pwg_db_fetch_row(pwg_query($query));
1271
1272  $insert = array(
1273    'name' => $category_name,
1274    'rank' => ++$current_rank,
1275    'commentable' => boolean_to_string($conf['newcat_default_commentable']),
1276    );
1277
1278  if ($parent_id != 'NULL')
1279  {
1280    $query = '
1281SELECT id, uppercats, global_rank, visible, status
1282  FROM '.CATEGORIES_TABLE.'
1283  WHERE id = '.$parent_id.'
1284;';
1285    $parent = pwg_db_fetch_assoc(pwg_query($query));
1286
1287    $insert['id_uppercat'] = $parent['id'];
1288    $insert['global_rank'] = $parent['global_rank'].'.'.$insert['rank'];
1289
1290    // at creation, must a category be visible or not ? Warning : if the
1291    // parent category is invisible, the category is automatically create
1292    // invisible. (invisible = locked)
1293    if ('false' == $parent['visible'])
1294    {
1295      $insert['visible'] = 'false';
1296    }
1297    else
1298    {
1299      $insert['visible'] = boolean_to_string($conf['newcat_default_visible']);
1300    }
1301
1302    // at creation, must a category be public or private ? Warning : if the
1303    // parent category is private, the category is automatically create
1304    // private.
1305    if ('private' == $parent['status'])
1306    {
1307      $insert['status'] = 'private';
1308    }
1309    else
1310    {
1311      $insert['status'] = $conf['newcat_default_status'];
1312    }
1313  }
1314  else
1315  {
1316    $insert['visible'] = boolean_to_string($conf['newcat_default_visible']);
1317    $insert['status'] = $conf['newcat_default_status'];
1318    $insert['global_rank'] = $insert['rank'];
1319  }
1320
1321  // we have then to add the virtual category
1322  mass_inserts(
1323    CATEGORIES_TABLE,
1324    array(
1325      'site_id', 'name', 'id_uppercat', 'rank', 'commentable',
1326      'visible', 'status', 'global_rank',
1327      ),
1328    array($insert)
1329    );
1330
1331  $inserted_id = pwg_db_insert_id(CATEGORIES_TABLE);
1332
1333  $query = '
1334UPDATE
1335  '.CATEGORIES_TABLE.'
1336  SET uppercats = \''.
1337    (isset($parent) ? $parent{'uppercats'}.',' : '').
1338    $inserted_id.
1339    '\'
1340  WHERE id = '.$inserted_id.'
1341;';
1342  pwg_query($query);
1343
1344  return array(
1345    'info' => l10n('Virtual album added'),
1346    'id'   => $inserted_id,
1347    );
1348}
1349
1350/**
1351 * Set tags to an image. Warning: given tags are all tags associated to the
1352 * image, not additionnal tags.
1353 *
1354 * @param array tag ids
1355 * @param int image id
1356 * @return void
1357 */
1358function set_tags($tags, $image_id)
1359{
1360  $query = '
1361DELETE
1362  FROM '.IMAGE_TAG_TABLE.'
1363  WHERE image_id = '.$image_id.'
1364;';
1365  pwg_query($query);
1366
1367  if (count($tags) > 0)
1368  {
1369    $inserts = array();
1370    foreach ($tags as $tag_id)
1371    {
1372      array_push(
1373        $inserts,
1374        array(
1375          'tag_id' => $tag_id,
1376          'image_id' => $image_id
1377          )
1378        );
1379    }
1380    mass_inserts(
1381      IMAGE_TAG_TABLE,
1382      array_keys($inserts[0]),
1383      $inserts
1384      );
1385  }
1386}
1387
1388/**
1389 * Add new tags to a set of images.
1390 *
1391 * @param array tag ids
1392 * @param array image ids
1393 * @return void
1394 */
1395function add_tags($tags, $images)
1396{
1397  if (count($tags) == 0 or count($images) == 0)
1398  {
1399    return;
1400  }
1401
1402  // we can't insert twice the same {image_id,tag_id} so we must first
1403  // delete lines we'll insert later
1404  $query = '
1405DELETE
1406  FROM '.IMAGE_TAG_TABLE.'
1407  WHERE image_id IN ('.implode(',', $images).')
1408    AND tag_id IN ('.implode(',', $tags).')
1409;';
1410  pwg_query($query);
1411
1412  $inserts = array();
1413  foreach ($images as $image_id)
1414  {
1415    foreach ($tags as $tag_id)
1416    {
1417      array_push(
1418        $inserts,
1419        array(
1420          'image_id' => $image_id,
1421          'tag_id' => $tag_id,
1422          )
1423        );
1424    }
1425  }
1426  mass_inserts(
1427    IMAGE_TAG_TABLE,
1428    array_keys($inserts[0]),
1429    $inserts
1430    );
1431}
1432
1433function tag_id_from_tag_name($tag_name)
1434{
1435  global $page;
1436
1437  $tag_name = trim($tag_name);
1438  if (isset($page['tag_id_from_tag_name_cache'][$tag_name]))
1439  {
1440    return $page['tag_id_from_tag_name_cache'][$tag_name];
1441  }
1442
1443  // does the tag already exists?
1444  $query = '
1445SELECT id
1446  FROM '.TAGS_TABLE.'
1447  WHERE name = \''.$tag_name.'\'
1448;';
1449  $existing_tags = array_from_query($query, 'id');
1450
1451  if (count($existing_tags) == 0)
1452  {
1453    mass_inserts(
1454      TAGS_TABLE,
1455      array('name', 'url_name'),
1456      array(
1457        array(
1458          'name' => $tag_name,
1459          'url_name' => str2url($tag_name),
1460          )
1461        )
1462      );
1463
1464    $page['tag_id_from_tag_name_cache'][$tag_name] = pwg_db_insert_id(TAGS_TABLE);
1465  }
1466  else
1467  {
1468    $page['tag_id_from_tag_name_cache'][$tag_name] = $existing_tags[0];
1469  }
1470
1471  return $page['tag_id_from_tag_name_cache'][$tag_name];
1472}
1473
1474function set_tags_of($tags_of)
1475{
1476  if (count($tags_of) > 0)
1477  {
1478    $query = '
1479DELETE
1480  FROM '.IMAGE_TAG_TABLE.'
1481  WHERE image_id IN ('.implode(',', array_keys($tags_of)).')
1482;';
1483    pwg_query($query);
1484
1485    $inserts = array();
1486
1487    foreach ($tags_of as $image_id => $tag_ids)
1488    {
1489      foreach ($tag_ids as $tag_id)
1490      {
1491        array_push(
1492          $inserts,
1493          array(
1494            'image_id' => $image_id,
1495            'tag_id' => $tag_id,
1496            )
1497          );
1498      }
1499    }
1500
1501    mass_inserts(
1502      IMAGE_TAG_TABLE,
1503      array_keys($inserts[0]),
1504      $inserts
1505      );
1506  }
1507}
1508
1509/**
1510 * Associate a list of images to a list of categories.
1511 *
1512 * The function will not duplicate links
1513 *
1514 * @param array images
1515 * @param array categories
1516 * @return void
1517 */
1518function associate_images_to_categories($images, $categories)
1519{
1520  if (count($images) == 0
1521      or count($categories) == 0)
1522  {
1523    return false;
1524  }
1525
1526  $query = '
1527DELETE
1528  FROM '.IMAGE_CATEGORY_TABLE.'
1529  WHERE image_id IN ('.implode(',', $images).')
1530    AND category_id IN ('.implode(',', $categories).')
1531;';
1532  pwg_query($query);
1533
1534  $inserts = array();
1535  foreach ($categories as $category_id)
1536  {
1537    foreach ($images as $image_id)
1538    {
1539      array_push(
1540        $inserts,
1541        array(
1542          'image_id' => $image_id,
1543          'category_id' => $category_id,
1544          )
1545        );
1546    }
1547  }
1548
1549  mass_inserts(
1550    IMAGE_CATEGORY_TABLE,
1551    array_keys($inserts[0]),
1552    $inserts
1553    );
1554
1555  update_category($categories);
1556}
1557
1558/**
1559 * Associate images associated to a list of source categories to a list of
1560 * destination categories.
1561 *
1562 * @param array sources
1563 * @param array destinations
1564 * @return void
1565 */
1566function associate_categories_to_categories($sources, $destinations)
1567{
1568  if (count($sources) == 0)
1569  {
1570    return false;
1571  }
1572
1573  $query = '
1574SELECT image_id
1575  FROM '.IMAGE_CATEGORY_TABLE.'
1576  WHERE category_id IN ('.implode(',', $sources).')
1577;';
1578  $images = array_from_query($query, 'image_id');
1579
1580  associate_images_to_categories($images, $destinations);
1581}
1582
1583/**
1584 * Refer main Piwigo URLs (currently PHPWG_DOMAIN domain)
1585 *
1586 * @param void
1587 * @return array like $conf['links']
1588 */
1589function pwg_URL()
1590{
1591  $urls = array(
1592    'HOME'       => 'http://'.PHPWG_DOMAIN,
1593    'WIKI'       => 'http://'.PHPWG_DOMAIN.'/doc',
1594    'DEMO'       => 'http://'.PHPWG_DOMAIN.'/demo',
1595    'FORUM'      => 'http://'.PHPWG_DOMAIN.'/forum',
1596    'BUGS'       => 'http://'.PHPWG_DOMAIN.'/bugs',
1597    'EXTENSIONS' => 'http://'.PHPWG_DOMAIN.'/ext',
1598    );
1599  return $urls;
1600}
1601
1602/**
1603 * Invalidates cahed data (permissions and category counts) for all users.
1604 */
1605function invalidate_user_cache($full = true)
1606{
1607  if ($full)
1608  {
1609    $query = '
1610TRUNCATE TABLE '.USER_CACHE_CATEGORIES_TABLE.';';
1611    pwg_query($query);
1612    $query = '
1613TRUNCATE TABLE '.USER_CACHE_TABLE.';';
1614    pwg_query($query);
1615  }
1616  else
1617  {
1618    $query = '
1619UPDATE '.USER_CACHE_TABLE.'
1620  SET need_update = \'true\';';
1621    pwg_query($query);
1622  }
1623  trigger_action('invalidate_user_cache', $full);
1624}
1625
1626/**
1627 * adds the caracter set to a create table sql query.
1628 * all CREATE TABLE queries must call this function
1629 * @param string query - the sql query
1630 */
1631function create_table_add_character_set($query)
1632{
1633  defined('DB_CHARSET') or fatal_error('create_table_add_character_set DB_CHARSET undefined');
1634  if ('DB_CHARSET'!='')
1635  {
1636    if ( version_compare(mysql_get_server_info(), '4.1.0', '<') )
1637    {
1638      return $query;
1639    }
1640    $charset_collate = " DEFAULT CHARACTER SET ".DB_CHARSET;
1641    if (DB_COLLATE!='')
1642    {
1643      $charset_collate .= " COLLATE ".DB_COLLATE;
1644    }
1645    if ( is_array($query) )
1646    {
1647      foreach( $query as $id=>$q)
1648      {
1649        $q=trim($q);
1650        $q=trim($q, ';');
1651        if (preg_match('/^CREATE\s+TABLE/i',$q))
1652        {
1653          $q.=$charset_collate;
1654        }
1655        $q .= ';';
1656        $query[$id] = $q;
1657      }
1658    }
1659    else
1660    {
1661      $query=trim($query);
1662      $query=trim($query, ';');
1663      if (preg_match('/^CREATE\s+TABLE/i',$query))
1664      {
1665        $query.=$charset_collate;
1666      }
1667      $query .= ';';
1668    }
1669  }
1670  return $query;
1671}
1672
1673/**
1674 * Returns array use on template with html_options method
1675 * @param Min and Max access to use
1676 * @return array of user access level
1677 */
1678function get_user_access_level_html_options($MinLevelAccess = ACCESS_FREE, $MaxLevelAccess = ACCESS_CLOSED)
1679{
1680  $tpl_options = array();
1681  for ($level = $MinLevelAccess; $level <= $MaxLevelAccess; $level++)
1682  {
1683    $tpl_options[$level] = l10n(sprintf('ACCESS_%d', $level));
1684  }
1685  return $tpl_options;
1686}
1687
1688/**
1689 * returns a list of templates currently available in template-extension
1690 * Each .tpl file is extracted from template-extension.
1691 * @return array
1692 */
1693function get_extents($start='')
1694{
1695  if ($start == '') { $start = './template-extension'; }
1696  $dir = opendir($start);
1697  $extents = array();
1698
1699  while (($file = readdir($dir)) !== false)
1700  {
1701    if ( $file == '.' or $file == '..' or $file == '.svn') continue;
1702    $path = $start . '/' . $file;
1703    if (is_dir($path))
1704    {
1705      $extents = array_merge($extents, get_extents($path));
1706    }
1707    elseif ( !is_link($path) and file_exists($path)
1708            and get_extension($path) == 'tpl' )
1709    {
1710      $extents[] = substr($path, 21);
1711    }
1712  }
1713  return $extents;
1714}
1715
1716function create_tag($tag_name)
1717{
1718  // does the tag already exists?
1719  $query = '
1720SELECT id
1721  FROM '.TAGS_TABLE.'
1722  WHERE name = \''.$tag_name.'\'
1723;';
1724  $existing_tags = array_from_query($query, 'id');
1725
1726  if (count($existing_tags) == 0)
1727  {
1728    mass_inserts(
1729      TAGS_TABLE,
1730      array('name', 'url_name'),
1731      array(
1732        array(
1733          'name' => $tag_name,
1734          'url_name' => str2url($tag_name),
1735          )
1736        )
1737      );
1738
1739    $inserted_id = pwg_db_insert_id(TAGS_TABLE);
1740
1741    return array(
1742      'info' => sprintf(
1743        l10n('Tag "%s" was added'),
1744        stripslashes($tag_name)
1745        ),
1746      'id' => $inserted_id,
1747      );
1748  }
1749  else
1750  {
1751    return array(
1752      'error' => sprintf(
1753        l10n('Tag "%s" already exists'),
1754        stripslashes($tag_name)
1755        )
1756      );
1757  }
1758}
1759
1760/**
1761 * Is the category accessible to the (Admin) user ?
1762 *
1763 * Note : if the user is not authorized to see this category, category jump
1764 * will be replaced by admin cat_modify page
1765 *
1766 * @param int category id to verify
1767 * @return bool
1768 */
1769function cat_admin_access($category_id)
1770{
1771  global $user;
1772
1773  // $filter['visible_categories'] and $filter['visible_images']
1774  // are not used because it's not necessary (filter <> restriction)
1775  if (in_array($category_id, explode(',', $user['forbidden_categories'])))
1776  {
1777    return false;
1778  }
1779  return true;
1780}
1781
1782/**
1783 * Retrieve data from external URL
1784 *
1785 * @param string $src: URL
1786 * @param global $dest: can be a file ressource or string
1787 * @return bool
1788 */
1789function fetchRemote($src, &$dest, $get_data=array(), $post_data=array(), $user_agent='Piwigo', $step=0)
1790{
1791  // Try to retrieve data from local file?
1792  if (!url_is_remote($src))
1793  {
1794    $content = @file_get_contents($src);
1795    if ($content !== false)
1796    {
1797      is_resource($dest) ? @fwrite($dest, $content) : $dest = $content;
1798      return true;
1799    }
1800    else
1801    {
1802      return false;
1803    }
1804  }
1805
1806  // After 3 redirections, return false
1807  if ($step > 3) return false;
1808
1809  // Initialization
1810  $method  = empty($post_data) ? 'GET' : 'POST';
1811  $request = empty($post_data) ? '' : http_build_query($post_data, '', '&');
1812  if (!empty($get_data))
1813  {
1814    $src .= strpos($src, '?') === false ? '?' : '&';
1815    $src .= http_build_query($get_data, '', '&');
1816  }
1817
1818  // Initialize $dest
1819  is_resource($dest) or $dest = '';
1820
1821  // Try curl to read remote file
1822  if (function_exists('curl_init'))
1823  {
1824    $ch = @curl_init();
1825    @curl_setopt($ch, CURLOPT_URL, $src);
1826    @curl_setopt($ch, CURLOPT_HEADER, 1);
1827    @curl_setopt($ch, CURLOPT_USERAGENT, $user_agent);
1828    @curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
1829    if ($method == 'POST')
1830    {
1831      @curl_setopt($ch, CURLOPT_POST, 1);
1832      @curl_setopt($ch, CURLOPT_POSTFIELDS, $request);
1833    }
1834    $content = @curl_exec($ch);
1835    $header_length = @curl_getinfo($ch, CURLINFO_HEADER_SIZE);
1836    $status = @curl_getinfo($ch, CURLINFO_HTTP_CODE);
1837    @curl_close($ch);
1838    if ($content !== false and $status >= 200 and $status < 400)
1839    {
1840      if (preg_match('/Location:\s+?(.+)/', substr($content, 0, $header_length), $m))
1841      {
1842        return fetchRemote($m[1], $dest, array(), array(), $user_agent, $step+1);
1843      }
1844      $content = substr($content, $header_length);
1845      is_resource($dest) ? @fwrite($dest, $content) : $dest = $content;
1846      return true;
1847    }
1848  }
1849
1850  // Try file_get_contents to read remote file
1851  if (ini_get('allow_url_fopen'))
1852  {
1853    $opts = array(
1854      'http' => array(
1855        'method' => $method,
1856        'user_agent' => $user_agent,
1857      )
1858    );
1859    if ($method == 'POST')
1860    {
1861      $opts['http']['content'] = $request;
1862    }
1863    $context = @stream_context_create($opts);
1864    $content = @file_get_contents($src, false, $context);
1865    if ($content !== false)
1866    {
1867      is_resource($dest) ? @fwrite($dest, $content) : $dest = $content;
1868      return true;
1869    }
1870  }
1871
1872  // Try fsockopen to read remote file
1873  $src = parse_url($src);
1874  $host = $src['host'];
1875  $path = isset($src['path']) ? $src['path'] : '/';
1876  $path .= isset($src['query']) ? '?'.$src['query'] : '';
1877
1878  if (($s = @fsockopen($host,80,$errno,$errstr,5)) === false)
1879  {
1880    return false;
1881  }
1882
1883  $http_request  = $method." ".$path." HTTP/1.0\r\n";
1884  $http_request .= "Host: ".$host."\r\n";
1885  if ($method == 'POST')
1886  {
1887    $http_request .= "Content-Type: application/x-www-form-urlencoded;\r\n";
1888    $http_request .= "Content-Length: ".strlen($request)."\r\n";
1889  }
1890  $http_request .= "User-Agent: ".$user_agent."\r\n";
1891  $http_request .= "Accept: */*\r\n";
1892  $http_request .= "\r\n";
1893  $http_request .= $request;
1894
1895  fwrite($s, $http_request);
1896
1897  $i = 0;
1898  $in_content = false;
1899  while (!feof($s))
1900  {
1901    $line = fgets($s);
1902
1903    if (rtrim($line,"\r\n") == '' && !$in_content)
1904    {
1905      $in_content = true;
1906      $i++;
1907      continue;
1908    }
1909    if ($i == 0)
1910    {
1911      if (!preg_match('/HTTP\/(\\d\\.\\d)\\s*(\\d+)\\s*(.*)/',rtrim($line,"\r\n"), $m))
1912      {
1913        fclose($s);
1914        return false;
1915      }
1916      $status = (integer) $m[2];
1917      if ($status < 200 || $status >= 400)
1918      {
1919        fclose($s);
1920        return false;
1921      }
1922    }
1923    if (!$in_content)
1924    {
1925      if (preg_match('/Location:\s+?(.+)$/',rtrim($line,"\r\n"),$m))
1926      {
1927        fclose($s);
1928        return fetchRemote(trim($m[1]),$dest,array(),array(),$user_agent,$step+1);
1929      }
1930      $i++;
1931      continue;
1932    }
1933    is_resource($dest) ? @fwrite($dest, $line) : $dest .= $line;
1934    $i++;
1935  }
1936  fclose($s);
1937  return true;
1938}
1939
1940
1941/**
1942 * returns the groupname corresponding to the given group identifier if
1943 * exists
1944 *
1945 * @param int group_id
1946 * @return mixed
1947 */
1948function get_groupname($group_id)
1949{
1950  $query = '
1951SELECT name
1952  FROM '.GROUPS_TABLE.'
1953  WHERE id = '.intval($group_id).'
1954;';
1955  $result = pwg_query($query);
1956  if (pwg_db_num_rows($result) > 0)
1957  {
1958    list($groupname) = pwg_db_fetch_row($result);
1959  }
1960  else
1961  {
1962    return false;
1963  }
1964
1965  return $groupname;
1966}
1967
1968/**
1969 * returns the username corresponding to the given user identifier if exists
1970 *
1971 * @param int user_id
1972 * @return mixed
1973 */
1974function get_username($user_id)
1975{
1976  global $conf;
1977
1978  $query = '
1979SELECT '.$conf['user_fields']['username'].'
1980  FROM '.USERS_TABLE.'
1981  WHERE '.$conf['user_fields']['id'].' = '.intval($user_id).'
1982;';
1983  $result = pwg_query($query);
1984  if (pwg_db_num_rows($result) > 0)
1985  {
1986    list($username) = pwg_db_fetch_row($result);
1987  }
1988  else
1989  {
1990    return false;
1991  }
1992
1993  return stripslashes($username);
1994}
1995
1996function get_newsletter_subscribe_base_url($language) {
1997  $subscribe_domain = 'piwigo.org';
1998
1999  $domain_of = array(
2000    'fr_FR' => 'fr.piwigo.org',
2001    'it_IT' => 'it.piwigo.org',
2002    'de_DE' => 'de.piwigo.org',
2003    'es_ES' => 'es.piwigo.org',
2004    'zh_CN' => 'cn.piwigo.org',
2005    'pl_PL' => 'pl.piwigo.org',
2006    'hu_HU' => 'hu.piwigo.org',
2007    'ru_RU' => 'ru.piwigo.org',
2008        'nl_NL' => 'nl.piwigo.org',
2009    );
2010
2011  if (isset($domain_of[$language])) {
2012    $subscribe_domain = $domain_of[$language];
2013  }
2014
2015  return 'http://'.$subscribe_domain.'/announcement/subscribe/';
2016}
2017
2018/**
2019 * Accordion menus need to know which section to open by default when
2020 * loading the page
2021 */
2022function get_active_menu($menu_page)
2023{
2024  global $page;
2025
2026  if (isset($page['active_menu']))
2027  {
2028    return $page['active_menu'];
2029  }
2030
2031  switch ($menu_page)
2032  {
2033    case 'photos_add':
2034    case 'rating':
2035    case 'tags':
2036    case 'picture_modify':
2037    case 'batch_manager':
2038      return 0;
2039
2040    case 'cat_list':
2041    case 'cat_modify':
2042    case 'cat_move':
2043    case 'cat_options':
2044    case 'cat_perm':
2045    case 'permalinks':
2046      return 1;
2047
2048    case 'user_list':
2049    case 'user_perm':
2050    case 'group_list':
2051    case 'group_perm':
2052    case 'notification_by_mail':
2053      return 2;
2054
2055    case 'plugins_list':
2056    case 'plugins_update':
2057    case 'plugins_new':
2058    case 'plugin':
2059      return 3;
2060
2061    case 'site_manager':
2062    case 'site_update':
2063    case 'stats':
2064    case 'history':
2065    case 'maintenance':
2066    case 'thumbnail':
2067    case 'comments':
2068      return 4;
2069
2070    case 'configuration':
2071    case 'extend_for_templates':
2072    case 'menubar':
2073    case 'themes_new':
2074    case 'themes_installed':
2075    case 'theme':
2076    case 'languages_installed':
2077    case 'languages_new':
2078      return 5;
2079  }
2080  return 0;
2081}
2082
2083function get_fckb_taglist($query)
2084{
2085  $result = pwg_query($query);
2086  $taglist = array();
2087  while ($row = pwg_db_fetch_assoc($result))
2088  {
2089    array_push(
2090      $taglist,
2091      array(
2092        'caption' => $row['tag_name'],
2093        'value' => '~~'.$row['tag_id'].'~~',
2094        )
2095      );
2096  }
2097
2098  return $taglist;
2099}
2100
2101function get_fckb_tag_ids($raw_tags)
2102{
2103  // In $raw_tags we receive something like array('~~6~~', '~~59~~', 'New
2104  // tag', 'Another new tag') The ~~34~~ means that it is an existing
2105  // tag. I've added the surrounding ~~ to permit creation of tags like "10"
2106  // or "1234" (numeric characters only)
2107
2108  $tag_ids = array();
2109
2110  foreach ($raw_tags as $raw_tag)
2111  {
2112    if (preg_match('/^~~(\d+)~~$/', $raw_tag, $matches))
2113    {
2114      array_push($tag_ids, $matches[1]);
2115    }
2116    else
2117    {
2118      // we have to create a new tag
2119      array_push(
2120        $tag_ids,
2121        tag_id_from_tag_name($raw_tag)
2122        );
2123    }
2124  }
2125
2126  return $tag_ids;
2127}
2128
2129/** returns the argument_ids array with new sequenced keys based on related
2130 * names. Sequence is not case sensitive.
2131 * Warning: By definition, this function breaks original keys
2132 */
2133function order_by_name($element_ids,$name)
2134{
2135  $ordered_element_ids = array();
2136  foreach ($element_ids as $k_id => $element_id)
2137  {
2138    $key = strtolower($name[$element_id]) .'-'. $name[$element_id] .'-'. $k_id;
2139    $ordered_element_ids[$key] = $element_id;
2140  }
2141  ksort($ordered_element_ids);
2142  return $ordered_element_ids;
2143}
2144
2145?>
Note: See TracBrowser for help on using the repository browser.