├── views └── elements │ └── flash_message.html.php ├── config └── bootstrap.php ├── .travis.yml ├── README.md ├── composer.json ├── extensions ├── helper │ └── FlashMessage.php └── storage │ └── FlashMessage.php └── tests └── cases └── extensions └── storage └── FlashMessageTest.php /views/elements/flash_message.html.php: -------------------------------------------------------------------------------- 1 | 6 |
> 7 | 8 |
-------------------------------------------------------------------------------- /config/bootstrap.php: -------------------------------------------------------------------------------- 1 | next($self, $params, $chain)); 8 | }); 9 | 10 | ?> -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: php 2 | 3 | php: 4 | - 5.3 5 | - 5.4 6 | 7 | before_script: 8 | - mkdir ../libraries 9 | - git clone --branch=master --depth=100 --quiet git://github.com/UnionOfRAD/lithium.git ../libraries/lithium 10 | 11 | script: ../libraries/lithium/console/li3 test --filters=Profiler tests/cases 12 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Flash Message Plugin for Lithium 2 | 3 | The Flash Message (`li3_flash_message`) plugin provides a straightforward interface for displaying status messages to the user. 4 | 5 | 6 | ## Goals 7 | 8 | - Use existing session storage 9 | - Eliminate message content from controllers 10 | - Easily localize messages 11 | - Use filters to integrate into existing workflow 12 | 13 | 14 | ## Integration 15 | 16 | ``` 17 | 24 | ``` -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "uor/li3_flash_message", 3 | "description": "The Flash Message plugin for Lithium provides a straightforward interface for displaying status messages to the user.", 4 | "type": "lithium-library", 5 | "version": "dev-master", 6 | "license": "MIT", 7 | "authors": [ 8 | { 9 | "name": "The Lithium Community", 10 | "homepage": "http://github.com/uor/li3_flash_message/graphs/contributors" 11 | }, 12 | { 13 | "name": "Michael Hüneburg", 14 | "email": "hello@michaelhue.com", 15 | "homepage": "http://michaelhue.com", 16 | "role": "Developer" 17 | } 18 | ], 19 | "require": { 20 | "composer/installers": "*" 21 | }, 22 | "suggest": { 23 | "UnionOfRAD/lithium": "Lithium is required for this plugin." 24 | }, 25 | "minimum-stability": "dev" 26 | } 27 | -------------------------------------------------------------------------------- /extensions/helper/FlashMessage.php: -------------------------------------------------------------------------------- 1 | 'li3_flash_message\extensions\storage\FlashMessage' 27 | ); 28 | 29 | /** 30 | * Outputs a flash message using a template. The message will be cleared afterwards. 31 | * With defaults settings it looks for the template 32 | * `app/views/elements/flash_message.html.php`. If it doesn't exist, the plugin's view 33 | * at `li3_flash_message/views/elements/flash_message.html.php` will be used. Use this 34 | * file as a starting point for your own flash message element. In order to use a 35 | * different template, adjust `$options['type']` and `$options['template']` to your needs. 36 | * 37 | * @param string [$key] Optional message key. 38 | * @param array [$options] Optional options. 39 | * - type: Template type that will be rendered. 40 | * - template: Name of the template that will be rendered. 41 | * - data: Additional data for the template. 42 | * - options: Additional options that will be passed to the renderer. 43 | * @return string Returns the rendered template. 44 | */ 45 | public function show($key = 'flash_message', array $options = array()) { 46 | $defaults = array( 47 | 'type' => 'element', 48 | 'template' => 'flash_message', 49 | 'data' => array(), 50 | 'options' => array() 51 | ); 52 | $options += $defaults; 53 | 54 | $storage = $this->_classes['storage']; 55 | $view = $this->_context->view(); 56 | $type = array($options['type'] => $options['template']); 57 | 58 | if (!$flash = $storage::read($key)) { 59 | return; 60 | } 61 | $data = $options['data'] + array('message' => $flash['message']) + $flash['attrs']; 62 | $storage::clear($key); 63 | 64 | try { 65 | return $view->render($type, $data, $options['options']); 66 | } catch (TemplateException $e) { 67 | return $view->render($type, $data, array('library' => 'li3_flash_message')); 68 | } 69 | } 70 | } 71 | 72 | ?> -------------------------------------------------------------------------------- /extensions/storage/FlashMessage.php: -------------------------------------------------------------------------------- 1 | redirect('Posts::index', array('message' => 'Post not found!')); 26 | * } 27 | * 28 | * // View 29 | * flashMessage->output(); ?> 30 | * }}} 31 | */ 32 | class FlashMessage extends \lithium\core\StaticObject { 33 | 34 | /** 35 | * Class dependencies. 36 | * 37 | * @var array 38 | */ 39 | protected static $_classes = array( 40 | 'session' => 'lithium\storage\Session' 41 | ); 42 | 43 | /** 44 | * Configuration directives for writing, storing, and rendering flash messages. 45 | * 46 | * @var array 47 | */ 48 | protected static $_session = array( 49 | 'config' => 'default', 50 | 'base' => null 51 | ); 52 | 53 | /** 54 | * The library containing the `messages.php` config file. 55 | * 56 | * @var array 57 | */ 58 | protected static $_library = true; 59 | 60 | /** 61 | * Stores message keys. 62 | * 63 | * @var array 64 | */ 65 | protected static $_messages = null; 66 | 67 | /** 68 | * Used to set configuration parameters for `FlashMessage`. 69 | * 70 | * @see li3_flash_message\extensions\storage\FlashMessage::$_config 71 | * @param array $config Possible key settings: 72 | * - `'classes'` _array_: Sets class dependencies (i.e. `'session'`). 73 | * - `'session'` _array_: Configuration for accessing and manipulating session 74 | * data. 75 | * @return array If no parameters are passed, returns an associative array with the current 76 | * configuration, otherwise returns `null`. 77 | */ 78 | public static function config(array $config = array()) { 79 | if (!$config) { 80 | return array('session' => static::$_session) + array('classes' => static::$_classes); 81 | } 82 | 83 | foreach ($config as $key => $val) { 84 | $key = "_{$key}"; 85 | if (isset(static::${$key})) { 86 | static::${$key} = is_array($val) ? $val + static::${$key} : $val; 87 | } 88 | } 89 | } 90 | 91 | /** 92 | * Binds the messaging system to a controller to enable `'message'` option flags in various 93 | * controller methods, such as `render()` and `redirect()`. 94 | * 95 | * @param object $controller An instance of `lithium\action\Controller`. 96 | * @param array $options Options. 97 | * @return object Returns the passed `$controller` instance. 98 | */ 99 | public static function bindTo($controller, array $options = array()) { 100 | if (!method_exists($controller, 'applyFilter')) { 101 | return $controller; 102 | } 103 | 104 | $controller->applyFilter('redirect', function($self, $params, $chain) use ($options) { 105 | $options =& $params['options']; 106 | 107 | if (isset($params['options']['message'])) { 108 | FlashMessage::write($params['options']['message']); 109 | unset($params['options']['message']); 110 | } 111 | return $chain->next($self, $params, $chain); 112 | }); 113 | 114 | return $controller; 115 | } 116 | 117 | /** 118 | * Writes a flash message. 119 | * 120 | * @todo Add closure support to messages 121 | * @param mixed $message Message the message to be stored. 122 | * @param array $attrs Optional attributes that will be available in the view. 123 | * @param string $key Optional key to store multiple flash messages. 124 | * @return boolean True on successful write, false otherwise. 125 | */ 126 | public static function write($message, array $attrs = array(), $key = 'flash_message') { 127 | $session = static::$_classes['session']; 128 | $key = static::_key($key); 129 | $name = static::$_session['config']; 130 | 131 | if (static::$_messages === null) { 132 | $path = Libraries::get(static::$_library, 'path') . '/config/messages.php'; 133 | static::$_messages = file_exists($path) ? include $path : array(); 134 | } 135 | 136 | $message = static::_translate($message, $attrs); 137 | 138 | return $session::write($key, compact('message', 'attrs'), compact('name')); 139 | } 140 | 141 | /** 142 | * Recursive message translation. 143 | * 144 | * @param mixed $message Message the message to be stored. 145 | * @param array $attrs Optional attributes that will be available in the view. 146 | * @return array 147 | */ 148 | protected static function _translate($message, array $attrs) { 149 | if (is_string($message)) { 150 | if (isset(static::$_messages[$message])) { 151 | $message = static::$_messages[$message]; 152 | } 153 | $message = String::insert($message, $attrs); 154 | } elseif (is_array($message)) { 155 | foreach ($message as $index => $value) { 156 | $message[$index] = static::_translate($value, $attrs); 157 | } 158 | } 159 | return $message; 160 | } 161 | /** 162 | * Reads a flash message. 163 | * 164 | * @param string [$key] Optional key. 165 | * @return array The stored flash message. 166 | */ 167 | public static function read($key = 'flash_message') { 168 | $session = static::$_classes['session']; 169 | $key = static::_key($key); 170 | return $session::read($key, array('name' => static::$_session['config'])); 171 | } 172 | 173 | /** 174 | * Delete a flash messages from the session. 175 | * 176 | * @return boolean 177 | */ 178 | public static function clear($key = 'flash_message') { 179 | $session = static::$_classes['session']; 180 | $key = static::_key($key); 181 | return $session::delete($key, array('name' => static::$_session['config'])); 182 | } 183 | 184 | /** 185 | * Reset the class. 186 | */ 187 | public static function reset() { 188 | static::$_library = true; 189 | static::$_messages = null; 190 | static::$_classes = array( 191 | 'session' => 'lithium\storage\Session' 192 | ); 193 | static::$_session = array( 194 | 'config' => 'default', 195 | 'base' => null 196 | ); 197 | } 198 | 199 | /** 200 | * Helper for building the key 201 | * 202 | * @param string $key The key. 203 | * @return string The complete key. 204 | */ 205 | protected static function _key($key) { 206 | $base = static::$_session['base']; 207 | return ($base ? "{$base}." : '') . $key; 208 | } 209 | } 210 | 211 | ?> -------------------------------------------------------------------------------- /tests/cases/extensions/storage/FlashMessageTest.php: -------------------------------------------------------------------------------- 1 | array( 20 | 'adapter' => 'Memory' 21 | ) 22 | )); 23 | } 24 | 25 | public function tearDown() { 26 | Session::delete('default'); 27 | FlashMessage::reset(); 28 | } 29 | 30 | public function testConfig() { 31 | $result = FlashMessage::config(); 32 | $expected = array( 33 | 'session' => array('config' => 'default', 'base' => null), 34 | 'classes' => array('session' => 'lithium\storage\Session') 35 | ); 36 | $this->assertEqual($expected, $result); 37 | 38 | FlashMessage::config(array('session' => array('base' => 'message'))); 39 | $result = FlashMessage::config(); 40 | $expected = array( 41 | 'session' => array('config' => 'default', 'base' => 'message'), 42 | 'classes' => array('session' => 'lithium\storage\Session') 43 | ); 44 | $this->assertEqual($expected, $result); 45 | } 46 | 47 | public function testReset() { 48 | FlashMessage::config(array('session' => array('base' => 'message'))); 49 | FlashMessage::reset(); 50 | $result = FlashMessage::config(); 51 | $expected = array( 52 | 'session' => array('config' => 'default', 'base' => null), 53 | 'classes' => array('session' => 'lithium\storage\Session') 54 | ); 55 | $this->assertEqual($expected, $result); 56 | } 57 | 58 | public function testWrite() { 59 | FlashMessage::write('Foo'); 60 | $expected = array('message' => 'Foo', 'attrs' => array()); 61 | $result = Session::read('flash_message'); 62 | $this->assertEqual($expected, $result); 63 | 64 | FlashMessage::write('Foo 2', array('type' => 'notice')); 65 | $expected = array('message' => 'Foo 2', 'attrs' => array('type' => 'notice')); 66 | $result = Session::read('flash_message'); 67 | $this->assertEqual($expected, $result); 68 | 69 | FlashMessage::write('Foo 3', array(), 'TestKey'); 70 | $expected = array('message' => 'Foo 3', 'attrs' => array()); 71 | $result = Session::read('TestKey'); 72 | $this->assertEqual($expected, $result); 73 | } 74 | 75 | public function testRead() { 76 | FlashMessage::write('Foo'); 77 | $expected = array('message' => 'Foo', 'attrs' => array()); 78 | $result = FlashMessage::read(); 79 | $this->assertEqual($expected, $result); 80 | 81 | FlashMessage::write('Foo 2', array('type' => 'notice')); 82 | $expected = array('message' => 'Foo 2', 'attrs' => array('type' => 'notice')); 83 | $result = FlashMessage::read(); 84 | $this->assertEqual($expected, $result); 85 | 86 | FlashMessage::write('Foo 3', array(), 'TestKey'); 87 | $expected = array('message' => 'Foo 3', 'attrs' => array()); 88 | $result = FlashMessage::read('TestKey'); 89 | $this->assertEqual($expected, $result); 90 | } 91 | 92 | public function testClear() { 93 | FlashMessage::write('Foo'); 94 | FlashMessage::clear(); 95 | $result = FlashMessage::read(); 96 | $this->assertNull($result); 97 | 98 | FlashMessage::write('Foo 2', array(), 'TestKey'); 99 | FlashMessage::clear('TestKey'); 100 | $result = FlashMessage::read('TestKey'); 101 | $this->assertNull($result); 102 | 103 | FlashMessage::write('Foo 3', array(), 'TestKey2'); 104 | FlashMessage::write('Foo 4', array(), 'TestKey3'); 105 | FlashMessage::clear(); 106 | $result = FlashMessage::read(); 107 | $this->assertNull($result); 108 | } 109 | 110 | public function testWriteWithBase() { 111 | FlashMessage::config(array('session' => array('base' => 'message'))); 112 | FlashMessage::write('Foo'); 113 | $expected = array('message' => 'Foo', 'attrs' => array()); 114 | $result = Session::read('message.flash_message'); 115 | $this->assertEqual($expected, $result); 116 | 117 | FlashMessage::write('Foo 2', array('type' => 'notice')); 118 | $expected = array('message' => 'Foo 2', 'attrs' => array('type' => 'notice')); 119 | $result = Session::read('message.flash_message'); 120 | $this->assertEqual($expected, $result); 121 | 122 | FlashMessage::write('Foo 3', array(), 'TestKey'); 123 | $expected = array('message' => 'Foo 3', 'attrs' => array()); 124 | $result = Session::read('message.TestKey'); 125 | $this->assertEqual($expected, $result); 126 | } 127 | 128 | public function testReadWithBase() { 129 | FlashMessage::config(array('session' => array('base' => 'message'))); 130 | FlashMessage::write('Foo'); 131 | $expected = array('message' => 'Foo', 'attrs' => array()); 132 | $result = FlashMessage::read(); 133 | $this->assertEqual($expected, $result); 134 | 135 | FlashMessage::write('Foo 2', array('type' => 'notice')); 136 | $expected = array('message' => 'Foo 2', 'attrs' => array('type' => 'notice')); 137 | $result = FlashMessage::read(); 138 | $this->assertEqual($expected, $result); 139 | 140 | FlashMessage::write('Foo 3', array(), 'TestKey'); 141 | $expected = array('message' => 'Foo 3', 'attrs' => array()); 142 | $result = FlashMessage::read('TestKey'); 143 | $this->assertEqual($expected, $result); 144 | } 145 | 146 | public function testClearWithBase() { 147 | FlashMessage::config(array('session' => array('base' => 'message'))); 148 | FlashMessage::write('Foo'); 149 | FlashMessage::clear(); 150 | $result = FlashMessage::read('flash_message'); 151 | $this->assertNull($result); 152 | 153 | FlashMessage::write('Foo 2', array(), 'TestKey'); 154 | FlashMessage::clear('TestKey'); 155 | $result = FlashMessage::read('TestKey'); 156 | $this->assertNull($result); 157 | 158 | FlashMessage::write('Foo 3', array(), 'TestKey2'); 159 | FlashMessage::write('Foo 4', array(), 'TestKey3'); 160 | FlashMessage::clear(); 161 | $result = FlashMessage::read(); 162 | $this->assertNull($result); 163 | } 164 | 165 | public function testMessageWithParameters() { 166 | FlashMessage::write('{:name}: the most rad php framework', array('name' => 'Lithium')); 167 | $result = FlashMessage::read('flash_message'); 168 | $expected = array( 169 | 'message' => 'Lithium: the most rad php framework', 170 | 'attrs' => array('name' => 'Lithium') 171 | ); 172 | $this->assertEqual($expected, $result); 173 | } 174 | 175 | public function testArrayOfStringAsMessage() { 176 | $messages = array( 177 | 'Name can\'t be empty.', 178 | 'Email required', 179 | 'Phone is invalid.' 180 | ); 181 | FlashMessage::write($messages); 182 | 183 | $expected = array( 184 | 'message' => $messages, 185 | 'attrs' => array() 186 | ); 187 | $result = FlashMessage::read('flash_message'); 188 | $this->assertEqual($expected, $result); 189 | } 190 | 191 | public function testNestedArrayAsMessage() { 192 | $messages = array( 193 | 'name' => array( 194 | 'Name is required.' 195 | ), 196 | 'email' => array( 197 | 'Email can\'t be empty.', 198 | 'Email is invalid.' 199 | ), 200 | 'phone' => array( 201 | 'Invalid phone number.' 202 | ) 203 | ); 204 | FlashMessage::write($messages); 205 | 206 | $expected = array( 207 | 'message' => $messages, 208 | 'attrs' => array() 209 | ); 210 | $result = FlashMessage::read('flash_message'); 211 | $this->assertEqual($expected, $result); 212 | } 213 | 214 | public function testMessageTranslation() { 215 | $testApp = Libraries::get(true, 'resources') . '/tmp/tests/test_app'; 216 | mkdir($testApp . '/config', 0777, true); 217 | 218 | $body = << 'Hello World.', 222 | 'advice' => 'Whatever advice you give, be short.', 223 | 'error' => 'To err is human, but for a real disaster you need a computer.' 224 | ); 225 | ?> 226 | EOD; 227 | $filepath = $testApp . '/config/messages.php'; 228 | file_put_contents($filepath, $body); 229 | 230 | Libraries::add('test_app', array('path' => $testApp)); 231 | 232 | FlashMessage::config(array('library' => 'test_app')); 233 | 234 | $messages = array('hello', 'advice', 'error'); 235 | FlashMessage::write($messages); 236 | 237 | $expected = array( 238 | 'message' => array( 239 | 'Hello World.', 240 | 'Whatever advice you give, be short.', 241 | 'To err is human, but for a real disaster you need a computer.' 242 | ), 243 | 'attrs' => array() 244 | ); 245 | $result = FlashMessage::read('flash_message'); 246 | $this->assertEqual($expected, $result); 247 | 248 | $message = 'hello'; 249 | FlashMessage::write($message); 250 | 251 | $expected = array( 252 | 'message' => 'Hello World.', 253 | 'attrs' => array() 254 | ); 255 | $result = FlashMessage::read('flash_message'); 256 | $this->assertEqual($expected, $result); 257 | 258 | $this->_cleanUp(); 259 | } 260 | } 261 | 262 | ?> --------------------------------------------------------------------------------