source: extensions/SmartAlbums/include/functions.inc.php @ 19446

Last change on this file since 19446 was 19446, checked in by mistic100, 11 years ago
  • add regex for phot name, author
  • add dimensions filter
  • rewrite javascript algorithms
  • add auto update on timeout (default 3 days)
  • display photos count on plugin albums list
File size: 16.9 KB
Line 
1<?php
2if (!defined('PHPWG_ROOT_PATH')) die('Hacking attempt!');
3
4/*
5 * Associates images to the category according to the filters
6 * @param int category_id
7 * @return array
8 */
9function smart_make_associations($cat_id)
10{
11  $query = '
12DELETE FROM '.IMAGE_CATEGORY_TABLE.'
13  WHERE
14    category_id = '.$cat_id.'
15    AND smart = true
16;';
17  pwg_query($query);
18 
19  $images = smart_get_pictures($cat_id);
20 
21  if (count($images) != 0)
22  {
23    foreach ($images as $img)
24    {
25      $datas[] = array(
26        'image_id' => $img,
27        'category_id' => $cat_id,
28        'smart' => true,
29        );
30    }
31    mass_inserts(
32      IMAGE_CATEGORY_TABLE, 
33      array_keys($datas[0]), 
34      $datas,
35      array('ignore'=>true)
36      );
37  }
38 
39  // representant, try to not overwrite if still in images list
40  $query = '
41SELECT representative_picture_id
42  FROM '.CATEGORIES_TABLE.'
43  WHERE id = '.$cat_id.'
44;';
45  list($rep_id) = pwg_db_fetch_row(pwg_query($query));
46 
47  if ( !in_array($rep_id, $images) )
48  {
49    include_once(PHPWG_ROOT_PATH.'admin/include/functions.php');
50    set_random_representant(array($cat_id));
51  }
52 
53  $query = '
54UPDATE '.CATEGORIES_TABLE.'
55  SET smart_update = NOW()
56  WHERE id = '.$cat_id.'
57;';
58  pwg_query($query);
59 
60  return $images;
61}
62
63
64/*
65 * Make associations for all SmartAlbums
66 * Called with invalidate_user_cache
67 */
68function smart_make_all_associations()
69{
70  global $conf;
71 
72  if ( defined('SMART_NOT_UPDATE') OR !$conf['SmartAlbums']['update_on_upload'] ) return;
73 
74  // get categories with smart filters
75  $query = '
76SELECT DISTINCT id
77  FROM '.CATEGORIES_TABLE.' AS c
78    INNER JOIN '.CATEGORY_FILTERS_TABLE.' AS cf
79    ON c.id = cf.category_id
80;';
81 
82  // regenerate photo list
83  $smart_cats = array_from_query($query, 'id');
84  array_map('smart_make_associations', $smart_cats);
85}
86
87
88/*
89 * Generates the list of images, according to the filters of the category
90 * @param int category_id
91 * @param array filters, if null => catch from db
92 * @return array
93 */
94function smart_get_pictures($cat_id, $filters = null)
95{
96  global $conf;
97
98  /* get filters */
99  if ($filters === null)
100  {
101    $query = '
102SELECT *
103  FROM '.CATEGORY_FILTERS_TABLE.'
104  WHERE category_id = '.$cat_id.'
105  ORDER BY type ASC, cond ASC
106;';
107    $result = pwg_query($query);
108   
109    if (!pwg_db_num_rows($result)) return array();
110   
111    $filters = array();
112    while ($row = pwg_db_fetch_assoc($result))
113    {
114      $filters[] = array(
115        'type' => $row['type'],
116        'cond' => $row['cond'],
117        'value' => $row['value'],
118        );
119    }
120  }
121  else if (!count($filters))
122  {
123    return array();
124  }
125 
126  $mode = 'and';
127 
128  /* build constrains */
129  ## generate 'join', 'where' arrays and 'limit' string to create the SQL query
130  ## inspired by PicsEngine 3 by Michael Villar
131  $i_tags = 1;
132  foreach ($filters as $filter)
133  {
134    switch ($filter['type'])
135    {
136      // tags
137      case 'tags':
138      {
139        switch ($filter['cond'])
140        {
141          // search images which have all tags
142          case 'all':
143          {
144            $tags_arr = explode(',', $filter['value']);
145            foreach($tags_arr as $value)
146            {
147              $join[] = IMAGE_TAG_TABLE.' AS it'.$i_tags.' ON i.id = it'.$i_tags.'.image_id';
148              $where[] = 'it'.$i_tags.'.tag_id = '.$value;
149              $i_tags++;
150            }
151           
152            break;
153          }
154          // search images which tags are in the list
155          case 'one':
156          {
157            $join[] = IMAGE_TAG_TABLE.' AS it'.$i_tags.' ON i.id = it'.$i_tags.'.image_id';
158            $where[] = 'it'.$i_tags.'.tag_id IN ('.$filter['value'].')';
159            $i_tags++;
160           
161            break;
162          }
163          // exclude images which tags are in the list
164          case 'none':
165          {
166            $sub_query = '
167      SELECT it'.$i_tags.'.image_id
168        FROM '.IMAGE_TAG_TABLE.' AS it'.$i_tags.'
169        WHERE
170          it'.$i_tags.'.image_id = i.id AND
171          it'.$i_tags.'.tag_id IN ('.$filter['value'].')
172        GROUP BY it'.$i_tags.'.image_id
173      ';
174            $join[] = IMAGE_TAG_TABLE.' AS it'.$i_tags.' ON i.id = it'.$i_tags.'.image_id';
175            $where[] = 'NOT EXISTS ('.$sub_query.')';
176            $i_tags++;
177           
178            break;
179          }
180          // exclude images which tags are not in the list and search images which have all tags
181          case 'only':
182          {
183            $sub_query = '
184      SELECT it'.$i_tags.'.image_id
185        FROM '.IMAGE_TAG_TABLE.' AS it'.$i_tags.'
186        WHERE
187          it'.$i_tags.'.image_id = i.id AND
188          it'.$i_tags.'.tag_id NOT IN ('.$filter['value'].')
189        GROUP BY it'.$i_tags.'.image_id
190      ';
191            $join[] = IMAGE_TAG_TABLE.' AS it'.$i_tags.' ON i.id = it'.$i_tags.'.image_id';
192            $where[] = 'NOT EXISTS ('.$sub_query.')';
193            $i_tags++;
194           
195            $tags_arr = explode(',', $filter['value']);
196            foreach($tags_arr as $value)
197            {
198              $join[] = IMAGE_TAG_TABLE.' AS it'.$i_tags.' ON i.id = it'.$i_tags.'.image_id';
199              $where[] = 'it'.$i_tags.'.tag_id = '.$value;
200              $i_tags++;
201            }
202           
203            break;
204          }
205        }
206       
207        break;
208      }
209   
210      // date
211      case 'date':
212      {
213        switch ($filter['cond'])
214        {
215          case 'the_post':
216            $where[] = 'date_available BETWEEN "'.$filter['value'].' 00:00:00" AND "'.$filter['value'].' 23:59:59"';
217            break;
218          case 'before_post':
219            $where[] = 'date_available < "'.$filter['value'].' 00:00:00"';
220            break;
221          case 'after_post':
222            $where[] = 'date_available > "'.$filter['value'].' 23:59:59"';
223            break;
224          case 'the_taken':
225            $where[] = 'date_creation BETWEEN "'.$filter['value'].' 00:00:00" AND "'.$filter['value'].' 23:59:59"';
226            break;
227          case 'before_taken':
228            $where[] = 'date_creation < "'.$filter['value'].' 00:00:00"';
229            break;
230          case 'after_taken':
231            $where[] = 'date_creation > "'.$filter['value'].' 23:59:59"';
232            break;
233        }
234       
235        break;
236      }
237     
238      // name
239      case 'name':
240      {
241        switch ($filter['cond'])
242        {
243          case 'contain':
244            $where[] = 'name LIKE "%'.$filter['value'].'%"';
245            break;
246          case 'begin':
247            $where[] = 'name LIKE "'.$filter['value'].'%"';
248            break;
249          case 'end':
250            $where[] = 'name LIKE "%'.$filter['value'].'"';
251            break;
252          case 'not_contain':
253            $where[] = 'name NOT LIKE "%'.$filter['value'].'%"';
254            break;
255          case 'not_begin':
256            $where[] = 'name NOT LIKE "'.$filter['value'].'%"';
257            break;
258          case 'not_end':
259            $where[] = 'name NOT LIKE "%'.$filter['value'].'"';
260            break;
261          case 'regex':
262            $where[] = 'name REGEXP "'.$filter['value'].'"';
263            break;
264        }
265       
266        break;
267      }
268     
269      // album
270      case 'album':
271      {
272        switch ($filter['cond'])
273        {
274          // search images existing in all albums
275          case 'all':
276          {
277            $albums_arr = explode(',', $filter['value']);
278            foreach($albums_arr as $value)
279            {
280              $sub_query = '
281      SELECT image_id
282        FROM '.IMAGE_CATEGORY_TABLE.'
283        WHERE category_id = '.$value.'
284      ';
285              $where[] = 'i.id IN ('.$sub_query.')';
286            }
287           
288            break;
289          }
290          // search images existing in one of these albums
291          case 'one':
292          {
293            $sub_query = '
294      SELECT image_id
295        FROM '.IMAGE_CATEGORY_TABLE.'
296        WHERE category_id IN('.$filter['value'].')
297      ';
298            $where[] = 'i.id IN ('.$sub_query.')';
299           
300            break;
301          }
302          // exclude images existing in one of these albums
303          case 'none':
304          {
305            $sub_query = '
306      SELECT image_id
307        FROM '.IMAGE_CATEGORY_TABLE.'
308        WHERE category_id IN('.$filter['value'].')
309      ';
310            $where[] = 'i.id NOT IN ('.$sub_query.')';
311           
312            break;
313          }
314          // exclude images existing on other albums, and search images existing in all albums
315          case 'only':
316          {
317            $sub_query = '
318      SELECT image_id
319        FROM '.IMAGE_CATEGORY_TABLE.'
320        WHERE category_id NOT IN('.$filter['value'].')
321      ';
322            $where[] = 'i.id NOT IN ('.$sub_query.')';
323           
324            $albums_arr = explode(',', $filter['value']);
325            foreach($albums_arr as $value)
326            {
327              $sub_query = '
328      SELECT image_id
329        FROM '.IMAGE_CATEGORY_TABLE.'
330        WHERE category_id = '.$value.'
331      ';
332              $where[] = 'i.id IN ('.$sub_query.')';
333            }
334           
335            break;
336          }
337        }
338       
339        break;
340      }
341     
342      // dimensions
343      case 'dimensions':
344      {
345        $filter['value'] = explode(',', $filter['value']);
346       
347        switch ($filter['cond'])
348        {
349          case 'width':
350            $where[] = 'width >= '.$filter['value'][0].' AND width <= '.$filter['value'][1];
351            break;
352          case 'height':
353            $where[] = 'height >= '.$filter['value'][0].' AND height <= '.$filter['value'][1];
354            break;
355          case 'ratio':
356            $where[] = 'width/height >= '.$filter['value'][0].' AND width/height < '.($filter['value'][1]+0.01);
357            break;
358        }
359      }
360     
361      // author
362      case 'author':
363      {
364        switch ($filter['cond'])
365        {
366          case 'is':
367            if ($filter['value'] != 'NULL') $filter['value'] = '"'.$filter['value'].'"';
368            $where[] = 'author = '.$filter['value'].'';
369            break;
370          case 'not_is':
371            if ($filter['value'] != 'NULL') $filter['value'] = '"'.$filter['value'].'"';
372            $where[] = 'author != '.$filter['value'].'';
373            break;
374          case 'in':
375            $filter['value'] = '"'.str_replace(',', '","', $filter['value']).'"';
376            $where[] = 'author IN('.$filter['value'].')';
377            break;
378          case 'not_in':
379            $filter['value'] = '"'.str_replace(',', '","', $filter['value']).'"';
380            $where[] = 'author NOT IN('.$filter['value'].')';
381            break;
382          case 'regex':
383            $where[] = 'author REGEXP "'.$filter['value'].'"';
384            break;
385        }
386       
387        break;
388      }
389     
390      // hit
391      case 'hit':
392      {
393        switch ($filter['cond'])
394        {
395          case 'less':
396            $where[] = 'hit < '.$filter['value'].'';
397            break;
398          case 'more':
399            $where[] = 'hit >= '.$filter['value'].'';
400            break;
401        }
402       
403        break;
404      }
405     
406      // rating_score
407      case 'rating_score':
408      {
409        switch ($filter['cond'])
410        {
411          case 'less':
412            $where[] = 'rating_score < '.$filter['value'].'';
413            break;
414          case 'more':
415            $where[] = 'rating_score >= '.$filter['value'].'';
416            break;
417        }
418       
419        break;
420      }
421     
422      // level
423      case 'level':
424      {
425        $where[] = 'level = '.$filter['value'].'';
426        break;
427      }
428     
429      // limit
430      case 'limit':
431      {
432        $limit = '0, '.$filter['value'];
433        break;
434      }
435     
436      // mode
437      case 'mode':
438      {
439        $mode = $filter['value'];
440        break;
441      }
442    }
443  }
444 
445  /* bluid query */
446  $MainQuery = '
447SELECT i.id
448  FROM '.IMAGES_TABLE.' AS i';
449   
450    if (isset($join))
451    {
452      $MainQuery.= '
453    LEFT JOIN '.implode("\n    LEFT JOIN ", $join);
454    }
455    if (isset($where))
456    {
457      $MainQuery.= '
458  WHERE
459    '.implode("\n    ".$mode." ", $where);
460    }
461
462  $MainQuery.= '
463  GROUP BY i.id
464  '.$conf['order_by'].'
465  '.(isset($limit) ? "LIMIT ".$limit : null).'
466;';
467
468  if (defined('SMART_DEBUG'))
469  {
470    file_put_contents(SMART_PATH.'dump_filters.txt', print_r($filters, true));
471    file_put_contents(SMART_PATH.'dump_query.sql', $MainQuery);
472  }
473 
474  return array_from_query($MainQuery, 'id');
475}
476
477
478/**
479 * Check if the filter is proper
480 * @param array filter
481 * @return array or false
482 */
483function smart_check_filter($filter)
484{
485  global $page, $limit_is_set, $level_is_set;
486  $error = false;
487 
488  if (!isset($limit_is_set)) $limit_is_set = false;
489  if (!isset($level_is_set)) $level_is_set = false;
490 
491  switch ($filter['type'])
492  {
493    # tags
494    case 'tags':
495    {
496      if ($filter['value'] == null)
497      {
498        $error = true;
499        array_push($page['errors'], l10n('No tag selected'));
500      }
501      else
502      {
503        $filter['value'] = implode(',', get_tag_ids($filter['value']));
504      }
505      break;
506    }
507    # date
508    case 'date':
509    {
510      if (!preg_match('#([0-9]{4})-([0-9]{2})-([0-9]{2})#', $filter['value']))
511      {
512        $error = true;
513        array_push($page['errors'], l10n('Date string is malformed'));
514      }
515      break;
516    }
517    # name
518    case 'name':
519    {
520      if (empty($filter['value']))
521      {
522        $error = true;
523        array_push($page['errors'], l10n('Name is empty'));
524      }
525      else if ( $filter['cond']=='regex' and @preg_match('/'.$filter['value'].'/', null)===false )
526      {
527        $error = true;
528        array_push($page['errors'], l10n('Regex is malformed'));
529      }
530      break;
531    }
532    # album
533    case 'album':
534    {
535      if (@$filter['value'] == null)
536      {
537        $error = true;
538        array_push($page['errors'], l10n('No album selected'));
539      }
540      else
541      {
542        $filter['value'] = implode(',', $filter['value']);
543      }
544      break;
545    }
546    # dimensions
547    case 'dimensions':
548    {
549      if ( empty($filter['value']['min']) or empty($filter['value']['max']) )
550      {
551        $error = true;
552      }
553      else
554      {
555        $filter['value'] = $filter['value']['min'].','.$filter['value']['max'];
556      }
557      break;
558    }
559    # author
560    case 'author':
561    {
562      if (empty($filter['value']))
563      {
564        $error = true;
565        array_push($page['errors'], l10n('Author is empty'));
566      }
567      else if ( $filter['cond']=='regex' and @preg_match('/'.$filter['value'].'/', null)===false )
568      {
569        $error = true;
570        array_push($page['errors'], l10n('Regex is malformed'));
571      }
572      else
573      {
574        $filter['value'] = preg_replace('#([ ]?),([ ]?)#', ',', $filter['value']);
575      }
576      break;
577    }
578    # hit
579    case 'hit':
580    {
581      if (!preg_match('#([0-9]{1,})#', $filter['value']))
582      {
583        $error = true;
584        array_push($page['errors'], l10n('Hits must be an integer'));
585      }
586      break;
587    }
588    # rating_score
589    case 'rating_score':
590    {
591      if (!preg_match('#([0-9]{1,})#', $filter['value']))
592      {
593        $error = true;
594        array_push($page['errors'], l10n('Rating score must be an integer'));
595      }
596      break;
597    }
598    # level
599    case 'level':
600    {
601      if ($level_is_set == true) // only one level is allowed, first is saved
602      {
603        $error = true;
604        array_push($page['errors'], l10n('You can\'t use more than one level filter'));
605      }
606      else
607      {
608        $filter['cond'] = 'level';
609        $level_is_set = true;
610      }
611      break;
612    }
613    # limit
614    case 'limit':
615    {
616      if ($limit_is_set == true) // only one limit is allowed, first is saved
617      {
618        $error = true;
619        array_push($page['errors'], l10n('You can\'t use more than one limit filter'));
620      }
621      else if (!preg_match('#([0-9]{1,})#', $filter['value']))
622      {
623        $error = true;
624        array_push($page['errors'], l10n('Limit must be an integer'));
625      }
626      else 
627      {
628        $filter['cond'] = 'level';
629        $limit_is_set = true;
630      }
631      break;
632    }
633    # mode
634    case 'mode':
635    {
636      $filter['cond'] = 'mode';
637      break;
638    }
639   
640    default:
641    {
642      $error = true;
643      break;
644    }
645  }
646 
647 
648  if ($error == false)
649  {
650    return $filter;
651  }
652  else
653  {
654    return false;
655  }
656}
657
658
659/**
660 * clean table when categories are deleted
661 */
662function smart_delete_categories($ids)
663{
664  $query = '
665DELETE FROM '.CATEGORY_FILTERS_TABLE.'
666  WHERE category_id IN('.implode(',', $ids).')
667;';
668  pwg_query($query);
669}
670
671/**
672 * update images list periodically
673 */
674function smart_periodic_update()
675{
676  global $conf;
677 
678  // we only search for old albums every hour, nevermind which user is connected
679  if ($conf['SmartAlbums']['last_update'] > time() - 3600) return;
680 
681  $conf['SmartAlbums']['last_update'] = time();
682  conf_update_param('SmartAlbums', serialize($conf['SmartAlbums']));
683 
684  // get categories with smart filters
685  $query = '
686SELECT DISTINCT id
687  FROM '.CATEGORIES_TABLE.' AS c
688    INNER JOIN '.CATEGORY_FILTERS_TABLE.' AS cf
689    ON c.id = cf.category_id
690  WHERE smart_update < DATE_SUB(NOW(), INTERVAL '.$conf['SmartAlbums']['update_timeout'].' DAY)
691;';
692 
693  // regenerate photo list
694  $smart_cats = array_from_query($query, 'id');
695  array_map('smart_make_associations', $smart_cats);
696}
697
698?>
Note: See TracBrowser for help on using the repository browser.