From c3120a1fb772e758f9ca28031e5e8088914d2ec1 Mon Sep 17 00:00:00 2001 From: Jackson Date: Mon, 30 Jul 2018 10:16:03 +1200 Subject: [PATCH] Initial module logic --- .editorconfig | 29 ++ .gitattributes | 5 + .scrutinizer.yml | 15 + .travis.yml | 36 ++ README.md | 241 +++++++++++- _config/announce.yml | 6 + composer.json | 37 ++ src/Announcements.php | 225 +++++++++++ src/Extension/ControllerExtension.php | 45 +++ src/Interfaces/AnnouncementStoreInterface.php | 26 ++ src/Model/Action.php | 320 ++++++++++++++++ src/Model/ActionList.php | 50 +++ src/Model/Announcement.php | 348 ++++++++++++++++++ src/Store/SessionStore.php | 58 +++ templates/CodeCraft/Announce/Model/Action.ss | 5 + .../CodeCraft/Announce/Model/Announcement.ss | 21 ++ tests/AnnouncementTest.php | 10 + tests/AnnouncementsTest.php | 10 + 18 files changed, 1486 insertions(+), 1 deletion(-) create mode 100644 .editorconfig create mode 100644 .gitattributes create mode 100644 .scrutinizer.yml create mode 100644 .travis.yml create mode 100644 _config/announce.yml create mode 100644 composer.json create mode 100644 src/Announcements.php create mode 100644 src/Extension/ControllerExtension.php create mode 100644 src/Interfaces/AnnouncementStoreInterface.php create mode 100644 src/Model/Action.php create mode 100644 src/Model/ActionList.php create mode 100644 src/Model/Announcement.php create mode 100644 src/Store/SessionStore.php create mode 100644 templates/CodeCraft/Announce/Model/Action.ss create mode 100644 templates/CodeCraft/Announce/Model/Announcement.ss create mode 100644 tests/AnnouncementTest.php create mode 100644 tests/AnnouncementsTest.php diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 0000000..9f76bfa --- /dev/null +++ b/.editorconfig @@ -0,0 +1,29 @@ +# For more information about the properties used in +# this file, please see the EditorConfig documentation: +# http://editorconfig.org/ + +root = true + +[*] +charset = utf-8 +end_of_line = lf +indent_size = 4 +indent_style = space +insert_final_newline = true +trim_trailing_whitespace = true + +[*.md] +trim_trailing_whitespace = false + +[*.yml] +indent_size = 2 +indent_style = space + +[*.{yml,json}] +# The indent size used in the `package.json` file cannot be changed +# https://github.com/npm/npm/pull/3180#issuecomment-16336516 +indent_size = 2 +indent_style = space + +[composer.json] +indent_size = 4 diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000..4965489 --- /dev/null +++ b/.gitattributes @@ -0,0 +1,5 @@ +/tests export-ignore +/.gitattributes export-ignore +/.gitignore export-ignore +/.travis.yml export-ignore +/.scrutinizer.yml export-ignore diff --git a/.scrutinizer.yml b/.scrutinizer.yml new file mode 100644 index 0000000..051ef9a --- /dev/null +++ b/.scrutinizer.yml @@ -0,0 +1,15 @@ +inherit: true + +build: + nodes: + analysis: + tests: + override: [php-scrutinizer-run] + +checks: + php: + code_rating: true + duplication: true + +filter: + paths: [src/*, tests/*] diff --git a/.travis.yml b/.travis.yml new file mode 100644 index 0000000..ecc5917 --- /dev/null +++ b/.travis.yml @@ -0,0 +1,36 @@ +language: php + +dist: trusty + +env: + global: + - COMPOSER_ROOT_VERSION=4.1.x-dev + +matrix: + include: + - php: 5.6 + env: DB=MYSQL RECIPE_VERSION=1.0.x-dev PHPCS_TEST=1 PHPUNIT_TEST=1 + - php: 7.0 + env: DB=MYSQL RECIPE_VERSION=1.1.x-dev PHPUNIT_TEST=1 + - php: 7.1 + env: DB=MYSQL RECIPE_VERSION=4.2.x-dev PHPUNIT_COVERAGE_TEST=1 + - php: 7.2 + env: DB=MYSQL RECIPE_VERSION=4.x-dev PHPUNIT_TEST=1 + +before_script: + # Init PHP + - phpenv rehash + - phpenv config-rm xdebug.ini + + # Install composer dependencies + - composer validate + - composer require --no-update silverstripe/recipe-cms:"$RECIPE_VERSION" + - composer install --prefer-dist --no-interaction --no-progress --no-suggest --optimize-autoloader --verbose --profile + +script: + - if [[ $PHPUNIT_TEST ]]; then vendor/bin/phpunit tests/php; fi + - if [[ $PHPUNIT_COVERAGE_TEST ]]; then phpdbg -qrr vendor/bin/phpunit --coverage-clover=coverage.xml tests/php; fi + - if [[ $PHPCS_TEST ]]; then vendor/bin/phpcs src/ tests/; fi + +after_success: + - if [[ $PHPUNIT_COVERAGE_TEST ]]; then bash <(curl -s https://codecov.io/bash) -f coverage.xml; fi diff --git a/README.md b/README.md index b0a45e9..035b655 100644 --- a/README.md +++ b/README.md @@ -1,2 +1,241 @@ # silverstripe-announce -Display announcement messages for SilverStripe + +Announce things to the page controller & view in SilverStripe. Can be used to power plain messages, modals, alerts, and callouts. + +Multiple announcements can be stacked. + +Announcements can be templated to match component libraries such as Bootstrap. + +## Installation + +**Composer** + +`composer require codecraft/silverstripe-announce` + +## Introduction + +Create announcements with Titles, Headings, Contents, Footers and Actions. Control whether they should be stored for later responses. Control whether they should be dismissable. + +Announcements are retained at StilverStripe's `Controller`, and stored in the Announcement store (default is `$_SESSION`). + +Each announcement is made available to the view via a SilverStripe `Controller` extension. + +Only one set of announcements can be stacked at a time. + +The announcement store can be replaced with a custom class that implements `AnnouncementStoreInterface`; + +## Usage + +Queue a new announcement +``` +Announcements::queue('AnnouncementName', 'Announcement Title', 'This is an announcement message'); +``` +`Accounments::queue()` will accept all arguements for an `Announcement` + +Queue an existing announcement + +``` +$msg = Announcement::create('AnnouncementName', 'Announcement Title', 'This is an announcement message'); + +Announcements::queue($msg); +``` + +Get all current announcements +``` +$announcements = Announcements::get(); +``` + +Get an announcement by name +``` +Announcements::get()->getByName('AnnouncementName'); +``` + +Get an announcement by type +``` +Announcements::get()->getByType(Announcement::DEFAULT); +``` + +Clear the announcements queue +``` +Announcements::clear(); +``` + +Display announcements in the top scope of a template +``` +<% if $Announcements %> + $Announcements +<% end_if %> +``` + +### Customise an announcement + +Example of all options + +``` +use CodeCraft\Announce\Model\Announcement; +use CodeCraft\Announce\Model\Action; + +... + +$announcement = Announcement::create( + $name = 'AnnouncementName', + $title = 'Announcement Title', + $content = 'A plain or html string', + $heading = 'A plain or html string', + $footer = 'A plain or html string', + $actions = [ + Action::create( + $name = 'ActionName', + $content = 'A plain or html string', + $type = Action::BUTTON, + $link = 'https://www.google.com/', + $extraClass = 'btn btn-primary' + ) + ], + $type = Announcement::DEFAULT +) + // Store announcement + ->setStoreable(true) + + // Dismissable + ->setDismissable(true) + + // Name + ->setName('AnnouncementName') + + // Title + ->setTitle('Announcement Title') + ->setTitle('

Announcement Title

') + + // Content + ->setContent('Announcement body') + ->setContent('

Announcement body

') + + // Heading + ->setHeading('Announcement heading') + ->setHeading('

Announcement heading

') + + // Footer + ->setFooter('Announcement footer') + ->setFooter('

Announcement footer

') + + // Action + ->addAction( + Action::create() + ->setName('ActionName') + ->setContent('OK') + ->setContent('

OK

') + ->setType(Action::BUTTON) + ->setLink('http://www.google.com/') + ->addExtraClass('btn btn-primary') + ->setAttribute('title', 'A helpful title') + ) + + // Type + ->setType(Announcement::DEFAULT) + + // Template + ->setTemplate('templates\CodeCraft\Announce\Announcement'); +``` + +### Announcement store + +Access the announcement store +``` +Announcements::get()->getStore(); +``` + +Expect announcements to be stored as an `array` + +#### Set storable + +Announcements are stored by default, so that they can be included in the next relevant response. + +Define if the announcement should be stored +``` +$msg = Announcement::create('AnnouncementName', 'Un-stored Announcement', 'This announcement is not stored') + ->setStoreable(false); +``` + +### Custom announcement template + +Set a custom template for all announcements by [overloading templates](#overloading-templates) + +Set a custom template for any announcement +``` +$msg = Announcement::create('AnnouncementName', 'Announcement Title', 'This is an announcement message') + ->setTemplate('MyTemplate'); +``` + +### Overloading templates + +**Announcement template** + +Overload the default template for all announcement actions by overloading `templates/CodeCraft/Announce/Model/Announcement` + +**Announcement Action template** + +Overload the default template for all announcement actions by overloading `templates/CodeCraft/Announce/Model/Action` + +Read more about SilverStripe [Template Inheritance](https://docs.silverstripe.org/en/4/developer_guides/templates/template_inheritance/) + +### Announcements by name + +Each announcement name is distinct. Call announcements by name + +**Back-end** +``` +Announcements::get()->getByName('AnnouncementName'); +``` + +**Template** +``` +$Announcements.ByName('AnnouncementName'); +``` + +### Announcements by type + +Set any announcement's type with one of the available types; `Announcement::DEFAULT`, `Announcement::MODAL`, `Announcement::TRAY`, `Announcement::MESSAGE`, or `Announcement::CALLOUT`. + +``` +$msg = Announcement::create('Name')->setType(Announcement::MODAL); +``` + +Call announcements by type + +**Back-end** +``` +Announcements::get()->getByType(Announcement::MODAL); +``` + +**Template** +``` +$Announcements.ByType('modal'); +``` +_Can be 'default', 'modal', 'tray', 'message', 'callout'_ + +## Requirements + +* SilverStripe >= 4.0 +* PHP >= 5.6 + +## License + +Modified BSD License + +Copyright (c) 2018, Jackson Darlow + +Read the [license](https://github.com/codecraft/silverstripe-announce/blob/master/LICENSE) + +## To do + +* Tests +* Add redundancies for cases where announcement store is ahead of announcement stack +* Add a way to disable the store +* Create a database announcement store +* Create a redis announcement store +* Replace Announcement type constants with an object-oriented model + * Create an email Announcement type +* An announcement history for user-focused announcements + * Flag announcement when viewed by user + diff --git a/_config/announce.yml b/_config/announce.yml new file mode 100644 index 0000000..e7f9556 --- /dev/null +++ b/_config/announce.yml @@ -0,0 +1,6 @@ +--- +Name: announce +--- +SilverStripe\Control\Controller: + extensions: + - CodeCraft\Announce\Extension\ControllerExtension diff --git a/composer.json b/composer.json new file mode 100644 index 0000000..428a15d --- /dev/null +++ b/composer.json @@ -0,0 +1,37 @@ +{ + "name": "codecraft/silverstripe-announce", + "description": "Add a queue of announcements to the SilverStripe controller", + "keywords": ["silverstripe", "announcements", "modal", "callout", "popup", "alert", "message"], + "type": "silverstripe-vendormodule", + "license": "BSD-3-Clause", + "authors": [ + { + "name": "Jackson Darlow", + "email": "jackson@codecraft.nz" + } + ], + "support": { + "issues": "http://github.com/codecraft/silverstripe-announce/issues" + }, + "require": { + "php": ">=5.6", + "silverstripe/framework": "^4" + }, + "require-dev": { + "phpunit/phpunit": "^5.7" + }, + "autoload": { + "psr-4": { + "CodeCraft\\Announce\\": "src/", + "CodeCraft\\Announce\\Tests\\": "tests/" + } + }, + "extra": { + "branch-alias": { + "dev-master": "0.1.x-dev" + } + }, + "target-dir": "announce", + "minimum-stability": "dev", + "prefer-stable": true +} diff --git a/src/Announcements.php b/src/Announcements.php new file mode 100644 index 0000000..b227759 --- /dev/null +++ b/src/Announcements.php @@ -0,0 +1,225 @@ +getStore()->get()) { + $announcements = array_merge($announcements, $stored); + } + } + + parent::__construct($announcements); + + if ($stack) { + // Assign new announcement stack to the current controller + Controller::curr()->setAnnouncements($this); + } + } + + /** + * Get the announcement store + * @return AnnouncementStoreInterface + */ + public function getStore() + { + return Injector::inst()->create($this->config()->get('store')); + } + + /** + * Queue an announcement in the announce stack + * @return Announcements A new announcements instance + */ + public static function queue() + { + $args = func_get_args(); + + return Controller::curr()->getAnnouncements()->push(...$args); + } + + /** + * Get the announcement queue + * @param string $type + * @return Announcements + */ + public static function get() + { + return Controller::curr()->getAnnouncements(); + } + + /** + * Clear the announcement queue + * @return Announcements + */ + public static function clear() + { + // Clear store + $this->getStore()->clear(); + + return static::create(); + } + + /** + * Merges one Announcements list with another. If any item's names conflict, + * the items from the $with announcements will overwrite this object's list. + * @param Announcements $with + * @return Announcements + */ + public function merge($with) + { + if (!$with instanceof Announcements) { + throw new Exception('Must be an Announcements list'); + } + + foreach ($with->toArray() as $announcement) { + $this->push($announcement); + } + + return $this; + } + + /** + * Add an announcement to the announce stack + * Will also accept arguments for @see Announcement::__construct() + * + * @param Announcement $announcement + * @return Announcements A new announcements instance + */ + public function push($announcement) + { + if (!$announcement instanceof Announcement) { + $args = func_get_args(); + try { + $announcement = new Announcement(...$args); + } catch (Exception $e) { + throw new Exception('Must supply Announcement object or valid arguments: ' . $e->getMessage()); + } + } + + // Avoid duplicates + $announcements = $this->toArray(); + $i = 0; + foreach ($announcements as $a) { + if ($a->getName() == $announcement->getName()) { + unset($announcements[$i]); + } + + $i++; + } + + $announcements[] = $announcement; + + return Announcements::create($announcements, true); + } + + /** + * Get announcementsby type + * @return array + */ + public function getByType($type = null) + { + $announcements = $this->toArray(); + + if ($type) { + $matching = []; + + foreach ($announcements as $announcement) { + if ($announcement->getType() == $type) { + $matching[] = $announcement; + } + } + + $announcements = $matching; + } + + return $announcements; + } + + /** + * Get an announcement by its name + * @param string $name + * @return Announcement|null + */ + public function getByName($name) + { + if (!is_string($name)) { + throw new Exception('Name must be a string'); + } + + $announcement = null; + + $announcements = $this->toArray(); + if (isset($announcements[$name])) { + $announcement = $announcements[$name]; + } + + return $announcements; + } + + /** + * Remove an announcement by its name + * @param string $name + * @return Announcements A new announcements instance + */ + public function removeByName($name) + { + if (!is_string($name)) { + throw new Exception('Name must be a string'); + } + + $announcements = $this->toArray(); + unset($announcements[$name]); + + return Announcements::create($announcements); + } + + /** + * Default template rendering of announcements + */ + public function forTemplate() + { + $output = ""; + foreach ($this as $announcement) { + $output .= $announcement->forTemplate(); + } + + // Clear store + $this->getStore()->clear(); + + return $output; + } +} diff --git a/src/Extension/ControllerExtension.php b/src/Extension/ControllerExtension.php new file mode 100644 index 0000000..1d2abc8 --- /dev/null +++ b/src/Extension/ControllerExtension.php @@ -0,0 +1,45 @@ +announcements) { + $this->announcements = Announcements::create(); + } + + return $this->announcements; + } + + /** + * @param Announcements $announcements + */ + public function setAnnouncements($announcements) + { + + // Filter for announcements to add to store + $store = array_filter($announcements->toArray(), function($a) { + return $a->canStore(); + }); + + // Store announcements + $announcements->getStore()->set($store); + + // Stack announcements + return $this->announcements = $announcements; + } +} diff --git a/src/Interfaces/AnnouncementStoreInterface.php b/src/Interfaces/AnnouncementStoreInterface.php new file mode 100644 index 0000000..ae58a02 --- /dev/null +++ b/src/Interfaces/AnnouncementStoreInterface.php @@ -0,0 +1,26 @@ + 'HTMLFragment', // Needed for templates + 'getContent' => 'HTMLFragment', + 'AttributesHTML' => 'HTMLFragment', // Needed for templates + 'getAttributesHTML' => 'HTMLFragment' + ]; + + /** + * @param string $name An identifier + * @param string $content + * @param string $type + * @param string $link + * @param string $extraClass + * @param array $actions + */ + public function __construct($name, $content = null, $type = self::BUTTON, $link = null, $extraClass = null) { + + // Name + $this->setName($name); + + // Content + if ($content) { + $this->setContent($content); + } + + // Type + if ($type) { + $this->setType($type); + } + + // Link + if ($link) { + $this->setLink($link); + } + + // Extra class + if ($extraClass) { + $this->addExtraClass($extraClass); + } + + parent::__construct(); + } + + /** + * Set the name of this action + * @param string $name The name of this action + * @return Action $this + */ + public function setName($name) + { + if (!is_string($name)) { + throw new Exception('Name must be a string'); + } + + $this->name = preg_replace("/[^a-zA-Z0-9_]+/", "", $name); + return $this; + } + + /** + * @return string + */ + public function getName() + { + return $this->name; + } + + /** + * Set the content of this announcement + * @param string $content + * @return Announcement $this + */ + public function setContent($content) + { + $this->content = $content; + return $this; + } + + /** + * @return string + */ + public function getContent() + { + return $this->content; + } + + /** + * @param string $type e.g Action::BUTTON + */ + public function setType($type) + { + if ($type !== self::BUTTON && + $type !== self::ANCHOR && + $type !== self::HIDDEN + ) { + throw new Exception('Invalid action type'); + } + + $this->type = $type; + + return $this; + } + + /** + * @return string + */ + public function getType() + { + return $this->type; + } + + /** + * Set the link of this action + * @param string $link + * @return Action $this + */ + public function setLink($link) + { + if (!is_string($link)) { + throw new Exception('Action link must be a string'); + } + + $this->attributes['href'] = $link; + $this->link = $link; + + return $this; + } + + /** + * @return string + */ + public function getLink() + { + return $this->type; + } + + /** + * @return array + */ + public function getExtraClasses() + { + return $this->extraClasses; + } + + /** + * Get all extra classes compiled into a single string + * @return string + */ + public function getExtraClassesString() + { + return implode(' ', $this->getExtraClasses()); + } + + /** + * Add one or more CSS-classes to the FormField container. + * Multiple class names should be space delimited. + * @param string $class + * @return Action $this + */ + public function addExtraClass($class) + { + $classes = preg_split('/\s+/', $class); + + $extra = $this->getExtraClasses(); + + foreach ($classes as $class) { + $extra[$class] = $class; + } + + $this->extraClasses = $extra; + + return $this; + } + + /** + * Remove one or more CSS-classes from the FormField container. + * + * @param string $class + * + * @return $this + */ + public function removeExtraClass($class) + { + $classes = preg_split('/\s+/', $class); + + $extra = $this->getExtraClasses(); + + foreach ($classes as $class) { + unset($extra[$class]); + } + + $this->extraClasses = $extra; + + return $this; + } + + /** + * Set an HTML attribute on the action element, mostly an input tag. + * @param string $name + * @param string $value + * @return Action $this + */ + public function setAttribute($name, $value) + { + $this->attributes[$name] = $value; + + return $this; + } + + /** + * @param string $name + * @return string + */ + public function getAttribute($name) + { + $attributes = $this->getAttributes(); + + if (isset($attributes[$name])) { + return $attributes[$name]; + } + + return null; + } + + + /** + * @return array + */ + public function getAttributes() + { + $attributes = [ + 'class' => $this->getExtraClassesString() + ]; + + if ($this->getType() == self::DEFAULT || $this->getType() == self::BUTTON) { + $attributes['type'] = 'button'; + } + + $attributes = array_merge($attributes, $this->attributes); + + return $attributes; + } + + /** + * @return string + */ + public function getAttributesHTML($attributes = null) + { + $attributes = (array) $this->getAttributes(); + + $attributes = array_filter($attributes, function ($v) { + return ($v || $v === 0 || $v === '0'); + }); + + // Create markup + $parts = array(); + + foreach ($attributes as $name => $value) { + if ($value === true) { + $parts[] = sprintf('%s="%s"', $name, $name); + } else { + $parts[] = sprintf('%s="%s"', $name, Convert::raw2att($value)); + } + } + + return implode(' ', $parts); + } + + /** + * @return string + */ + public function forTemplate() + { + return $this->renderWith(static::class); + } +} diff --git a/src/Model/ActionList.php b/src/Model/ActionList.php new file mode 100644 index 0000000..ab60201 --- /dev/null +++ b/src/Model/ActionList.php @@ -0,0 +1,50 @@ +items as $item) { + if ($action->Name == $name) { + $action = $item; + } + } + + return $action; + } + + /** + * @param Action $item + * @return + */ + public function push($item) { + if (!$item instanceof Action) { + throw new Exception('Must be an Action'); + } + + return parent::push($item); + } + + /** + * Default template rendering of announcements + */ + public function forTemplate() + { + $output = ""; + foreach ($this as $action) { + $output .= $action->forTemplate(); + } + return $output; + } +} diff --git a/src/Model/Announcement.php b/src/Model/Announcement.php new file mode 100644 index 0000000..e0ad6ea --- /dev/null +++ b/src/Model/Announcement.php @@ -0,0 +1,348 @@ + 'Boolean', + 'Title' => 'HTMLFragment', // Needed for templates + 'getTitle' => 'HTMLFragment', + 'Content' => 'HTMLFragment', // Needed for templates + 'getContent' => 'HTMLFragment', + 'Heading' => 'HTMLFragment', // Needed for templates + 'getHeading' => 'HTMLFragment', + 'Footer' => 'HTMLFragment', // Needed for templates + 'getFooter' => 'HTMLFragment' + ]; + + /** + * @param string $name An identifier + * @param string $title The title to display + * @param string $heading + * @param string $content + * @param string $footer + * @param array $actions + */ + public function __construct($name, $title = null, $content = null, $heading = null, $footer = null, $actions = [], $type = self::DEFAULT) + { + // Name + $this->setName($name); + + // Title + if ($title) { + $this->setTitle($title); + } + + // Type + $this->setType($type); + + // Heading + if ($heading) { + $this->setHeading($heading); + } + + // Content + if ($content) { + $this->setContent($content); + } + + // Foot + if ($footer) { + $this->setFooter($footer); + } + + if (is_array($actions) && count($actions)) { + foreach ($actions as $action) { + $this->addAction($action); + } + } + + $this->extend('updateDefaultActions', $actions); + + foreach ($actions as $action) { + $this->getActions()->push($action); + } + + parent::__construct(); + } + + /** + * @return boolean + */ + public function canStore() + { + return $this->storable; + } + + /** + * @param boolean $bool + * @return Announcement + */ + public function setStoreable($bool) + { + $this->storable = $bool; + return $this; + } + + /** + * @return boolean + */ + public function canDismiss() + { + return $this->dismissable; + } + + /** + * Define if announcement can be dismissed + * @param boolean $bool + * @return Announcement + */ + public function setDismissable($bool) + { + $this->dismissable = $bool; + return $this; + } + + /** + * Set the name of this announcement + * @param string $name The name of this announcement + * @return Announcement $this + */ + public function setName($name) + { + if (!is_string($name)) { + throw new Exception('Name must be a string'); + } + + $this->name = preg_replace("/[^a-zA-Z0-9_]+/", "", $name); + return $this; + } + + /** + * @return string + */ + public function getName() + { + return $this->name; + } + + /** + * Set the title of this announcement + * @param string $title The title to display + * @return Announcement $this + */ + public function setTitle($title) + { + $this->title = $title; + return $this; + } + + /** + * @return string + */ + public function getTitle() + { + return $this->title; + } + + /** + * @param string $type e.g Announcement::MODAL + */ + public function setType($type) + { + if ($type !== self::DEFAULT && + $type !== self::MODAL && + $type !== self::TRAY && + $type !== self::MESSAGE && + $type !== self::CALLOUT + ) { + throw new Exception('Invalid announcement type'); + } + + $this->type = $type; + + return $this; + } + + /** + * @return string + */ + public function getType() + { + return $this->type; + } + + /** + * Set the heading of this announcement + * @param string $heading + * @return Announcement $this + */ + public function setHeading($heading) + { + $this->heading = $heading; + return $this; + } + + /** + * @return string + */ + public function getHeading() + { + return $this->heading; + } + + /** + * Set the content of this announcement + * @param string $content + * @return Announcement $this + */ + public function setContent($content) + { + $this->content = $content; + return $this; + } + + /** + * @return string + */ + public function getContent() + { + return $this->content; + } + + /** + * Set the footer of this announcement + * @param string $footer + * @return Announcement $this + */ + public function setFooter($footer) + { + $this->footer = $footer; + return $this; + } + + /** + * @return string + */ + public function getFooter() + { + return $this->footer; + } + + /** + * @param Action $action + * @return Announcement $this + */ + public function addAction($action) + { + $this->getActions()->push($action); + } + + /** + * @return array + */ + public function getActions() + { + if (!$this->actions) { + $this->actions = ActionList::create(); + } + + return $this->actions; + } + + /** + * Set the SS template that this announcement should use to render with. The default is the FQCN + * @param string|array $template The name of the template (without the .ss extension) or array + */ + public function setTemplate($template) + { + $this->template = $template; + } + + /** + * Return the template to render this announcement with + * + * @return string|array + */ + public function getTemplate() + { + return $this->template; + } + + /** + * @return string + */ + public function forTemplate() + { + $template = static::class; + + if ($this->template) { + $template = $this->template; + } + + return $this->renderWith($template); + } +} diff --git a/src/Store/SessionStore.php b/src/Store/SessionStore.php new file mode 100644 index 0000000..306acff --- /dev/null +++ b/src/Store/SessionStore.php @@ -0,0 +1,58 @@ +getRequest()->getSession(); + } + + /** + * Save the announcements to the store + * @param array $list + * @return AnnouncementStoreInterface + */ + public function set($list) + { + $this->getSession()->set($this->config()->get('session_name'), $list); + + return $this; + } + + /** + * Clear the announcement store + * @return AnnouncementStoreInterface + */ + public function clear() + { + $this->getSession()->clear($this->config()->get('session_name')); + } + + /** + * Get announcements from the store + * @param string $name + * @return array|Announcement + */ + public function get($name = null) + { + $stored = $this->getSession()->get($this->config()->get('session_name')); + + if ($stored && $name && isset($stored[$name])) { + $stored = $stored[$name]; + } + + return $stored; + } +} diff --git a/templates/CodeCraft/Announce/Model/Action.ss b/templates/CodeCraft/Announce/Model/Action.ss new file mode 100644 index 0000000..cc7e4b8 --- /dev/null +++ b/templates/CodeCraft/Announce/Model/Action.ss @@ -0,0 +1,5 @@ +<% if $Type == 'button' || $Type == 'default' %> + +<% else_if $Type == 'anchor' %> + $Content +<% end_if %> diff --git a/templates/CodeCraft/Announce/Model/Announcement.ss b/templates/CodeCraft/Announce/Model/Announcement.ss new file mode 100644 index 0000000..4dcdeb4 --- /dev/null +++ b/templates/CodeCraft/Announce/Model/Announcement.ss @@ -0,0 +1,21 @@ +
+ <% if $Title %>

$Title

<% end_if %> + <% if $Heading %> +
+ $Heading +
+ <% end_if %> +
+ $Content +
+ <% if $Footer || $Actions %> + + <% end_if %> +
diff --git a/tests/AnnouncementTest.php b/tests/AnnouncementTest.php new file mode 100644 index 0000000..14390db --- /dev/null +++ b/tests/AnnouncementTest.php @@ -0,0 +1,10 @@ +