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

Last change on this file since 1064 was 1064, checked in by plg, 18 years ago

new feature: source/destination links between categories. Will we keep this
feature? Code is complicated and very few people will understand how it
works...

modification: #images.storage_category_id replaced by
#image_category.is_storage

improvement: many code refactoring to improve readibility

improvement: virtual category creation code was moved to a dedicated
function in order to be called from admin/cat_list.php and
admin/cat_modify.php (create a new destination category)

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 48.5 KB
Line 
1
2<?php
3// +-----------------------------------------------------------------------+
4// | PhpWebGallery - a PHP based picture gallery                           |
5// | Copyright (C) 2002-2003 Pierrick LE GALL - pierrick@phpwebgallery.net |
6// | Copyright (C) 2003-2006 PhpWebGallery Team - http://phpwebgallery.net |
7// +-----------------------------------------------------------------------+
8// | branch        : BSF (Best So Far)
9// | file          : $RCSfile$
10// | last update   : $Date: 2006-03-04 23:31:46 +0000 (Sat, 04 Mar 2006) $
11// | last modifier : $Author: plg $
12// | revision      : $Revision: 1064 $
13// +-----------------------------------------------------------------------+
14// | This program is free software; you can redistribute it and/or modify  |
15// | it under the terms of the GNU General Public License as published by  |
16// | the Free Software Foundation                                          |
17// |                                                                       |
18// | This program is distributed in the hope that it will be useful, but   |
19// | WITHOUT ANY WARRANTY; without even the implied warranty of            |
20// | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU      |
21// | General Public License for more details.                              |
22// |                                                                       |
23// | You should have received a copy of the GNU General Public License     |
24// | along with this program; if not, write to the Free Software           |
25// | Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, |
26// | USA.                                                                  |
27// +-----------------------------------------------------------------------+
28
29include(PHPWG_ROOT_PATH.'admin/include/functions_metadata.php');
30
31/**
32 * returns an array with all picture files according to $conf['file_ext']
33 *
34 * @param string $dir
35 * @return array
36 */
37function get_pwg_files($dir)
38{
39  global $conf;
40
41  $pictures = array();
42  if ($opendir = opendir($dir))
43  {
44    while ($file = readdir($opendir))
45    {
46      if (in_array(get_extension($file), $conf['file_ext']))
47      {
48        array_push($pictures, $file);
49      }
50    }
51  }
52  return $pictures;
53}
54
55/**
56 * returns an array with all thumbnails according to $conf['picture_ext']
57 * and $conf['prefix_thumbnail']
58 *
59 * @param string $dir
60 * @return array
61 */
62function get_thumb_files($dir)
63{
64  global $conf;
65
66  $prefix_length = strlen($conf['prefix_thumbnail']);
67
68  $thumbnails = array();
69  if ($opendir = @opendir($dir.'/thumbnail'))
70  {
71    while ($file = readdir($opendir))
72    {
73      if (in_array(get_extension($file), $conf['picture_ext'])
74          and substr($file, 0, $prefix_length) == $conf['prefix_thumbnail'])
75      {
76        array_push($thumbnails, $file);
77      }
78    }
79  }
80  return $thumbnails;
81}
82
83/**
84 * returns an array with representative picture files of a directory
85 * according to $conf['picture_ext']
86 *
87 * @param string $dir
88 * @return array
89 */
90function get_representative_files($dir)
91{
92  global $conf;
93
94  $pictures = array();
95  if ($opendir = @opendir($dir.'/pwg_representative'))
96  {
97    while ($file = readdir($opendir))
98    {
99      if (in_array(get_extension($file), $conf['picture_ext']))
100      {
101        array_push($pictures, $file);
102      }
103    }
104  }
105  return $pictures;
106}
107
108// The function delete_site deletes a site and call the function
109// delete_categories for each primary category of the site
110function delete_site( $id )
111{
112  // destruction of the categories of the site
113  $query = '
114SELECT id
115  FROM '.CATEGORIES_TABLE.'
116  WHERE site_id = '.$id.'
117;';
118  $result = pwg_query($query);
119  $category_ids = array();
120  while ($row = mysql_fetch_array($result))
121  {
122    array_push($category_ids, $row['id']);
123  }
124  delete_categories($category_ids);
125
126  // destruction of the site
127  $query = '
128DELETE FROM '.SITES_TABLE.'
129  WHERE id = '.$id.'
130;';
131  pwg_query($query);
132}
133
134
135// The function delete_categories deletes the categories identified by the
136// (numeric) key of the array $ids. It also deletes (in the database) :
137//    - all the elements of the category (delete_elements, see further)
138//    - all the links between elements and this category
139//    - all the restrictions linked to the category
140// The function works recursively.
141function delete_categories($ids)
142{
143  global $counts;
144
145  if (count($ids) == 0)
146  {
147    return;
148  }
149
150  // add sub-category ids to the given ids : if a category is deleted, all
151  // sub-categories must be so
152  $ids = get_subcat_ids($ids);
153
154  // destruction of all the related elements
155  $query = '
156SELECT image_id
157  FROM '.IMAGE_CATEGORY_TABLE.'
158  WHERE is_storage = \'true\'
159    AND category_id IN ('.
160    wordwrap(
161      implode(', ', $ids),
162      80,
163      "\n"
164      ).
165    ')
166;';
167  $result = pwg_query($query);
168  $element_ids = array();
169  while ($row = mysql_fetch_array($result))
170  {
171    array_push($element_ids, $row['image_id']);
172  }
173  delete_elements($element_ids);
174
175  // destruction of the links between images and this category
176  $query = '
177DELETE FROM '.IMAGE_CATEGORY_TABLE.'
178  WHERE category_id IN (
179'.wordwrap(implode(', ', $ids), 80, "\n").')
180;';
181  pwg_query($query);
182
183  // destruction of the access linked to the category
184  $query = '
185DELETE FROM '.USER_ACCESS_TABLE.'
186  WHERE cat_id IN (
187'.wordwrap(implode(', ', $ids), 80, "\n").')
188;';
189  pwg_query($query);
190 
191  $query = '
192DELETE FROM '.GROUP_ACCESS_TABLE.'
193  WHERE cat_id IN (
194'.wordwrap(implode(', ', $ids), 80, "\n").')
195;';
196  pwg_query($query);
197
198  // source/destination links deletion
199  $query = '
200SELECT destination, source
201  FROM '.CATEGORIES_LINK_TABLE.'
202  WHERE source IN ('.implode(',', $ids).')
203    OR destination IN ('.implode(',', $ids).')
204;';
205  $result = pwg_query($query);
206
207  $sources_of = array();
208 
209  while ($row = mysql_fetch_array($result))
210  {
211    if (!isset($sources_of[ $row['destination'] ]))
212    {
213      $sources_of[ $row['destination'] ] = array();
214    }
215
216    array_push(
217      $sources_of[ $row['destination'] ],
218      $row['source']
219      );
220  }
221
222  foreach ($sources_of as $destination => $sources)
223  {
224    delete_sources($destination, $sources);
225  }
226
227  update_category();
228
229  // destruction of the category
230  $query = '
231DELETE FROM '.CATEGORIES_TABLE.'
232  WHERE id IN (
233'.wordwrap(implode(', ', $ids), 80, "\n").')
234;';
235  pwg_query($query);
236
237  if (isset($counts['del_categories']))
238  {
239    $counts['del_categories']+= count($ids);
240  }
241}
242
243// The function delete_elements deletes the elements identified by the
244// (numeric) values of the array $ids. It also deletes (in the database) :
245//    - all the comments related to elements
246//    - all the links between categories and elements
247//    - all the favorites associated to elements
248function delete_elements($ids)
249{
250  global $counts;
251
252  if (count($ids) == 0)
253  {
254    return;
255  }
256
257  // destruction of the comments on the image
258  $query = '
259DELETE FROM '.COMMENTS_TABLE.'
260  WHERE image_id IN (
261'.wordwrap(implode(', ', $ids), 80, "\n").')
262;';
263  pwg_query($query);
264
265  // destruction of the links between images and this category
266  $query = '
267DELETE FROM '.IMAGE_CATEGORY_TABLE.'
268  WHERE image_id IN (
269'.wordwrap(implode(', ', $ids), 80, "\n").')
270;';
271  pwg_query($query);
272
273  // destruction of the favorites associated with the picture
274  $query = '
275DELETE FROM '.FAVORITES_TABLE.'
276  WHERE image_id IN (
277'.wordwrap(implode(', ', $ids), 80, "\n").')
278;';
279  pwg_query($query);
280
281  // destruction of the rates associated to this element
282  $query = '
283DELETE FROM '.RATE_TABLE.'
284  WHERE element_id IN (
285'.wordwrap(implode(', ', $ids), 80, "\n").')
286;';
287  pwg_query($query);
288
289  // destruction of the rates associated to this element
290  $query = '
291DELETE FROM '.CADDIE_TABLE.'
292  WHERE element_id IN (
293'.wordwrap(implode(', ', $ids), 80, "\n").')
294;';
295  pwg_query($query);
296
297  // destruction of the image
298  $query = '
299DELETE FROM '.IMAGES_TABLE.'
300  WHERE id IN (
301'.wordwrap(implode(', ', $ids), 80, "\n").')
302;';
303  pwg_query($query);
304
305  if (isset($counts['del_elements']))
306  {
307    $counts['del_elements']+= count($ids);
308  }
309}
310
311// The delete_user function delete a user identified by the $user_id
312// It also deletes :
313//     - all the access linked to this user
314//     - all the links to any group
315//     - all the favorites linked to this user
316//     - calculated permissions linked to the user
317//     - all datas about notifications for the user
318function delete_user($user_id)
319{
320  global $conf;
321
322  // destruction of the access linked to the user
323  $query = '
324DELETE FROM '.USER_ACCESS_TABLE.'
325  WHERE user_id = '.$user_id.'
326;';
327  pwg_query($query);
328
329  // destruction of data notification by mail for this user
330  $query = '
331DELETE FROM '.USER_MAIL_NOTIFICATION_TABLE.'
332  WHERE user_id = '.$user_id.'
333;';
334  pwg_query($query);
335
336  // destruction of data RSS notification for this user
337  $query = '
338DELETE FROM '.USER_FEED_TABLE.'
339  WHERE user_id = '.$user_id.'
340;';
341  pwg_query($query);
342
343  // destruction of the group links for this user
344  $query = '
345DELETE FROM '.USER_GROUP_TABLE.'
346  WHERE user_id = '.$user_id.'
347;';
348  pwg_query($query);
349
350  // destruction of the favorites associated with the user
351  $query = '
352DELETE FROM '.FAVORITES_TABLE.'
353  WHERE user_id = '.$user_id.'
354;';
355  pwg_query($query);
356
357  // deletion of calculated permissions linked to the user
358  $query = '
359DELETE FROM '.USER_CACHE_TABLE.'
360  WHERE user_id = '.$user_id.'
361;';
362  pwg_query($query);
363
364  // deletion of phpwebgallery specific informations
365  $query = '
366DELETE FROM '.USER_INFOS_TABLE.'
367  WHERE user_id = '.$user_id.'
368;';
369  pwg_query($query);
370
371  // destruction of the user
372  $query = '
373DELETE FROM '.USERS_TABLE.'
374  WHERE '.$conf['user_fields']['id'].' = '.$user_id.'
375;';
376  pwg_query($query);
377}
378
379/**
380 * updates calculated informations about a set of categories : date_last and
381 * nb_images. It also verifies that the representative picture is really
382 * linked to the category. Optionnaly recursive.
383 *
384 * @param mixed category id
385 * @param boolean recursive
386 * @returns void
387 */
388function update_category($ids = 'all', $recursive = false)
389{
390  global $conf;
391
392  // retrieving all categories to update
393  $cat_ids = array();
394
395  $query = '
396SELECT id
397  FROM '.CATEGORIES_TABLE;
398  if (is_array($ids))
399  {
400    if ($recursive)
401    {
402      foreach ($ids as $num => $id)
403      {
404        if ($num == 0)
405        {
406          $query.= '
407  WHERE ';
408        }
409        else
410        {
411          $query.= '
412  OR    ';
413        }
414        $query.= 'uppercats REGEXP \'(^|,)'.$id.'(,|$)\'';
415      }
416    }
417    else
418    {
419      $query.= '
420  WHERE id IN ('.wordwrap(implode(', ', $ids), 80, "\n").')';
421    }
422  }
423  $query.= '
424;';
425  $cat_ids = array_unique(array_from_query($query, 'id'));
426
427  if (count($cat_ids) == 0)
428  {
429    return false;
430  }
431
432  // calculate informations about categories retrieved
433  $query = '
434SELECT category_id,
435       COUNT(image_id) AS nb_images,
436       MAX(date_available) AS date_last
437  FROM '.IMAGES_TABLE.'
438    INNER JOIN '.IMAGE_CATEGORY_TABLE.' ON id = image_id
439  WHERE category_id IN ('.wordwrap(implode(', ', $cat_ids), 80, "\n").')
440  GROUP BY category_id
441;';
442  $result = pwg_query($query);
443  $datas = array();
444  $query_ids = array();
445  while ($row = mysql_fetch_array($result))
446  {
447    array_push($query_ids, $row['category_id']);
448   
449    array_push(
450      $datas,
451      array(
452        'id'        => $row['category_id'],
453        'date_last' => $row['date_last'],
454        'nb_images' => $row['nb_images']
455        )
456      );
457  }
458  // if all links between a category and elements have disappeared, no line
459  // is returned but the update must be done !
460  foreach (array_diff($cat_ids, $query_ids) as $id)
461  {
462    array_push(
463      $datas,
464      array(
465        'id'        => $id,
466        'nb_images' => 0,
467        )
468      );
469  }
470
471  mass_updates(
472    CATEGORIES_TABLE,
473    array(
474      'primary' => array('id'),
475      'update'  => array('date_last', 'nb_images')
476      ),
477    $datas
478    );
479
480  // representative pictures
481  if (count($cat_ids) > 0)
482  {
483    // find all categories where the setted representative is not possible :
484    // the picture does not exist
485    $query = '
486SELECT c.id
487  FROM '.CATEGORIES_TABLE.' AS c LEFT JOIN '.IMAGES_TABLE.' AS i
488    ON c.representative_picture_id = i.id
489  WHERE representative_picture_id IS NOT NULL
490    AND c.id IN ('.wordwrap(implode(', ', $cat_ids), 80, "\n").')
491    AND i.id IS NULL
492;';
493    $wrong_representant = array_from_query($query, 'id');
494
495    if ($conf['allow_random_representative'])
496    {
497      if (count($wrong_representant) > 0)
498      {
499        $query = '
500UPDATE '.CATEGORIES_TABLE.'
501  SET representative_picture_id = NULL
502  WHERE id IN ('.wordwrap(implode(', ', $wrong_representant), 80, "\n").')
503;';
504        pwg_query($query);
505      }
506    }
507    else
508    {
509      $to_null = array();
510      $to_rand = array();
511
512      if (count($wrong_representant) > 0)
513      {
514        // among the categories with an unknown representant, we dissociate
515        // categories containing pictures and categories containing no
516        // pictures. Indeed, the representant must set to NULL if no picture
517        // in the category and set to a random picture otherwise.
518        $query = '
519SELECT id
520  FROM '.CATEGORIES_TABLE.'
521  WHERE id IN ('.wordwrap(implode(', ', $wrong_representant), 80, "\n").')
522    AND nb_images = 0
523;';
524        $to_null = array_from_query($query, 'id');
525        $to_rand = array_diff($wrong_representant, $to_null);
526      }
527
528      if (count($to_null) > 0)
529      {
530        $query = '
531UPDATE '.CATEGORIES_TABLE.'
532  SET representative_picture_id = NULL
533  WHERE id IN ('.wordwrap(implode(', ', $to_null), 80, "\n").')
534;';
535        pwg_query($query);
536      }
537
538      // If the random representant is not allowed, we need to find
539      // categories with elements and with no representant. Those categories
540      // must be added to the list of categories to set to a random
541      // representant.
542      $query = '
543SELECT id
544  FROM '.CATEGORIES_TABLE.'
545  WHERE representative_picture_id IS NULL
546    AND nb_images != 0
547    AND id IN ('.wordwrap(implode(', ', $cat_ids), 80, "\n").')
548;';
549      $to_rand =
550        array_unique(
551          array_merge(
552            $to_rand,
553            array_from_query($query, 'id')
554            )
555          );
556
557      if (count($to_rand) > 0)
558      {
559        set_random_representant($to_rand);
560      }
561    }
562  }
563}
564
565function date_convert_back( $date )
566{
567  // date arrives at this format : YYYY-MM-DD
568  // It must be transformed in DD/MM/YYYY
569  if ( $date != '' )
570  {
571    list($year,$month,$day) = explode( '-', $date );
572    return $day.'/'.$month.'/'.$year;
573  }
574  else
575  {
576    return '';
577  }
578}
579
580/**
581 * returns an array with relevant keywords found in the given string.
582 *
583 * Keywords must be separated by comma or space characters.
584 *
585 * @param string keywords_string
586 * @return array
587 */
588function get_keywords($keywords_string)
589{
590  return
591    array_unique(
592      preg_split(
593        '/[\s,]+/',
594        $keywords_string
595        )
596      );
597}
598
599/**
600 * returns an array containing sub-directories which can be a category,
601 * recursive by default
602 *
603 * directories nammed "thumbnail", "pwg_high" or "pwg_representative" are
604 * omitted
605 *
606 * @param string $basedir
607 * @return array
608 */
609function get_fs_directories($path, $recursive = true)
610{
611  $dirs = array();
612
613  if (is_dir($path))
614  {
615    if ($contents = opendir($path))
616    {
617      while (($node = readdir($contents)) !== false)
618      {
619        if (is_dir($path.'/'.$node)
620            and $node != '.'
621            and $node != '..'
622            and $node != '.svn'
623            and $node != 'thumbnail'
624            and $node != 'pwg_high'
625            and $node != 'pwg_representative')
626        {
627          array_push($dirs, $path.'/'.$node);
628          if ($recursive)
629          {
630            $dirs = array_merge($dirs, get_fs_directories($path.'/'.$node));
631          }
632        }
633      }
634    }
635  }
636
637  return $dirs;
638}
639
640/**
641 * inserts multiple lines in a table
642 *
643 * @param string table_name
644 * @param array dbfields
645 * @param array inserts
646 * @return void
647 */
648function mass_inserts($table_name, $dbfields, $datas)
649{
650  // inserts all found categories
651  $query = '
652INSERT INTO '.$table_name.'
653  ('.implode(',', $dbfields).')
654   VALUES';
655  foreach ($datas as $insert_id => $insert)
656  {
657    $query.= '
658  ';
659    if ($insert_id > 0)
660    {
661      $query.= ',';
662    }
663    $query.= '(';
664    foreach ($dbfields as $field_id => $dbfield)
665    {
666      if ($field_id > 0)
667      {
668        $query.= ',';
669      }
670
671      if (!isset($insert[$dbfield]) or $insert[$dbfield] == '')
672      {
673        $query.= 'NULL';
674      }
675      else
676      {
677        $query.= "'".$insert[$dbfield]."'";
678      }
679    }
680    $query.=')';
681  }
682  $query.= '
683;';
684  pwg_query($query);
685}
686
687/**
688 * updates multiple lines in a table
689 *
690 * @param string table_name
691 * @param array dbfields
692 * @param array datas
693 * @return void
694 */
695function mass_updates($tablename, $dbfields, $datas)
696{
697  // depending on the MySQL version, we use the multi table update or N
698  // update queries
699  $query = 'SELECT VERSION() AS version;';
700  list($mysql_version) = mysql_fetch_array(pwg_query($query));
701  if (count($datas) < 10 or version_compare($mysql_version, '4.0.4') < 0)
702  {
703    // MySQL is prior to version 4.0.4, multi table update feature is not
704    // available
705    foreach ($datas as $data)
706    {
707      $query = '
708UPDATE '.$tablename.'
709  SET ';
710      $is_first = true;
711      foreach ($dbfields['update'] as $num => $key)
712      {
713        if (!$is_first)
714        {
715          $query.= ",\n      ";
716        }
717        $query.= $key.' = ';
718        if (isset($data[$key]) and $data[$key] != '')
719        {
720          $query.= '\''.$data[$key].'\'';
721        }
722        else
723        {
724          $query.= 'NULL';
725        }
726        $is_first = false;
727      }
728      $query.= '
729  WHERE ';
730      foreach ($dbfields['primary'] as $num => $key)
731      {
732        if ($num > 1)
733        {
734          $query.= ' AND ';
735        }
736        $query.= $key.' = \''.$data[$key].'\'';
737      }
738      $query.= '
739;';
740      pwg_query($query);
741    }
742  }
743  else
744  {
745    // creation of the temporary table
746    $query = '
747DESCRIBE '.$tablename.'
748;';
749    $result = pwg_query($query);
750    $columns = array();
751    $all_fields = array_merge($dbfields['primary'], $dbfields['update']);
752    while ($row = mysql_fetch_array($result))
753    {
754      if (in_array($row['Field'], $all_fields))
755      {
756        $column = $row['Field'];
757        $column.= ' '.$row['Type'];
758        if (!isset($row['Null']) or $row['Null'] == '')
759        {
760          $column.= ' NOT NULL';
761        }
762        if (isset($row['Default']))
763        {
764          $column.= " default '".$row['Default']."'";
765        }
766        array_push($columns, $column);
767      }
768    }
769
770    $temporary_tablename = $tablename.'_'.micro_seconds();
771
772    $query = '
773CREATE TABLE '.$temporary_tablename.'
774(
775'.implode(",\n", $columns).',
776PRIMARY KEY ('.implode(',', $dbfields['primary']).')
777)
778;';
779    pwg_query($query);
780    mass_inserts($temporary_tablename, $all_fields, $datas);
781    // update of images table by joining with temporary table
782    $query = '
783UPDATE '.$tablename.' AS t1, '.$temporary_tablename.' AS t2
784  SET '.implode("\n    , ",
785                array_map(
786                  create_function('$s', 'return "t1.$s = t2.$s";')
787                  , $dbfields['update'])).'
788  WHERE '.implode("\n    AND ",
789                array_map(
790                  create_function('$s', 'return "t1.$s = t2.$s";')
791                  , $dbfields['primary'])).'
792;';
793    pwg_query($query);
794    $query = '
795DROP TABLE '.$temporary_tablename.'
796;';
797    pwg_query($query);
798  }
799}
800
801/**
802 * updates the global_rank of categories under the given id_uppercat
803 *
804 * @param int id_uppercat
805 * @return void
806 */
807function update_global_rank($id_uppercat = 'all')
808{
809  $query = '
810SELECT id,rank
811  FROM '.CATEGORIES_TABLE.'
812;';
813  $result = pwg_query($query);
814  $ranks_array = array();
815  while ($row = mysql_fetch_array($result))
816  {
817    $ranks_array[$row['id']] = $row['rank'];
818  }
819
820  // which categories to update ?
821  $uppercats_array = array();
822
823  $query = '
824SELECT id,uppercats
825  FROM '.CATEGORIES_TABLE;
826  if (is_numeric($id_uppercat))
827  {
828    $query.= '
829  WHERE uppercats REGEXP \'(^|,)'.$id_uppercat.'(,|$)\'
830    AND id != '.$id_uppercat.'
831';
832  }
833  $query.= '
834;';
835  $result = pwg_query($query);
836  while ($row = mysql_fetch_array($result))
837  {
838    $uppercats_array[$row['id']] =  $row['uppercats'];
839  }
840
841  $datas = array();
842  foreach ($uppercats_array as $id => $uppercats)
843  {
844    $data = array();
845    $data['id'] = $id;
846    $global_rank = preg_replace('/(\d+)/e',
847                                "\$ranks_array['$1']",
848                                str_replace(',', '.', $uppercats));
849    $data['global_rank'] = $global_rank;
850    array_push($datas, $data);
851  }
852
853  $fields = array('primary' => array('id'), 'update' => array('global_rank'));
854  mass_updates(CATEGORIES_TABLE, $fields, $datas);
855}
856
857/**
858 * change the visible property on a set of categories
859 *
860 * @param array categories
861 * @param string value
862 * @return void
863 */
864function set_cat_visible($categories, $value)
865{
866  if (!in_array($value, array('true', 'false')))
867  {
868    return false;
869  }
870
871  // unlocking a category => all its parent categories become unlocked
872  if ($value == 'true')
873  {
874    $uppercats = get_uppercat_ids($categories);
875    $query = '
876UPDATE '.CATEGORIES_TABLE.'
877  SET visible = \'true\'
878  WHERE id IN ('.implode(',', $uppercats).')
879;';
880    pwg_query($query);
881  }
882  // locking a category   => all its child categories become locked
883  if ($value == 'false')
884  {
885    $subcats = get_subcat_ids($categories);
886    $query = '
887UPDATE '.CATEGORIES_TABLE.'
888  SET visible = \'false\'
889  WHERE id IN ('.implode(',', $subcats).')
890;';
891    pwg_query($query);
892  }
893}
894
895/**
896 * change the status property on a set of categories : private or public
897 *
898 * @param array categories
899 * @param string value
900 * @return void
901 */
902function set_cat_status($categories, $value)
903{
904  if (!in_array($value, array('public', 'private')))
905  {
906    return false;
907  }
908
909  // make public a category => all its parent categories become public
910  if ($value == 'public')
911  {
912    $uppercats = get_uppercat_ids($categories);
913    $query = '
914UPDATE '.CATEGORIES_TABLE.'
915  SET status = \'public\'
916  WHERE id IN ('.implode(',', $uppercats).')
917;';
918    pwg_query($query);
919  }
920  // make a category private => all its child categories become private
921  if ($value == 'private')
922  {
923    $subcats = get_subcat_ids($categories);
924    $query = '
925UPDATE '.CATEGORIES_TABLE.'
926  SET status = \'private\'
927  WHERE id IN ('.implode(',', $subcats).')
928;';
929    pwg_query($query);
930  }
931}
932
933/**
934 * returns all uppercats category ids of the given category ids
935 *
936 * @param array cat_ids
937 * @return array
938 */
939function get_uppercat_ids($cat_ids)
940{
941  if (!is_array($cat_ids) or count($cat_ids) < 1)
942  {
943    return array();
944  }
945
946  $uppercats = array();
947
948  $query = '
949SELECT uppercats
950  FROM '.CATEGORIES_TABLE.'
951  WHERE id IN ('.implode(',', $cat_ids).')
952;';
953  $result = pwg_query($query);
954  while ($row = mysql_fetch_array($result))
955  {
956    $uppercats = array_merge($uppercats,
957                             explode(',', $row['uppercats']));
958  }
959  $uppercats = array_unique($uppercats);
960
961  return $uppercats;
962}
963
964/**
965 * set a new random representant to the categories
966 *
967 * @param array categories
968 */
969function set_random_representant($categories)
970{
971  $datas = array();
972  foreach ($categories as $category_id)
973  {
974    $query = '
975SELECT image_id
976  FROM '.IMAGE_CATEGORY_TABLE.'
977  WHERE category_id = '.$category_id.'
978  ORDER BY RAND()
979  LIMIT 0,1
980;';
981    list($representative) = mysql_fetch_array(pwg_query($query));
982    $data = array('id' => $category_id,
983                  'representative_picture_id' => $representative);
984    array_push($datas, $data);
985  }
986
987  $fields = array('primary' => array('id'),
988                  'update' => array('representative_picture_id'));
989  mass_updates(CATEGORIES_TABLE, $fields, $datas);
990}
991
992/**
993 * order categories (update categories.rank and global_rank database fields)
994 *
995 * the purpose of this function is to give a rank for all categories
996 * (insides its sub-category), even the newer that have none at te
997 * beginning. For this, ordering function selects all categories ordered by
998 * rank ASC then name ASC for each uppercat.
999 *
1000 * @returns void
1001 */
1002function ordering()
1003{
1004  $current_rank = 0;
1005  $current_uppercat = '';
1006
1007  $query = '
1008SELECT id, if(id_uppercat is null,\'\',id_uppercat) AS id_uppercat
1009  FROM '.CATEGORIES_TABLE.'
1010  ORDER BY id_uppercat,rank,name
1011;';
1012  $result = pwg_query($query);
1013  $datas = array();
1014  while ($row = mysql_fetch_array($result))
1015  {
1016    if ($row['id_uppercat'] != $current_uppercat)
1017    {
1018      $current_rank = 0;
1019      $current_uppercat = $row['id_uppercat'];
1020    }
1021    $data = array('id' => $row['id'], 'rank' => ++$current_rank);
1022    array_push($datas, $data);
1023  }
1024
1025  $fields = array('primary' => array('id'), 'update' => array('rank'));
1026  mass_updates(CATEGORIES_TABLE, $fields, $datas);
1027}
1028
1029/**
1030 * returns the fulldir for each given category id
1031 *
1032 * @param array cat_ids
1033 * @return array
1034 */
1035function get_fulldirs($cat_ids)
1036{
1037  if (count($cat_ids) == 0)
1038  {
1039    return array();
1040  }
1041
1042  // caching directories of existing categories
1043  $query = '
1044SELECT id, dir
1045  FROM '.CATEGORIES_TABLE.'
1046  WHERE dir IS NOT NULL
1047;';
1048  $result = pwg_query($query);
1049  $cat_dirs = array();
1050  while ($row = mysql_fetch_array($result))
1051  {
1052    $cat_dirs[$row['id']] = $row['dir'];
1053  }
1054
1055  // caching galleries_url
1056  $query = '
1057SELECT id, galleries_url
1058  FROM '.SITES_TABLE.'
1059;';
1060  $result = pwg_query($query);
1061  $galleries_url = array();
1062  while ($row = mysql_fetch_array($result))
1063  {
1064    $galleries_url[$row['id']] = $row['galleries_url'];
1065  }
1066
1067  // categories : id, site_id, uppercats
1068  $categories = array();
1069
1070  $query = '
1071SELECT id, uppercats, site_id
1072  FROM '.CATEGORIES_TABLE.'
1073  WHERE id IN (
1074'.wordwrap(implode(', ', $cat_ids), 80, "\n").')
1075;';
1076  $result = pwg_query($query);
1077  while ($row = mysql_fetch_array($result))
1078  {
1079    array_push($categories, $row);
1080  }
1081
1082  // filling $cat_fulldirs
1083  $cat_fulldirs = array();
1084  foreach ($categories as $category)
1085  {
1086    $uppercats = str_replace(',', '/', $category['uppercats']);
1087    $cat_fulldirs[$category['id']] = $galleries_url[$category['site_id']];
1088    $cat_fulldirs[$category['id']].= preg_replace('/(\d+)/e',
1089                                                  "\$cat_dirs['$1']",
1090                                                  $uppercats);
1091  }
1092
1093  return $cat_fulldirs;
1094}
1095
1096/**
1097 * returns an array with all file system files according to
1098 * $conf['file_ext']
1099 *
1100 * @param string $path
1101 * @param bool recursive
1102 * @return array
1103 */
1104function get_fs($path, $recursive = true)
1105{
1106  global $conf;
1107
1108  // because isset is faster than in_array...
1109  if (!isset($conf['flip_picture_ext']))
1110  {
1111    $conf['flip_picture_ext'] = array_flip($conf['picture_ext']);
1112  }
1113  if (!isset($conf['flip_file_ext']))
1114  {
1115    $conf['flip_file_ext'] = array_flip($conf['file_ext']);
1116  }
1117
1118  $fs['elements'] = array();
1119  $fs['thumbnails'] = array();
1120  $fs['representatives'] = array();
1121  $subdirs = array();
1122
1123  if (is_dir($path))
1124  {
1125    if ($contents = opendir($path))
1126    {
1127      while (($node = readdir($contents)) !== false)
1128      {
1129        if (is_file($path.'/'.$node))
1130        {
1131          $extension = get_extension($node);
1132
1133//          if (in_array($extension, $conf['picture_ext']))
1134          if (isset($conf['flip_picture_ext'][$extension]))
1135          {
1136            if (basename($path) == 'thumbnail')
1137            {
1138              array_push($fs['thumbnails'], $path.'/'.$node);
1139            }
1140            else if (basename($path) == 'pwg_representative')
1141            {
1142              array_push($fs['representatives'], $path.'/'.$node);
1143            }
1144            else
1145            {
1146              array_push($fs['elements'], $path.'/'.$node);
1147            }
1148          }
1149//          else if (in_array($extension, $conf['file_ext']))
1150          else if (isset($conf['flip_file_ext'][$extension]))
1151          {
1152            array_push($fs['elements'], $path.'/'.$node);
1153          }
1154        }
1155        else if (is_dir($path.'/'.$node)
1156                 and $node != '.'
1157                 and $node != '..'
1158                 and $node != 'pwg_high'
1159                 and $recursive)
1160        {
1161          array_push($subdirs, $node);
1162        }
1163      }
1164    }
1165    closedir($contents);
1166
1167    foreach ($subdirs as $subdir)
1168    {
1169      $tmp_fs = get_fs($path.'/'.$subdir);
1170
1171      $fs['elements']        = array_merge($fs['elements'],
1172                                           $tmp_fs['elements']);
1173
1174      $fs['thumbnails']      = array_merge($fs['thumbnails'],
1175                                           $tmp_fs['thumbnails']);
1176
1177      $fs['representatives'] = array_merge($fs['representatives'],
1178                                           $tmp_fs['representatives']);
1179    }
1180  }
1181  return $fs;
1182}
1183
1184/**
1185 * stupidly returns the current microsecond since Unix epoch
1186 */
1187function micro_seconds()
1188{
1189  $t1 = explode(' ', microtime());
1190  $t2 = explode('.', $t1[0]);
1191  $t2 = $t1[1].substr($t2[1], 0, 6);
1192  return $t2;
1193}
1194
1195/**
1196 * synchronize base users list and related users list
1197 *
1198 * compares and synchronizes base users table (USERS_TABLE) with its child
1199 * tables (USER_INFOS_TABLE, USER_ACCESS, USER_CACHE, USER_GROUP) : each
1200 * base user must be present in child tables, users in child tables not
1201 * present in base table must be deleted.
1202 *
1203 * @return void
1204 */
1205function sync_users()
1206{
1207  global $conf;
1208
1209  $query = '
1210SELECT '.$conf['user_fields']['id'].' AS id
1211  FROM '.USERS_TABLE.'
1212;';
1213  $base_users = array_from_query($query, 'id');
1214
1215  $query = '
1216SELECT user_id
1217  FROM '.USER_INFOS_TABLE.'
1218;';
1219  $infos_users = array_from_query($query, 'user_id');
1220
1221  // users present in $base_users and not in $infos_users must be added
1222  $to_create = array_diff($base_users, $infos_users);
1223
1224  if (count($to_create) > 0)
1225  {
1226    $inserts = array();
1227
1228    list($dbnow) = mysql_fetch_row(pwg_query('SELECT NOW();'));
1229
1230    foreach ($to_create as $user_id)
1231    {
1232      $insert = array();
1233      $insert['user_id'] = $user_id;
1234      $insert['status'] = 'guest';
1235      $insert['template'] = $conf['default_template'];
1236      $insert['nb_image_line'] = $conf['nb_image_line'];
1237      $insert['nb_line_page'] = $conf['nb_line_page'];
1238      $insert['language'] = $conf['default_language'];
1239      $insert['recent_period'] = $conf['recent_period'];
1240      $insert['expand'] = boolean_to_string($conf['auto_expand']);
1241      $insert['show_nb_comments'] =
1242        boolean_to_string($conf['show_nb_comments']);
1243      $insert['maxwidth'] = $conf['default_maxwidth'];
1244      $insert['maxheight'] = $conf['default_maxheight'];
1245      $insert['registration_date'] = $dbnow;
1246
1247      array_push($inserts, $insert);
1248    }
1249
1250    mass_inserts(USER_INFOS_TABLE,
1251                 array_keys($inserts[0]),
1252                 $inserts);
1253  }
1254
1255  // users present in user related tables must be present in the base user
1256  // table
1257  $tables =
1258    array(
1259      USER_MAIL_NOTIFICATION_TABLE,
1260      USER_FEED_TABLE,
1261      USER_INFOS_TABLE,
1262      USER_ACCESS_TABLE,
1263      USER_CACHE_TABLE,
1264      USER_GROUP_TABLE
1265      );
1266  foreach ($tables as $table)
1267  {
1268    $query = '
1269SELECT user_id
1270  FROM '.$table.'
1271;';
1272    $to_delete =
1273      array_diff(
1274        array_from_query($query, 'user_id'),
1275        $base_users
1276        );
1277
1278    if (count($to_delete) > 0)
1279    {
1280      $query = '
1281DELETE
1282  FROM '.$table.'
1283  WHERE user_id in ('.implode(',', $to_delete).')
1284;';
1285      pwg_query($query);
1286    }
1287  }
1288}
1289
1290/**
1291 * updates categories.uppercats field based on categories.id +
1292 * categories.id_uppercat
1293 *
1294 * @return void
1295 */
1296function update_uppercats()
1297{
1298  $uppercat_ids = array();
1299
1300  $query = '
1301SELECT id, id_uppercat
1302  FROM '.CATEGORIES_TABLE.'
1303;';
1304  $result = pwg_query($query);
1305  while ($row = mysql_fetch_array($result))
1306  {
1307    $uppercat_ids[$row['id']] =
1308      !empty($row['id_uppercat']) ? $row['id_uppercat'] : 'NULL';
1309  }
1310
1311  // uppercats array associates a category id to the list of uppercats id.
1312  $uppercats = array();
1313
1314  foreach (array_keys($uppercat_ids) as $id)
1315  {
1316    $uppercats[$id] = array();
1317
1318    $uppercat = $id;
1319
1320    while ($uppercat != 'NULL')
1321    {
1322      array_push($uppercats[$id], $uppercat);
1323      $uppercat = $uppercat_ids[$uppercat];
1324    }
1325  }
1326
1327  $datas = array();
1328
1329  foreach ($uppercats as $id => $list)
1330  {
1331    array_push(
1332      $datas,
1333      array(
1334        'id' => $id,
1335        'uppercats' => implode(',', array_reverse($list))
1336        )
1337      );
1338  }
1339
1340  $fields = array('primary' => array('id'), 'update' => array('uppercats'));
1341  mass_updates(CATEGORIES_TABLE, $fields, $datas);
1342}
1343
1344/**
1345 * update images.path field
1346 *
1347 * @return void
1348 */
1349function update_path()
1350{
1351  $images_of = array();
1352 
1353  $query = '
1354SELECT category_id, image_id
1355  FROM '.IMAGE_CATEGORY_TABLE.'
1356  WHERE is_storage = \'true\'
1357;';
1358  $result = pwg_query($query);
1359  while ($row = mysql_fetch_array($result))
1360  {
1361    if (!isset($images_of[ $row['category_id'] ]))
1362    {
1363      $images_of[ $row['category_id'] ] = array();
1364    }
1365
1366    array_push(
1367      $images_of[ $row['category_id'] ],
1368      $row['image_id']
1369      );
1370  }
1371 
1372  $fulldirs = get_fulldirs(
1373    array_keys($images_of)
1374    );
1375
1376  foreach ($images_of as $cat_id => $image_ids)
1377  {
1378    $query = '
1379UPDATE '.IMAGES_TABLE.'
1380  SET path = CONCAT(\''.$fulldirs[$cat_id].'\',\'/\',file)
1381  WHERE id IN ('.
1382      wordwrap(
1383        implode(', ', $image_ids),
1384        80,
1385        "\n").
1386      ')
1387;';
1388    pwg_query($query);
1389  }
1390}
1391
1392/**
1393 * update images.average_rate field
1394 * param int $element_id optional, otherwise applies to all
1395 * @return void
1396 */
1397function update_average_rate( $element_id=-1 )
1398{
1399  $query = '
1400SELECT element_id,
1401       ROUND(AVG(rate),2) AS average_rate
1402  FROM '.RATE_TABLE;
1403  if ( $element_id != -1 )
1404  {
1405    $query .= ' WHERE element_id=' . $element_id;
1406  }
1407  $query .= ' GROUP BY element_id;';
1408
1409  $result = pwg_query($query);
1410
1411  $datas = array();
1412
1413  while ($row = mysql_fetch_array($result))
1414  {
1415    array_push(
1416      $datas,
1417      array(
1418        'id' => $row['element_id'],
1419        'average_rate' => $row['average_rate']
1420        )
1421      );
1422  }
1423
1424  mass_updates(
1425    IMAGES_TABLE,
1426    array(
1427      'primary' => array('id'),
1428      'update' => array('average_rate')
1429      ),
1430    $datas
1431    );
1432
1433  $query='
1434SELECT id FROM '.IMAGES_TABLE .'
1435  LEFT JOIN '.RATE_TABLE.' ON id=element_id
1436  WHERE element_id IS NULL AND average_rate IS NOT NULL';
1437  if ( $element_id != -1 )
1438  {
1439    $query .= ' AND id=' . $element_id;
1440  }
1441  $to_update = array_from_query( $query, 'id');
1442
1443  if ( !empty($to_update) )
1444  {
1445    $query='
1446UPDATE '.IMAGES_TABLE .'
1447  SET average_rate=NULL
1448  WHERE id IN (' . implode(',',$to_update) . ')';
1449    pwg_query($query);
1450  }
1451}
1452
1453/**
1454 * change the parent category of the given categories. The categories are
1455 * supposed virtual.
1456 *
1457 * @param array category identifiers
1458 * @param int parent category identifier
1459 * @return void
1460 */
1461function move_categories($category_ids, $new_parent = -1)
1462{
1463  global $page;
1464
1465  if (count($category_ids) == 0)
1466  {
1467    return;
1468  }
1469
1470  $new_parent = $new_parent < 1 ? 'NULL' : $new_parent;
1471
1472  $categories = array();
1473
1474  $query = '
1475SELECT id, id_uppercat, status, uppercats
1476  FROM '.CATEGORIES_TABLE.'
1477  WHERE id IN ('.implode(',', $category_ids).')
1478;';
1479  $result = pwg_query($query);
1480  while ($row = mysql_fetch_array($result))
1481  {
1482    $categories[$row['id']] =
1483      array(
1484        'parent' => empty($row['id_uppercat']) ? 'NULL' : $row['id_uppercat'],
1485        'status' => $row['status'],
1486        'uppercats' => $row['uppercats']
1487        );
1488  }
1489
1490  // is the movement possible? The movement is impossible if you try to move
1491  // a category in a sub-category or itself
1492  if ('NULL' != $new_parent)
1493  {
1494    $query = '
1495SELECT uppercats
1496  FROM '.CATEGORIES_TABLE.'
1497  WHERE id = '.$new_parent.'
1498;';
1499    list($new_parent_uppercats) = mysql_fetch_row(pwg_query($query));
1500
1501    foreach ($categories as $category)
1502    {
1503      // technically, you can't move a category with uppercats 12,125,13,14
1504      // into a new parent category with uppercats 12,125,13,14,24
1505      if (preg_match('/^'.$category['uppercats'].'/', $new_parent_uppercats))
1506      {
1507        array_push(
1508          $page['errors'],
1509          l10n('You cannot move a category in its own sub category')
1510          );
1511        return;
1512      }
1513    }
1514  }
1515
1516  $tables =
1517    array(
1518      USER_ACCESS_TABLE => 'user_id',
1519      GROUP_ACCESS_TABLE => 'group_id'
1520      );
1521
1522  $query = '
1523UPDATE '.CATEGORIES_TABLE.'
1524  SET id_uppercat = '.$new_parent.'
1525  WHERE id IN ('.implode(',', $category_ids).')
1526;';
1527  pwg_query($query);
1528
1529  update_uppercats();
1530  ordering();
1531  update_global_rank();
1532
1533  // status and related permissions management
1534  if ('NULL' == $new_parent)
1535  {
1536    $parent_status = 'public';
1537  }
1538  else
1539  {
1540    $query = '
1541SELECT status
1542  FROM '.CATEGORIES_TABLE.'
1543  WHERE id = '.$new_parent.'
1544;';
1545    list($parent_status) = mysql_fetch_row(pwg_query($query));
1546  }
1547
1548  if ('private' == $parent_status)
1549  {
1550    foreach ($categories as $cat_id => $category)
1551    {
1552      switch ($category['status'])
1553      {
1554        case 'public' :
1555        {
1556          set_cat_status(array($cat_id), 'private');
1557          break;
1558        }
1559        case 'private' :
1560        {
1561          $subcats = get_subcat_ids(array($cat_id));
1562
1563          foreach ($tables as $table => $field)
1564          {
1565            $query = '
1566SELECT '.$field.'
1567  FROM '.$table.'
1568  WHERE cat_id = '.$cat_id.'
1569;';
1570            $category_access = array_from_query($query, $field);
1571
1572            $query = '
1573SELECT '.$field.'
1574  FROM '.$table.'
1575  WHERE cat_id = '.$new_parent.'
1576;';
1577            $parent_access = array_from_query($query, $field);
1578
1579            $to_delete = array_diff($parent_access, $category_access);
1580
1581            if (count($to_delete) > 0)
1582            {
1583              $query = '
1584DELETE FROM '.$table.'
1585  WHERE '.$field.' IN ('.implode(',', $to_delete).')
1586    AND cat_id IN ('.implode(',', $subcats).')
1587;';
1588              pwg_query($query);
1589            }
1590          }
1591          break;
1592        }
1593      }
1594    }
1595  }
1596
1597  array_push(
1598    $page['infos'],
1599    sprintf(
1600      l10n('%d categories moved'),
1601      count($categories)
1602      )
1603    );
1604}
1605
1606/**
1607 * Returns all destinations of a list of source categories. This function
1608 * solves transitivity.
1609 *
1610 * @param mixed array of category ids, empty for all categories
1611 */
1612function get_destinations($categories = 'all')
1613{
1614  $query = '
1615SELECT source, destination
1616  FROM '.CATEGORIES_LINK_TABLE.'
1617';
1618  $result = pwg_query($query);
1619
1620  $destinations_of = array();
1621 
1622  while ($row = mysql_fetch_array($result))
1623  {
1624    if (!isset($destinations_of[ $row['source'] ]))
1625    {
1626      $destinations_of[ $row['source'] ] = array();
1627    }
1628
1629    array_push(
1630      $destinations_of[ $row['source'] ],
1631      $row['destination']
1632      );
1633  }
1634
1635  // transitivity resolution: if " => " means "source of", if A=>B=>C
1636  // implies A=>B and A=>C. So A has 2 destinations: B and C.
1637  do
1638  {
1639    // let's suppose we only need a single turn
1640    $need_new_turn = false;
1641   
1642    foreach ($destinations_of as $source => $destinations)
1643    {
1644      foreach ($destinations as $destination)
1645      {
1646        // does the current destination has destinations itself?
1647        if (isset($destinations_of[$destination]))
1648        {
1649          // are there destinations of current destination not already among
1650          // destinations of the current source? (advise: take a piece of
1651          // paper and draw a schema). The source itself must not be counted
1652          // as a destination, thus avoiding cyclic links.
1653          $missing_destinations = array_diff(
1654            $destinations_of[$destination],
1655            $destinations,
1656            array($source) // no cyclic link
1657            );
1658         
1659          if (count($missing_destinations) > 0)
1660          {
1661            $destinations_of[$source] = array_unique(
1662              array_merge(
1663                $destinations,
1664                $missing_destinations
1665                )
1666              );
1667
1668            // a category has a least one new destination, we have to check
1669            // one more time that it doesn't generate more destinations
1670            $need_new_turn = true;
1671          }
1672        }
1673      }
1674    }
1675  } while ($need_new_turn);
1676
1677  if (is_array($categories))
1678  {
1679    $filtered_destinations_of = array();
1680
1681    // Even if there is no destinations for the requested categories, we
1682    // return empty arrays
1683    foreach ($categories as $category)
1684    {
1685      $filtered_destinations_of[$category] = array();
1686    }
1687   
1688    foreach ($destinations_of as $source => $destinations)
1689    {
1690      if (in_array($source, $categories))
1691      {
1692        $filtered_destinations_of[$source] = $destinations;
1693      }
1694    }
1695
1696    return $filtered_destinations_of;
1697  }
1698  else
1699  {
1700    return $destinations_of;
1701  }
1702}
1703
1704/**
1705 * Returns all sources of a list of destination categories. This function
1706 * solves transitivity.
1707 *
1708 * @param mixed array of category ids, empty for all categories
1709 */
1710function get_sources($categories = 'all')
1711{
1712  $destinations_of = get_destinations();
1713
1714  $sources_of = array();
1715 
1716  foreach ($destinations_of as $source => $destinations)
1717  {
1718    foreach ($destinations as $destination)
1719    {
1720      if (!isset($sources_of[$destination]))
1721      {
1722        $sources_of[$destination] = array();
1723      }
1724
1725      array_push($sources_of[$destination], $source);
1726    }
1727  }
1728 
1729  // eventually, filter
1730  if (is_array($categories))
1731  {
1732    $filtered_sources_of = array();
1733
1734    // Even if there is no sources for the requested categories, we return
1735    // empty arrays
1736    foreach ($categories as $category)
1737    {
1738      $filtered_sources_of[$category] = array();
1739    }
1740   
1741    foreach ($sources_of as $destination => $sources)
1742    {
1743      if (in_array($destination, $categories))
1744      {
1745        $filtered_sources_of[$destination] = $sources;
1746      }
1747    }
1748
1749    return $filtered_sources_of;
1750  }
1751  else
1752  {
1753    return $sources_of;
1754  }
1755}
1756
1757/**
1758 * Checks categories links are respected for a given list of destinations.
1759 *
1760 * Checking categories links means that each destination must be associated
1761 * to the images of its sources.
1762 *
1763 * @param mixed source category ids
1764 */
1765function check_links($destinations = 'all')
1766{
1767  $sources_of = get_sources($destinations);
1768
1769  if (empty($sources_of))
1770  {
1771    return true;
1772  }
1773
1774  // we need to search images of all sources and destinations
1775  $images_of = array();
1776
1777  foreach ($sources_of as $destination => $sources)
1778  {
1779    $images_of[$destination] = array();
1780
1781    foreach ($sources as $source)
1782    {
1783      $images_of[$source] = array();
1784    }
1785  }
1786 
1787  $query = '
1788SELECT image_id, category_id
1789  FROM '.IMAGE_CATEGORY_TABLE.'
1790  WHERE category_id IN ('.
1791    implode(',', array_keys($images_of)).
1792    ')
1793;';
1794  $result = pwg_query($query);
1795
1796  while ($row = mysql_fetch_array($result))
1797  {
1798    array_push(
1799      $images_of[ $row['category_id'] ],
1800      $row['image_id']
1801      );
1802  }
1803
1804  $inserts = array();
1805 
1806  foreach ($sources_of as $destination => $sources)
1807  {
1808    // merge all images from the sources of this destination
1809    $sources_images = array();
1810   
1811    foreach ($sources as $source)
1812    {
1813      $sources_images = array_merge(
1814        $sources_images,
1815        $images_of[$source]
1816        );
1817    }
1818
1819    $sources_images = array_unique($sources_images);
1820
1821    // are there images among the sources that are not linked to the
1822    // destination?
1823    $missing_images = array_diff(
1824      $sources_images,
1825      $images_of[$destination]
1826      );
1827
1828    // if we find missing images (missing links in reality), we prepare the
1829    // final mass_inserts
1830    if (count($missing_images) > 0)
1831    {
1832      foreach ($missing_images as $missing_image)
1833      {
1834        array_push(
1835          $inserts,
1836          array(
1837            'category_id' => $destination,
1838            'image_id'    => $missing_image,
1839            )
1840          );
1841      }
1842    }
1843  }
1844
1845  if (count($inserts) > 0)
1846  {
1847    mass_inserts(
1848      IMAGE_CATEGORY_TABLE,
1849      array_keys($inserts[0]),
1850      $inserts
1851      );
1852  }
1853}
1854
1855/**
1856 * Based on categories links, delete image_category links on destinations.
1857 *
1858 * The rule is the following: if an image belong to the category and to the
1859 * source, we suppose it comes from the source. If the source/destination
1860 * link is broken, we delete the image/category link if the only origin of
1861 * the link was the broken categories link.
1862 *
1863 * Example: "=>" means "source of". Between brackets the associated images.
1864 *
1865 * A (1,2,9) => \
1866 *               |=> C (1,2,3,4,5,9) => D (1,2,3,4,5,6,9)
1867 * B (3,4,9) => /
1868 *
1869 * In category C, we suppose (1,2) come from A, (3,4) from B, 9 from A or B
1870 * and 5 was manually added. In category D, 6 was added manually.
1871 *
1872 * If we break A=>C, C and D loose (1,2) but not 9 because it can come from
1873 * B. If we break C=>D, D loose (3,4,5,9) but not 6 because it was
1874 * associated manually to 9.
1875 *
1876 * Warning: only virtual links can be removed, physical links are protected.
1877 *
1878 * @param int destination
1879 * @param array sources
1880 */
1881function delete_sources($destination, $sources)
1882{
1883  // if no sources to unlink, we stop with OK status
1884  if (count($sources) == 0)
1885  {
1886    return true;
1887  }
1888
1889  $query = '
1890DELETE
1891  FROM '.CATEGORIES_LINK_TABLE.'
1892  WHERE destination = '.$destination.'
1893    AND source IN ('.implode(',', $sources).')
1894;';
1895  pwg_query($query);
1896 
1897  // The strategy is the following:
1898  //
1899  // * first we brutally delete the image/category associations on
1900  // destinations categories for all images belonging to sources.
1901  //
1902  // * then we check_links on destinations to rebuild missing image/category
1903  // associations.
1904
1905  // what are the images associated to the sources to unlink
1906  $query = '
1907SELECT image_id
1908  FROM '.IMAGE_CATEGORY_TABLE.'
1909  WHERE category_id IN ('.
1910    implode(',', $sources).
1911    ')
1912;';
1913  $sources_images = array_unique(
1914    array_from_query($query, 'image_id')
1915    );
1916
1917  if (count($sources_images) == 0)
1918  {
1919    return true;
1920  }
1921
1922  // retrieve all direct and indirect destinations of the current
1923  // destination
1924  $destinations_of = get_destinations(array($destination));
1925
1926  $destinations = array_merge(
1927    array($destination),
1928    $destinations_of[$destination]
1929    );
1930 
1931  // unlink sources images from destinations
1932  $query = '
1933DELETE
1934  FROM '.IMAGE_CATEGORY_TABLE.'
1935  WHERE category_id IN ('.implode(',', $destinations).')
1936    AND image_id IN ('.implode(',', $sources_images).')
1937    AND is_storage = \'false\'
1938;';
1939  pwg_query($query);
1940
1941  // if the representative thumbnail of destinations was a picture from
1942  // $sources_images, we request a new random representant
1943  $query = '
1944SELECT id, representative_picture_id
1945  FROM '.CATEGORIES_TABLE.'
1946  WHERE id IN ('.implode(',', $destinations).')
1947;';
1948  $result = pwg_query($query);
1949
1950  $request_random = array();
1951 
1952  while ($row = mysql_fetch_array($result))
1953  {
1954    if (isset($row['representative_picture_id']))
1955    {
1956      if (in_array($row['representative_picture_id'], $sources_images))
1957      {
1958        array_push($request_random, $row['id']);
1959      }
1960    }
1961  }
1962
1963  set_random_representant($request_random);
1964
1965  // eventually, we check_links to rebuild missing associations
1966  check_links($destinations);
1967
1968  return true;
1969}
1970
1971/**
1972 * create a virtual category
1973 *
1974 * @param string category name
1975 * @param int parent category id
1976 * @return array with ('info' and 'id') or ('error') key
1977 */
1978function create_virtual_category($category_name, $parent_id=null)
1979{
1980  global $conf;
1981 
1982  // is the given category name only containing blank spaces ?
1983  if (preg_match('/^\s*$/', $category_name))
1984  {
1985    return array('error' => l10n('cat_error_name'));
1986  }
1987       
1988  $parent_id = !empty($parent_id) ? $parent_id : 'NULL';
1989
1990  $query = '
1991SELECT MAX(rank)
1992  FROM '.CATEGORIES_TABLE.'
1993  WHERE id_uppercat '.(is_numeric($parent_id) ? '= '.$parent_id : 'IS NULL').'
1994;';
1995  list($current_rank) = mysql_fetch_array(pwg_query($query));
1996 
1997  $insert = array(
1998    'name' => $category_name,
1999    'rank' => ++$current_rank,
2000    'commentable' => $conf['newcat_default_commentable'],
2001    'uploadable' => 'false',
2002    );
2003   
2004  if ($parent_id != 'NULL')
2005  {
2006    $query = '
2007SELECT id, uppercats, global_rank, visible, status
2008  FROM '.CATEGORIES_TABLE.'
2009  WHERE id = '.$parent_id.'
2010;';
2011    $parent = mysql_fetch_array(pwg_query($query));
2012
2013    $insert{'id_uppercat'} = $parent{'id'};
2014    $insert{'global_rank'} = $parent{'global_rank'}.'.'.$insert{'rank'};
2015   
2016    // at creation, must a category be visible or not ? Warning : if the
2017    // parent category is invisible, the category is automatically create
2018    // invisible. (invisible = locked)
2019    if ('false' == $parent['visible'])
2020    {
2021      $insert{'visible'} = 'false';
2022    }
2023    else
2024    {
2025      $insert{'visible'} = $conf['newcat_default_visible'];
2026    }
2027   
2028    // at creation, must a category be public or private ? Warning : if the
2029    // parent category is private, the category is automatically create
2030    // private.
2031    if ('private' == $parent['status'])
2032    {
2033      $insert{'status'} = 'private';
2034    }
2035    else
2036    {
2037      $insert{'status'} = $conf['newcat_default_status'];
2038    }
2039  }
2040  else
2041  {
2042    $insert{'visible'} = $conf['newcat_default_visible'];
2043    $insert{'status'} = $conf['newcat_default_status'];
2044    $insert{'global_rank'} = $insert{'rank'};
2045  }
2046
2047  // we have then to add the virtual category
2048  mass_inserts(
2049    CATEGORIES_TABLE,
2050    array(
2051      'site_id', 'name', 'id_uppercat', 'rank', 'commentable',
2052      'uploadable', 'visible', 'status', 'global_rank',
2053      ),
2054    array($insert)
2055    );
2056
2057  $inserted_id = mysql_insert_id();
2058
2059  $query = '
2060UPDATE
2061  '.CATEGORIES_TABLE.'
2062  SET uppercats = \''.
2063    (isset($parent) ? $parent{'uppercats'}.',' : '').
2064    $inserted_id.
2065    '\'
2066  WHERE id = '.$inserted_id.'
2067;';
2068  pwg_query($query);
2069 
2070  return array(
2071    'info' => l10n('cat_virtual_added'),
2072    'id'   => $inserted_id,
2073    );
2074}
2075?>
Note: See TracBrowser for help on using the repository browser.