From 63761f3a69e9e34be24943eb62ce6042b365ae2e Mon Sep 17 00:00:00 2001 From: "James (DPC)" <69664712+JamesDPC@users.noreply.github.com> Date: Tue, 13 Aug 2024 11:33:10 +1000 Subject: [PATCH 01/18] WIP: initial refactor commit --- .editorconfig | 17 +- .gitattributes | 14 +- .gitignore | 17 +- .scrutinizer.yml | 69 ----- contributing.md => CONTRIBUTING.md | 0 README.md | 83 ++---- code-of-conduct.md | 1 + composer.json | 28 ++- docs/en/001_index.md | 108 ++++++++ lang/en.yml | 6 +- src/Extensions/Embeddable.php | 300 ++++++++++++++++++++++ src/{models => Models}/Embed.php | 62 ++--- src/Models/Video.php | 42 ++++ src/extensions/Embeddable.php | 333 ------------------------- src/models/Video.php | 45 ---- templates/NSWDPC/Embed/Models/Embed.ss | 1 + tests/.gitkeep | 0 17 files changed, 546 insertions(+), 580 deletions(-) delete mode 100644 .scrutinizer.yml rename contributing.md => CONTRIBUTING.md (100%) create mode 100644 code-of-conduct.md create mode 100644 docs/en/001_index.md create mode 100644 src/Extensions/Embeddable.php rename src/{models => Models}/Embed.php (54%) create mode 100644 src/Models/Video.php delete mode 100644 src/extensions/Embeddable.php delete mode 100644 src/models/Video.php create mode 100644 templates/NSWDPC/Embed/Models/Embed.ss create mode 100644 tests/.gitkeep diff --git a/.editorconfig b/.editorconfig index 9943c25..af9174b 100644 --- a/.editorconfig +++ b/.editorconfig @@ -1,7 +1,3 @@ -# For more information about the properties used in this file, -# please see the EditorConfig documentation: -# http://editorconfig.org - [*] charset = utf-8 end_of_line = lf @@ -10,8 +6,15 @@ indent_style = space insert_final_newline = true trim_trailing_whitespace = true -[{*.yml,*.json}] +[*.md] +trim_trailing_whitespace = false + +[*.{yml,js,json,css,scss,eslintrc,feature}] +indent_size = 2 +indent_style = space + +[package.json] indent_size = 2 -[{*.ss}] -indent_style = tab +[composer.json] +indent_size = 4 diff --git a/.gitattributes b/.gitattributes index dfe0770..6d32411 100644 --- a/.gitattributes +++ b/.gitattributes @@ -1,2 +1,12 @@ -# Auto detect text files and perform LF normalization -* text=auto +/tests export-ignore +/docs export-ignore +/client/src export-ignore +/.editorconfig export-ignore +/.gitattributes export-ignore +/.gitignore export-ignore +/.php-cs-fixer.dist.php export-ignore +/phpunit.xml.dist export-ignore +/.waratah export-ignore +/code-of-conduct.md export-ignore +/CONTRIBUTING.md export-ignore +/README.md export-ignore diff --git a/.gitignore b/.gitignore index bd09225..62e4929 100644 --- a/.gitignore +++ b/.gitignore @@ -1,14 +1,7 @@ -Thumbs.db -web.config -.log -.buildpath -.project -.settings -.idea +/client/node_modules +/vendor/ .DS_Store -._.DS_Store -.codekit -.sublime-* -.sass-cache -prepros.cfg +/.php-cs-fixer.cache +/public/ +/composer.lock node_modules diff --git a/.scrutinizer.yml b/.scrutinizer.yml deleted file mode 100644 index af8f18e..0000000 --- a/.scrutinizer.yml +++ /dev/null @@ -1,69 +0,0 @@ -inherit: true - -checks: - php: - verify_property_names: true - verify_argument_usable_as_reference: true - verify_access_scope_valid: true - useless_calls: true - use_statement_alias_conflict: true - variable_existence: true - unused_variables: true - unused_properties: true - unused_parameters: true - unused_methods: true - unreachable_code: true - too_many_arguments: true - sql_injection_vulnerabilities: true - simplify_boolean_return: true - side_effects_or_types: true - security_vulnerabilities: true - return_doc_comments: true - return_doc_comment_if_not_inferrable: true - require_scope_for_properties: true - require_scope_for_methods: true - require_php_tag_first: true - psr2_switch_declaration: true - psr2_class_declaration: true - property_assignments: true - prefer_while_loop_over_for_loop: true - precedence_mistakes: true - precedence_in_conditions: true - phpunit_assertions: true - php5_style_constructor: true - parse_doc_comments: true - parameter_non_unique: true - parameter_doc_comments: true - param_doc_comment_if_not_inferrable: true - optional_parameters_at_the_end: true - one_class_per_file: true - no_unnecessary_if: true - no_trailing_whitespace: true - no_property_on_interface: true - no_non_implemented_abstract_methods: true - no_error_suppression: true - no_duplicate_arguments: true - no_commented_out_code: true - newline_at_end_of_file: true - missing_arguments: true - method_calls_on_non_object: true - instanceof_class_exists: true - foreach_traversable: true - fix_line_ending: true - fix_doc_comments: true - duplication: true - deprecated_code_usage: true - deadlock_detection_in_loops: true - code_rating: true - closure_use_not_conflicting: true - catch_class_exists: true - blank_line_after_namespace_declaration: false - avoid_multiple_statements_on_same_line: true - avoid_duplicate_types: true - avoid_conflicting_incrementers: true - avoid_closing_tag: true - assignment_of_null_return: true - argument_type_checks: true - -filter: - paths: [src/*, tests/*] diff --git a/contributing.md b/CONTRIBUTING.md similarity index 100% rename from contributing.md rename to CONTRIBUTING.md diff --git a/README.md b/README.md index 72b73b9..9113a53 100644 --- a/README.md +++ b/README.md @@ -1,82 +1,35 @@ # Silverstripe embed -[![ko-fi](https://www.ko-fi.com/img/donate_sm.png)](https://ko-fi.com/E1E5HWRR) - -Adds embed and video a dataobject along with dataextension to apply embed to existing objects. +This module adds embed functionality to data objects ## Installation -Composer is the recommended way of installing SilverStripe modules. + +The only supported method of installing this module is via composer: + ``` composer require gorriecoe/silverstripe-embed ``` ## Requirements -- silverstripe/framework ^4.0 +- silverstripe/framework ^5 ## Maintainers -- [Gorrie Coe](https://github.com/gorriecoe) ++ PD web team ++ Original maintainer: Gorrie Coe + ## Usage -Relationship to Embed Dataobjects -```php -use gorriecoe\Embed\Models\Embed; - -class ClassName extends DataObject -{ - private static $has_one = [ - 'Embed' => Embed::class, - 'Video' => Video::class - ]; - - public function getCMSFields() - { - ... - $fields->addFieldsToTab( - 'Main', - [ - HasOneButtonField::create( - 'Embed', - 'Embed', - $this - ), - HasOneButtonField::create( - 'Video', - 'Video', - $this - ) - ] - ); - ... - } -} -``` -Update current DataObject to be Embeddable with DataExtension -```php -use gorriecoe\Embed\Extensions\Embeddable; - -class ClassName extends DataObject -{ - private static $extensions = [ - Embeddable::class, - ]; - - /** - * List the allowed included embed types. If null all are allowed. - * @var array - */ - private static $allowed_embed_types = [ - 'video', - 'photo' - ]; - - /** - * Defines tab to insert the embed fields into. - * @var string - */ - private static $embed_tab = 'Main'; -} +See [documentation for usage](./docs/en/001_index.md). -``` +## Legacy + +Copyright: see [LICENSE.md](./LICENSE.md), with additions and improvements + +This is a fork of Gorrie Coe's [embed module](https://github.com/gorriecoe/silverstripe-embed). We have maintained the following link from the original module: + +[![ko-fi](https://www.ko-fi.com/img/donate_sm.png)](https://ko-fi.com/E1E5HWRR) + +[Further information](https://github.com/elliot-sawyer/silverstripe-link/issues/31). diff --git a/code-of-conduct.md b/code-of-conduct.md new file mode 100644 index 0000000..53bf39c --- /dev/null +++ b/code-of-conduct.md @@ -0,0 +1 @@ +When having discussions about this module in issues or pull request please adhere to the [SilverStripe Community Code of Conduct](https://docs.silverstripe.org/en/contributing/code_of_conduct). diff --git a/composer.json b/composer.json index e8c67d7..b90934c 100644 --- a/composer.json +++ b/composer.json @@ -1,12 +1,17 @@ { - "name": "gorriecoe/silverstripe-embed", - "description": "Adds embed and video a dataobject along with dataextension to apply embed to existing objects.", + "name": "nswdpc/silverstripe-embed", + "description": "Adds embed functionality to data objects.", "type": "silverstripe-vendormodule", "license": "BSD-3-Clause", "authors": [ { "name": "Gorrie Coe", - "email": "gorriecoe@gmail.com" + "email": "gorriecoe@gmail.com", + "role": "Original author" + }, + { + "name": "PD web team", + "role": "Maintainer" } ], "keywords": [ @@ -14,12 +19,19 @@ "cms", "embed" ], - "homepage": "http://github.com/gorriecoe/silverstripe-embed", + "homepage": "http://github.com/nswdpc/silverstripe-embed", "require": { - "silverstripe/framework": "^4", - "gorriecoe/silverstripe-htmltag": "^1.0" + "embed/embed": "^4.4", + "silverstripe/framework": "^5", + "silverstripe/asset-admin": "^2" + }, + "replace": { + "gorriecoe/silverstripe-embed": "self.version" }, - "extra": { - "installer-name": "embed" + "autoload": { + "psr-4": { + "NSWDPC\\Embed\\": "src/", + "NSWDPC\\Embed\\Tests\\": "tests/" + } } } diff --git a/docs/en/001_index.md b/docs/en/001_index.md new file mode 100644 index 0000000..80570fe --- /dev/null +++ b/docs/en/001_index.md @@ -0,0 +1,108 @@ +# Documentation + +## Changes + +Since the original v1: + ++ Namespace update ++ Template change in Embeddable from Embed to NSWDPC/Embed/Models/Embed ++ Dropped support for gorriecode/silverstripe-htmltag ++ Requires embed/embed:^4 ++ Requires silverstripe/framework:^5 ++ Requires silverstripe/asset-admin:^2 ++ Remove logic no longer supported by embed/embed:^4 + +You can diff across repos to view all changes. + +## Usage + +The module can be used via standard Silverstripe relationship handling or via the `Embeddable` extension (or both) + + +### Via relationship + +Example implementation, using HasOneButtonField + +```php + Embed::class, + 'Video' => Video::class + ]; + + /** + * @inheritdoc + */ + public function getCMSFields() + { + $fields = parent::getCMSFields(); + $fields->addFieldsToTab( + 'Main', + [ + HasOneButtonField::create( + 'Embed', + 'Embed', + $this + ), + HasOneButtonField::create( + 'Video', + 'Video', + $this + ) + ] + ); + return $fields; + } +} +``` + +### Via extension + +Update current DataObject to be Embeddable with DataExtension + +```php + 'Varchar(255)', + 'EmbedType' => 'Varchar', + 'EmbedSourceURL' => 'Varchar(255)', + 'EmbedSourceImageURL' => 'Varchar(255)', + 'EmbedHTML' => 'HTMLText', + 'EmbedWidth' => 'Varchar', + 'EmbedHeight' => 'Varchar', + 'EmbedAspectRatio' => 'Varchar', + 'EmbedDescription' => 'HTMLText' + ]; + + /** + * @inheritdoc + */ + private static array $has_one = [ + 'EmbedImage' => Image::class + ]; + + /** + * @inheritdoc + */ + private static array $owns = [ + 'EmbedImage' + ]; + + /** + * Defines tab to insert the embed fields into. + * @var string + */ + private static string $embed_tab = 'Main'; + + /** + * List of custom CSS classes for template. + * @var array + */ + protected array $classes = []; + + /** + * Defines the template to render the embed in. + * @var string + */ + protected string $template = 'NSWDPC/Embed/Models/Embed'; + + /** + * @inheritdoc + */ + public function updateCMSFields(FieldList $fields) + { + $owner = $this->getOwner(); + $tab = $owner->config()->get('embed_tab'); + $tab = isset($tab) ? $tab : 'Main'; + + // Ensure these fields don't get added by fields scaffold + $fields->removeByName([ + 'EmbedTitle', + 'EmbedType', + 'EmbedSourceURL', + 'EmbedSourceImageURL', + 'EmbedHTML', + 'EmbedWidth', + 'EmbedHeight', + 'EmbedAspectRatio', + 'EmbedDescription', + 'EmbedImage' + ]); + + $fields->addFieldsToTab( + 'Root.' . $tab, + [ + TextField::create( + 'EmbedTitle', + _t(__CLASS__ . '.TITLELABEL', 'Title') + ) + ->setDescription( + _t(__CLASS__ . '.TITLEDESCRIPTION', 'Optional. Will be auto-generated if left blank') + ), + TextField::create( + 'EmbedSourceURL', + _t(__CLASS__ . '.SOURCEURLLABEL', 'Source URL') + ) + ->setDescription( + _t(__CLASS__ . '.SOURCEURLDESCRIPTION', 'Specify a external URL') + ), + UploadField::create( + 'EmbedImage', + _t(__CLASS__ . '.IMAGELABEL', 'Image') + ) + ->setFolderName($owner->EmbedFolder) + ->setAllowedExtensions(['jpg','png','gif']), + TextareaField::create( + 'EmbedDescription', + _t(__CLASS__ . '.DESCRIPTIONLABEL', 'Description') + ) + ] + ); + + if (isset($owner->AllowedEmbedTypes) && count($owner->AllowedEmbedTypes) > 1) { + $fields->addFieldToTab( + 'Root.' . $tab, + ReadonlyField::create( + 'EmbedType', + _t(__CLASS__ . '.TYPELABEL', 'Type') + ), + 'EmbedImage' + ); + } + + return $fields; + } + + /** + * Get the embed data using a source URL and write relevant data to the owner + */ + protected function writeFromEmbed(string $sourceURL): bool { + try { + if($sourceURL === '') { + throw new \RuntimeException(_t(self::class . '.EMPTY_SOURCE_URL', 'Source URL is empty')); + } + $embed = new Embed($sourceURL); + if(!$embed) { + throw new \RuntimeException(_t(self::class . '.INVALID_EMBED', 'The embed record is invalid')); + } + + $owner = $this->getOwner(); + // write title if current is empty + if ($owner->EmbedTitle == '') { + $owner->EmbedTitle = $embed->Title; + } + // write description if current is empty + if ($owner->EmbedDescription == '') { + $owner->EmbedDescription = $embed->Description; + } + + if ($this->owner->isChanged('EmbedSourceURL')) { + // embed data from updated source URL + $owner->EmbedHTML = $embed->code->html; + $owner->EmbedType = null;// update embed type in your own DataObject + $owner->EmbedWidth = $embed->code->width; + $owner->EmbedHeight = $embed->code->height; + $owner->EmbedAspectRatio = $embed->code->ratio; + // allow some customisation from the owner object prior to write, when the source url has changed + $owner->extend('onEmbedSourceChange', $embed); + } + + } catch (\Throwable $e) { + Logger::log("Error writing embed object: " . $e->getMessage()); + throw new ValidationException( + _t( + self::class . ".FAILED_TO_WRITE_EMBED", + "Sorry, the embed details could not be found or saved. Please check the URL entered and try again" + ) + ); + } + } + + /** + * @inheritdoc + */ + public function onBeforeWrite() + { + parent::onBeforeWrite(); + $owner = $this->getOwner(); + $this->writeFromEmbed($owner->EmbedSourceURL ?? ''); + } + + /** + * Get embed types allowed in this instance + */ + public function getAllowedEmbedTypes() : array|null + { + return $this->getOwner()->config()->get('allowed_embed_types'); + } + + /** + * @return string + */ + public function getEmbedFolder(): string + { + $owner = $this->getOwner(); + $folder = (string)$owner->config()->get('embed_folder'); + if ($folder === '') { + $folder = 'Embeddable'; + } + return $folder; + } + + /** + * Set CSS classes for templates + * @param string $class CSS classes + */ + public function setEmbedClass(string $class): DataObject + { + $classes = ($class) ? explode(' ', $class) : []; + foreach ($classes as $key => $value) { + $this->classes[$value] = $value; + } + return $this->getOwner(); + } + + /** + * Returns the CSS classes for this embed + */ + public function getEmbedClass(): string + { + return implode(' ', $this->classes); + } + + /** + * Set CSS classes for templates + * @param string $template template name without the .ss + * @return DataObject Owner + */ + public function setEmbedTemplate(string $template): DataObject + { + $this->template = $template; + return $this->getOwner(); + } + + /** + * Renders embed into appropriate template HTML + * @return HTML + */ + public function getEmbed() + { + $owner = $this->getOwner(); + $title = $owner->EmbedTitle; + $cssClasses = $owner->EmbedClass; + $type = $owner->EmbedType; + $template = $this->template; + $embedHTML = $owner->EmbedHTML; + $sourceURL = $owner->EmbedSourceURL; + $templates = []; + if($type !== '') { + $templates[] = $template . '_' . $type; + } + $templates[] = $template; + $templates[] = "Embed";// BC support for original Embed template + if (SSViewer::hasTemplate($templates)) { + return $owner->renderWith($templates); + } + $html = ''; + $attributes = []; + if($cssClasses !== '') { + $attributes['class'] = $cssClasses; + } + switch ($type) { + case 'video': + case 'rich': + $html = HTML::createTag('div', $attributes, $embedHTML); + break; + case 'link': + $attributes['href'] = $sourceURL; + $html = HTML::createTag('a', $attributes, $title); + break; + case 'photo': + case 'image': + case 'picture': + $attributes['src'] = $sourceURL; + $attributes['width'] = $this->Width; + $attributes['height'] = $this->Height; + $attributes['alt'] = $title ?? ''; + $html = HTML::createTag('img', $attributes); + break; + default: + $html = ""; + break; + } + return $html; + } +} diff --git a/src/models/Embed.php b/src/Models/Embed.php similarity index 54% rename from src/models/Embed.php rename to src/Models/Embed.php index 60706a7..e9bf5d1 100644 --- a/src/models/Embed.php +++ b/src/Models/Embed.php @@ -1,12 +1,12 @@ 'Title', 'EmbedType' => 'Type', 'EmbedSourceURL' => 'URL' - ); + ]; /** - * Defines extension names and parameters to be applied - * to this object upon construction. - * @var array + * @inheritdoc */ - private static $extensions = array( + private static array $extensions = [ Embeddable::class - ); - + ]; /** * List the allowed included embed types. If null all are allowed. * - * @var array + * @var array|null */ private static $allowed_embed_types = null; @@ -64,11 +56,10 @@ class Embed extends DataObject * * @var string */ - private static $embed_folder = 'Embed'; + private static string $embed_folder = 'Embed'; /** - * CMS Fields - * @return FieldList + * @inheritdoc */ public function getCMSFields() { @@ -77,16 +68,14 @@ public function getCMSFields() "Root", Tab::create("Main") ) - ->setTitle(_t('SiteTree.TABMAIN', "Main")) + ->setTitle(_t(self::class. '.TABMAIN', "Main")) ); $this->extend('updateCMSFields', $fields); return $fields; } /** - * Alias for EmbedTitle - * This is used by CMS Title and breadcrumbs. - * @return String + * @inheritdoc */ public function getTitle() { @@ -95,10 +84,10 @@ public function getTitle() /** * Set CSS classes for templates - * @param string $class CSS classes. - * @return $this + * See Embeddable::getEmbedClass() + * @param string $class CSS classes */ - public function setClass($class) + public function setClass(string $class): self { $this->setEmbedClass($class); return $this; @@ -106,15 +95,16 @@ public function setClass($class) /** * Returns the classes for this embed. - * @return string + * See Embeddable::getEmbedClass() */ - public function getClass() + public function getClass(): string { - return $this->EmbedClass; + return $this->getEmbedClass(); } /** * Renders an HTML anchor tag for this link + * See Embeddable::getEmbed() * @return HTML */ public function forTemplate() diff --git a/src/Models/Video.php b/src/Models/Video.php new file mode 100644 index 0000000..bbbfbf0 --- /dev/null +++ b/src/Models/Video.php @@ -0,0 +1,42 @@ + 'Varchar(255)', - 'EmbedType' => 'Varchar', - 'EmbedSourceURL' => 'Varchar(255)', - 'EmbedSourceImageURL' => 'Varchar(255)', - 'EmbedHTML' => 'HTMLText', - 'EmbedWidth' => 'Varchar', - 'EmbedHeight' => 'Varchar', - 'EmbedAspectRatio' => 'Varchar', - 'EmbedDescription' => 'HTMLText' - ]; - - /** - * Has_one relationship - * @var array - */ - private static $has_one = [ - 'EmbedImage' => Image::class - ]; - - /** - * Relationship version ownership - * @var array - */ - private static $owns = [ - 'EmbedImage' - ]; - - /** - * Defines tab to insert the embed fields into. - * @var string - */ - private static $embed_tab = 'Main'; - - /** - * List of custom CSS classes for template. - * @var array - */ - protected $classes = []; - - /** - * Defines the template to render the embed in. - * @var string - */ - protected $template = 'Embed'; - - /** - * Update Fields - * @return FieldList - */ - public function updateCMSFields(FieldList $fields) - { - $owner = $this->owner; - $tab = $owner->config()->get('embed_tab'); - $tab = isset($tab) ? $tab : 'Main'; - - // Ensure these fields don't get added by fields scaffold - $fields->removeByName([ - 'EmbedTitle', - 'EmbedType', - 'EmbedSourceURL', - 'EmbedSourceImageURL', - 'EmbedHTML', - 'EmbedWidth', - 'EmbedHeight', - 'EmbedAspectRatio', - 'EmbedDescription', - 'EmbedImage' - ]); - - $fields->addFieldsToTab( - 'Root.' . $tab, - array( - TextField::create( - 'EmbedTitle', - _t(__CLASS__ . '.TITLELABEL', 'Title') - ) - ->setDescription( - _t(__CLASS__ . '.TITLEDESCRIPTION', 'Optional. Will be auto-generated if left blank') - ), - TextField::create( - 'EmbedSourceURL', - _t(__CLASS__ . '.SOURCEURLLABEL', 'Source URL') - ) - ->setDescription( - _t(__CLASS__ . '.SOURCEURLDESCRIPTION', 'Specify a external URL') - ), - UploadField::create( - 'EmbedImage', - _t(__CLASS__ . '.IMAGELABEL', 'Image') - ) - ->setFolderName($owner->EmbedFolder) - ->setAllowedExtensions(['jpg','png','gif']), - TextareaField::create( - 'EmbedDescription', - _t(__CLASS__ . '.DESCRIPTIONLABEL', 'Description') - ) - ) - ); - - if (isset($owner->AllowedEmbedTypes) && Count($owner->AllowedEmbedTypes) > 1) { - $fields->addFieldToTab( - 'Root.' . $tab, - ReadonlyField::create( - 'EmbedType', - _t(__CLASS__ . '.TYPELABEL', 'Type') - ), - 'EmbedImage' - ); - } - - return $fields; - } - - /** - * Event handler called before writing to the database. - */ - public function onBeforeWrite() - { - $owner = $this->owner; - if ($sourceURL = $owner->EmbedSourceURL) { - $embed = Embed::create($sourceURL); - if ($owner->EmbedTitle == '') { - $owner->EmbedTitle = $embed->Title; - } - if (!$owner->EmbedDescription == '') { - $owner->EmbedDescription = $embed->Description; - } - $changes = $owner->getChangedFields(); - if (isset($changes['EmbedSourceURL']) && !$owner->EmbedImageID) { - $owner->EmbedHTML = $embed->Code; - $owner->EmbedType = $embed->Type; - $owner->EmbedWidth = $embed->Width; - $owner->EmbedHeight = $embed->Height; - $owner->EmbedAspectRatio = $embed->AspectRatio; - if ($owner->EmbedSourceImageURL != $embed->Image) { - $owner->EmbedSourceImageURL = $embed->Image; - $fileExplode = explode('.', $embed->Image); - $fileExtension = end($fileExplode); - $fileName = Convert::raw2url($owner->obj('EmbedTitle')->LimitCharacters(55)) . '.' . $fileExtension; - $parentFolder = Folder::find_or_make($owner->EmbedFolder); - - $imageObject = DataObject::get_one( - Image::class, - [ - 'Name' => $fileName, - 'ParentID' => $parentFolder->ID - ] - ); - if(!$imageObject){ - // Save image to server - $imageObject = Image::create(); - $imageObject->setFromString( - file_get_contents($embed->Image), - $owner->EmbedFolder . '/' . $fileName, - null, - null, - [ - 'conflict' => AssetStore::CONFLICT_OVERWRITE - ] - ); - } - - // Check existing for image object or create new - $imageObject->ParentID = $parentFolder->ID; - $imageObject->Name = $fileName; - $imageObject->Title = $embed->getTitle(); - $imageObject->OwnerID = (Member::currentUserID() ? Member::currentUserID() : 0); - $imageObject->ShowInSearch = false; - $imageObject->write(); - - $owner->EmbedImageID = $imageObject->ID; - } - } - } - } - - /** - * @return array()|null - */ - public function getAllowedEmbedTypes() - { - return $this->owner->config()->get('allowed_embed_types'); - } - - /** - * @param ValidationResult $validationResult - * @return ValidationResult - */ - public function validate(ValidationResult $validationResult) - { - $owner = $this->owner; - $allowed_types = $owner->AllowedEmbedTypes; - $sourceURL = $owner->EmbedSourceURL; - if ($sourceURL && isset($allowed_types)) { - $embed = Embed::create($sourceURL); - if (!in_array($embed->Type, $allowed_types)) { - $string = implode(', ', $allowed_types); - $string = (substr($string, -1) == ',') ? substr_replace($string, ' or', -1) : $string; - $validationResult->addError( - _t(__CLASS__ . '.ERRORNOTSTRING', "The embed content is not a $string") - ); - } - } - return $validationResult; - } - - /** - * @return string - */ - public function getEmbedFolder() - { - $owner = $this->owner; - $folder = $owner->config()->get('embed_folder'); - if (!isset($folder)) { - $folder = $owner->ClassName; - } - return $folder; - } - - /** - * Set CSS classes for templates - * @param string $class CSS classes. - * @return DataObject Owner - */ - public function setEmbedClass($class) - { - $classes = ($class) ? explode(' ', $class) : []; - foreach ($classes as $key => $value) { - $this->classes[$value] = $value; - } - return $this->owner; - } - - /** - * Returns the classes for this embed. - * @return string - */ - public function getEmbedClass() - { - $classes = $this->classes; - if (Count($classes)) { - return implode(' ', $classes); - } - } - - /** - * Set CSS classes for templates - * @param string $class CSS classes. - * @return DataObject Owner - */ - public function setEmbedTemplate($template) - { - if (isset($template)) { - $this->template = $template; - } - return $this->owner; - } - - /** - * Renders embed into appropriate template HTML - * @return HTML - */ - public function getEmbed() - { - $owner = $this->owner; - $title = $owner->EmbedTitle; - $class = $owner->EmbedClass; - $type = $owner->EmbedType; - $template = $this->template; - $embedHTML = $owner->EmbedHTML; - $sourceURL = $owner->EmbedSourceURL; - $templates = []; - if ($type) { - $templates[] = $template . '_' . $type; - } - $templates[] = $template; - if (SSViewer::hasTemplate($templates)) { - return $owner->renderWith($templates); - } - switch ($type) { - case 'video': - case 'rich': - $html = HTMLTag::create($embedHTML, 'div'); - break; - case 'link': - $html = HTMLTag::create($title, 'a')->setAttribute('href', $sourceURL); - break; - case 'photo': - $html = HTMLTag::create($sourceURL, 'img')->setAttribute([ - 'width' => $this->Width, - 'height' => $this->Height, - 'alt' => $title - ]); - break; - } - return $html->setClass($class); - } -} diff --git a/src/models/Video.php b/src/models/Video.php deleted file mode 100644 index ba91c95..0000000 --- a/src/models/Video.php +++ /dev/null @@ -1,45 +0,0 @@ - diff --git a/tests/.gitkeep b/tests/.gitkeep new file mode 100644 index 0000000..e69de29 From 2a5f8bb4b0791ce1751458876596def9ab3b4687 Mon Sep 17 00:00:00 2001 From: "James (DPC)" <69664712+JamesDPC@users.noreply.github.com> Date: Tue, 13 Aug 2024 11:36:36 +1000 Subject: [PATCH 02/18] Add dev requirements, clean up formatting per .editorconfig --- composer.json | 78 +++++++++++++++++++++++++++++---------------------- 1 file changed, 45 insertions(+), 33 deletions(-) diff --git a/composer.json b/composer.json index b90934c..e630d0e 100644 --- a/composer.json +++ b/composer.json @@ -1,37 +1,49 @@ { - "name": "nswdpc/silverstripe-embed", - "description": "Adds embed functionality to data objects.", - "type": "silverstripe-vendormodule", - "license": "BSD-3-Clause", - "authors": [ - { - "name": "Gorrie Coe", - "email": "gorriecoe@gmail.com", - "role": "Original author" + "name": "nswdpc/silverstripe-embed", + "description": "Adds embed functionality to data objects.", + "type": "silverstripe-vendormodule", + "license": "BSD-3-Clause", + "authors": [ + { + "name": "Gorrie Coe", + "email": "gorriecoe@gmail.com", + "role": "Original author" + }, + { + "name": "PD web team", + "role": "Maintainer" + } + ], + "keywords": [ + "silverstripe", + "cms", + "embed" + ], + "homepage": "http://github.com/nswdpc/silverstripe-embed", + "require": { + "embed/embed": "^4.4", + "silverstripe/framework": "^5", + "silverstripe/asset-admin": "^2" }, - { - "name": "PD web team", - "role": "Maintainer" + "require-dev": { + "cambis/silverstripe-rector": "^0.5.1", + "phpunit/phpunit": "^9.5", + "syntro/silverstripe-phpstan": "^5", + "nswdpc/ci-files": "^1" + }, + "repositories": [ + { + "type": "git", + "url": "https://github.com/nswdpc/ci-files.git" + } + ], + "replace": { + "gorriecoe/silverstripe-embed": "self.version" + }, + "autoload": { + "psr-4": { + "NSWDPC\\Embed\\": "src/", + "NSWDPC\\Embed\\Tests\\": "tests/" + } } - ], - "keywords": [ - "silverstripe", - "cms", - "embed" - ], - "homepage": "http://github.com/nswdpc/silverstripe-embed", - "require": { - "embed/embed": "^4.4", - "silverstripe/framework": "^5", - "silverstripe/asset-admin": "^2" - }, - "replace": { - "gorriecoe/silverstripe-embed": "self.version" - }, - "autoload": { - "psr-4": { - "NSWDPC\\Embed\\": "src/", - "NSWDPC\\Embed\\Tests\\": "tests/" - } - } } From fdc4be426339758fe5b25521edcd7020d5b061ca Mon Sep 17 00:00:00 2001 From: "James (DPC)" <69664712+JamesDPC@users.noreply.github.com> Date: Tue, 13 Aug 2024 11:38:14 +1000 Subject: [PATCH 03/18] (php-cs-fixer) fix --- src/Extensions/Embeddable.php | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/Extensions/Embeddable.php b/src/Extensions/Embeddable.php index ba87c2a..9a17fdb 100644 --- a/src/Extensions/Embeddable.php +++ b/src/Extensions/Embeddable.php @@ -141,7 +141,8 @@ public function updateCMSFields(FieldList $fields) /** * Get the embed data using a source URL and write relevant data to the owner */ - protected function writeFromEmbed(string $sourceURL): bool { + protected function writeFromEmbed(string $sourceURL): bool + { try { if($sourceURL === '') { throw new \RuntimeException(_t(self::class . '.EMPTY_SOURCE_URL', 'Source URL is empty')); @@ -196,7 +197,7 @@ public function onBeforeWrite() /** * Get embed types allowed in this instance */ - public function getAllowedEmbedTypes() : array|null + public function getAllowedEmbedTypes(): array|null { return $this->getOwner()->config()->get('allowed_embed_types'); } From 88bc1d90d2ca51016a5539861b87b799bf5218c1 Mon Sep 17 00:00:00 2001 From: "James (DPC)" <69664712+JamesDPC@users.noreply.github.com> Date: Tue, 13 Aug 2024 11:52:51 +1000 Subject: [PATCH 04/18] (ci) add workflow --- .github/workflows/ci.yml | 13 +++++++++++++ 1 file changed, 13 insertions(+) create mode 100644 .github/workflows/ci.yml diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 0000000..6df18e4 --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,13 @@ +name: CI + +on: + pull_request: null + +jobs: + Silverstripe: + name: 'Silverstripe (bundle)' + uses: nswdpc/ci-files/.github/workflows/silverstripe.yml@1.1.1 + PHPStan: + name: 'PHPStan (analyse)' + uses: nswdpc/ci-files/.github/workflows/phpstan.silverstripe.yml@1.1.1 + needs: Silverstripe From 63b09f7e144fdef50898dbbb947b36032c8f0281 Mon Sep 17 00:00:00 2001 From: "James (DPC)" <69664712+JamesDPC@users.noreply.github.com> Date: Tue, 13 Aug 2024 11:53:31 +1000 Subject: [PATCH 05/18] Add logger class --- src/Services/Logger.php | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) create mode 100644 src/Services/Logger.php diff --git a/src/Services/Logger.php b/src/Services/Logger.php new file mode 100644 index 0000000..2cc8262 --- /dev/null +++ b/src/Services/Logger.php @@ -0,0 +1,17 @@ +get(LoggerInterface::class)->log($level, $message); + } +} From 3527f4907a5abb0911a3049f8831660d42c4475c Mon Sep 17 00:00:00 2001 From: "James (DPC)" <69664712+JamesDPC@users.noreply.github.com> Date: Tue, 13 Aug 2024 11:53:46 +1000 Subject: [PATCH 06/18] Minor code quality improvements --- src/Extensions/Embeddable.php | 20 +++++++++----------- src/Models/Embed.php | 4 +--- src/Models/Video.php | 4 ---- 3 files changed, 10 insertions(+), 18 deletions(-) diff --git a/src/Extensions/Embeddable.php b/src/Extensions/Embeddable.php index 9a17fdb..6800e21 100644 --- a/src/Extensions/Embeddable.php +++ b/src/Extensions/Embeddable.php @@ -3,6 +3,7 @@ namespace NSWDPC\Embed\Extensions; use Embed\Embed; +use NSWDPC\Embed\Services\Logger; use SilverStripe\Assets\Image; use SilverStripe\Assets\Folder; use SilverStripe\Assets\File; @@ -55,19 +56,16 @@ class Embeddable extends DataExtension /** * Defines tab to insert the embed fields into. - * @var string */ private static string $embed_tab = 'Main'; /** * List of custom CSS classes for template. - * @var array */ protected array $classes = []; /** * Defines the template to render the embed in. - * @var string */ protected string $template = 'NSWDPC/Embed/Models/Embed'; @@ -78,7 +76,7 @@ public function updateCMSFields(FieldList $fields) { $owner = $this->getOwner(); $tab = $owner->config()->get('embed_tab'); - $tab = isset($tab) ? $tab : 'Main'; + $tab = is_string($tab) ? $tab : 'Main'; // Ensure these fields don't get added by fields scaffold $fields->removeByName([ @@ -124,7 +122,8 @@ public function updateCMSFields(FieldList $fields) ] ); - if (isset($owner->AllowedEmbedTypes) && count($owner->AllowedEmbedTypes) > 1) { + $allowedEmbedTypes = $this->getOwner()->getAllowedEmbedTypes(); + if (is_array($allowedEmbedTypes) && count($allowedEmbedTypes) > 1) { $fields->addFieldToTab( 'Root.' . $tab, ReadonlyField::create( @@ -141,8 +140,7 @@ public function updateCMSFields(FieldList $fields) /** * Get the embed data using a source URL and write relevant data to the owner */ - protected function writeFromEmbed(string $sourceURL): bool - { + protected function writeFromEmbed(string $sourceURL): bool { try { if($sourceURL === '') { throw new \RuntimeException(_t(self::class . '.EMPTY_SOURCE_URL', 'Source URL is empty')); @@ -162,7 +160,7 @@ protected function writeFromEmbed(string $sourceURL): bool $owner->EmbedDescription = $embed->Description; } - if ($this->owner->isChanged('EmbedSourceURL')) { + if ($owner->isChanged('EmbedSourceURL')) { // embed data from updated source URL $owner->EmbedHTML = $embed->code->html; $owner->EmbedType = null;// update embed type in your own DataObject @@ -178,7 +176,7 @@ protected function writeFromEmbed(string $sourceURL): bool throw new ValidationException( _t( self::class . ".FAILED_TO_WRITE_EMBED", - "Sorry, the embed details could not be found or saved. Please check the URL entered and try again" + "Sorry, the embed details could not be found or saved. Please check the URL entered and try again." ) ); } @@ -197,13 +195,13 @@ public function onBeforeWrite() /** * Get embed types allowed in this instance */ - public function getAllowedEmbedTypes(): array|null + public function getAllowedEmbedTypes() : array|null { return $this->getOwner()->config()->get('allowed_embed_types'); } /** - * @return string + * Return embed folder from configuration or default */ public function getEmbedFolder(): string { diff --git a/src/Models/Embed.php b/src/Models/Embed.php index e9bf5d1..ec33e1e 100644 --- a/src/Models/Embed.php +++ b/src/Models/Embed.php @@ -49,12 +49,10 @@ class Embed extends DataObject * * @var array|null */ - private static $allowed_embed_types = null; + private static $allowed_embed_types; /** * Defines upload folder for embedded assets - * - * @var string */ private static string $embed_folder = 'Embed'; diff --git a/src/Models/Video.php b/src/Models/Video.php index bbbfbf0..7d38976 100644 --- a/src/Models/Video.php +++ b/src/Models/Video.php @@ -26,8 +26,6 @@ class Video extends Embed /** * List the allowed included embed types. If null all are allowed. - * - * @var array */ private static array $allowed_embed_types = [ 'video' @@ -35,8 +33,6 @@ class Video extends Embed /** * Defines upload folder for embedded assets - * - * @var string */ private static string $embed_folder = 'Video'; } From 804c4c6336b448f020fd156f0f32fb6efcc26575 Mon Sep 17 00:00:00 2001 From: JamesDPC Date: Tue, 13 Aug 2024 01:56:42 +0000 Subject: [PATCH 07/18] [php-cs-fixer] Automated updates generated by php-cs-fixer configuration --- composer.json | 8 ++++++++ src/Extensions/Embeddable.php | 5 +++-- 2 files changed, 11 insertions(+), 2 deletions(-) diff --git a/composer.json b/composer.json index e630d0e..4d312ee 100644 --- a/composer.json +++ b/composer.json @@ -45,5 +45,13 @@ "NSWDPC\\Embed\\": "src/", "NSWDPC\\Embed\\Tests\\": "tests/" } + }, + "config": { + "allow-plugins": { + "composer/installers": true, + "silverstripe/recipe-plugin": true, + "silverstripe/vendor-plugin": true, + "phpstan/extension-installer": true + } } } diff --git a/src/Extensions/Embeddable.php b/src/Extensions/Embeddable.php index 6800e21..df0f4a2 100644 --- a/src/Extensions/Embeddable.php +++ b/src/Extensions/Embeddable.php @@ -140,7 +140,8 @@ public function updateCMSFields(FieldList $fields) /** * Get the embed data using a source URL and write relevant data to the owner */ - protected function writeFromEmbed(string $sourceURL): bool { + protected function writeFromEmbed(string $sourceURL): bool + { try { if($sourceURL === '') { throw new \RuntimeException(_t(self::class . '.EMPTY_SOURCE_URL', 'Source URL is empty')); @@ -195,7 +196,7 @@ public function onBeforeWrite() /** * Get embed types allowed in this instance */ - public function getAllowedEmbedTypes() : array|null + public function getAllowedEmbedTypes(): array|null { return $this->getOwner()->config()->get('allowed_embed_types'); } From b0dfdc2e3bdba917b13ab44bb18aed01b510a261 Mon Sep 17 00:00:00 2001 From: JamesDPC Date: Tue, 13 Aug 2024 01:56:49 +0000 Subject: [PATCH 08/18] [rector] Automated updates generated by rector configuration --- src/Extensions/Embeddable.php | 38 ++++++++++++++++++++--------------- 1 file changed, 22 insertions(+), 16 deletions(-) diff --git a/src/Extensions/Embeddable.php b/src/Extensions/Embeddable.php index df0f4a2..76964f8 100644 --- a/src/Extensions/Embeddable.php +++ b/src/Extensions/Embeddable.php @@ -97,27 +97,27 @@ public function updateCMSFields(FieldList $fields) [ TextField::create( 'EmbedTitle', - _t(__CLASS__ . '.TITLELABEL', 'Title') + _t(self::class . '.TITLELABEL', 'Title') ) ->setDescription( - _t(__CLASS__ . '.TITLEDESCRIPTION', 'Optional. Will be auto-generated if left blank') + _t(self::class . '.TITLEDESCRIPTION', 'Optional. Will be auto-generated if left blank') ), TextField::create( 'EmbedSourceURL', - _t(__CLASS__ . '.SOURCEURLLABEL', 'Source URL') + _t(self::class . '.SOURCEURLLABEL', 'Source URL') ) ->setDescription( - _t(__CLASS__ . '.SOURCEURLDESCRIPTION', 'Specify a external URL') + _t(self::class . '.SOURCEURLDESCRIPTION', 'Specify a external URL') ), UploadField::create( 'EmbedImage', - _t(__CLASS__ . '.IMAGELABEL', 'Image') + _t(self::class . '.IMAGELABEL', 'Image') ) ->setFolderName($owner->EmbedFolder) ->setAllowedExtensions(['jpg','png','gif']), TextareaField::create( 'EmbedDescription', - _t(__CLASS__ . '.DESCRIPTIONLABEL', 'Description') + _t(self::class . '.DESCRIPTIONLABEL', 'Description') ) ] ); @@ -128,7 +128,7 @@ public function updateCMSFields(FieldList $fields) 'Root.' . $tab, ReadonlyField::create( 'EmbedType', - _t(__CLASS__ . '.TYPELABEL', 'Type') + _t(self::class . '.TYPELABEL', 'Type') ), 'EmbedImage' ); @@ -146,6 +146,7 @@ protected function writeFromEmbed(string $sourceURL): bool if($sourceURL === '') { throw new \RuntimeException(_t(self::class . '.EMPTY_SOURCE_URL', 'Source URL is empty')); } + $embed = new Embed($sourceURL); if(!$embed) { throw new \RuntimeException(_t(self::class . '.INVALID_EMBED', 'The embed record is invalid')); @@ -156,6 +157,7 @@ protected function writeFromEmbed(string $sourceURL): bool if ($owner->EmbedTitle == '') { $owner->EmbedTitle = $embed->Title; } + // write description if current is empty if ($owner->EmbedDescription == '') { $owner->EmbedDescription = $embed->Description; @@ -172,14 +174,12 @@ protected function writeFromEmbed(string $sourceURL): bool $owner->extend('onEmbedSourceChange', $embed); } - } catch (\Throwable $e) { - Logger::log("Error writing embed object: " . $e->getMessage()); - throw new ValidationException( - _t( - self::class . ".FAILED_TO_WRITE_EMBED", - "Sorry, the embed details could not be found or saved. Please check the URL entered and try again." - ) - ); + } catch (\Throwable $throwable) { + Logger::log("Error writing embed object: " . $throwable->getMessage()); + throw \SilverStripe\ORM\ValidationException::create(_t( + self::class . ".FAILED_TO_WRITE_EMBED", + "Sorry, the embed details could not be found or saved. Please check the URL entered and try again." + )); } } @@ -211,6 +211,7 @@ public function getEmbedFolder(): string if ($folder === '') { $folder = 'Embeddable'; } + return $folder; } @@ -221,9 +222,10 @@ public function getEmbedFolder(): string public function setEmbedClass(string $class): DataObject { $classes = ($class) ? explode(' ', $class) : []; - foreach ($classes as $key => $value) { + foreach ($classes as $value) { $this->classes[$value] = $value; } + return $this->getOwner(); } @@ -263,16 +265,19 @@ public function getEmbed() if($type !== '') { $templates[] = $template . '_' . $type; } + $templates[] = $template; $templates[] = "Embed";// BC support for original Embed template if (SSViewer::hasTemplate($templates)) { return $owner->renderWith($templates); } + $html = ''; $attributes = []; if($cssClasses !== '') { $attributes['class'] = $cssClasses; } + switch ($type) { case 'video': case 'rich': @@ -295,6 +300,7 @@ public function getEmbed() $html = ""; break; } + return $html; } } From d0d40e18563a643ebe5f0848d9e58d19af21d81b Mon Sep 17 00:00:00 2001 From: "James (DPC)" <69664712+JamesDPC@users.noreply.github.com> Date: Tue, 13 Aug 2024 12:25:24 +1000 Subject: [PATCH 09/18] (phpstan) fix and update complaints --- src/Extensions/Embeddable.php | 35 +++++++++++++++++++++-------------- src/Models/Embed.php | 9 +++------ src/Models/Video.php | 2 +- 3 files changed, 25 insertions(+), 21 deletions(-) diff --git a/src/Extensions/Embeddable.php b/src/Extensions/Embeddable.php index 76964f8..ab340f2 100644 --- a/src/Extensions/Embeddable.php +++ b/src/Extensions/Embeddable.php @@ -18,6 +18,7 @@ use SilverStripe\ORM\ValidationException; use SilverStripe\ORM\ValidationResult; use SilverStripe\ORM\DataExtension; +use SilverStripe\View\HTML; use SilverStripe\View\SSViewer; /** @@ -122,8 +123,8 @@ public function updateCMSFields(FieldList $fields) ] ); - $allowedEmbedTypes = $this->getOwner()->getAllowedEmbedTypes(); - if (is_array($allowedEmbedTypes) && count($allowedEmbedTypes) > 1) { + $allowedEmbedTypes = $this->getAllowedEmbedTypes(); + if (count($allowedEmbedTypes) > 1) { $fields->addFieldToTab( 'Root.' . $tab, ReadonlyField::create( @@ -147,20 +148,18 @@ protected function writeFromEmbed(string $sourceURL): bool throw new \RuntimeException(_t(self::class . '.EMPTY_SOURCE_URL', 'Source URL is empty')); } - $embed = new Embed($sourceURL); - if(!$embed) { - throw new \RuntimeException(_t(self::class . '.INVALID_EMBED', 'The embed record is invalid')); - } + $embed = new Embed(); + $embed = $embed->get($sourceURL); $owner = $this->getOwner(); // write title if current is empty if ($owner->EmbedTitle == '') { - $owner->EmbedTitle = $embed->Title; + $owner->EmbedTitle = $embed->title; } // write description if current is empty if ($owner->EmbedDescription == '') { - $owner->EmbedDescription = $embed->Description; + $owner->EmbedDescription = $embed->description; } if ($owner->isChanged('EmbedSourceURL')) { @@ -174,6 +173,8 @@ protected function writeFromEmbed(string $sourceURL): bool $owner->extend('onEmbedSourceChange', $embed); } + return true; + } catch (\Throwable $throwable) { Logger::log("Error writing embed object: " . $throwable->getMessage()); throw \SilverStripe\ORM\ValidationException::create(_t( @@ -196,9 +197,14 @@ public function onBeforeWrite() /** * Get embed types allowed in this instance */ - public function getAllowedEmbedTypes(): array|null + public function getAllowedEmbedTypes() : array { - return $this->getOwner()->config()->get('allowed_embed_types'); + $allowedEmbedTypes = $this->getOwner()->config()->get('allowed_embed_types'); + if(!is_array($allowedEmbedTypes)) { + $allowedEmbedTypes = []; + } + + return $allowedEmbedTypes; } /** @@ -250,9 +256,8 @@ public function setEmbedTemplate(string $template): DataObject /** * Renders embed into appropriate template HTML - * @return HTML */ - public function getEmbed() + public function getEmbed(): string { $owner = $this->getOwner(); $title = $owner->EmbedTitle; @@ -261,6 +266,8 @@ public function getEmbed() $template = $this->template; $embedHTML = $owner->EmbedHTML; $sourceURL = $owner->EmbedSourceURL; + $width = $owner->EmbedWidth; + $height = $owner->EmbedHeight; $templates = []; if($type !== '') { $templates[] = $template . '_' . $type; @@ -291,8 +298,8 @@ public function getEmbed() case 'image': case 'picture': $attributes['src'] = $sourceURL; - $attributes['width'] = $this->Width; - $attributes['height'] = $this->Height; + $attributes['width'] = $width; + $attributes['height'] = $height; $attributes['alt'] = $title ?? ''; $html = HTML::createTag('img', $attributes); break; diff --git a/src/Models/Embed.php b/src/Models/Embed.php index ec33e1e..de3ee53 100644 --- a/src/Models/Embed.php +++ b/src/Models/Embed.php @@ -45,11 +45,9 @@ class Embed extends DataObject ]; /** - * List the allowed included embed types. If null all are allowed. - * - * @var array|null + * List the allowed included embed types. If empty all are allowed. */ - private static $allowed_embed_types; + private static array $allowed_embed_types = []; /** * Defines upload folder for embedded assets @@ -103,10 +101,9 @@ public function getClass(): string /** * Renders an HTML anchor tag for this link * See Embeddable::getEmbed() - * @return HTML */ public function forTemplate() { - return $this->Embed; + return $this->getEmbed(); } } diff --git a/src/Models/Video.php b/src/Models/Video.php index 7d38976..0c880e1 100644 --- a/src/Models/Video.php +++ b/src/Models/Video.php @@ -25,7 +25,7 @@ class Video extends Embed private static string $plural_name = 'Video'; /** - * List the allowed included embed types. If null all are allowed. + * List the allowed included embed types. If empty all are allowed. */ private static array $allowed_embed_types = [ 'video' From 3c82ba4d82eec353bff087dd604961600ff6c543 Mon Sep 17 00:00:00 2001 From: JamesDPC Date: Tue, 13 Aug 2024 02:26:15 +0000 Subject: [PATCH 10/18] [php-cs-fixer] Automated updates generated by php-cs-fixer configuration --- src/Extensions/Embeddable.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Extensions/Embeddable.php b/src/Extensions/Embeddable.php index ab340f2..aa7ca45 100644 --- a/src/Extensions/Embeddable.php +++ b/src/Extensions/Embeddable.php @@ -197,7 +197,7 @@ public function onBeforeWrite() /** * Get embed types allowed in this instance */ - public function getAllowedEmbedTypes() : array + public function getAllowedEmbedTypes(): array { $allowedEmbedTypes = $this->getOwner()->config()->get('allowed_embed_types'); if(!is_array($allowedEmbedTypes)) { From 25b26347e3ee14d6a4e7c227fb8f3e9f0458f9f0 Mon Sep 17 00:00:00 2001 From: "James (DPC)" <69664712+JamesDPC@users.noreply.github.com> Date: Tue, 13 Aug 2024 12:49:36 +1000 Subject: [PATCH 11/18] (docs) tweaks to documentation --- docs/en/001_index.md | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/docs/en/001_index.md b/docs/en/001_index.md index 80570fe..5b55afe 100644 --- a/docs/en/001_index.md +++ b/docs/en/001_index.md @@ -5,11 +5,11 @@ Since the original v1: + Namespace update -+ Template change in Embeddable from Embed to NSWDPC/Embed/Models/Embed -+ Dropped support for gorriecode/silverstripe-htmltag ++ Template name/path change in Embeddable from Embed to NSWDPC/Embed/Models/Embed, although `templates/Embed.ss` is still allowed ++ Dropped support for gorriecoe/silverstripe-htmltag + Requires embed/embed:^4 + Requires silverstripe/framework:^5 -+ Requires silverstripe/asset-admin:^2 ++ Requires silverstripe/asset-admin:^2 (`UploadField`) + Remove logic no longer supported by embed/embed:^4 You can diff across repos to view all changes. @@ -37,7 +37,7 @@ class ClassName extends DataObject /** * @inheritdoc */ - private static $has_one = [ + private static array $has_one = [ 'Embed' => Embed::class, 'Video' => Video::class ]; @@ -84,15 +84,15 @@ class OtherClassName extends DataObject /** * @inheritdoc */ - private static $extensions = [ + private static array $extensions = [ Embeddable::class, ]; /** - * List the allowed included embed types. If null all are allowed. + * List the allowed included embed types. If empty all are allowed. * @var array */ - private static $allowed_embed_types = [ + private static array $allowed_embed_types = [ 'video', 'photo' ]; @@ -101,7 +101,7 @@ class OtherClassName extends DataObject * Defines tab to insert the embed fields into. * @var string */ - private static $embed_tab = 'Main'; + private static string $embed_tab = 'Main'; // other logic for the class } From 273c1ba0ae097767f3945eba7bb5da3b32960ba7 Mon Sep 17 00:00:00 2001 From: "James (DPC)" <69664712+JamesDPC@users.noreply.github.com> Date: Tue, 13 Aug 2024 12:53:57 +1000 Subject: [PATCH 12/18] Remove 'replaces' for now --- composer.json | 3 --- 1 file changed, 3 deletions(-) diff --git a/composer.json b/composer.json index 4d312ee..3cc8e59 100644 --- a/composer.json +++ b/composer.json @@ -37,9 +37,6 @@ "url": "https://github.com/nswdpc/ci-files.git" } ], - "replace": { - "gorriecoe/silverstripe-embed": "self.version" - }, "autoload": { "psr-4": { "NSWDPC\\Embed\\": "src/", From bc746d5fed00338adcaa20798af91f4cb5e62f63 Mon Sep 17 00:00:00 2001 From: "James (DPC)" <69664712+JamesDPC@users.noreply.github.com> Date: Tue, 13 Aug 2024 13:06:51 +1000 Subject: [PATCH 13/18] Modify the config block name to avoid clashes --- _config/config.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/_config/config.yml b/_config/config.yml index c5698db..8c7d858 100644 --- a/_config/config.yml +++ b/_config/config.yml @@ -1,3 +1,3 @@ --- -Name: embedconfig +Name: nswdpc-silverstripe-embed --- From fa40ae6b2c7cfaee5e050735d55ace72f57394c3 Mon Sep 17 00:00:00 2001 From: "James (DPC)" <69664712+JamesDPC@users.noreply.github.com> Date: Tue, 13 Aug 2024 13:22:05 +1000 Subject: [PATCH 14/18] Update log level --- src/Extensions/Embeddable.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Extensions/Embeddable.php b/src/Extensions/Embeddable.php index aa7ca45..6dc2ebb 100644 --- a/src/Extensions/Embeddable.php +++ b/src/Extensions/Embeddable.php @@ -176,7 +176,7 @@ protected function writeFromEmbed(string $sourceURL): bool return true; } catch (\Throwable $throwable) { - Logger::log("Error writing embed object: " . $throwable->getMessage()); + Logger::log("Error writing embed object: " . $throwable->getMessage(), "NOTICE"); throw \SilverStripe\ORM\ValidationException::create(_t( self::class . ".FAILED_TO_WRITE_EMBED", "Sorry, the embed details could not be found or saved. Please check the URL entered and try again." From 825daba32dfa2b8332b1430740848b9dcd705c71 Mon Sep 17 00:00:00 2001 From: "James (DPC)" <69664712+JamesDPC@users.noreply.github.com> Date: Tue, 13 Aug 2024 16:08:43 +1000 Subject: [PATCH 15/18] Improvements to template handling --- src/Extensions/Embeddable.php | 153 ++++++++++++++++++++++++---------- 1 file changed, 108 insertions(+), 45 deletions(-) diff --git a/src/Extensions/Embeddable.php b/src/Extensions/Embeddable.php index 6dc2ebb..9043d9d 100644 --- a/src/Extensions/Embeddable.php +++ b/src/Extensions/Embeddable.php @@ -3,11 +3,15 @@ namespace NSWDPC\Embed\Extensions; use Embed\Embed; +use Embed\Extractor; +use Embed\OEmbed; use NSWDPC\Embed\Services\Logger; use SilverStripe\Assets\Image; use SilverStripe\Assets\Folder; use SilverStripe\Assets\File; use SilverStripe\Assets\Storage\AssetStore; +use SilverStripe\Forms\CheckboxField; +use SilverStripe\Forms\CompositeField; use SilverStripe\Forms\FieldList; use SilverStripe\Forms\TextField; use SilverStripe\Forms\TextareaField; @@ -18,6 +22,8 @@ use SilverStripe\ORM\ValidationException; use SilverStripe\ORM\ValidationResult; use SilverStripe\ORM\DataExtension; +use SilverStripe\ORM\FieldType\DBField; +use SilverStripe\ORM\FieldType\DBHTMLText; use SilverStripe\View\HTML; use SilverStripe\View\SSViewer; @@ -26,6 +32,14 @@ */ class Embeddable extends DataExtension { + + public const EMBED_TYPE_VIDEO = 'video'; + public const EMBED_TYPE_RICH = 'rich'; + public const EMBED_TYPE_IMAGE = 'image'; + public const EMBED_TYPE_PHOTO = 'photo'; + public const EMBED_TYPE_PICTURE = 'picture'; + public const EMBED_TYPE_LINK = 'link'; + /** * @inheritdoc */ @@ -68,7 +82,7 @@ class Embeddable extends DataExtension /** * Defines the template to render the embed in. */ - protected string $template = 'NSWDPC/Embed/Models/Embed'; + protected string $embedTemplate = 'NSWDPC/Embed/Models/Embed'; /** * @inheritdoc @@ -103,12 +117,18 @@ public function updateCMSFields(FieldList $fields) ->setDescription( _t(self::class . '.TITLEDESCRIPTION', 'Optional. Will be auto-generated if left blank') ), - TextField::create( - 'EmbedSourceURL', - _t(self::class . '.SOURCEURLLABEL', 'Source URL') - ) - ->setDescription( - _t(self::class . '.SOURCEURLDESCRIPTION', 'Specify a external URL') + CompositeField::create( + TextField::create( + 'EmbedSourceURL', + _t(self::class . '.SOURCEURLLABEL', 'Source URL') + ) + ->setDescription( + _t(self::class . '.SOURCEURLDESCRIPTION', 'Specify an external URL') + ), + CheckboxField::create( + 'ForceUpdate', + _t(self::class . '.FORCEUPDATE', 'Check to update the meta data for this URL') + ) ), UploadField::create( 'EmbedImage', @@ -139,36 +159,62 @@ public function updateCMSFields(FieldList $fields) } /** - * Get the embed data using a source URL and write relevant data to the owner + * Get the Extractor for the source URL */ - protected function writeFromEmbed(string $sourceURL): bool + public function getExtractor(): Extractor { - try { - if($sourceURL === '') { - throw new \RuntimeException(_t(self::class . '.EMPTY_SOURCE_URL', 'Source URL is empty')); - } + $sourceURL = $this->getOwner()->EmbedSourceURL ?? ''; + if($sourceURL === '') { + throw new \RuntimeException(_t(self::class . '.EMPTY_SOURCE_URL', 'Source URL is empty')); + } + $parts = parse_url($sourceURL); + if(!isset($parts['scheme'])) { + throw new \RuntimeException(_t(self::class . '.EMPTY_SOURCE_URL_SCHEME', 'Source URL has no scheme')); + } + if(!isset($parts['host'])) { + throw new \RuntimeException(_t(self::class . '.EMPTY_SOURCE_URL_HOST', 'Source URL has no host')); + } + $embed = new Embed(); + $extractor = $embed->get($sourceURL); + return $extractor; + } - $embed = new Embed(); - $embed = $embed->get($sourceURL); + /** + * Return the OEmbed for the URL, if it exists + */ + public function getOEmbed(Extractor $extractor) : OEmbed + { + return $extractor->getOEmbed(); + } + /** + * Get the embed data using a source URL and write relevant data to the owner + */ + protected function writeFromEmbed(bool $force = false): bool + { + try { + $extractor = $this->getExtractor(); $owner = $this->getOwner(); // write title if current is empty if ($owner->EmbedTitle == '') { - $owner->EmbedTitle = $embed->title; + $owner->EmbedTitle = $extractor->title; } // write description if current is empty if ($owner->EmbedDescription == '') { - $owner->EmbedDescription = $embed->description; + $owner->EmbedDescription = $extractor->description; } - if ($owner->isChanged('EmbedSourceURL')) { + $urlChanged = $owner->isChanged('EmbedSourceURL', DataObject::CHANGE_VALUE); + if ($force || $urlChanged) { // embed data from updated source URL - $owner->EmbedHTML = $embed->code->html; - $owner->EmbedType = null;// update embed type in your own DataObject - $owner->EmbedWidth = $embed->code->width; - $owner->EmbedHeight = $embed->code->height; - $owner->EmbedAspectRatio = $embed->code->ratio; + $owner->EmbedHTML = $extractor->code->html; + $oembed = $this->getOEmbed($extractor); + // save type for oembed, if it exists + $owner->EmbedType = $oembed ? strtolower($oembed->get('type') ?? '') : ''; + $owner->EmbedWidth = $extractor->code->width; + $owner->EmbedHeight = $extractor->code->height; + $owner->EmbedAspectRatio = $extractor->code->ratio; // allow some customisation from the owner object prior to write, when the source url has changed $owner->extend('onEmbedSourceChange', $embed); } @@ -191,7 +237,7 @@ public function onBeforeWrite() { parent::onBeforeWrite(); $owner = $this->getOwner(); - $this->writeFromEmbed($owner->EmbedSourceURL ?? ''); + $this->writeFromEmbed($this->owner->ForceUpdate == '1'); } /** @@ -244,30 +290,32 @@ public function getEmbedClass(): string } /** - * Set CSS classes for templates + * Set the template to use for Embed * @param string $template template name without the .ss * @return DataObject Owner */ public function setEmbedTemplate(string $template): DataObject { - $this->template = $template; + $this->embedTemplate = $template; return $this->getOwner(); } + /** + * Get the template to use for Embed + */ + public function getEmbedTemplate(): string + { + return $this->embedTemplate; + } + /** * Renders embed into appropriate template HTML */ - public function getEmbed(): string + public function getEmbed(): DBHTMLText { $owner = $this->getOwner(); - $title = $owner->EmbedTitle; - $cssClasses = $owner->EmbedClass; - $type = $owner->EmbedType; - $template = $this->template; - $embedHTML = $owner->EmbedHTML; - $sourceURL = $owner->EmbedSourceURL; - $width = $owner->EmbedWidth; - $height = $owner->EmbedHeight; + $type = (string)$owner->EmbedType; + $template = $this->getEmbedTemplate(); $templates = []; if($type !== '') { $templates[] = $template . '_' . $type; @@ -276,27 +324,43 @@ public function getEmbed(): string $templates[] = $template; $templates[] = "Embed";// BC support for original Embed template if (SSViewer::hasTemplate($templates)) { - return $owner->renderWith($templates); + $embed = $owner->renderWith($templates); + } else { + // get HTML based on type + $embed = $this->getEmbedByType(); } + return $embed; + } + /** + * Return embed code by type + */ + public function getEmbedByType(): DBHTMLText { + $owner = $this->getOwner(); + $title = $owner->EmbedTitle; + $type = (string)$owner->EmbedType; + $cssClasses = $owner->EmbedClass; + $embedHTML = $owner->EmbedHTML; + $sourceURL = $owner->EmbedSourceURL; + $width = $owner->EmbedWidth; + $height = $owner->EmbedHeight; $html = ''; $attributes = []; if($cssClasses !== '') { $attributes['class'] = $cssClasses; } - switch ($type) { - case 'video': - case 'rich': + case self::EMBED_TYPE_VIDEO: + case self::EMBED_TYPE_RICH: $html = HTML::createTag('div', $attributes, $embedHTML); break; - case 'link': + case self::EMBED_TYPE_LINK: $attributes['href'] = $sourceURL; $html = HTML::createTag('a', $attributes, $title); break; - case 'photo': - case 'image': - case 'picture': + case self::EMBED_TYPE_PHOTO: + case self::EMBED_TYPE_IMAGE: + case self::EMBED_TYPE_PICTURE: $attributes['src'] = $sourceURL; $attributes['width'] = $width; $attributes['height'] = $height; @@ -307,7 +371,6 @@ public function getEmbed(): string $html = ""; break; } - - return $html; + return DBField::create_field(DBHTMLText::class, $html); } } From 4391f55ae8eff0c534d9246d48ceed62bd393355 Mon Sep 17 00:00:00 2001 From: JamesDPC Date: Tue, 13 Aug 2024 06:09:22 +0000 Subject: [PATCH 16/18] [php-cs-fixer] Automated updates generated by php-cs-fixer configuration --- src/Extensions/Embeddable.php | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/Extensions/Embeddable.php b/src/Extensions/Embeddable.php index 9043d9d..55e0500 100644 --- a/src/Extensions/Embeddable.php +++ b/src/Extensions/Embeddable.php @@ -32,7 +32,6 @@ */ class Embeddable extends DataExtension { - public const EMBED_TYPE_VIDEO = 'video'; public const EMBED_TYPE_RICH = 'rich'; public const EMBED_TYPE_IMAGE = 'image'; @@ -182,7 +181,7 @@ public function getExtractor(): Extractor /** * Return the OEmbed for the URL, if it exists */ - public function getOEmbed(Extractor $extractor) : OEmbed + public function getOEmbed(Extractor $extractor): OEmbed { return $extractor->getOEmbed(); } @@ -335,7 +334,8 @@ public function getEmbed(): DBHTMLText /** * Return embed code by type */ - public function getEmbedByType(): DBHTMLText { + public function getEmbedByType(): DBHTMLText + { $owner = $this->getOwner(); $title = $owner->EmbedTitle; $type = (string)$owner->EmbedType; From d42dda7533d9a2196969b0ef8be90500c523bcd9 Mon Sep 17 00:00:00 2001 From: JamesDPC Date: Tue, 13 Aug 2024 06:09:29 +0000 Subject: [PATCH 17/18] [rector] Automated updates generated by rector configuration --- src/Extensions/Embeddable.php | 28 +++++++++++++++++----------- 1 file changed, 17 insertions(+), 11 deletions(-) diff --git a/src/Extensions/Embeddable.php b/src/Extensions/Embeddable.php index 55e0500..97dd5ab 100644 --- a/src/Extensions/Embeddable.php +++ b/src/Extensions/Embeddable.php @@ -33,10 +33,15 @@ class Embeddable extends DataExtension { public const EMBED_TYPE_VIDEO = 'video'; + public const EMBED_TYPE_RICH = 'rich'; + public const EMBED_TYPE_IMAGE = 'image'; + public const EMBED_TYPE_PHOTO = 'photo'; + public const EMBED_TYPE_PICTURE = 'picture'; + public const EMBED_TYPE_LINK = 'link'; /** @@ -166,16 +171,18 @@ public function getExtractor(): Extractor if($sourceURL === '') { throw new \RuntimeException(_t(self::class . '.EMPTY_SOURCE_URL', 'Source URL is empty')); } + $parts = parse_url($sourceURL); if(!isset($parts['scheme'])) { throw new \RuntimeException(_t(self::class . '.EMPTY_SOURCE_URL_SCHEME', 'Source URL has no scheme')); } + if(!isset($parts['host'])) { throw new \RuntimeException(_t(self::class . '.EMPTY_SOURCE_URL_HOST', 'Source URL has no host')); } + $embed = new Embed(); - $extractor = $embed->get($sourceURL); - return $extractor; + return $embed->get($sourceURL); } /** @@ -235,8 +242,8 @@ protected function writeFromEmbed(bool $force = false): bool public function onBeforeWrite() { parent::onBeforeWrite(); - $owner = $this->getOwner(); - $this->writeFromEmbed($this->owner->ForceUpdate == '1'); + $this->getOwner(); + $this->writeFromEmbed($this->getOwner()->ForceUpdate == '1'); } /** @@ -321,13 +328,10 @@ public function getEmbed(): DBHTMLText } $templates[] = $template; - $templates[] = "Embed";// BC support for original Embed template - if (SSViewer::hasTemplate($templates)) { - $embed = $owner->renderWith($templates); - } else { - // get HTML based on type - $embed = $this->getEmbedByType(); - } + $templates[] = "Embed"; + // BC support for original Embed template + $embed = SSViewer::hasTemplate($templates) ? $owner->renderWith($templates) : $this->getEmbedByType(); + return $embed; } @@ -349,6 +353,7 @@ public function getEmbedByType(): DBHTMLText if($cssClasses !== '') { $attributes['class'] = $cssClasses; } + switch ($type) { case self::EMBED_TYPE_VIDEO: case self::EMBED_TYPE_RICH: @@ -371,6 +376,7 @@ public function getEmbedByType(): DBHTMLText $html = ""; break; } + return DBField::create_field(DBHTMLText::class, $html); } } From d839594a8c409a1cd5b499c62fc0b31fdfa20074 Mon Sep 17 00:00:00 2001 From: "James (DPC)" <69664712+JamesDPC@users.noreply.github.com> Date: Tue, 13 Aug 2024 16:11:30 +1000 Subject: [PATCH 18/18] (phpstan) fix complaint --- src/Extensions/Embeddable.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Extensions/Embeddable.php b/src/Extensions/Embeddable.php index 97dd5ab..9089e70 100644 --- a/src/Extensions/Embeddable.php +++ b/src/Extensions/Embeddable.php @@ -217,7 +217,7 @@ protected function writeFromEmbed(bool $force = false): bool $owner->EmbedHTML = $extractor->code->html; $oembed = $this->getOEmbed($extractor); // save type for oembed, if it exists - $owner->EmbedType = $oembed ? strtolower($oembed->get('type') ?? '') : ''; + $owner->EmbedType = strtolower($oembed->get('type') ?? ''); $owner->EmbedWidth = $extractor->code->width; $owner->EmbedHeight = $extractor->code->height; $owner->EmbedAspectRatio = $extractor->code->ratio;