public function testHasOnes() { $obj1 = $this->objFromFixture('DataDifferencerTest_Object', 'obj1'); $image1 = $this->objFromFixture('DataDifferencerTest_MockImage', 'image1'); $image2 = $this->objFromFixture('DataDifferencerTest_MockImage', 'image2'); $relobj1 = $this->objFromFixture('DataDifferencerTest_HasOneRelationObject', 'relobj1'); $relobj2 = $this->objFromFixture('DataDifferencerTest_HasOneRelationObject', 'relobj2'); // in order to ensure the Filename path is correct, append the correct FRAMEWORK_DIR to the start // this is only really necessary to make the test pass when FRAMEWORK_DIR is not "framework" $image1->Filename = FRAMEWORK_DIR . substr($image1->Filename, 9); $image2->Filename = FRAMEWORK_DIR . substr($image2->Filename, 9); $origUpdateFilesystem = Config::inst()->get('File', 'update_filesystem'); // we don't want the filesystem being updated on write, as we're only dealing with mock files Config::inst()->update('File', 'update_filesystem', false); $image1->write(); $image2->write(); Config::inst()->update('File', 'update_filesystem', $origUpdateFilesystem); // create a new version $obj1->ImageID = $image2->ID; $obj1->HasOneRelationID = $relobj2->ID; $obj1->write(); $obj1v1 = Versioned::get_version('DataDifferencerTest_Object', $obj1->ID, $obj1->Version - 1); $obj1v2 = Versioned::get_version('DataDifferencerTest_Object', $obj1->ID, $obj1->Version); $differ = new DataDifferencer($obj1v1, $obj1v2); $obj1Diff = $differ->diffedData(); $this->assertContains($image1->Filename, $obj1Diff->getField('Image')); $this->assertContains($image2->Filename, $obj1Diff->getField('Image')); $this->assertContains('<ins>obj2</ins><del>obj1</del>', str_replace(' ', '', $obj1Diff->getField('HasOneRelationID'))); }
public function testHasOnes() { /** @var DataDifferencerTest_Object $obj1 */ $obj1 = $this->objFromFixture('DataDifferencerTest_Object', 'obj1'); $image1 = $this->objFromFixture('Image', 'image1'); $image2 = $this->objFromFixture('Image', 'image2'); $relobj1 = $this->objFromFixture('DataDifferencerTest_HasOneRelationObject', 'relobj1'); $relobj2 = $this->objFromFixture('DataDifferencerTest_HasOneRelationObject', 'relobj2'); // create a new version $beforeVersion = $obj1->Version; $obj1->ImageID = $image2->ID; $obj1->HasOneRelationID = $relobj2->ID; $obj1->write(); $afterVersion = $obj1->Version; $this->assertNotEquals($beforeVersion, $afterVersion); /** @var DataDifferencerTest_Object $obj1v1 */ $obj1v1 = Versioned::get_version('DataDifferencerTest_Object', $obj1->ID, $beforeVersion); /** @var DataDifferencerTest_Object $obj1v2 */ $obj1v2 = Versioned::get_version('DataDifferencerTest_Object', $obj1->ID, $afterVersion); $differ = new DataDifferencer($obj1v1, $obj1v2); $obj1Diff = $differ->diffedData(); $this->assertContains($image1->Name, $obj1Diff->getField('Image')); $this->assertContains($image2->Name, $obj1Diff->getField('Image')); $this->assertContains('<ins>obj2</ins><del>obj1</del>', str_replace(' ', '', $obj1Diff->getField('HasOneRelationID'))); }
public function handleItem($gridField, $request) { $controller = $gridField->getForm()->Controller(); if (is_numeric($request->param('ID')) && is_numeric($request->param('Version'))) { $record = Versioned::get_version($gridField->getModelClass(), $request->param('ID'), $request->param('Version')); } elseif (is_numeric($request->param('ID'))) { $record = Versioned::get_latest_version($gridField->getModelClass(), $request->param('ID')); } else { $record = Object::create($gridField->getModelClass()); } $class = $this->getItemRequestClass(); $handler = Object::create($class, $gridField, $this, $record, $controller, $this->name); $handler->setTemplate($this->template); return $handler->handleRequest($request, DataModel::inst()); }
function testHasOnes() { $obj1 = $this->objFromFixture('DataDifferencerTest_Object', 'obj1'); $image1 = $this->objFromFixture('DataDifferencerTest_MockImage', 'image1'); $image2 = $this->objFromFixture('DataDifferencerTest_MockImage', 'image2'); $relobj1 = $this->objFromFixture('DataDifferencerTest_HasOneRelationObject', 'relobj1'); $relobj2 = $this->objFromFixture('DataDifferencerTest_HasOneRelationObject', 'relobj2'); // create a new version $obj1->ImageID = $image2->ID; $obj1->HasOneRelationID = $relobj2->ID; $obj1->write(); $obj1v1 = Versioned::get_version('DataDifferencerTest_Object', $obj1->ID, $obj1->Version - 1); $obj1v2 = Versioned::get_version('DataDifferencerTest_Object', $obj1->ID, $obj1->Version); $differ = new DataDifferencer($obj1v1, $obj1v2); $obj1Diff = $differ->diffedData(); $this->assertContains($image1->Filename, $obj1Diff->getField('Image')); $this->assertContains($image2->Filename, $obj1Diff->getField('Image')); $this->assertContains('<ins>obj2</ins><del>obj1</del>', $obj1Diff->getField('HasOneRelationID')); }
public function ProductVariation($current = false) { if ($current) { return DataObject::get_by_id('ProductVariation', $this->_productVariationID); } else { return Versioned::get_version('ProductVariation', $this->_productVariationID, $this->_productVariationVersion); } }
/** * Get related product * - live version if in cart, or * - historical version if order is placed * * @param boolean $forcecurrent - force getting latest version of the product. * @return Product */ public function Product($forcecurrent = false) { //TODO: this might need some unit testing to make sure it compliles with comment description //ie use live if in cart (however I see no logic for checking cart status) if ($this->ProductID && $this->ProductVersion && !$forcecurrent) { return Versioned::get_version('Product', $this->ProductID, $this->ProductVersion); } elseif ($this->ProductID && ($product = Versioned::get_one_by_stage("Product", "Live", "\"Product\".\"ID\" = " . $this->ProductID))) { return $product; } return false; }
public function addVersionViewer(FieldList $fields) { if ($this->owner->hasExtension('Versioned') || $this->owner->hasExtension('VersionedDataObject')) { // Get the object where this function was called for reference purposes $object = $this->owner; // Get all tabs in the current model admin and prepart to put within a tabset called "Current" $current_tabs = $fields->find('Name', 'Root')->Tabs(); $fields = FieldList::create(TabSet::create("Root", $currenttab = TabSet::create("Current"), $historytab = TabSet::create("History")->addExtraClass("vertical-tabs"))); // Add all existing tabs to "Current" tabset $first = true; foreach ($current_tabs as $tab) { // If we have the getVersionedState function, // add a notice regarding the versioned state to the first tab // TODO incorporate VersionedDataObjectState extension into this module if ($first && $object->hasMethod('getVersionedState')) { $fields->addFieldToTab("Root.Current." . $tab->title, LiteralField::create('VersionedState', '<div class="message notice"><p>' . $object->getVersionedState() . '</p></div>')); $first = false; } $fields->addFieldsToTab("Root.Current." . $tab->title, $tab->Fields()); } // Remove any fields that have VersionViewerVisibility turned off foreach ($current_tabs as &$tab) { foreach ($tab->Fields() as $field) { // echo '<pre>'.$field->Name.' Viewable? '; print_r($field->versionViewerVisibility); if (!$field->versionViewerVisibility && $tab->fieldByName($field->Name)) { $tab->removeByName($field->Name); } } } // die(); // Also, as of now, Versioned does not track has_many or many_many relationships // So find fields relating to those relationships, remove them, // and add a message regarding this $untracked_msg = ""; foreach ($current_tabs as &$tab) { foreach ($tab->Fields() as $field) { $rel_class = $object->getRelationClass($field->Name); if ($rel_class) { if (in_array($rel_class, $object->has_many()) || in_array($rel_class, $object->many_many())) { if ($tab->fieldByName($field->Name)) { $tab->removeByName($field->Name); if (!$untracked_msg) { // $untracked_msg = '<div class="message notice"><p>Note: the following relationships are not tracked by versioning because they involve multiple records:<br />'; $untracked_msg = '<p>' . $field->Title(); } else { $untracked_msg .= "<br />" . $field->Title(); } } } } } } if ($untracked_msg) { $untracked_msg .= '</p>'; } // Get all past versions of this data object and put the relevant data in a set of tabs // within a tabset called "History" $versions = $object->allVersions(); foreach ($versions as $version) { // Get a record of this version of the object $record = Versioned::get_version($object->ClassName, $object->ID, $version->Version); // Make a set of read-only fields for use in assembling the History tabs $read_fields = $current_tabs->makeReadonly(); // Make a form using the relevant fields and load it with data from this record $form = new Form($object, "old_version", $read_fields, $object->getCMSActions()); $form->loadDataFrom($record); // Add the version number to each field name so we don't have duplicate field names if ($form->fields->dataFields()) { foreach ($form->fields->dataFields() as $field) { $field->Name = $field->Name . "version" . $version->Version; } } // Generate some heading strings describing this version $was_published = $version->WasPublished ? "P" : "D"; $was_published_full = $version->WasPublished ? "Published" : "Saved as draft"; $publishedby = Member::get()->byId($version->PublisherID); $authoredby = Member::get()->byId($version->AuthorID); $publisher_heading = ""; $author_heading = ""; if ($publishedby) { $publisher_heading = " by " . $publishedby->getName(); } if ($authoredby) { $author_heading = " (Authored by " . $authoredby->getName() . ")"; } $up_date = new SS_Datetime('update'); $up_date->setValue($version->LastEdited); $nice_date = $up_date->FormatFromSettings(); $tab_title = $version->Version . " - " . $nice_date . " (" . $was_published . ")"; $latest_version_notice = ""; if ($version->isLatestVersion()) { $latest_version_notice = " (latest version)"; } $tab_heading = "<div class='message notice'><p><strong>Viewing version " . $version->Version . $latest_version_notice . ".</strong><br>" . $was_published_full . $publisher_heading . " on " . $nice_date . $author_heading . "</p></div>"; // Add fields to a tab headed with a description of this version $fields->addFieldsToTab('Root.History.' . $tab_title, LiteralField::create('versionHeader' . $version->Version, $tab_heading)); $fields->addFieldsToTab('Root.History.' . $tab_title, $form->fields); // Add notice regarding untracked relationships if ($untracked_msg) { $fields->addFieldsToTab('Root.History.' . $tab_title, HeaderField::create('untrackedMessageHeader' . $version->Version, 'Note: the relationships listed below are not tracked by versioning because they involve multiple records', 4)); $fields->addFieldsToTab('Root.History.' . $tab_title, LiteralField::create('untrackedMessage' . $version->Version, $untracked_msg)); // $fields->addFieldsToTab('Root.History.' . $tab_title, LiteralField::create('untrackedMessage'.$version->Version, $untracked_msg)); } } } return $fields; }
/** * Compare two version, and return the diff between them. * @param string $from The version to compare from. * @param string $to The version to compare to. * @return DataObject */ function compareVersions($from, $to) { $fromRecord = Versioned::get_version($this->owner->class, $this->owner->ID, $from); $toRecord = Versioned::get_version($this->owner->class, $this->owner->ID, $to); $diff = new DataDifferencer($fromRecord, $toRecord); return $diff->diffedData(); }
/** * @return Form */ function CompareVersionsForm($versionID, $otherVersionID) { if ($versionID > $otherVersionID) { $toVersion = $versionID; $fromVersion = $otherVersionID; } else { $toVersion = $otherVersionID; $fromVersion = $versionID; } if (!$toVersion || !$toVersion) { return false; } $id = $this->currentPageID(); $page = DataObject::get_by_id("SiteTree", $id); if ($page && !$page->canView()) { return Security::permissionFailure($this); } $record = $page->compareVersions($fromVersion, $toVersion); $fromVersionRecord = Versioned::get_version('SiteTree', $id, $fromVersion); $toVersionRecord = Versioned::get_version('SiteTree', $id, $toVersion); if (!$fromVersionRecord) { user_error("Can't find version {$fromVersion} of page {$id}", E_USER_ERROR); } if (!$toVersionRecord) { user_error("Can't find version {$toVersion} of page {$id}", E_USER_ERROR); } if ($record) { $form = $this->getEditForm($id, null, null, true); $form->setActions(new FieldList()); $form->addExtraClass('compare'); // Comparison views shouldn't be editable. // Its important to convert fields *before* loading data, // as the comparison output is HTML and not valid values for the various field types $readonlyFields = $form->Fields()->makeReadonly(); $form->setFields($readonlyFields); $form->loadDataFrom($record); $form->loadDataFrom(array("ID" => $id, "Version" => $fromVersion)); foreach ($form->Fields()->dataFields() as $field) { $field->dontEscape = true; } return $form; } }
/** * Push a content changeset to a syncro node * * @param ContentChangeset $changeset * @param RemoteSyncroNode $node * */ public function pushChangeset($changeset, $node) { $cs = $changeset->ChangesetItems(); $update = array('changes' => array(), 'deletes' => array(), 'rels' => array()); foreach ($cs as $changesetItem) { $record = null; // if it's a delete, we create a syncro delete, otherwise get the versioned item if ($changesetItem->ChangeType == 'Draft Deleted') { // find the syncrodelete record $record = DataList::create('SyncroDelete')->filter(array('ContentID' => $changesetItem->OtherContentID))->first(); $del = array('SYNCRODELETE' => 'DELETE', 'ContentID' => $record->ContentID, 'Type' => $record->Type, 'MasterNode' => SiteConfig::current_site_config()->SystemID, 'Deleted' => $record->Created); $update['deletes'][] = $del; } else { $record = Versioned::get_version($changesetItem->OtherClass, $changesetItem->OtherID, $changesetItem->ContentVersion); $syncd = $this->syncroObject($record); $syncd['ClassName'] = $record->ClassName; if (count($syncd['has_one']) || count($syncd['many_many'])) { $relInfo = new stdClass(); $relInfo->SYNCROREL = true; $relInfo->Type = $record->ClassName; $relInfo->ContentID = $record->ContentID; $relInfo->MasterNode = SiteConfig::current_site_config()->SystemID; $relInfo->has_one = $syncd['has_one']; $relInfo->many_many = $syncd['many_many']; $update['rels'][] = $relInfo; } unset($syncd['has_one']); unset($syncd['many_many']); $update['changes'][] = $syncd; } } if ($update && $node) { $url = $node->NodeURL; $service = new RestfulService($url, -1); $params = array('changes' => $update['changes'], 'deletes' => $update['deletes'], 'rels' => $update['rels']); $headers = array('X-Auth-Token: ' . $node->APIToken, 'Content-Type: application/json'); $body = json_encode($params); $response = $service->request(self::PUSH_URL, 'POST', $body, $headers); //, $headers, $curlOptions); if ($response && $response->isError()) { $body = $response->getBody(); if ($body) { $this->log->logError($body); } return array(false, "Deployment failed to {$url}: status {$response->getStatusCode()}"); } $body = $response->getBody(); if ($body) { return array(true, 'Deployment successful'); } } }
/** * Get the product for the item * * @return Mixed Product if it exists, otherwise null */ function Product() { return Versioned::get_version('Product', $this->ProductID, $this->ProductVersion); }
function compareversions() { $id = $this->request->param('ID') ? $this->request->param('ID') : $this->request->requestVar('ID'); $versions = $this->request->requestVar('Versions'); $version1 = $versions && isset($versions[0]) ? $versions[0] : $this->request->getVar('From'); $version2 = $versions && isset($versions[1]) ? $versions[1] : $this->request->getVar('To'); if ($version1 > $version2) { $toVersion = $version1; $fromVersion = $version2; } else { $toVersion = $version2; $fromVersion = $version1; } if (!$toVersion || !$toVersion) { return false; } $page = DataObject::get_by_id("SiteTree", $id); if ($page && !$page->canView()) { return Security::permissionFailure($this); } $record = $page->compareVersions($fromVersion, $toVersion); $fromVersionRecord = Versioned::get_version('SiteTree', $id, $fromVersion); $toVersionRecord = Versioned::get_version('SiteTree', $id, $toVersion); if (!$fromVersionRecord) { user_error("Can't find version {$fromVersion} of page {$id}", E_USER_ERROR); } if (!$toVersionRecord) { user_error("Can't find version {$toVersion} of page {$id}", E_USER_ERROR); } if ($record) { $fromDateNice = $fromVersionRecord->obj('LastEdited')->Ago(); $toDateNice = $toVersionRecord->obj('LastEdited')->Ago(); $fromAuthor = DataObject::get_by_id('Member', $fromVersionRecord->AuthorID); if (!$fromAuthor) { $fromAuthor = new ArrayData(array('Title' => 'Unknown author')); } $toAuthor = DataObject::get_by_id('Member', $toVersionRecord->AuthorID); if (!$toAuthor) { $toAuthor = new ArrayData(array('Title' => 'Unknown author')); } $fields = $record->getCMSFields($this); $fields->push(new HiddenField("ID")); $fields->push(new HiddenField("Version")); $fields->insertBefore(new LiteralField('YouAreComparingHeader', '<p class="message notice">' . sprintf(_t('CMSMain.COMPARINGV', "Comparing versions %s and %s"), "<a href=\"admin/getversion/{$id}/{$fromVersionRecord->Version}\" title=\"{$fromAuthor->Title}\">{$fromVersionRecord->Version}</a> <small>({$fromDateNice})</small>", "<a href=\"admin/getversion/{$id}/{$toVersionRecord->Version}\" title=\"{$toAuthor->Title}\">{$toVersionRecord->Version}</a> <small>({$toDateNice})</small>") . '</p>'), "Root"); $actions = new FieldSet(); $form = new Form($this, "EditForm", $fields, $actions); $form->loadDataFrom($record); $form->loadDataFrom(array("ID" => $id, "Version" => $fromVersion)); $form->addExtraClass('compare'); // comparison views shouldn't be editable $readonlyFields = $form->Fields()->makeReadonly(); $form->setFields($readonlyFields); foreach ($form->Fields()->dataFields() as $field) { $field->dontEscape = true; } if ($this->isAjax()) { return $form->formHtmlContent(); } else { $templateData = $this->customise(array("EditForm" => $form)); return $templateData->renderWith('LeftAndMain'); } } }
/** * HACK: Versioned is BROKEN this method helps in fixing it. * Basically, in Versioned, you get a hard-coded error * when you retrieve an older version of a DataObject. * This method returns null if it does not exist. * * Idea is from Jeremy: https://github.com/burnbright/silverstripe-shop/blob/master/code/products/FixVersioned.php * @param String $class * @param Int $id * @param Int $version * @return DataObject | Null */ public static function get_version($class, $id, $version) { $oldMode = Versioned::get_reading_mode(); Versioned::set_reading_mode(''); $versionedObject = Versioned::get_version($class, $id, $version); Versioned::set_reading_mode($oldMode); return $versionedObject; }
/** * @see Versioned::doRollBackTo * @param string $version Either the string 'Live' or a version number * * @return null */ public function onAfterRollback($version) { // get the owner data for this version if (is_numeric($version)) { $page = Versioned::get_version($this->owner->ClassName, $this->owner->ID, $version); } else { $this->owner->flushCache(); $baseTable = $this->owner->baseTable(); $page = Versioned::get_one_by_stage($this->owner->ClassName, $version, "\"{$baseTable}\".\"ID\"={$this->owner->ID}"); } $date = $page->LastEdited; $oldMode = Versioned::get_reading_mode(); // get the current items Versioned::set_reading_mode('Stage.Stage'); $current_items = array(); foreach ($this->getItemsToPublish() as $field) { $current_items[] = $field; } // Get items in the version that we want by setting reading mode to date of version if (is_numeric($version)) { Versioned::set_reading_mode('Archive.' . $date); } else { Versioned::set_reading_mode('Stage.Live'); } $version_items = array(); $version_ids = array(); foreach ($this->getItemsToPublish() as $field) { $version_items[] = $field; $version_ids[] = $field->ClassName . '_' . $field->ID; } Versioned::set_reading_mode($oldMode); // rollback version items foreach ($version_items as $field) { $fieldVersion = is_numeric($version) ? $field->Version : 'Live'; if ($field->hasMethod('doDoRollBackTo')) { $field->doDoRollBackTo($fieldVersion); } else { $field->doRollBackTo($fieldVersion); } } // delete items not in version foreach ($current_items as $field) { if (!in_array($field->ClassName . '_' . $field->ID, $version_ids)) { $clone = clone $field; if ($clone->hasMethod('doDeleteFromStage')) { $clone->doDeleteFromStage('Stage'); } else { $clone->deleteFromStage('Stage'); } } } }
/** * Compare two version, and return the diff between them. * @param string $from The version to compare from. * @param string $to The version to compare to. * @return DataObject */ function compareVersions($from, $to) { $fromRecord = Versioned::get_version($this->owner->class, $this->owner->ID, $from); $toRecord = Versioned::get_version($this->owner->class, $this->owner->ID, $to); $fields = array_keys($fromRecord->getAllFields()); foreach ($fields as $field) { if (in_array($field, array("ID", "Version", "RecordID", "AuthorID", "ParentID"))) { continue; } $fromRecord->{$field} = Diff::compareHTML($fromRecord->{$field}, $toRecord->{$field}); } return $fromRecord; }
/** * Retrieve the object this item represents, usually a {@link Variation}. * Uses the object version so that the correct object details such as price are * retrieved. * * @return DataObject */ function Object() { return Versioned::get_version($this->ObjectClass, $this->ObjectID, $this->ObjectVersion); }
public function onAfterWrite() { // Need to flush cache to avoid outdated versionnumber references $this->flushCache(); $linkedPages = $this->VirtualPages(); if ($linkedPages) { // The only way after a write() call to determine if it was triggered by a writeWithoutVersion(), // which we have to pass on to the virtual page writes as well. $previous = $this->Version > 1 ? Versioned::get_version($this->class, $this->ID, $this->Version - 1) : null; $withoutVersion = $this->getExtensionInstance('Versioned')->_nextWriteWithoutVersion; foreach ($linkedPages as $page) { $page->copyFrom($page->CopyContentFrom()); if ($withoutVersion) { $page->writeWithoutVersion(); } else { $page->write(); } } } parent::onAfterWrite(); }
public function getCMSFields() { $fields = parent::getCMSFields(); // Remove defailt item admin $fields->removeByName('Items'); $fields->removeByName('EmailDispatchSent'); $fields->removeByName('PostageID'); $fields->removeByName('PaymentID'); $fields->removeByName('GatewayData'); // Remove Billing Details $fields->removeByName('Address1'); $fields->removeByName('Address2'); $fields->removeByName('City'); $fields->removeByName('PostCode'); $fields->removeByName('Country'); // Remove Delivery Details $fields->removeByName('DeliveryFirstnames'); $fields->removeByName('DeliverySurname'); $fields->removeByName('DeliveryAddress1'); $fields->removeByName('DeliveryAddress2'); $fields->removeByName('DeliveryCity'); $fields->removeByName('DeliveryPostCode'); $fields->removeByName('DeliveryCountry'); // Remove default postage fields $fields->removeByName('PostageType'); $fields->removeByName('PostageCost'); $fields->removeByName('PostageTax'); $fields->addFieldToTab('Root.Main', ReadonlyField::create('OrderNumber', "#"), 'Status'); $fields->addFieldToTab('Root.Main', ReadonlyField::create('Created')); $fields->addFieldToTab('Root.Main', ReadonlyField::create('LastEdited', 'Last time order was saved')); // Structure billing details $billing_fields = ToggleCompositeField::create('BillingDetails', 'Billing Details', array(TextField::create('Address1', 'Address 1'), TextField::create('Address2', 'Address 2'), TextField::create('City', 'City'), TextField::create('PostCode', 'Post Code'), TextField::create('Country', 'Country')))->setHeadingLevel(4); // Structure delivery details $delivery_fields = ToggleCompositeField::create('DeliveryDetails', 'Delivery Details', array(TextField::create('DeliveryFirstnames', 'First Name(s)'), TextField::create('DeliverySurname', 'Surname'), TextField::create('DeliveryAddress1', 'Address 1'), TextField::create('DeliveryAddress2', 'Address 2'), TextField::create('DeliveryCity', 'City'), TextField::create('DeliveryPostCode', 'Post Code'), TextField::create('DeliveryCountry', 'Country')))->setHeadingLevel(4); // Postage details // Structure billing details $postage_fields = ToggleCompositeField::create('Postage', 'Postage Details', array(ReadonlyField::create('PostageType'), ReadonlyField::create('PostageCost'), ReadonlyField::create('PostageTax')))->setHeadingLevel(4); $fields->addFieldToTab('Root.Main', $billing_fields); $fields->addFieldToTab('Root.Main', $delivery_fields); $fields->addFieldToTab('Root.Main', $postage_fields); // Add order items and totals $fields->addFieldToTab('Root.Items', GridField::create('Items', "Order Items", $this->Items(), GridFieldConfig::create()->addComponents(new GridFieldSortableHeader(), new GridFieldDataColumns()))); $fields->addFieldToTab("Root.Items", ReadonlyField::create("SubTotal")->setValue($this->getSubTotal())); $fields->addFieldToTab("Root.Items", ReadonlyField::create("DiscountAmount")->setValue($this->DiscountAmount)); $fields->addFieldToTab("Root.Items", ReadonlyField::create("Tax")->setValue($this->getTaxTotal())); $fields->addFieldToTab("Root.Items", ReadonlyField::create("Total")->setValue($this->getTotal())); $member = Member::currentUser(); if (Permission::check('ADMIN', 'any', $member)) { // Add non-editable payment ID $paymentid_field = TextField::create('PaymentID', "Payment gateway ID number")->setReadonly(true)->performReadonlyTransformation(); $gateway_data = LiteralField::create("FormattedGatewayData", "<strong>Data returned from the payment gateway:</strong><br/><br/>" . str_replace(",", ",<br/>", $this->GatewayData)); $fields->addFieldToTab('Root.Gateway', $paymentid_field); $fields->addFieldToTab("Root.Gateway", $gateway_data); } if (Permission::check(array('COMMERCE_ORDER_HISTORY', 'ADMIN'), 'any', $member)) { // Setup basic history of this order $versions = $this->AllVersions(); $curr_version = $versions->First()->Version; $message = ""; foreach ($versions as $version) { $i = $version->Version; $name = "History_{$i}"; if ($i > 1) { $frm = Versioned::get_version($this->class, $this->ID, $i - 1); $to = Versioned::get_version($this->class, $this->ID, $i); $diff = new DataDifferencer($frm, $to); if ($version->Author()) { $message = "<p>{$version->Author()->FirstName} ({$version->LastEdited})</p>"; } else { $message = "<p>Unknown ({$version->LastEdited})</p>"; } if ($diff->ChangedFields()->exists()) { $message .= "<ul>"; // Now loop through all changed fields and track as message foreach ($diff->ChangedFields() as $change) { if ($change->Name != "LastEdited") { $message .= "<li>{$change->Title}: {$change->Diff}</li>"; } } $message .= "</ul>"; } } $fields->addFieldToTab("Root.History", LiteralField::create($name, "<div class=\"field\">{$message}</div>")); } } $this->extend("updateCMSFields", $fields); return $fields; }
public function testLazyLoadedFieldsOnVersionedRecords() { // Save another record, sanity check that we're getting the right one $obj2 = new VersionedTest_Subclass(); $obj2->Name = "test2"; $obj2->ExtraField = "foo2"; $obj2->write(); // Save the actual inspected record $obj1 = new VersionedTest_Subclass(); $obj1->Name = "test"; $obj1->ExtraField = "foo"; $obj1->write(); $version1 = $obj1->Version; $obj1->Name = "test2"; $obj1->ExtraField = "baz"; $obj1->write(); $version2 = $obj1->Version; $reloaded = Versioned::get_version('VersionedTest_Subclass', $obj1->ID, $version1); $this->assertEquals($reloaded->Name, 'test'); $this->assertEquals($reloaded->ExtraField, 'foo'); $reloaded = Versioned::get_version('VersionedTest_Subclass', $obj1->ID, $version2); $this->assertEquals($reloaded->Name, 'test2'); $this->assertEquals($reloaded->ExtraField, 'baz'); $reloaded = Versioned::get_latest_version('VersionedTest_Subclass', $obj1->ID); $this->assertEquals($reloaded->Version, $version2); $this->assertEquals($reloaded->Name, 'test2'); $this->assertEquals($reloaded->ExtraField, 'baz'); $allVersions = Versioned::get_all_versions('VersionedTest_Subclass', $obj1->ID); $this->assertEquals(2, $allVersions->Count()); $this->assertEquals($allVersions->First()->Version, $version1); $this->assertEquals($allVersions->First()->Name, 'test'); $this->assertEquals($allVersions->First()->ExtraField, 'foo'); $this->assertEquals($allVersions->Last()->Version, $version2); $this->assertEquals($allVersions->Last()->Name, 'test2'); $this->assertEquals($allVersions->Last()->ExtraField, 'baz'); $obj1->delete(); }
/** * Overloaded relationship, for getting versioned variations * @param boolean $current */ public function ProductVariation($forcecurrent = false) { if ($this->ProductVariationID && $this->ProductVariationVersion && !$forcecurrent) { return Versioned::get_version('ProductVariation', $this->ProductVariationID, $this->ProductVariationVersion); } elseif ($this->ProductVariationID && ($product = DataObject::get_by_id('ProductVariation', $this->ProductVariationID))) { return $product; } return false; }
function testActionsViewingOldVersion() { $p = new Page(); $p->Content = 'test page first version'; $p->write(); $p->Content = 'new content'; $p->write(); // Looking at the old version, the ability to rollback to that version is available $version = DB::query('SELECT "Version" FROM "SiteTree_versions" WHERE "Content" = \'test page first version\'')->value(); $old = Versioned::get_version('Page', $p->ID, $version); $actions = $old->getCMSActions(); $this->assertNull($actions->dataFieldByName('action_save')); $this->assertNull($actions->dataFieldByName('action_publish')); $this->assertNull($actions->dataFieldByName('action_unpublish')); $this->assertNull($actions->dataFieldByName('action_delete')); $this->assertNotNull($actions->dataFieldByName('action_email')); $this->assertNotNull($actions->dataFieldByName('action_rollback')); }
public function ItemEditForm() { Requirements::javascript("orders/javascript/entwine.orders.js"); $form = parent::ItemEditForm(); $fields = $form->Fields(); $actions = $form->Actions(); $record = $this->record; $member = Member::currentUser(); $can_view = $this->record->canView(); $can_edit = $this->record->canEdit(); $can_change_status = $this->record->canChangeStatus(); $can_delete = $this->record->canDelete(); $can_create = $this->record->canCreate(); // First remove the delete button $actions->removeByName("action_doDelete"); // Deal with Estimate objects if ($record->ClassName == "Estimate") { if ($record->ID && $record->AccessKey) { $frontend_url = Controller::join_links(Director::absoluteBaseUrl(), "OrdersFront", "quote", $record->ID, $record->AccessKey); $html = '<a href="' . $frontend_url . '" '; $html .= 'target="_blank" '; $html .= 'class="action ss-ui-button ui-button ui-corner-all open-external" '; $html .= '>' . _t('Orders.ViewQuote', 'View Quote') . '</a>'; $actions->insertAfter(LiteralField::create('openQuote', $html), "action_doSave"); } if ($record->ID && $can_edit) { $actions->insertAfter(FormAction::create('doConvert', _t('Orders.ConvertToOrder', 'Convert To Order'))->setUseButtonTag(true), "action_doSave"); } } // Deal with Order objects if ($record->ClassName == "Order") { // Set our status field as a dropdown (has to be here to // ignore canedit) // Allow users to change status (as long as they have permission) if ($can_edit || $can_change_status) { $status_field = DropdownField::create('Status', null, $record->config()->statuses); // Set default status if we can if (!$record->Status && !$record->config()->default_status) { $status_field->setValue($record->config()->default_status); } else { $status_field->setValue($record->Status); } $fields->replaceField("Status", $status_field); } // Setup order history if (Permission::check(array('COMMERCE_ORDER_HISTORY', 'ADMIN'), 'any', $member)) { $versions = $record->AllVersions(); $first_version = $versions->First(); $curr_version = $first_version ? $versions->First() : null; $message = ""; foreach ($versions as $version) { $i = $version->Version; $name = "History_{$i}"; if ($i > 0) { $frm = Versioned::get_version($record->class, $record->ID, $i - 1); $to = Versioned::get_version($record->class, $record->ID, $i); $diff = new DataDifferencer($frm, $to); if ($version->Author()) { $message = "<p>{$version->Author()->FirstName} ({$version->LastEdited})</p>"; } else { $message = "<p>Unknown ({$version->LastEdited})</p>"; } if ($diff->ChangedFields()->exists()) { $message .= "<ul>"; // Now loop through all changed fields and track as message foreach ($diff->ChangedFields() as $change) { if ($change->Name != "LastEdited") { $message .= "<li>{$change->Title}: {$change->Diff}</li>"; } } $message .= "</ul>"; } $fields->addFieldToTab("Root.History", LiteralField::create($name, "<div class=\"field\">{$message}</div>")); } } } // Is user cannot edit, but can change status, add change // status button if ($record->ID && !$can_edit && $can_change_status) { $actions->push(FormAction::create('doChangeStatus', _t('Orders.Save', 'Save'))->setUseButtonTag(true)->addExtraClass('ss-ui-action-constructive')->setAttribute('data-icon', 'accept')); } if ($record->ID && $record->AccessKey) { $frontend_url = Controller::join_links(Director::absoluteBaseUrl(), "OrdersFront", "invoice", $record->ID, $record->AccessKey); $html = '<a href="' . $frontend_url . '" '; $html .= 'target="_blank" '; $html .= 'class="action ss-ui-button ui-button ui-corner-all open-external" '; $html .= '>' . _t('Orders.ViewInvoice', 'View Invoice') . '</a>'; $link_field = LiteralField::create('openQuote', $html); if ($actions->find("Name", "action_doSave")) { $actions->insertAfter($link_field, "action_doSave"); } if ($actions->find("Name", "action_doChangeStatus")) { $actions->insertAfter($link_field, "action_doChangeStatus"); } } } // Add a duplicate button, either after the save button or // the change status "save" button. if ($record->ID) { $duplicate_button = FormAction::create('doDuplicate', _t('Orders.Duplicate', 'Duplicate'))->setUseButtonTag(true); if ($actions->find("Name", "action_doSave")) { $actions->insertAfter($duplicate_button, "action_doSave"); } if ($actions->find("Name", "action_doChangeStatus")) { $actions->insertAfter($duplicate_button, "action_doChangeStatus"); } } // Finally, if allowed, re-add the delete button (so it is last) if ($record->ID && $can_delete) { $actions->push(FormAction::create('doDelete', _t('GridFieldDetailForm.Delete', 'Delete'))->setUseButtonTag(true)->addExtraClass('ss-ui-action-destructive action-delete')); } // Set our custom template $form->setTemplate("OrdersItemEditForm"); $this->extend("updateItemEditForm", $form); return $form; }
function compareversions() { $id = (int) $this->urlParams['ID']; $version1 = (int) $_REQUEST['From']; $version2 = (int) $_REQUEST['To']; if ($version1 > $version2) { $toVersion = $version1; $fromVersion = $version2; } else { $toVersion = $version2; $fromVersion = $version1; } $page = DataObject::get_by_id("SiteTree", $id); if ($page && !$page->canView()) { return Security::permissionFailure($this); } $record = $page->compareVersions($fromVersion, $toVersion); $fromVersionRecord = Versioned::get_version('SiteTree', $id, $fromVersion); $toVersionRecord = Versioned::get_version('SiteTree', $id, $toVersion); if (!$fromVersionRecord) { user_error("Can't find version {$fromVersion} of page {$id}", E_USER_ERROR); } if (!$toVersionRecord) { user_error("Can't find version {$toVersion} of page {$id}", E_USER_ERROR); } if ($record) { $fromDateNice = $fromVersionRecord->obj('LastEdited')->Ago(); $toDateNice = $toVersionRecord->obj('LastEdited')->Ago(); $fromAuthor = DataObject::get_by_id('Member', $fromVersionRecord->AuthorID); if (!$fromAuthor) { $fromAuthor = new ArrayData(array('Title' => 'Unknown author')); } $toAuthor = DataObject::get_by_id('Member', $toVersionRecord->AuthorID); if (!$toAuthor) { $toAuthor = new ArrayData(array('Title' => 'Unknown author')); } $fields = $record->getCMSFields($this); $fields->push(new HiddenField("ID")); $fields->push(new HiddenField("Version")); $fields->insertBefore(new LiteralField('YouAreComparingHeader', '<p class="message notice">' . sprintf(_t('CMSMain.COMPARINGV', "Comparing versions %s and %s"), "<a href=\"admin/getversion/{$id}/{$fromVersionRecord->Version}\" title=\"{$fromAuthor->Title}\">{$fromVersionRecord->Version}</a> <small>({$fromDateNice})</small>", "<a href=\"admin/getversion/{$id}/{$toVersionRecord->Version}\" title=\"{$toAuthor->Title}\">{$toVersionRecord->Version}</a> <small>({$toDateNice})</small>") . '</p>'), "Root"); $actions = new FieldSet(); $form = new Form($this, "EditForm", $fields, $actions); $form->loadDataFrom($record); $form->loadDataFrom(array("ID" => $id, "Version" => $fromVersion)); // comparison views shouldn't be editable $readonlyFields = $form->Fields()->makeReadonly(); $form->setFields($readonlyFields); foreach ($form->Fields()->dataFields() as $field) { $field->dontEscape = true; } return $this->sendFormToBrowser(array("EditForm" => $form)); } }
/** * Overloaded Product accessor method. * * Overloaded from the default has_one accessor to * retrieve a product by it's version, this is extremely * useful because we can set in stone the version of * a product at the time when the user adds the item to * their cart, so if the CMS admin changes the price, it * remains the same for this order. * * @param boolean $current If set to TRUE, returns the latest published version of the Product, * If set to FALSE, returns the set version number of the Product * (instead of the latest published version) * @return Product object */ public function Product($current = false) { if ($current) { return DataObject::get_by_id('Product', $this->_productID); } elseif ($this->_productID && $this->_productVersion) { return Versioned::get_version('Product', $this->_productID, $this->_productVersion); } }
public function testGetVersionWhenClassnameChanged() { $obj = new VersionedTest_DataObject(); $obj->Name = "test"; $obj->write(); $obj->Name = "test2"; $obj->ClassName = "VersionedTest_Subclass"; $obj->write(); $subclassVersion = $obj->Version; $obj->Name = "test3"; $obj->ClassName = "VersionedTest_DataObject"; $obj->write(); // We should be able to pass the subclass and still get the correct class back $obj2 = Versioned::get_version("VersionedTest_Subclass", $obj->ID, $subclassVersion); $this->assertInstanceOf("VersionedTest_Subclass", $obj2); $this->assertEquals("test2", $obj2->Name); $obj3 = Versioned::get_latest_version("VersionedTest_Subclass", $obj->ID); $this->assertEquals("test3", $obj3->Name); $this->assertInstanceOf("VersionedTest_DataObject", $obj3); }
function getversion() { $id = $this->urlParams['ID']; $version = str_replace('&ajax=1', '', $this->urlParams['OtherID']); $record = Versioned::get_version("SiteTree", $id, $version); if ($record) { $fields = $record->getCMSFields($this); $fields->removeByName("Status"); $fields->push(new HiddenField("ID")); $fields->push(new HiddenField("Version")); $fields->push(new HeaderField(sprintf(_t('CMSMain.VIEWING', "You are viewing version #%d, created %s"), $version, $record->obj('LastEdited')->Ago()))); $actions = new FieldSet(new FormAction("email", _t('CMSMain.EMAIL', "Email")), new FormAction("print", _t('CMSMain.PRINT', "Print")), new FormAction("rollback", _t('CMSMain.ROLLBACK', "Roll back to this version"))); // encode the message to appear in the body of the email $archiveURL = Director::absoluteBaseURL() . $record->URLSegment . '?archiveDate=' . $record->obj('LastEdited')->URLDatetime(); $archiveEmailMessage = urlencode($this->customise(array('ArchiveDate' => $record->obj('LastEdited'), 'ArchiveURL' => $archiveURL))->renderWith('ViewArchivedEmail')); $archiveEmailMessage = preg_replace('/\\+/', '%20', $archiveEmailMessage); $fields->push(new HiddenField('ArchiveEmailMessage', '', $archiveEmailMessage)); $fields->push(new HiddenField('ArchiveEmailSubject', '', preg_replace('/\\+/', '%20', urlencode('Archived version of ' . $record->Title)))); $fields->push(new HiddenField('ArchiveURL', '', $archiveURL)); $form = new Form($this, "EditForm", $fields, $actions); $form->loadDataFrom($record); $form->loadDataFrom(array("ID" => $id, "Version" => $version)); $form->makeReadonly(); $templateData = $this->customise(array("EditForm" => $form)); SSViewer::setOption('rewriteHashlinks', false); $result = $templateData->renderWith($this->class . '_right'); $parts = split('</?form[^>]*>', $result); return $parts[sizeof($parts) - 2]; } }
/** * Get a database record to be managed by the CMS. * * @param int $id Record ID * @param int $versionID optional Version id of the given record */ public function getRecord($id, $versionID = null) { $treeClass = $this->stat('tree_class'); if ($id instanceof $treeClass) { return $id; } else { if ($id && is_numeric($id)) { if ($this->request->getVar('Version')) { $versionID = (int) $this->request->getVar('Version'); } if ($versionID) { $record = Versioned::get_version($treeClass, $id, $versionID); } else { $record = DataObject::get_one($treeClass, "\"{$treeClass}\".\"ID\" = {$id}"); } // Then, try getting a record from the live site if (!$record) { // $record = Versioned::get_one_by_stage($treeClass, "Live", "\"$treeClass\".\"ID\" = $id"); Versioned::reading_stage('Live'); singleton($treeClass)->flushCache(); $record = DataObject::get_one($treeClass, "\"{$treeClass}\".\"ID\" = {$id}"); if ($record) { Versioned::set_reading_mode(''); } } // Then, try getting a deleted record if (!$record) { $record = Versioned::get_latest_version($treeClass, $id); } // Don't open a page from a different locale /** The record's Locale is saved in database in 2.4, and not related with Session, * we should not check their locale matches the Translatable::get_current_locale, * here as long as we all the HTTPRequest is init with right locale. * This bit breaks the all FileIFrameField functions if the field is used in CMS * and its relevent ajax calles, like loading the tree dropdown for TreeSelectorField. */ /* if($record && Object::has_extension('SiteTree', 'Translatable') && $record->Locale && $record->Locale != Translatable::get_current_locale()) { $record = null; }*/ return $record; } else { if (substr($id, 0, 3) == 'new') { return $this->getNewItem($id); } } } }
public function dataObj() { if (is_numeric($this->itemID) && is_numeric($this->versionID)) { return Versioned::get_version(ClassInfo::baseDataClass(Object::getCustomClass($this->ctf->sourceClass())), $this->itemID, $this->versionID); } }