1 <?php
2
3 namespace Icybee\Modules\Taxonomy\Vocabulary;
4
5 use ICanBoogie\Event;
6 use ICanBoogie\I18n;
7
8 use Brickrouge\Element;
9 use Brickrouge\Form;
10 use Brickrouge\Text;
11
12 use Icybee\Modules\Views\ActiveRecordProvider;
13 use Icybee\Modules\Views\Collection as ViewsCollection;
14 use Icybee\Modules\Views\Provider;
15
16 class Hooks
17 {
18 static private $cache_vocabularies = array();
19 static private $cache_record_terms = array();
20 static private $cache_record_properties = array();
21
22 static public function get_term(\ICanBoogie\Object\PropertyEvent $event, \Icybee\Modules\Nodes\Node $target)
23 {
24 global $core;
25
26 $property = $event->property;
27 $cache_record_properties_key = spl_object_hash($target) . '_' . $property;
28
29 if (isset(self::$cache_record_properties[$cache_record_properties_key]))
30 {
31 $event->value = self::$cache_record_properties[$cache_record_properties_key];
32 $event->stop();
33
34 return;
35 }
36
37 $constructor = $target->constructor;
38 $siteid = $target->siteid;
39 $nid = $target->nid;
40
41 $use_slug = false;
42 $vocabularyslug = $property;
43
44 if (substr($property, -4, 4) === 'slug')
45 {
46 $use_slug = true;
47 $vocabularyslug = substr($property, 0, -4);
48 }
49
50 $cache_key = $siteid . '>' . $constructor . '>' . $vocabularyslug;
51 $cache_record_terms_key = $cache_key . '>' . $nid;
52
53 if (!isset(self::$cache_record_terms[$cache_record_terms_key]))
54 {
55
56
57
58
59 if (!isset(self::$cache_vocabularies[$cache_key]))
60 {
61 self::$cache_vocabularies[$cache_key] = $core->models['taxonomy.vocabulary']
62 ->joins(':taxonomy.vocabulary/scopes')
63 ->where('siteid = 0 OR siteid = ?', $target->siteid)
64 ->filter_by_constructor((string) $constructor)
65 ->filter_by_vocabularyslug($vocabularyslug)
66 ->order('siteid DESC')
67 ->one;
68 }
69
70 $vocabulary = self::$cache_vocabularies[$cache_key];
71
72 if (!$vocabulary)
73 {
74 return;
75 }
76
77 if ($vocabulary->is_required)
78 {
79 $event->value = 'uncategorized';
80 }
81
82 $terms = $vocabulary->terms;
83 $rc = null;
84
85 if ($vocabulary->is_multiple || $vocabulary->is_tags)
86 {
87 foreach ($terms as $term)
88 {
89 if (empty($term->nodes_keys[$nid]))
90 {
91 continue;
92 }
93
94 $rc[] = $term;
95 }
96 }
97 else
98 {
99 foreach ($terms as $term)
100 {
101 if (empty($term->nodes_keys[$nid]))
102 {
103 continue;
104 }
105
106 $rc = $term;
107
108 break;
109 }
110 }
111
112 self::$cache_record_terms[$cache_record_terms_key] = $rc === null ? false : $rc;
113 }
114
115 $rc = self::$cache_record_terms[$cache_record_terms_key];
116
117 if ($rc === false)
118 {
119 return;
120 }
121
122 if ($use_slug)
123 {
124 if (is_array($rc))
125 {
126 $terms = $rc;
127 $rc = array();
128
129 foreach ($terms as $term)
130 {
131 $rc[] = $term->termslug;
132 }
133 }
134 else
135 {
136 $rc = $rc->termslug;
137 }
138 }
139
140 self::$cache_record_properties[$cache_record_properties_key] = $rc;
141
142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158
159
160 $target->$property = $rc;
161
162 $event->value = $rc;
163 $event->stop();
164 }
165
166 static public function on_nodes_editblock_alter_children(Event $event, \Icybee\Modules\Nodes\EditBlock $block)
167 {
168 global $core;
169
170 $document = $core->document;
171
172 $document->css->add(DIR . 'public/support.css');
173 $document->js->add(DIR . 'public/support.js');
174
175 $vocabularies = $core->models['taxonomy.vocabulary']
176 ->joins('INNER JOIN {self}__scopes USING(vid)')
177 ->where('constructor = ? AND (siteid = 0 OR siteid = ?)', (string) $event->module, $core->site_id)
178 ->order('weight')
179 ->all;
180
181
182
183
184 $terms_model = $core->models['taxonomy.terms'];
185 $nodes_model = $core->models['taxonomy.terms/nodes'];
186
187 $nid = $event->key;
188 $identifier_base = 'vocabulary[vid]';
189 $children = &$event->children;
190
191 foreach ($vocabularies as $vocabulary)
192 {
193 $vid = $vocabulary->vid;;
194
195 $identifier = $identifier_base . '[' . $vid . ']';
196
197 if ($vocabulary->is_multiple)
198 {
199 $options = $terms_model->select('term, count(nid)')
200 ->joins('inner join {self}__nodes using(vtid)')
201 ->filter_by_vid($vid)
202 ->group('term')->order('term')->pairs;
203
204 $value = $nodes_model->select('term')
205 ->filter_by_vid_and_nid($vid, $nid)
206 ->order('term')
207 ->all(\PDO::FETCH_COLUMN);
208 $value = implode(', ', $value);
209
210 $label = $vocabulary->vocabulary;
211
212 $children[] = new Element
213 (
214 'div', array
215 (
216 Form::LABEL => $label,
217
218 Element::GROUP => 'organize',
219 Element::WEIGHT => 100,
220
221 Element::CHILDREN => array
222 (
223 new Text
224 (
225 array
226 (
227 'value' => $value,
228 'name' => $identifier
229 )
230 ),
231
232 new CloudElement
233 (
234 'ul', array
235 (
236 Element::OPTIONS => $options,
237 'class' => 'cloud'
238 )
239 )
240 ),
241
242 'class' => 'taxonomy-tags widget-bordered'
243 )
244 );
245 }
246 else
247 {
248 $options = $terms_model->select('term.vtid, term')->filter_by_vid($vid)->order('term')->pairs;
249
250 if (!$options)
251 {
252
253 }
254
255 $value = $nodes_model->select('term_node.vtid')->filter_by_vid_and_nid($vid, $nid)->order('term')->rc;
256
257 $edit_url = $core->site->path . '/admin/taxonomy.vocabulary/' . $vocabulary->vid . '/edit';
258
259 $children[$identifier] = new Element
260 (
261 'select', array
262 (
263 Form::LABEL => $vocabulary->vocabulary,
264 Element::GROUP => 'organize',
265 Element::OPTIONS => array(null => '') + $options,
266 Element::REQUIRED => $vocabulary->is_required,
267 Element::INLINE_HELP => '<a href="' . $edit_url . '">' . I18n\t('Edit the vocabulary <q>!vocabulary</q>', array('!vocabulary' => $vocabulary->vocabulary)) . '</a>.',
268
269 'value' => $value
270 )
271 );
272 }
273 }
274
275
276
277
278 $event->attributes[Element::GROUPS]['organize'] = array
279 (
280 'title' => 'Organization',
281 'weight' => 500
282 );
283 }
284
285 static public function on_node_save(\ICanBoogie\Operation\ProcessEvent $event, \Icybee\Modules\Nodes\SaveOperation $target)
286 {
287 global $core;
288
289 $name = 'vocabulary';
290 $request = $event->request;
291 $vocabularies = $request[$name];
292
293 if (!$vocabularies)
294 {
295 return;
296 }
297
298 $nid = $event->rc['key'];
299 $vocabularies = $vocabularies['vid'];
300
301
302
303
304
305 $vocabulary_model = $core->models['taxonomy.vocabulary'];
306 $terms_model = $core->models['taxonomy.terms'];
307 $nodes_model = $core->models['taxonomy.terms/nodes'];
308
309 $nodes_model->where('nid = ?', $nid)->delete();
310
311
312
313
314
315 foreach ($vocabularies as $vid => $values)
316 {
317 if (!$values)
318 {
319 continue;
320 }
321
322 $vocabulary = $vocabulary_model[$vid];
323
324 if ($vocabulary->is_tags)
325 {
326
327
328
329
330
331
332 $terms = explode(',', $values);
333 $terms = array_map('trim', $terms);
334
335 $values = array();
336
337 foreach ($terms as $term)
338 {
339 $vtid = $terms_model->select('vtid')->where('vid = ? and term = ?', $vid, $term)->rc;
340
341
342
343 if (!$vtid)
344 {
345 $vtid = $terms_model->save
346 (
347 array
348 (
349 'vid' => $vid,
350 'term' => $term
351 )
352 );
353 }
354
355 $values[] = $vtid;
356 }
357 }
358
359 foreach ((array) $values as $vtid)
360 {
361 $nodes_model->insert
362 (
363 array
364 (
365 'vtid' => $vtid,
366 'nid' => $nid
367 ),
368
369 array
370 (
371 'ignore' => true
372 )
373 );
374 }
375 }
376 }
377
378 static public function on_collect_views(ViewsCollection\CollectEvent $event, ViewsCollection $target)
379 {
380 global $core;
381
382 $vocabulary = $core->models['taxonomy.vocabulary']->all;
383 $collection = &$event->collection;
384
385 foreach ($vocabulary as $v)
386 {
387 $scope = $v->scope;
388 $vocabulary_name = $v->vocabulary;
389 $vocabulary_slug = $v->vocabularyslug;
390
391 foreach ($scope as $constructor)
392 {
393 $view_home = $constructor . '/home';
394 $view_home = isset($collection[$view_home]) ? $collection[$view_home] : null;
395
396 $view_list = $constructor . '/list';
397 $view_list = isset($collection[$view_list]) ? $collection[$view_list] : null;
398
399 if ($view_home)
400 {
401 $collection["$constructor/vocabulary/$vocabulary_slug/vocabulary-home"] = array
402 (
403 'title' => 'Home for vocabulary %name',
404 'title args' => array('name' => $v->vocabulary),
405 'taxonomy vocabulary' => $v
406 )
407
408 + $view_home;
409 }
410
411 if ($view_list)
412 {
413 $collection["$constructor/vocabulary/$vocabulary_slug/list"] = array
414 (
415 'title' => 'Records list, in vocabulary %vocabulary and a term',
416 'title args' => array('vocabulary' => $vocabulary_name),
417 'taxonomy vocabulary' => $v
418 )
419
420 + $view_list;
421 }
422
423 foreach ($v->terms as $term)
424 {
425 $term_name = $term->term;
426 $term_slug = $term->termslug;
427
428 if ($view_home)
429 {
430 $collection["$constructor/vocabulary/$vocabulary_slug/$term_slug/home"] = array
431 (
432 'title' => 'Records home, in vocabulary %vocabulary and term %term',
433 'title args' => array('vocabulary' => $vocabulary_name, 'term' => $term_name),
434 'taxonomy vocabulary' => $v,
435 'taxonomy term' => $term,
436 )
437
438 + $view_home;
439 }
440
441 if ($view_list)
442 {
443 $collection["$constructor/vocabulary/$vocabulary_slug/$term_slug/list"] = array
444 (
445 'title' => 'Records list, in vocabulary %vocabulary and term %term',
446 'title args' => array('vocabulary' => $vocabulary_name, 'term' => $term_name),
447 'taxonomy vocabulary' => $v,
448 'taxonomy term' => $term
449 )
450
451 + $view_list;
452 }
453 }
454 }
455 }
456 }
457
458 static public function on_alter_provider_query(\Icybee\Modules\Views\ActiveRecordProvider\AlterQueryEvent $event, \Icybee\Modules\Views\ActiveRecordProvider $provider)
459 {
460 global $core;
461
462
463
464 $options = $event->view->options;
465
466 if (isset($options['taxonomy vocabulary']) && isset($options['taxonomy term']))
467 {
468 return self::for_vocabulary_and_term($event, $provider, $options, $options['taxonomy vocabulary'], $options['taxonomy term']);
469 }
470
471 if (empty($event->view->options['taxonomy vocabulary']))
472 {
473 return;
474 }
475
476 $vocabulary = $event->view->options['taxonomy vocabulary'];
477 $condition = $vocabulary->vocabularyslug . 'slug';
478
479
480
481
482
483
484
485
486
487 if (empty($event->conditions[$condition]))
488 {
489
490
491 $event->view->range['limit'] = null;
492
493 $core->events->attach(array(__CLASS__, 'on_alter_provider_result'));
494
495 return;
496 }
497
498 $condition_value = $event->conditions[$condition];
499
500 $term = $core->models['taxonomy.terms']->where('vid = ? AND termslug = ?', array($vocabulary->vid, $condition_value))->order('term.weight')->one;
501
502 $core->events->attach(function(ActiveRecordProvider\AlterContextEvent $event, ActiveRecordProvider $target) use($term) {
503
504 $event->context['term'] = $term;
505
506 });
507
508 $event->query->where('nid IN (SELECT nid FROM {prefix}taxonomy_terms
509 INNER JOIN {prefix}taxonomy_terms__nodes USING(vtid) WHERE vtid = ?)', $term ? $term->vtid : 0);
510
511
512
513 global $core;
514
515 $page = isset($core->request->context->page) ? $core->request->context->page : null;
516
517 if ($page && $term)
518 {
519 $page->title = \ICanBoogie\format($page->title, array(':term' => $term->term));
520 }
521 }
522
523 static public function on_alter_provider_result(\Icybee\Modules\Views\ActiveRecordProvider\AlterResultEvent $event, \Icybee\Modules\Views\ActiveRecordProvider $provider)
524 {
525 global $core;
526
527 $vocabulary = $event->view->options['taxonomy vocabulary'];
528
529 $ids = '';
530 $records_by_id = array();
531
532 foreach ($event->result as $record)
533 {
534 if (!($record instanceof \Icybee\Modules\Nodes\Node))
535 {
536 537 538 539 540 541 542 543 544 545 546
547
548 continue;
549 }
550
551 $nid = $record->nid;
552 $ids .= ',' . $nid;
553 $records_by_id[$nid] = $record;
554 }
555
556 if (!$ids)
557 {
558 return;
559 }
560
561 $ids = substr($ids, 1);
562
563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589
590
591 $ids_by_vtid = $core->models['taxonomy.terms/nodes']
592 ->joins(':nodes')
593 ->select('vtid, nid')
594 ->order('term.weight, term.term')
595 ->where('vid = ? AND nid IN(' . $ids . ')', $vocabulary->vid)
596 ->all(\PDO::FETCH_GROUP | \PDO::FETCH_COLUMN);
597
598 $terms = $core->models['taxonomy.terms']->find(array_keys($ids_by_vtid));
599
600 $result = array();
601
602 foreach ($ids_by_vtid as $vtid => $ids)
603 {
604 $result[$vtid]['term'] = $terms[$vtid];
605 $result[$vtid]['nodes'] = array_intersect_key($records_by_id, array_combine($ids, $ids));
606 }
607
608 $event->result = $result;
609 }
610
611 static private function for_vocabulary_and_term(Event $event, Provider $provider, $options, \Icybee\Modules\Taxonomy\Vocabulary\Vocabulary $vocabulary, \Icybee\Modules\Taxonomy\Terms\Term $term)
612 {
613 $event->query->where('nid IN (SELECT nid FROM {prefix}taxonomy_terms
614 INNER JOIN {prefix}taxonomy_terms__nodes USING(vtid) WHERE vtid = ?)', $term ? $term->vtid : 0);
615
616
617
618 619 620 621 622 623 624 625 626
627 }
628
629 static public function before_breadcrumb_render_inner_html(\Icybee\Modules\Pages\BreadcrumbElement\BeforeRenderInnerHTMLEvent $event, \Icybee\Modules\Pages\BreadcrumbElement $target)
630 {
631 foreach ($event->slices as &$slice)
632 {
633 if (strpos($slice['label'], ':term') === false || empty($event->page->node)) continue;
634
635 $slice['label'] = \ICanBoogie\format($slice['label'], array('term' => (string) $event->page->node->category));
636 }
637 }
638 }