Source of file Link.php
Size: 14,944 Bytes - Last Modified: 2021-12-23T10:20:18+00:00
/var/www/docs.ssmods.com/process/src/src/Models/Link.php
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538 | <?php namespace Sheadawson\Linkable\Models; use SilverStripe\Assets\File; use SilverStripe\Forms\DropdownField; use SilverStripe\Forms\CheckboxField; use SilverStripe\Forms\FieldList; use SilverStripe\Forms\TextField; use SilverStripe\Forms\TreeDropdownField; use SilverStripe\ORM\FieldType\DBHTMLText; use UncleCheese\DisplayLogic\Forms\Wrapper; use SilverStripe\Core\Convert; use SilverStripe\ORM\ValidationResult; use SilverStripe\ORM\DataObject; /** * Class Link * @license BSD License http://www.silverstripe.org/bsd-license * @author <shea@silverstripe.com.au> * @property string Title * @property string Type * @property string URL * @property string Email * @property string Phone * @property bool OpenInNewWindow * @property string Template * @package Sheadawson\Linkable\Models */ class Link extends DataObject { /** * @var string custom CSS classes for template */ protected $cssClass; /** * @var array */ private static $db = [ 'Title' => 'Varchar(255)', 'Type' => 'Varchar', 'URL' => 'Varchar(255)', 'Email' => 'Varchar(255)', 'Phone' => 'Varchar(255)', 'OpenInNewWindow' => 'Boolean', 'Template' => 'Varchar(255)', ]; /** * @var array */ private static $has_one = [ 'File' => File::class, ]; /** * @var string */ private static $table_name = 'LinkableLink'; /** * @var array */ private static $summary_fields = [ 'Title', 'LinkType', 'LinkURL', ]; /** * @var array */ private static $searchable_fields = [ 'Title' => 'PartialMatchFilter', 'URL' => 'PartialMatchFilter', 'Phone' => 'PartialMatchFilter', 'Email' => 'PartialMatchFilter', ]; /** * A map of templates that are available for rendering * Link objects with * * @var array */ private static $templates = []; /** * A map of object types that can be linked to * Custom dataobjects can be added to this * * @var array */ private static $types = [ 'URL' => 'URL', 'Email' => 'Email address', 'Phone' => 'Phone number', 'File' => 'File on this website', ]; /** * List the allowed included link types. If null all are allowed. * global config * * @var array * @config */ private static $allowed_types = null; private static $casting = [ 'ClassAttr' => 'HTMLFragment', 'TargetAttr' => 'HTMLFragment', ]; /** * List the allowed included link types. If null all are allowed. * Instance specific override * * @var array */ protected $allowed_types_override = null; /** * @return FieldList */ public function getCMSFields() { $fields = $this->scaffoldFormFields([ // Don't allow has_many/many_many relationship editing before the record is first saved 'includeRelations' => ($this->ID > 0), 'tabbed' => true, 'ajaxSafe' => true, ]); $fields->removeByName([ 'SiteTreeID', // seem to need to remove both of these for different SS versions... 'FileID', 'File', 'Template', 'Anchor', ]); // remove default fields $dbFields = $this->config()->get('db'); if (!empty($dbFields) && is_array($dbFields)) { $fields->removeByName(array_keys($dbFields)); } $templates = $this->config()->get('templates'); if ($templates) { $i18nTemplates = []; foreach ($templates as $key => $label) { $i18nTemplates[$key] = _t('Linkable.STYLE' . strtoupper($key), $label); } $fields->addFieldToTab( 'Root.Main', DropdownField::create( 'Template', _t('Linkable.STYLE', 'Style'), $i18nTemplates )->setEmptyString('Default') ); } $fields->addFieldsToTab('Root.Main', [ $title = TextField::create('Title'), $type = DropdownField::create( 'Type', _t('Linkable.LINKTYPE', 'Link Type'), $this->getTypes() ), $url = TextField::create('URL', 'URL'), $file = Wrapper::create( TreeDropdownField::create( 'FileID', _t('Linkable.FILE', 'File'), File::class, 'ID', 'Title' ) ), $email = TextField::create('Email', _t('Linkable.EMAILADDRESS', 'Email Address')), $phone = TextField::create('Phone', _t('Linkable.PHONENUMBER', 'Phone Number')), $openInNewWindow = CheckboxField::create( 'OpenInNewWindow', _t('Linkable.OPENINNEWWINDOW', 'Open link in a new window') ), ]); $title ->setTitle(_t('Linkable.TITLE', 'Title')) ->setRightTitle( _t('Linkable.OPTIONALTITLE', 'Optional. Will be auto-generated from link if left blank') ); $openInNewWindow ->displayIf('Type')->isEqualTo('URL') ->orIf()->isEqualTo('File') ->orIf()->isEqualTo('SiteTree') ->end(); $file ->displayIf('Type') ->isEqualTo('File') ->end(); $url ->displayIf('Type') ->isEqualTo('URL'); ->displayIf('Type') ->isEqualTo('Email'); $phone ->displayIf('Type') ->isEqualTo('Phone'); $this->extend('updateCMSFields', $fields); return $fields; } /** * If the title is empty, set it to getLinkURL() */ public function onAfterWrite() { parent::onAfterWrite(); if (!$this->Title) { switch ($this->Type) { case 'URL': case 'Email': case 'Phone': $this->Title = $this->{$this->Type}; break; case 'SiteTree': $this->Title = $this->SiteTree()->MenuTitle; break; default: if ($this->Type && $component = $this->getComponent($this->Type)) { $this->Title = $component->Title; } break; } if (!$this->Title) { $this->Title = 'Link-' . $this->ID; } $this->write(); } } /** * @param string $class * @return $this */ public function setCSSClass($class) { $this->cssClass = $class; return $this; } /** * Sets allowed link types * * @param array $types * @return $this */ public function setAllowedTypes($types = null) { $this->allowed_types_override = $types; return $this; } /** * Returns allowed link types * * @return array */ public function getTypes() { $types = $this->config()->get('types'); $i18nTypes = []; $allowedTypes = $this->config()->get('allowed_types'); if ($this->allowed_types_override) { // Prioritise local field over global settings $allowedTypes = $this->allowed_types_override; } if ($allowedTypes) { foreach ($allowedTypes as $type) { if (!array_key_exists($type, $types)) { user_error("{$type} is not a valid link type"); } } foreach (array_diff_key($types, array_flip($allowedTypes)) as $key => $value) { unset($types[$key]); } } // Get translatable labels foreach ($types as $key => $label) { $i18nTypes[$key] = _t('Linkable.TYPE' . strtoupper($key), $label); } return $i18nTypes; } /** * Renders an HTML anchor tag for this link * * @return DBHTMLText|string */ public function forTemplate() { if ($this->LinkURL) { $link = $this->renderWith([ // Render link with this template if its found. eg Link_button.ss Link::class . '_' . $this->Template, Link::class ]); // Legacy. Recommended to use templating above. $this->extend('updateLinkTemplate', $this, $link); return $link; } return ''; } /** * Works out what the URL for this link should be based on it's Type * * @return bool|mixed|null|string */ public function getLinkURL() { if (!$this->ID) { return ''; } $type = $this->Type; switch ($type) { case 'URL': $LinkURL = $this->URL; break; case 'Email': $LinkURL = $this->Email ? "mailto:$this->Email" : null; break; case 'Phone': $LinkURL = $this->Phone ? "tel:$this->Phone" : null; break; default: if ($this->getTypeHasDbField()) { $LinkURL = $this->{$type}; } else { if ($type && $component = $this->getComponent($type)) { if (!$component->exists()) { $LinkURL = false; } elseif ($component->hasMethod('Link')) { $LinkURL = $component->Link() . $this->Anchor; } else { $LinkURL = "Please implement a Link() method on your dataobject \"$type\""; } } } break; } $this->extend('updateLinkURL', $LinkURL); return $LinkURL; } /** * Gets the classes for this link. * * @return array|string */ public function getClasses() { $classes = explode(' ', $this->cssClass); $this->extend('updateClasses', $classes); $classes = implode(' ', $classes); return $classes; } /** * Gets the html class attribute for this link. * * @return string */ public function getClassAttr() { $class = $this->Classes ? Convert::raw2att($this->Classes) : ''; return $class ? " class='$class'" : ''; } /** * Gets the html target attribute for the anchor tag * * @return string */ public function getTargetAttr() { return $this->OpenInNewWindow ? " target='_blank'" : ''; } /** * Gets the description label of this links type * * @return null|string */ public function getLinkType() { $types = $this->config()->get('types'); return isset($types[$this->Type]) ? _t('Linkable.TYPE' . strtoupper($this->Type), $types[$this->Type]) : null; } /** * Check if the selected type has a db field otherwise assume its a related object. * * @return bool */ public function getTypeHasDbField() { return in_array( $this->Type, array_keys($this->Config()->get('db')) ); } /** * Validate * * @return ValidationResult */ public function validate() { $valid = true; $message = null; $type = $this->Type; // Check if empty strings switch ($type) { case 'URL': case 'Email': case 'Phone': if ($this->{$type} == '') { $valid = false; $message = _t( 'Linkable.VALIDATIONERROR_EMPTY' . strtoupper($type), "You must enter a $type for a link type of \"$this->LinkType\"" ); } break; default: if ($this->getTypeHasDbField()) { if ($type && empty($this->{$type})) { $valid = false; $message = _t( 'Linkable.VALIDATIONERROR_EMPTY', "You must enter a $type for a link type of \"$this->LinkType\"" ); } } else { if ($type && empty($this->{$type . 'ID'})) { $valid = false; $message = _t( 'Linkable.VALIDATIONERROR_OBJECT', "Please select a {value} object to link to", array('value' => $type) ); } } break; } // if its already failed don't bother checking the rest if ($valid) { switch ($type) { case 'URL': $allowedFirst = array('#', '/'); if (!in_array(substr($this->URL, 0, 1), $allowedFirst) && !filter_var($this->URL, FILTER_VALIDATE_URL)) { $valid = false; $message = _t( 'Linkable.VALIDATIONERROR_VALIDURL', 'Please enter a valid URL. Be sure to include http:// for an external URL. Or begin your internal url/anchor with a "/" character' ); } break; case 'Email': if (!filter_var($this->Email, FILTER_VALIDATE_EMAIL)) { $valid = false; $message = _t( 'Linkable.VALIDATIONERROR_VALIDEMAIL', 'Please enter a valid Email address' ); } break; case 'Phone': if (!preg_match("/^\+?[0-9]{1,5}[- ]{0,1}[0-9]{3,4}[- ]{0,1}[0-9]{4}$/", $this->Phone)) { $valid = false; $message = _t( 'Linkable.VALIDATIONERROR_VALIDPHONE', 'Please enter a valid Phone number' ); } break; } } $result = ValidationResult::create(); if (!$valid) { $result->addError($message); } $this->extend('updateValidate', $result); return $result; } } |