| 1 | <?php |
||
| 2 | |||
| 3 | namespace LeKoala\CommonExtensions; |
||
| 4 | |||
| 5 | use SilverStripe\Control\Director; |
||
| 6 | use SilverStripe\ORM\DataObject; |
||
| 7 | use SilverStripe\Forms\FieldList; |
||
| 8 | use SilverStripe\ORM\DataExtension; |
||
| 9 | use SilverStripe\ORM\ValidationResult; |
||
| 10 | use SilverStripe\View\Parsers\URLSegmentFilter; |
||
| 11 | |||
| 12 | /** |
||
| 13 | * URL Segment extension |
||
| 14 | * |
||
| 15 | * By default link will be applied WITHOUT actions |
||
| 16 | * |
||
| 17 | * @property DataObject $owner |
||
| 18 | * @property string $URLSegment |
||
| 19 | */ |
||
| 20 | class URLSegmentExtension extends DataExtension |
||
| 21 | { |
||
| 22 | private static $db = [ |
||
|
0 ignored issues
–
show
introduced
by
Loading history...
|
|||
| 23 | "URLSegment" => "Varchar(191)", |
||
| 24 | ]; |
||
| 25 | private static $indexes = [ |
||
|
0 ignored issues
–
show
|
|||
| 26 | "URLSegmentUnique" => [ |
||
| 27 | "type" => "unique", |
||
| 28 | "columns" => ["URLSegment"] |
||
| 29 | ], |
||
| 30 | ]; |
||
| 31 | |||
| 32 | /** |
||
| 33 | * Get a class by URLSegment |
||
| 34 | * |
||
| 35 | * @param string $class |
||
| 36 | * @param string $URLSegment |
||
| 37 | * @return DataObject |
||
| 38 | */ |
||
| 39 | public static function getByURLSegment($class, $URLSegment) |
||
| 40 | { |
||
| 41 | return $class::get()->filter('URLSegment', $URLSegment)->first(); |
||
| 42 | } |
||
| 43 | |||
| 44 | public function updateCMSFields(FieldList $fields) |
||
| 45 | { |
||
| 46 | $URLSegment = $fields->dataFieldByName('URLSegment'); |
||
| 47 | if ($URLSegment) { |
||
| 48 | $URLSegment->setTitle(_t('URLSegmentExtension.URLSEGMENT', 'URL Segment')); |
||
| 49 | |||
| 50 | $Title = $fields->dataFieldByName('Title'); |
||
| 51 | if ($Title) { |
||
| 52 | $fields->insertAfter('Title', $URLSegment); |
||
| 53 | } |
||
| 54 | } |
||
| 55 | } |
||
| 56 | |||
| 57 | /** |
||
| 58 | * @return boolean |
||
| 59 | */ |
||
| 60 | public function isSearchable() |
||
| 61 | { |
||
| 62 | return $this->owner->hasMethod('Page'); |
||
| 63 | } |
||
| 64 | |||
| 65 | /** |
||
| 66 | * We have this link method by default that allows us to define |
||
| 67 | * a link for this record |
||
| 68 | * |
||
| 69 | * You can define your own methods in your DataObject class |
||
| 70 | * |
||
| 71 | * @param string $action |
||
| 72 | * @return string |
||
| 73 | */ |
||
| 74 | public function Link($action = null) |
||
| 75 | { |
||
| 76 | $url = $this->owner->Page()->Link($this->owner->URLSegment); |
||
| 77 | if ($action) { |
||
| 78 | $url .= "/$action"; |
||
| 79 | } |
||
| 80 | return $url; |
||
| 81 | } |
||
| 82 | |||
| 83 | /** |
||
| 84 | * @return string |
||
| 85 | */ |
||
| 86 | public function AbsoluteLink() |
||
| 87 | { |
||
| 88 | return Director::absoluteURL($this->Link()); |
||
| 89 | } |
||
| 90 | |||
| 91 | /** |
||
| 92 | * Cannot use the same url segment |
||
| 93 | * |
||
| 94 | * @param ValidationResult $validationResult |
||
| 95 | * @return void |
||
| 96 | */ |
||
| 97 | public function validate(ValidationResult $validationResult) |
||
| 98 | { |
||
| 99 | $duplicate = $this->getDuplicateRecord(); |
||
| 100 | if ($duplicate) { |
||
|
0 ignored issues
–
show
|
|||
| 101 | $validationResult->addFieldError("URLSegment", "Segment already used by record #" . $duplicate->ID); |
||
| 102 | } |
||
| 103 | } |
||
| 104 | |||
| 105 | /** |
||
| 106 | * Find another record with the same url segment |
||
| 107 | * |
||
| 108 | * @param string $segment |
||
| 109 | * @return DataObject |
||
| 110 | */ |
||
| 111 | public function getDuplicateRecord($segment = null) |
||
| 112 | { |
||
| 113 | if ($segment === null) { |
||
| 114 | $segment = $this->owner->URLSegment; |
||
| 115 | } |
||
| 116 | if (!$segment) { |
||
| 117 | return false; |
||
| 118 | } |
||
| 119 | $class = get_class($this->owner); |
||
| 120 | return $class::get()->exclude('ID', $this->owner->ID)->filter("URLSegment", $segment)->first(); |
||
| 121 | } |
||
| 122 | |||
| 123 | /** |
||
| 124 | * This method allows you to customize url segment generation |
||
| 125 | * |
||
| 126 | * By default, URL segment is based on page title |
||
| 127 | * |
||
| 128 | * @return string |
||
| 129 | */ |
||
| 130 | public function getBaseURLSegment() |
||
| 131 | { |
||
| 132 | $segment = $this->owner->getTitle(); |
||
| 133 | if ($this->owner->hasMethod('updateURLSegment')) { |
||
| 134 | $this->owner->updateURLSegment($segment); |
||
| 135 | } |
||
| 136 | $filter = new URLSegmentFilter(); |
||
| 137 | $baseSegment = $filter->filter($segment); |
||
| 138 | if (is_numeric($baseSegment)) { |
||
| 139 | return false; |
||
| 140 | } |
||
| 141 | return $baseSegment; |
||
| 142 | } |
||
| 143 | |||
| 144 | /** |
||
| 145 | * Generate a new url segment and checks for duplicates |
||
| 146 | * |
||
| 147 | * @return string |
||
| 148 | */ |
||
| 149 | public function generateURLSegment() |
||
| 150 | { |
||
| 151 | $baseSegment = $segment = $this->getBaseURLSegment(); |
||
| 152 | if (!$baseSegment) { |
||
| 153 | return; |
||
| 154 | } |
||
| 155 | $duplicate = $this->getDuplicateRecord($segment); |
||
| 156 | $i = 0; |
||
| 157 | while ($duplicate) { |
||
| 158 | $i++; |
||
| 159 | $segment = $baseSegment . '-' . $i; |
||
| 160 | $duplicate = $this->getDuplicateRecord($segment); |
||
| 161 | } |
||
| 162 | return $segment; |
||
| 163 | } |
||
| 164 | |||
| 165 | public function onBeforeWrite() |
||
| 166 | { |
||
| 167 | // Generate segment if no segment |
||
| 168 | if (!$this->owner->URLSegment && $this->getBaseURLSegment()) { |
||
| 169 | $this->owner->URLSegment = $this->generateURLSegment(); |
||
| 170 | } |
||
| 171 | } |
||
| 172 | } |
||
| 173 |