From 33de612cfd899a8f85e9bb33880d24b95bdeeb02 Mon Sep 17 00:00:00 2001 From: John Milmine Date: Mon, 3 Oct 2016 10:38:10 +1300 Subject: [PATCH 1/2] made virtaulised elements more transparent enables virtaulised elements to handle forms and the like much more seemlessly --- code/controllers/BaseElementController.php | 28 +++++++++ code/extensions/BaseElementExtension.php | 28 +++++++++ code/models/BaseElement.php | 38 +++++++++++- code/models/ElementVirtualLinked.php | 70 +++++++++++++++++++++- templates/ElementHolder_VirtualLinked.ss | 3 + 5 files changed, 164 insertions(+), 3 deletions(-) create mode 100644 templates/ElementHolder_VirtualLinked.ss diff --git a/code/controllers/BaseElementController.php b/code/controllers/BaseElementController.php index adeaab146..6ee665fc1 100644 --- a/code/controllers/BaseElementController.php +++ b/code/controllers/BaseElementController.php @@ -17,4 +17,32 @@ public function WidgetHolder() { return $this->renderWith("ElementHolder"); } + + + public function Link($action = null) + { + + if($this->data()->virtualOwner) { + $controller = new BaseElement_Controller($this->data()->virtualOwner); + return $controller->Link($action); + } + + return Parent::Link($action); + } + + + /** + * if this is a virtual request, change the hash if set. + */ + public function redirect($url, $code=302) { + + if($this->data()->virtualOwner) { + $parts = explode('#', $url); + if(isset($parts[1])) { + $url = $parts[0] . '#' . $this->data()->virtualOwner->ID; + } + } + + return parent::redirect($url, $code); + } } diff --git a/code/extensions/BaseElementExtension.php b/code/extensions/BaseElementExtension.php index d4133e7fc..46cc5a91e 100644 --- a/code/extensions/BaseElementExtension.php +++ b/code/extensions/BaseElementExtension.php @@ -38,4 +38,32 @@ public function canCreate($member = null) { return (Permission::check('CMS_ACCESS_CMSMain', 'any', $member)) ? true : null; } + + /** + * Handles unpublishing as VersionedDataObjects doesn't + * Modelled on SiteTree::doUnpublish + * + */ + public function doUnpublish() { + if(!$this->owner->ID) return false; + + $this->owner->extend('onBeforeUnpublish'); + + $origStage = Versioned::get_reading_mode(); + Versioned::set_reading_mode('Stage.Live'); + + // This way our ID won't be unset + $clone = clone $this->owner; + $clone->delete(); + + Versioned::set_reading_mode($origStage); + + $virtualLinkedElements = $this->owner->getPublishedVirtualLinkedElements(); + if ($virtualLinkedElements) foreach($virtualLinkedElements as $vle) $vle->doUnpublish(); + + $this->owner->extend('onAfterUnpublish'); + + return true; + } + } diff --git a/code/models/BaseElement.php b/code/models/BaseElement.php index 87c2f0111..971ad777a 100644 --- a/code/models/BaseElement.php +++ b/code/models/BaseElement.php @@ -63,6 +63,12 @@ class BaseElement extends Widget */ private static $enable_title_in_template = false; + /** + * @var Object + * The virtual owner VirtualLinkedElement + */ + public $virtualOwner; + public function getCMSFields() { @@ -230,6 +236,10 @@ public function ControllerTop() public function getPage() { + if ($this->virtualOwner) { + return $this->virtualOwner->getPage(); + } + $area = $this->Parent(); if ($area instanceof ElementalArea) { @@ -267,7 +277,7 @@ public function onBeforeVersionedPublish() } - public static function all_allowed_elements() { + public static function all_allowed_elements() { $classes = array(); // get all dataobject with the elemental extension @@ -312,4 +322,30 @@ public function getDefaultSearchContext() $filters ); } + + public function setVirtualOwner(ElementVirtualLinked $virtualOwner) { + $this->virtualOwner = $virtualOwner; + } + + /** + * Finds and returns elements + * that are virtual elements which link to this element + */ + public function getVirtualLinkedElements() { + return ElementVirtualLinked::get()->filter('LinkedElementID', $this->ID); + } + + /** + * Finds and returns published elements + * that are virtual elements which link to this element + */ + public function getPublishedVirtualLinkedElements() { + $current = Versioned::get_reading_mode(); + Versioned::set_reading_mode('Stage.Live'); + $v = $this->getVirtualLinkedElements(); + Versioned::set_reading_mode($current); + return $v; + } } + + diff --git a/code/models/ElementVirtualLinked.php b/code/models/ElementVirtualLinked.php index 3940c40e5..43908c789 100644 --- a/code/models/ElementVirtualLinked.php +++ b/code/models/ElementVirtualLinked.php @@ -36,6 +36,11 @@ public function i18n_singular_name() return _t(__CLASS__, $this->LinkedElement()->config()->title); } + public function __construct($record = null, $isSingleton = false, $model = null) { + parent::__construct($record, $isSingleton, $model); + $this->LinkedElement()->setVirtualOwner($this); + } + public function getCMSFields() { $message = sprintf('

%s

%2$s

', _t('ElementVirtualLinked.DESCRIBE', 'This is a virtual copy of a block. To edit, visit'), @@ -55,7 +60,68 @@ public function getCMSFields() { return $fields; } - public function getExtraClass() { - return $this->LinkedElement()->ClassName . ' ' . $this->getField('ExtraClass'); +} + +class ElementVirtualLinked_Controller extends BaseElement_Controller { + + protected $controllerClass; + + public function __construct($widget) { + parent::__construct($widget); + + $controllerClass = get_class($this->LinkedElement()) . '_Controller'; + if(class_exists($controllerClass)) { + $this->controllerClass = $controllerClass; + } else { + $this->controllerClass = 'BaseElement_Controller'; + } + } + + /** + * Returns the current widget in scope rendered into its' holder + * + * @return HTML + */ + public function WidgetHolder() + { + return $this->renderWith("ElementHolder_VirtualLinked"); + } + + public function __call($method, $arguments) { + try { + $retVal = parent::__call($method, $arguments); + + } catch (Exception $e) { + $controller = new $this->controllerClass($this->LinkedElement()); + $retVal = call_user_func_array(array($controller, $method), $arguments); + } + return $retVal; + } + + public function hasMethod($action) { + if(parent::hasMethod($action)) { + return true; + } + + $controller = new $this->controllerClass($this->LinkedElement()); + return $controller->hasMethod($action); + } + + public function hasAction($action) { + if(parent::hasAction($action)) { + return true; + } + + $controller = new $this->controllerClass($this->LinkedElement()); + return $controller->hasAction($action); + } + + public function checkAccessAction($action) { + if(parent::checkAccessAction($action)) { + return true; + } + + $controller = new $this->controllerClass($this->LinkedElement()); + return $controller->checkAccessAction($action); } } diff --git a/templates/ElementHolder_VirtualLinked.ss b/templates/ElementHolder_VirtualLinked.ss new file mode 100644 index 000000000..6a384cddd --- /dev/null +++ b/templates/ElementHolder_VirtualLinked.ss @@ -0,0 +1,3 @@ +
+ $Widget +
From 62afe99f81ee9ad7944009687702bc10ffe3472a Mon Sep 17 00:00:00 2001 From: John Milmine Date: Mon, 3 Oct 2016 11:02:05 +1300 Subject: [PATCH 2/2] hadling page and element deleting with virtualising Ensures element moves to new page if page is deleted, and recreats element if original is deleted when being virtualised --- code/extensions/ElementPageExtension.php | 42 +++++++++++++++++ code/models/BaseElement.php | 58 ++++++++++++++++++++++++ 2 files changed, 100 insertions(+) diff --git a/code/extensions/ElementPageExtension.php b/code/extensions/ElementPageExtension.php index eb7365615..eb18c3d2b 100644 --- a/code/extensions/ElementPageExtension.php +++ b/code/extensions/ElementPageExtension.php @@ -227,6 +227,48 @@ public function onBeforeWrite() parent::onBeforeWrite(); } + /** + * Ensure that if there are elements that belong to this page + * and are virtualised (Virtual Element links to them), that we move the + * original element to replace one of the virtual elements + * But only if it's a delete not an unpublish + */ + public function onAfterDelete() { + if(Versioned::get_reading_mode() == 'Stage.Stage') { + $area = $this->owner->ElementArea(); + foreach ($area->Widgets() as $element) { + $firstVirtual = false; + if ($element->getPublishedVirtualLinkedElements()->Count() > 0) { + // choose the first one + $firstVirtual = $element->getPublishedVirtualLinkedElements()->First(); + $wasPublished = true; + } else if ($element->getVirtualLinkedElements()->Count() > 0) { + // choose the first one + $firstVirtual = $element->getVirtualLinkedElements()->First(); + $wasPublished = false; + } + if ($firstVirtual) { + $origParentID = $element->ParentID; + $origSort = $element->Sort; + + // change element to first's values + $element->ParentID = $firstVirtual->ParentID; + $element->Sort = $firstVirtual->Sort; + + $firstVirtual->ParentID = $origParentID; + $firstVirtual->Sort = $origSort; + // write + $element->write(); + $firstVirtual->write(); + if ($wasPublished) { + $element->doPublish(); + $firstVirtual->doPublish(); + } + } + } + } + } + /** * @return boolean */ diff --git a/code/models/BaseElement.php b/code/models/BaseElement.php index 971ad777a..7a65596aa 100644 --- a/code/models/BaseElement.php +++ b/code/models/BaseElement.php @@ -182,6 +182,64 @@ public function onBeforeWrite() } } + /** + * Ensure that if there are elements that are virtualised from this element + * that we move the original element to replace one of the virtual elements + * But only if it's a delete not an unpublish + */ + public function onBeforeDelete() { + parent::onBeforeDelete(); + + if(Versioned::get_reading_mode() == 'Stage.Stage') { + $firstVirtual = false; + $allVirtual = $this->getVirtualLinkedElements(); + if ($this->getPublishedVirtualLinkedElements()->Count() > 0) { + // choose the first one + $firstVirtual = $this->getPublishedVirtualLinkedElements()->First(); + $wasPublished = true; + } else if ($allVirtual->Count() > 0) { + // choose the first one + $firstVirtual = $this->getVirtualLinkedElements()->First(); + $wasPublished = false; + } + if ($firstVirtual) { + $origParentID = $this->ParentID; + $origSort = $this->Sort; + + $clone = $this->duplicate(false); + + // set clones values to first virtual's values + $clone->ParentID = $firstVirtual->ParentID; + $clone->Sort = $firstVirtual->Sort; + + $clone->write(); + if ($wasPublished) { + $clone->doPublish(); + $firstVirtual->doUnpublish(); + } + + // clone has a new ID, so need to repoint + // all the other virtual elements + foreach($allVirtual as $virtual) { + if ($virtual->ID == $firstVirtual->ID) { + continue; + } + $pub = false; + if ($virtual->isPublished()) { + $pub = true; + } + $virtual->LinkedElementID = $clone->ID; + $virtual->write(); + if ($pub) { + $virtual->doPublish(); + } + } + + $firstVirtual->delete(); + } + } + } + /** * @return string */