/** * Compile a list of changes to the current page, excluding non-published and explicitly secured versions. * * @param int $highestVersion Top version number to consider. * @param int $limit Limit to the amount of items returned. * * @returns ArrayList List of cleaned records. */ public function getDiffList($highestVersion = null, $limit = 100) { // This can leak secured content if it was protected via inherited setting. // For now the users will need to be aware about this shortcoming. $offset = $highestVersion ? "AND \"SiteTree_versions\".\"Version\"<='" . (int) $highestVersion . "'" : ''; // Get just enough elements for diffing. We need one more than desired to have something to compare to. $qLimit = (int) $limit + 1; $versions = $this->owner->allVersions("\"WasPublished\"='1' AND \"CanViewType\" IN ('Anyone', 'Inherit') {$offset}", "\"LastEdited\" DESC", $qLimit); // Process the list to add the comparisons. $changeList = new ArrayList(); $previous = null; $count = 0; foreach ($versions as $version) { $changed = false; // Check if we have something to compare with. if (isset($previous)) { // Produce the diff fields for use in the template. if ($version->Title != $previous->Title) { $diffTitle = Diff::compareHTML($version->Title, $previous->Title); $version->DiffTitle = new HTMLText(); $version->DiffTitle->setValue(sprintf('<div><em>%s</em> ' . $diffTitle . '</div>', _t('RSSHistory.TITLECHANGED', 'Title has changed:'))); $changed = true; } if ($version->Content != $previous->Content) { $diffContent = Diff::compareHTML($version->Content, $previous->Content); $version->DiffContent = new HTMLText(); $version->DiffContent->setValue('<div>' . $diffContent . '</div>'); $changed = true; } // Copy the link so it can be cached by SS_Cache. $version->GeneratedLink = $version->AbsoluteLink(); } // Omit the versions that haven't been visibly changed (only takes the above fields into consideration). if ($changed) { $changeList->push($version); $count++; } // Store the last version for comparison. $previous = $version; } // Make sure enough diff items have been generated to satisfy the $limit. If we ran out, add the final, // non-diffed item (the initial version). This will also work for a single-diff request: if we are requesting // a diff on the initial version we will just get that version, verbatim. if ($previous && $versions->count() < $qLimit) { $first = clone $previous; $first->DiffContent = new HTMLText(); $first->DiffContent->setValue('<div>' . $first->Content . '</div>'); // Copy the link so it can be cached by SS_Cache. $first->GeneratedLink = $first->AbsoluteLink(); $changeList->push($first); } return $changeList; }
/** * @see https://groups.google.com/forum/#!topic/silverstripe-dev/yHcluCvuszo */ public function testTableDiff() { if (!class_exists('DOMDocument')) { $this->markTestSkipped('"DOMDocument" required'); return; } $from = "<table> \n\t\t<tbody> \n\t\t\t<tr class=\"blah\"> \n\t\t\t\t<td colspan=\"2\">Row 1</td> \n\t\t\t</tr> \n\t\t\t<tr class=\"foo\"> \n\t\t\t\t<td>Row 2</td> \n\t\t\t\t<td>Row 2</td> \n\t\t\t</tr> \n\t\t\t<tr> \n\t\t\t\t<td>Row 3</td> \n\t\t\t\t<td>Row 3</td> \n\t\t\t</tr> \n\t\t\t</tbody> \n\t\t</table>"; $to = "<table class=\"new-class\"> \n\t\t<tbody> \n\t\t\t<tr class=\"blah\"> \n\t\t\t\t<td colspan=\"2\">Row 1</td> \n\t\t\t</tr> \n\t\t\t<tr class=\"foo\"> \n\t\t\t\t<td>Row 2</td> \n\t\t\t\t<td>Row 2</td> \n\t\t\t</tr> \n\t\t</tbody> \n\t\t</table>"; $expected = "<ins>" . $to . "</ins>" . "<del>" . $from . "</del>"; $compare = Diff::compareHTML($from, $to); // Very hard to debug this way, wouldn't need to do this if PHP had an *actual* DOM parsing lib, // and not just the poor excuse that is DOMDocument $compare = preg_replace('/[\\s\\t\\n\\r]*/', '', $compare); $expected = preg_replace('/[\\s\\t\\n\\r]*/', '', $expected); $this->assertEquals($compare, $expected); }
/** * Get a SS_List of the changed fields. * Each element is an array data containing * - Name: The field name * - Title: The human-readable field title * - Diff: An HTML diff showing the changes * - From: The older version of the field * - To: The newer version of the field */ function ChangedFields() { $changedFields = new ArrayList(); if ($this->fromRecord) { $base = $this->fromRecord; $fields = array_keys($this->fromRecord->toMap()); } else { $base = $this->toRecord; $fields = array_keys($this->toRecord->toMap()); } foreach ($fields as $field) { if (in_array($field, $this->ignoredFields)) { continue; } if (!$this->fromRecord || $this->fromRecord->{$field} != $this->toRecord->{$field}) { // Only show HTML diffs for fields which allow HTML values in the first place $fieldObj = $this->toRecord->dbObject($field); if ($this->fromRecord) { $fieldDiff = Diff::compareHTML($this->fromRecord->{$field}, $this->toRecord->{$field}, !$fieldObj || $fieldObj->stat('escape_type') != 'xml'); } else { if ($fieldObj && $fieldObj->stat('escape_type') == 'xml') { $fieldDiff = "<ins>" . $this->toRecord->{$field} . "</ins>"; } else { $fieldDiff = "<ins>" . Convert::raw2xml($this->toRecord->{$field}) . "</ins>"; } } $changedFields->push(new ArrayData(array('Name' => $field, 'Title' => $base->fieldLabel($field), 'Diff' => $this->fromRecord ? Diff::compareHTML($this->fromRecord->{$field}, $this->toRecord->{$field}) : "<ins>" . $this->toRecord->{$field} . "</ins>", 'From' => $this->fromRecord ? $this->fromRecord->{$field} : null, 'To' => $this->toRecord ? $this->toRecord->{$field} : null))); } } return $changedFields; }
/** * Get a DataObjectSet of the changed fields. * Each element is an array data containing * - Name: The field name * - Title: The human-readable field title * - Diff: An HTML diff showing the changes * - From: The older version of the field * - To: The newer version of the field */ function ChangedFields() { $changedFields = new DataObjectSet(); if ($this->fromRecord) { $base = $this->fromRecord; $fields = array_keys($this->fromRecord->getAllFields()); } else { $base = $this->toRecord; $fields = array_keys($this->toRecord->getAllFields()); } foreach ($fields as $field) { if (in_array($field, $this->ignoredFields)) { continue; } if (!$this->fromRecord || $this->fromRecord->{$field} != $this->toRecord->{$field}) { $changedFields->push(new ArrayData(array('Name' => $field, 'Title' => $base->fieldLabel($field), 'Diff' => $this->fromRecord ? Diff::compareHTML($this->fromRecord->{$field}, $this->toRecord->{$field}) : "<ins>" . $this->toRecord->{$field} . "</ins>", 'From' => $this->fromRecord ? $this->fromRecord->{$field} : null, 'To' => $this->toRecord ? $this->toRecord->{$field} : null))); } } return $changedFields; }
/** * 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; }