diff --git a/README.md b/README.md index 9de76fc..7704783 100644 --- a/README.md +++ b/README.md @@ -30,6 +30,8 @@ composer install trms/carousel [Bulletins](#bulletin) +[Bulletin & Template Blocks](#bulletinblock) + [Groups](#group) [Media](#media) @@ -247,6 +249,91 @@ A template is the starting point for a standard Bulletin and is comprised of a b |PreviewImageUrl|string|A low resolution rendering of the template| |FullImageUrl|string|A full resolution rendering of the template| +### BulletinBlock +`TRMS\Carousel\Models\BulletinBlock` + +A BulletinBlock is an area of content within a `Bulletin` or a `Template`. Blocks can be `Text`, `Picture`, `Video`, or `WebPicture`. Text blocks are just that, blocks containing text. Picture and Video blocks will take a Media Object. WebPicture blocks take the URL of an image. There are many properties on a block some of which apply to only one of the four `BlockType`s. Many have several properties that are used to make an effect, like a drop shadow or a text glow. +#### Methods +|Method|Parameters|Returns|Description| +|------|----------|-------|-----------| +|setMedia|Media Object|self - chainable|Sets the Media to be used in this model and sets the `blockType` to the corresponding Media type.| +|getMedia|none|Media Object|Gets the related Media model.| +#### Properties +|Property|type|Description| +|--------|----|-----------| +BlockType|enumerable|Indicates the type of the block: `Rectangle`, `Ellipse`, `Text`, `Picture`, `WebPicture`, `Video`| +Name|string|The name of the block| +|**Size and Position**| +X|int|The x coordinate of the upper left corner of the block.| +Y|int|The y coordinate of the upper left corner of the block.| +Width|int|The width in pixels of the block.| +Height|int|The height in pixels of the block.| +RotateDegrees:|int|The rotation of the block in degrees| +|**Text Basic**| +Text|string|The text to be displayed in a text block| +AutoSizeText|boolean|If the block should auto size the text to fill the available block. If false the `TextSize` will be used| +TextSize|int|The font size in points| +|**Text Style**| +TextFont|string|The font name of an installed font. (this is a relationship, getting the installed fonts is not currently supported in this package)| +TextBold|boolean|Bold styling| +TextItalic|boolean|Italic styling| +TextStrikeout|boolean|Strikeout styling| +TextUnderline|boolean|Underline styling| +|**Text Color**| +TextColor|css color value|The text color| +TextColorOpacity|float 0-1|The opacity of the text| +|**Text Alignment**| +TextHorizAlignment|enumerable|The horizontal alignment of the text: `Near`,`Far`,`Center`| +TextVertAlignment|enumerable|The horizontal alignment of the text: `Near`,`Far`,`Center`| +TextRightToLeft|boolean|Draw the text right to left| +TextWrap|boolean|Should the text line wrap| +|**Text Gradient Fill**| +TextGradient|boolean|Enable text gradient| +TextGradientColor|css color value|The color of the gradient| +TextGradientMode|enumerable|The direction of the gradient: `Horizontal`,`Vertical`,`ForwardDiagonal`,`BackwardDiagonal`| +TextGradientOpacity|float 0-1|The opacity of the gradient| +|**Text Stroke**| +TextOutline|boolean|Enable text stroke| +TextOutlineColor|css color value|The color of the stroke| +TextOutlineOpacity|float 0-1|The opacity of the stroke| +|**Text Drop Shadow**| +TextShadow|boolean|Enable text shadow| +TextShadowColor|css color value|The color of the shadow| +TextShadowDepth|int|Value in pixels of the shadow depth| +TextShadowOpacity|float 0-1|The opacity of the shadow| +|**Text Glow**| +TextGlow|boolean|Enable text glow| +TextGlowColor|css color value|The color of the text glow| +|**Block Color Fill**| +RectColor|css color value|The background fill of the block| +RectColorOpacity|float 0-1|The opacity of the fill| +|**Block Gradient**| +RectGradient||boolean|Enable block gradient| +RectGradientColor|css color value|The color of the gradient| +RectGradientMode|enumerable|The direction of the gradient: `Horizontal`,`Vertical`,`ForwardDiagonal`,`BackwardDiagonal`| +RectGradientOpacity|float 0-1|The opacity of the gradient| +|**Block Stroke**| +RectOutline|boolean|Enable block stroke| +RectOutlineColor|css color value|The color of the stroke| +RectOutlineWidth|int|The width in pixels of the stroke| +RectOutlineOpacity|float 0-1|The opacity of the stroke| +|**Block Drop Shadow**| +RectShadow|boolean|Enable block shadow| +RectShadowColor|css color value|The color of the shadow| +RectShadowDepth|int|Value in pixels of the shadow depth| +RectShadowOpacity|float 0-1|The opacity of the shadow| +|**Block Reflection**| +Reflection|boolean|Enable block reflection| +ReflectionOffset|int|Value in pixels of the reflection offset| +ReflectionHeight|int|Value in pixels of the reflection height| +ReflectionOpacity|float 0-1|The opacity of the reflection| +|**Picture Block**| +HorizAlignment|enumerable|The horizontal position of the image in the block: `Left`,`Center`,`Right`| +VertAlignment|enumerable|The vertical position of the image in the block: `Top`,`Center`,`Bottom`| +MaintainAspectRatio|boolean|Should the image stretch to fill the block| +|**Web Picture Block**| +ImageURL|string|URL of a remote image asset| + ### Group `TRMS\Carousel\Models\Group` diff --git a/Tests/BulletinBlockTest.php b/Tests/BulletinBlockTest.php new file mode 100644 index 0000000..a0515c6 --- /dev/null +++ b/Tests/BulletinBlockTest.php @@ -0,0 +1,104 @@ +'1','Type'=>'Picture']); + $video = new Media(['id'=>'2','Type'=>'Video']); + + $block->setMedia($picture); + + $this->assertEquals($picture->id, $block->MediaID); + $this->assertEquals($picture, $block->MediaObject); + $this->assertEquals('Picture',$block->BlockType); + + $block->setMedia($video); + + $this->assertEquals($video->id, $block->MediaID); + $this->assertEquals($video, $block->MediaObject); + $this->assertEquals('Video',$block->BlockType); + } + + function test_a_Sound_media_cannot_be_added_to_a_block_and_throws_an_exception() + { + $block = new BulletinBlock(); + $sound = new Media(['id'=>'1','Type'=>'Sound']); + + try{ + $block->setMedia($sound); + } catch(CarouselModelException $e){ + return; + } + + $this->fail('the exception was not thrown'); + } + + function test_a_Background_media_cannot_be_added_to_a_block_and_throws_an_exception() + { + $block = new BulletinBlock(); + $background = new Media(['id'=>'1','Type'=>'Background']); + + try{ + $block->setMedia($background); + } catch(CarouselModelException $e){ + return; + } + + $this->fail('the exception was not thrown'); + } + + function test_you_can_get_the_background_media_relationship() + { + $block = new BulletinBlock(); + $picture = new Media(['id'=>'1','Type'=>'Picture']); + + $block->setMedia($picture); + $media = $block->getMedia(); + + $this->assertEquals($picture,$media); + } + + function test_if_the_media_object_has_not_been_resolved_an_api_request_will_be_made() + { + $mock = new MockHandler([ + new Response(200,[],json_encode(['id'=>'12'])), + ]); + $handler = HandlerStack::create($mock); + + $api = new API(); + $api->addMockHandler($handler); + + $block = new BulletinBlock(['BlockType'=>'Picture','MediaID'=>'12']); + $block->setApi($api); + $media = $block->getMedia(); + + $this->assertInstanceOf(Media::class, $media); + $this->assertEquals('12',$media->id); + } + + function test_using_getMedia_on_a_non_media_using_block_will_return_null() + { + $block = new BulletinBlock(); + $block->BlockType === 'Text'; + + $media = $block->getMedia(); + + $this->assertEquals(null, $media); + + $block->BlockType === 'Rectangle'; + + $this->assertEquals(null, $media); + } +} \ No newline at end of file diff --git a/Tests/BulletinTest.php b/Tests/BulletinTest.php index 76c69da..2edb4d5 100644 --- a/Tests/BulletinTest.php +++ b/Tests/BulletinTest.php @@ -32,8 +32,8 @@ function test_bulletin_blocks_serialize_correctly() $bulletin->Blocks = [$block1, $block2]; - $this->assertEquals(['BlockType'=>'Text','Text'=>'foobarbaz'], $bulletin->toArray()['Blocks'][0]); - $this->assertEquals(['BlockType'=>'Rectangle'], $bulletin->toArray()['Blocks'][1]); + $this->assertArraySubset(['BlockType'=>'Text','Text'=>'foobarbaz'], $bulletin->toArray()['Blocks'][0]); + $this->assertArraySubset(['BlockType'=>'Rectangle'], $bulletin->toArray()['Blocks'][1]); } function test_the_resolvePartial_method_will_get_partial_bulletins_from_the_api() @@ -48,6 +48,7 @@ function test_the_resolvePartial_method_will_get_partial_bulletins_from_the_api( $this->assertEquals(2, count($bulletin->Blocks)); $this->assertInstanceOf(BulletinBlock::class, $bulletin->Blocks[0]); + $this->assertInstanceOf(API::class, $bulletin->Blocks[0]->getApi()); $this->assertEquals(false, $bulletin->PartialBulletin); \Mockery::close(); } diff --git a/Tests/TemplateTest.php b/Tests/TemplateTest.php index a1d1695..f621ca4 100644 --- a/Tests/TemplateTest.php +++ b/Tests/TemplateTest.php @@ -14,8 +14,8 @@ function test_template_blocks_serialize_correctly() $template->Blocks = [$block1, $block2]; - $this->assertEquals(['BlockType'=>'Text','Text'=>'foobarbaz'], $template->toArray()['Blocks'][0]); - $this->assertEquals(['BlockType'=>'Rectangle'], $template->toArray()['Blocks'][1]); + $this->assertArraySubset(['BlockType'=>'Text','Text'=>'foobarbaz'], $template->toArray()['Blocks'][0]); + $this->assertArraySubset(['BlockType'=>'Rectangle'], $template->toArray()['Blocks'][1]); } } \ No newline at end of file diff --git a/src/Models/BulletinBlock.php b/src/Models/BulletinBlock.php index 95a037a..de9e7d0 100644 --- a/src/Models/BulletinBlock.php +++ b/src/Models/BulletinBlock.php @@ -1,9 +1,34 @@ Type === 'Sound' || $media->Type === 'Background'){ + throw new CarouselModelException('Sound and Background Media cannot be added to a block.'); + } + $this->setBelongsTo('Media', $media); + $this->BlockType = $media->Type; + } + + public function getMedia() + { + if($this->BlockType !== 'Picture' && $this->BlockType !== 'Video'){ + return null; + } + if($media = $this->getBelongsTo('Media',Media::class)){ + return $media; + } + throw new CarouselModelException("use setMedia method to set the block's media relationship"); } } \ No newline at end of file diff --git a/src/Models/BulletinOrder.php b/src/Models/BulletinOrder.php index c85d987..9fa925b 100644 --- a/src/Models/BulletinOrder.php +++ b/src/Models/BulletinOrder.php @@ -3,12 +3,17 @@ use TRMS\Carousel\Exceptions\CarouselModelException; use TRMS\Carousel\Models\BulletinOrderEntry; +use TRMS\Carousel\Server\API; + class BulletinOrder extends CarouselModel { protected $endpoint = 'orderentries'; - public function __construct(string $ZoneID, Array $props) + public function __construct(string $ZoneID, Array $props, API $api=null) { + if($api){ + $this->setApi($api); + } $this->ZoneID = $ZoneID; $this->setOrderEntries($props); } diff --git a/src/Models/CarouselModel.php b/src/Models/CarouselModel.php index a32d089..78f25ef 100644 --- a/src/Models/CarouselModel.php +++ b/src/Models/CarouselModel.php @@ -12,11 +12,11 @@ abstract class CarouselModel implements SaveableInterface public function __construct(Array $props = [],API $api=null) { - $this->setProps($props); - if($api){ $this->setApi($api); } + $this->setProps($props); + } public function setProps(Array $props) diff --git a/src/Models/Traits/HasBlocks.php b/src/Models/Traits/HasBlocks.php index 8764948..2821bce 100644 --- a/src/Models/Traits/HasBlocks.php +++ b/src/Models/Traits/HasBlocks.php @@ -19,6 +19,10 @@ private function setBlocksFromProps($props) public function addBlock(BulletinBlock $block) { + $api = $this->getApi(); + if($api){ + $block->setApi($api); + } array_push($this->Blocks, $block); return $this; } diff --git a/src/Server/API.php b/src/Server/API.php index bc66004..ecfab46 100644 --- a/src/Server/API.php +++ b/src/Server/API.php @@ -66,7 +66,7 @@ public function get(ModelRequest $request) $response = $apiRequest->get($request->url(),$request->queryParams); if($responseClass === BulletinOrder::class){ - return new BulletinOrder($request->queryParams['ZoneID'], $response); + return new BulletinOrder($request->queryParams['ZoneID'], $response, $this); } return collect($response)->filter()->map(function($properties) use ($responseClass){ @@ -79,7 +79,7 @@ public function save(CarouselModel $model) { $endpoint = $model->getSaveEndpoint(); $method = $model->getSaveMethod(); - if($model->getApi() && $model->PartialBulletin){ + if($model->getApi() && empty($model->PartialBulletin) === false){ $model->resolvePartial(); } $options = [