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

Revision 13077, 53.4 KB checked in by plg, 8 years ago (diff)

feature 2564: redesign on photo administration screen.

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