Overview

Namespaces

  • Charcoal
    • Admin
      • Widget
        • Cms
    • Cms
      • Config
      • Mixin
        • Traits
      • Route
      • Section
      • Service
        • Loader
        • Manager
      • ServiceProvider
      • Support
        • Helpers
        • Interfaces
        • Traits
    • Property

Classes

  • Charcoal\Admin\Widget\Cms\HierarchicalSectionTableWidget
  • Charcoal\Admin\Widget\Cms\SectionTableWidget
  • Charcoal\Cms\AbstractDocument
  • Charcoal\Cms\AbstractEvent
  • Charcoal\Cms\AbstractFaq
  • Charcoal\Cms\AbstractImage
  • Charcoal\Cms\AbstractNews
  • Charcoal\Cms\AbstractSection
  • Charcoal\Cms\AbstractText
  • Charcoal\Cms\AbstractVideo
  • Charcoal\Cms\Config
  • Charcoal\Cms\Config\CmsConfig
  • Charcoal\Cms\Config\EventConfig
  • Charcoal\Cms\Config\NewsConfig
  • Charcoal\Cms\Config\SectionConfig
  • Charcoal\Cms\Document
  • Charcoal\Cms\DocumentCategory
  • Charcoal\Cms\EmptySection
  • Charcoal\Cms\Event
  • Charcoal\Cms\EventCategory
  • Charcoal\Cms\ExternalSection
  • Charcoal\Cms\Faq
  • Charcoal\Cms\FaqCategory
  • Charcoal\Cms\Image
  • Charcoal\Cms\ImageCategory
  • Charcoal\Cms\News
  • Charcoal\Cms\NewsCategory
  • Charcoal\Cms\Route\EventRoute
  • Charcoal\Cms\Route\GenericRoute
  • Charcoal\Cms\Route\NewsRoute
  • Charcoal\Cms\Route\SectionRoute
  • Charcoal\Cms\Section
  • Charcoal\Cms\Section\BlocksSection
  • Charcoal\Cms\Section\ContentSection
  • Charcoal\Cms\Service\Loader\AbstractLoader
  • Charcoal\Cms\Service\Loader\EventLoader
  • Charcoal\Cms\Service\Loader\NewsLoader
  • Charcoal\Cms\Service\Loader\SectionLoader
  • Charcoal\Cms\Service\Manager\AbstractManager
  • Charcoal\Cms\Service\Manager\EventManager
  • Charcoal\Cms\Service\Manager\NewsManager
  • Charcoal\Cms\ServiceProvider\CmsServiceProvider
  • Charcoal\Cms\Support\Helpers\DateHelper
  • Charcoal\Cms\Tag
  • Charcoal\Cms\Text
  • Charcoal\Cms\TextCategory
  • Charcoal\Cms\Video
  • Charcoal\Cms\VideoCategory
  • Charcoal\Property\TemplateOptionsProperty
  • Charcoal\Property\TemplateProperty

Interfaces

  • Charcoal\Cms\DocumentInterface
  • Charcoal\Cms\EventInterface
  • Charcoal\Cms\FaqInterface
  • Charcoal\Cms\ImageInterface
  • Charcoal\Cms\MetatagInterface
  • Charcoal\Cms\Mixin\HasContentBlocksInterface
  • Charcoal\Cms\NewsInterface
  • Charcoal\Cms\SearchableInterface
  • Charcoal\Cms\SectionInterface
  • Charcoal\Cms\Support\Interfaces\EventManagerAwareInterface
  • Charcoal\Cms\Support\Interfaces\NewsManagerAwareInterface
  • Charcoal\Cms\Support\Interfaces\SectionLoaderAwareInterface
  • Charcoal\Cms\TemplateableInterface
  • Charcoal\Cms\TextInterface
  • Charcoal\Cms\VideoInterface

Traits

  • Charcoal\Admin\Widget\Cms\SectionTableTrait
  • Charcoal\Cms\MetatagTrait
  • Charcoal\Cms\Mixin\Traits\HasContentBlocksTrait
  • Charcoal\Cms\SearchableTrait
  • Charcoal\Cms\Support\Traits\DateHelperAwareTrait
  • Charcoal\Cms\Support\Traits\EventManagerAwareTrait
  • Charcoal\Cms\Support\Traits\NewsManagerAwareTrait
  • Charcoal\Cms\Support\Traits\SectionLoaderAwareTrait
  • Charcoal\Cms\TemplateableTrait
  • Overview
  • Namespace
  • Class
  1: <?php
  2: 
  3: namespace Charcoal\Cms;
  4: 
  5: use InvalidArgumentException;
  6: 
  7: // From 'charcoal-core'
  8: use Charcoal\Model\Collection;
  9: use Charcoal\Loader\CollectionLoader;
 10: 
 11: // From 'charcoal-object'
 12: use Charcoal\Object\Content;
 13: use Charcoal\Object\HierarchicalInterface;
 14: use Charcoal\Object\HierarchicalTrait;
 15: use Charcoal\Object\RoutableInterface;
 16: use Charcoal\Object\RoutableTrait;
 17: 
 18: // From 'charcoal-translator'
 19: use Charcoal\Translator\Translation;
 20: 
 21: // From 'charcoal-cms'
 22: use Charcoal\Cms\MetatagInterface;
 23: use Charcoal\Cms\SearchableInterface;
 24: use Charcoal\Cms\SectionInterface;
 25: use Charcoal\Cms\TemplateableInterface;
 26: 
 27: /**
 28:  * A Section is a unique, reachable page.
 29:  *
 30:  * ## Types of sections
 31:  * There can be different types of section. 4 exists in the CMS module:
 32:  * - `blocks`
 33:  * - `content`
 34:  * - `empty`
 35:  * - `external`
 36:  *
 37:  * ## External implementations
 38:  * Sections implement the following _Interface_ / _Trait_:
 39:  * - From the `Charcoal\Object` namespace (in `charcoal-base`)
 40:  *   - `Hierarchical`
 41:  *   - `Routable`
 42:  * - From the local `Charcoal\Cms` namespace
 43:  *   - `Metatag`
 44:  *   - `Searchable`
 45:  *
 46:  */
 47: abstract class AbstractSection extends Content implements
 48:     HierarchicalInterface,
 49:     MetatagInterface,
 50:     RoutableInterface,
 51:     SearchableInterface,
 52:     SectionInterface,
 53:     TemplateableInterface
 54: {
 55:     use HierarchicalTrait;
 56:     use MetatagTrait;
 57:     use RoutableTrait;
 58:     use SearchableTrait;
 59:     use TemplateableTrait;
 60: 
 61:     const TYPE_BLOCKS = 'charcoal/cms/section/blocks-section';
 62:     const TYPE_CONTENT = 'charcoal/cms/section/content-section';
 63:     const TYPE_EMPTY = 'charcoal/cms/section/empty-section';
 64:     const TYPE_EXTERNAL = 'charcoal/cms/section/external-section';
 65:     const DEFAULT_TYPE = self::TYPE_CONTENT;
 66: 
 67:     /**
 68:      * @var string
 69:      */
 70:     private $sectionType = self::DEFAULT_TYPE;
 71: 
 72:     /**
 73:      * @var Translation|string|null
 74:      */
 75:     private $title;
 76: 
 77:     /**
 78:      * @var Translation|string|null
 79:      */
 80:     private $subtitle;
 81: 
 82:     /**
 83:      * @var Translation|string|null
 84:      */
 85:     private $content;
 86: 
 87:     /**
 88:      * @var Translation|string|null
 89:      */
 90:     private $image;
 91: 
 92:     /**
 93:      * The menus this object is shown in.
 94:      *
 95:      * @var string[]
 96:      */
 97:     protected $inMenu;
 98: 
 99:     /**
100:      * @var array
101:      */
102:     protected $keywords;
103: 
104:     /**
105:      * @var Translation|string $summary
106:      */
107:     protected $summary;
108: 
109:     /**
110:      * @var string $externalUrl
111:      */
112:     protected $externalUrl;
113: 
114:     /**
115:      * @var boolean $locked
116:      */
117:     protected $locked;
118: 
119:     // ==========================================================================
120:     // INIT
121:     // ==========================================================================
122: 
123:     /**
124:      * Section constructor.
125:      * @param array $data Init data.
126:      */
127:     public function __construct(array $data = null)
128:     {
129:         parent::__construct($data);
130: 
131:         if (is_callable([ $this, 'defaultData' ])) {
132:             $this->setData($this->defaultData());
133:         }
134:     }
135: 
136:     // ==========================================================================
137:     // FUNCTIONS
138:     // ==========================================================================
139: 
140:     /**
141:      * Determine if the object can be deleted.
142:      *
143:      * @return boolean
144:      */
145:     public function isDeletable()
146:     {
147:         return !!$this->id() && !$this->locked();
148:     }
149: 
150:     /**
151:      * Retrieve the object's title.
152:      *
153:      * @return string
154:      */
155:     public function hierarchicalLabel()
156:     {
157:         return str_repeat('— ', ($this->hierarchyLevel() - 1)).$this->title();
158:     }
159: 
160:     /**
161:      * HierarchicalTrait > loadChildren
162:      *
163:      * @return \ArrayAccess|\Traversable
164:      */
165:     public function loadChildren()
166:     {
167:         $loader = new CollectionLoader([
168:             'logger'  => $this->logger,
169:             'factory' => $this->modelFactory()
170:         ]);
171:         $loader->setModel($this);
172:         $loader->addFilter([
173:             'property' => 'master',
174:             'val'      => $this->id()
175:         ]);
176:         $loader->addFilter([
177:             'property' => 'active',
178:             'val'      => true
179:         ]);
180: 
181:         $loader->addOrder([
182:             'property' => 'position',
183:             'mode'     => 'asc'
184:         ]);
185: 
186:         return $loader->load();
187:     }
188: 
189:     // ==========================================================================
190:     // SETTERS
191:     // ==========================================================================
192: 
193:     /**
194:      * Set the section's type.
195:      *
196:      * @param  string $type The section type.
197:      * @throws InvalidArgumentException If the section type is not a string or not a valid section type.
198:      * @return self
199:      */
200:     public function setSectionType($type)
201:     {
202:         if (!is_string($type)) {
203:             throw new InvalidArgumentException(
204:                 'Section type must be a string'
205:             );
206:         }
207: 
208:         $this->sectionType = $type;
209: 
210:         return $this;
211:     }
212: 
213:     /**
214:      * Set the menus this object belongs to.
215:      *
216:      * @param  string|string[] $menu One or more menu identifiers.
217:      * @return self
218:      */
219:     public function setInMenu($menu)
220:     {
221:         $this->inMenu = $menu;
222: 
223:         return $this;
224:     }
225: 
226:     /**
227:      * Set the object's keywords.
228:      *
229:      * @param  string|string[] $keywords One or more entries.
230:      * @return self
231:      */
232:     public function setKeywords($keywords)
233:     {
234:         $this->keywords = $this->parseAsMultiple($keywords);
235: 
236:         return $this;
237:     }
238: 
239:     /**
240:      * @param Translation|string|null $summary The summary.
241:      * @return self
242:      */
243:     public function setSummary($summary)
244:     {
245:         $this->summary = $this->translator()->translation($summary);
246: 
247:         return $this;
248:     }
249: 
250:     /**
251:      * @param Translation|string|null $externalUrl The external url.
252:      * @return self
253:      */
254:     public function setExternalUrl($externalUrl)
255:     {
256:         $this->externalUrl = $this->translator()->translation($externalUrl);
257: 
258:         return $this;
259:     }
260: 
261:     /**
262:      * Section is locked when you can't change the URL
263:      * @param boolean $locked Prevent new route creation about that object.
264:      * @return self
265:      */
266:     public function setLocked($locked)
267:     {
268:         $this->locked = $locked;
269: 
270:         return $this;
271:     }
272: 
273:     /**
274:      * @param Translation|string|null $title The section title (localized).
275:      * @return self
276:      */
277:     public function setTitle($title)
278:     {
279:         $this->title = $this->translator()->translation($title);
280: 
281:         return $this;
282:     }
283: 
284:     /**
285:      * @param Translation|string|null $subtitle The section subtitle (localized).
286:      * @return self
287:      */
288:     public function setSubtitle($subtitle)
289:     {
290:         $this->subtitle = $this->translator()->translation($subtitle);
291: 
292:         return $this;
293:     }
294: 
295:     /**
296:      * @param Translation|string|null $content The section content (localized).
297:      * @return self
298:      */
299:     public function setContent($content)
300:     {
301:         $this->content = $this->translator()->translation($content);
302: 
303:         return $this;
304:     }
305: 
306:     /**
307:      * @param mixed $image The section main image (localized).
308:      * @return self
309:      */
310:     public function setImage($image)
311:     {
312:         $this->image = $this->translator()->translation($image);
313: 
314:         return $this;
315:     }
316: 
317:     // ==========================================================================
318:     // GETTERS
319:     // ==========================================================================
320: 
321:     /**
322:      * Retrieve the section's type.
323:      *
324:      * @return string
325:      */
326:     public function sectionType()
327:     {
328:         return $this->sectionType;
329:     }
330: 
331:     /**
332:      * @return Translation|string|null
333:      */
334:     public function title()
335:     {
336:         return $this->title;
337:     }
338: 
339:     /**
340:      * @return Translation|string|null
341:      */
342:     public function subtitle()
343:     {
344:         return $this->subtitle;
345:     }
346: 
347:     /**
348:      * @return Translation|string|null
349:      */
350:     public function content()
351:     {
352:         return $this->content;
353:     }
354: 
355:     /**
356:      * @return Translation|string|null
357:      */
358:     public function image()
359:     {
360:         return $this->image;
361:     }
362: 
363:     /**
364:      * Retrieve the menus this object belongs to.
365:      *
366:      * @return Translation|string|null
367:      */
368:     public function inMenu()
369:     {
370:         return $this->inMenu;
371:     }
372: 
373:     /**
374:      * Retrieve the object's keywords.
375:      *
376:      * @return string[]
377:      */
378:     public function keywords()
379:     {
380:         return $this->keywords;
381:     }
382: 
383:     /**
384:      * HierarchicalTrait > loadChildren
385:      *
386:      * @return Translation|string|null
387:      */
388:     public function summary()
389:     {
390:         return $this->summary;
391:     }
392: 
393:     /**
394:      * @return Translation|string|null
395:      */
396:     public function externalUrl()
397:     {
398:         return $this->externalUrl;
399:     }
400: 
401:     /**
402:      * @return boolean Or Null.
403:      */
404:     public function locked()
405:     {
406:         return $this->locked;
407:     }
408: 
409:     // ==========================================================================
410:     // DEFAULT META
411:     // ==========================================================================
412: 
413:     /**
414:      * MetatagTrait > canonicalUrl
415:      *
416:      * @todo
417:      * @return string
418:      */
419:     public function canonicalUrl()
420:     {
421:         return $this->url();
422:     }
423: 
424:     /**
425:      * @return Translation|string|null
426:      */
427:     public function defaultMetaTitle()
428:     {
429:         return $this->title();
430:     }
431: 
432:     /**
433:      * @return Translation|string|null
434:      */
435:     public function defaultMetaDescription()
436:     {
437:         $content = $this->translator()->translation($this->content());
438:         if ($content instanceof Translation) {
439:             $desc = [];
440:             foreach ($content->data() as $lang => $text) {
441:                 $desc[$lang] = strip_tags($text);
442:             }
443: 
444:             return $this->translator()->translation($desc);
445:         }
446: 
447:         return null;
448:     }
449: 
450:     /**
451:      * @return Translation|string|null
452:      */
453:     public function defaultMetaImage()
454:     {
455:         return $this->image();
456:     }
457: 
458:     // ==========================================================================
459:     // Utils
460:     // ==========================================================================
461: 
462:     /**
463:      * Parse the property value as a "multiple" value type.
464:      *
465:      * @param  mixed                    $value     The value being converted to an array.
466:      * @param  string|PropertyInterface $separator The boundary string.
467:      * @return array
468:      */
469:     public function parseAsMultiple($value, $separator = ',')
470:     {
471:         if (!isset($value) ||
472:             (is_string($value) && !strlen(trim($value))) ||
473:             (is_array($value) && !count(array_filter($value, 'strlen')))
474:         ) {
475:             return [];
476:         }
477: 
478:         /**
479:          * This property is marked as "multiple".
480:          * Manually handling the resolution to array
481:          * until the property itself manages this.
482:          */
483:         if (is_string($value)) {
484:             return explode($separator, $value);
485:         }
486: 
487:         /**
488:          * If the parameter isn't an array yet,
489:          * means we might be dealing with an integer,
490:          * an empty string, or an object.
491:          */
492:         if (!is_array($value)) {
493:             return [ $value ];
494:         }
495: 
496:         return $value;
497:     }
498: 
499:     // ==========================================================================
500:     // EVENTS
501:     // ==========================================================================
502: 
503:     /**
504:      * Route generated on postSave in case
505:      * it contains the ID of the section, which
506:      * you only get once you have save
507:      *
508:      * @return boolean
509:      */
510:     public function postSave()
511:     {
512:         // RoutableTrait
513:         if (!$this->locked()) {
514:             $this->generateObjectRoute($this->slug());
515:         }
516: 
517:         return parent::postSave();
518:     }
519: 
520:     /**
521:      * Check whatever before the update.
522:      *
523:      * @param  array|null $properties Properties.
524:      * @return boolean
525:      */
526:     public function postUpdate(array $properties = null)
527:     {
528:         if (!$this->locked()) {
529:             $this->generateObjectRoute($this->slug());
530:         }
531: 
532:         return parent::postUpdate($properties);
533:     }
534: 
535:     /**
536:      * {@inheritdoc}
537:      *
538:      * @return boolean
539:      */
540:     public function preSave()
541:     {
542:         if (!$this->locked()) {
543:             $this->setSlug($this->generateSlug());
544:         }
545: 
546:         return parent::preSave();
547:     }
548: 
549:     /**
550:      * {@inheritdoc}
551:      *
552:      * @param array $properties Optional properties to update.
553:      * @return boolean
554:      */
555:     public function preUpdate(array $properties = null)
556:     {
557:         if (!$this->locked()) {
558:             $this->setSlug($this->generateSlug());
559:         }
560: 
561:         return parent::preUpdate($properties);
562:     }
563: 
564:     /**
565:      * Event called before _deleting_ the object.
566:      *
567:      * @see    \Charcoal\Model\AbstractModel::preDelete() For the "delete" Event.
568:      * @return boolean
569:      */
570:     public function preDelete()
571:     {
572:         if ($this->locked()) {
573:             return false;
574:         }
575:         // Routable trait
576:         // Remove all unnecessary routes.
577:         $this->deleteObjectRoutes();
578: 
579:         return parent::preDelete();
580:     }
581: }
582: 
API documentation generated by ApiGen