mirror of
https://github.com/silverstripe/silverstripe-versionfeed
synced 2024-10-22 11:05:31 +02:00
FIX: Minor functional alterations and CI improvements
FIX: PSR-2 codebase. Formatting via phpcbf FIX: rendering bug in allchanges FIX: update .gitattributes to not export codecov's config file FIX: Update SiteTree_versions to the ss4 equivalent SiteTree_Versions
This commit is contained in:
parent
17cf3d7487
commit
67e112fd12
1
.gitattributes
vendored
1
.gitattributes
vendored
@ -4,3 +4,4 @@
|
||||
/.gitignore export-ignore
|
||||
/.travis.yml export-ignore
|
||||
/.scrutinizer.yml export-ignore
|
||||
/codecov.yml export-ignore
|
||||
|
@ -22,7 +22,6 @@ before_script:
|
||||
|
||||
# Install composer dependencies
|
||||
- composer validate
|
||||
- composer require --no-update silverstripe/recipe-core:1.0.x-dev
|
||||
- if [[ $DB == PGSQL ]]; then composer require --no-update silverstripe/postgresql 2.0.x-dev; fi
|
||||
- composer install --prefer-dist --no-interaction --no-progress --no-suggest --optimize-autoloader --verbose --profile
|
||||
|
||||
|
@ -12,7 +12,9 @@
|
||||
],
|
||||
"require":
|
||||
{
|
||||
"silverstripe/recipe-cms": "^1"
|
||||
"silverstripe/cms": "^4",
|
||||
"silverstripe/versioned": "^1",
|
||||
"silverstripe/siteconfig": "^4"
|
||||
},
|
||||
"require-dev": {
|
||||
"phpunit/phpunit": "^5.7",
|
||||
|
@ -1,4 +1,4 @@
|
||||
<phpunit bootstrap="vendor/silverstripe/framework/tests/bootstrap.php" colors="true">
|
||||
<phpunit bootstrap="vendor/silverstripe/cms/tests/bootstrap.php" colors="true">
|
||||
<testsuite name="Default">
|
||||
<directory>tests/</directory>
|
||||
</testsuite>
|
||||
|
@ -2,39 +2,39 @@
|
||||
|
||||
namespace SilverStripe\VersionFeed\Filters;
|
||||
|
||||
|
||||
use SilverStripe\Core\Config\Config;
|
||||
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Caches results of a callback
|
||||
*/
|
||||
class CachedContentFilter extends ContentFilter {
|
||||
|
||||
/**
|
||||
* Enable caching
|
||||
*
|
||||
* @config
|
||||
* @var boolean
|
||||
*/
|
||||
private static $cache_enabled = true;
|
||||
|
||||
public function getContent($key, $callback) {
|
||||
$cache = $this->getCache();
|
||||
|
||||
// Return cached value if available
|
||||
$cacheEnabled = Config::inst()->get(get_class(), 'cache_enabled');
|
||||
$result = (isset($_GET['flush']) || !$cacheEnabled)
|
||||
? null
|
||||
: $cache->get($key);
|
||||
if($result) return $result;
|
||||
|
||||
// Fallback to generate result
|
||||
$result = parent::getContent($key, $callback);
|
||||
$lifetime = Config::inst()->get(ContentFilter::class, 'cache_lifetime') ?: null;
|
||||
$cache->set($key, $result, $lifetime);
|
||||
return $result;
|
||||
}
|
||||
class CachedContentFilter extends ContentFilter
|
||||
{
|
||||
|
||||
/**
|
||||
* Enable caching
|
||||
*
|
||||
* @config
|
||||
* @var boolean
|
||||
*/
|
||||
private static $cache_enabled = true;
|
||||
|
||||
public function getContent($key, $callback)
|
||||
{
|
||||
$cache = $this->getCache();
|
||||
|
||||
// Return cached value if available
|
||||
$cacheEnabled = Config::inst()->get(get_class(), 'cache_enabled');
|
||||
$result = (isset($_GET['flush']) || !$cacheEnabled)
|
||||
? null
|
||||
: $cache->get($key);
|
||||
if ($result) {
|
||||
return $result;
|
||||
}
|
||||
|
||||
// Fallback to generate result
|
||||
$result = parent::getContent($key, $callback);
|
||||
$lifetime = Config::inst()->get(ContentFilter::class, 'cache_lifetime') ?: null;
|
||||
$cache->set($key, $result, $lifetime);
|
||||
return $result;
|
||||
}
|
||||
}
|
||||
|
@ -7,58 +7,60 @@ use SilverStripe\Core\Config\Configurable;
|
||||
use Psr\SimpleCache\CacheInterface;
|
||||
use SilverStripe\Core\Injector\Injector;
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Conditionally executes a given callback, attempting to return the desired results
|
||||
* of its execution.
|
||||
*/
|
||||
abstract class ContentFilter {
|
||||
abstract class ContentFilter
|
||||
{
|
||||
|
||||
use Configurable;
|
||||
|
||||
/**
|
||||
* Nested content filter
|
||||
*
|
||||
* @var ContentFilter
|
||||
*/
|
||||
protected $nestedContentFilter;
|
||||
use Configurable;
|
||||
|
||||
/**
|
||||
* Nested content filter
|
||||
*
|
||||
* @var ContentFilter
|
||||
*/
|
||||
protected $nestedContentFilter;
|
||||
|
||||
/**
|
||||
* Cache lifetime
|
||||
*
|
||||
* @config
|
||||
* @var int
|
||||
*/
|
||||
private static $cache_lifetime = 300;
|
||||
|
||||
public function __construct($nestedContentFilter = null) {
|
||||
$this->nestedContentFilter = $nestedContentFilter;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the cache to use
|
||||
*
|
||||
* @return Zend_Cache_Frontend
|
||||
*/
|
||||
protected function getCache() {
|
||||
return Injector::inst()->get(
|
||||
CacheInterface::class . '.VersionFeedController'
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Evaluates the result of the given callback
|
||||
*
|
||||
* @param string $key Unique key for this
|
||||
* @param callable $callback Callback for evaluating the content
|
||||
* @return mixed Result of $callback()
|
||||
*/
|
||||
public function getContent($key, $callback) {
|
||||
if($this->nestedContentFilter) {
|
||||
return $this->nestedContentFilter->getContent($key, $callback);
|
||||
} else {
|
||||
return call_user_func($callback);
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Cache lifetime
|
||||
*
|
||||
* @config
|
||||
* @var int
|
||||
*/
|
||||
private static $cache_lifetime = 300;
|
||||
|
||||
public function __construct($nestedContentFilter = null)
|
||||
{
|
||||
$this->nestedContentFilter = $nestedContentFilter;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the cache to use
|
||||
*
|
||||
* @return Zend_Cache_Frontend
|
||||
*/
|
||||
protected function getCache()
|
||||
{
|
||||
return Injector::inst()->get(
|
||||
CacheInterface::class . '.VersionFeedController'
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Evaluates the result of the given callback
|
||||
*
|
||||
* @param string $key Unique key for this
|
||||
* @param callable $callback Callback for evaluating the content
|
||||
* @return mixed Result of $callback()
|
||||
*/
|
||||
public function getContent($key, $callback)
|
||||
{
|
||||
if ($this->nestedContentFilter) {
|
||||
return $this->nestedContentFilter->getContent($key, $callback);
|
||||
} else {
|
||||
return call_user_func($callback);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -2,123 +2,120 @@
|
||||
|
||||
namespace SilverStripe\VersionFeed\Filters;
|
||||
|
||||
|
||||
|
||||
|
||||
use SilverStripe\Core\Config\Config;
|
||||
use SilverStripe\Control\Controller;
|
||||
use SilverStripe\Control\HTTPResponse_Exception;
|
||||
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Provides rate limiting of execution of a callback
|
||||
*/
|
||||
class RateLimitFilter extends ContentFilter {
|
||||
|
||||
/**
|
||||
* Time duration (in second) to allow for generation of cached results. Requests to
|
||||
* pages that within this time period that do not hit the cache (and would otherwise trigger
|
||||
* a version query) will be presented with a 429 (rate limit) HTTP error
|
||||
*
|
||||
* @config
|
||||
* @var int
|
||||
*/
|
||||
private static $lock_timeout = 5;
|
||||
|
||||
/**
|
||||
* Determine if the cache generation should be locked on a per-page basis. If true, concurrent page versions
|
||||
* may be generated without rate interference.
|
||||
*
|
||||
* @config
|
||||
* @var bool
|
||||
*/
|
||||
private static $lock_bypage = false;
|
||||
|
||||
/**
|
||||
* Determine if rate limiting should be applied independently to each IP address. This method is not
|
||||
* reliable, as most DDoS attacks use multiple IP addresses.
|
||||
*
|
||||
* @config
|
||||
* @var bool
|
||||
*/
|
||||
private static $lock_byuserip = false;
|
||||
class RateLimitFilter extends ContentFilter
|
||||
{
|
||||
|
||||
/**
|
||||
* Time duration (in second) to allow for generation of cached results. Requests to
|
||||
* pages that within this time period that do not hit the cache (and would otherwise trigger
|
||||
* a version query) will be presented with a 429 (rate limit) HTTP error
|
||||
*
|
||||
* @config
|
||||
* @var int
|
||||
*/
|
||||
private static $lock_timeout = 5;
|
||||
|
||||
/**
|
||||
* Determine if the cache generation should be locked on a per-page basis. If true, concurrent page versions
|
||||
* may be generated without rate interference.
|
||||
*
|
||||
* @config
|
||||
* @var bool
|
||||
*/
|
||||
private static $lock_bypage = false;
|
||||
|
||||
/**
|
||||
* Determine if rate limiting should be applied independently to each IP address. This method is not
|
||||
* reliable, as most DDoS attacks use multiple IP addresses.
|
||||
*
|
||||
* @config
|
||||
* @var bool
|
||||
*/
|
||||
private static $lock_byuserip = false;
|
||||
|
||||
/**
|
||||
* Time duration (in sections) to deny further search requests after a successful search.
|
||||
* Search requests within this time period while another query is in progress will be
|
||||
* presented with a 429 (rate limit)
|
||||
*
|
||||
* @config
|
||||
* @var int
|
||||
*/
|
||||
private static $lock_cooldown = 2;
|
||||
|
||||
/**
|
||||
* Cache key prefix
|
||||
*/
|
||||
const CACHE_PREFIX = 'RateLimitBegin';
|
||||
|
||||
/**
|
||||
* Determines the key to use for saving the current rate
|
||||
*
|
||||
* @param string $itemkey Input key
|
||||
* @return string Result key
|
||||
*/
|
||||
protected function getCacheKey($itemkey) {
|
||||
$key = self::CACHE_PREFIX;
|
||||
|
||||
// Add global identifier
|
||||
if($this->config()->get('lock_bypage')) {
|
||||
$key .= '_' . md5($itemkey);
|
||||
}
|
||||
|
||||
// Add user-specific identifier
|
||||
if($this->config()->get('lock_byuserip') && Controller::has_curr()) {
|
||||
$ip = Controller::curr()->getRequest()->getIP();
|
||||
$key .= '_' . md5($ip);
|
||||
}
|
||||
|
||||
return $key;
|
||||
}
|
||||
/**
|
||||
* Time duration (in sections) to deny further search requests after a successful search.
|
||||
* Search requests within this time period while another query is in progress will be
|
||||
* presented with a 429 (rate limit)
|
||||
*
|
||||
* @config
|
||||
* @var int
|
||||
*/
|
||||
private static $lock_cooldown = 2;
|
||||
|
||||
/**
|
||||
* Cache key prefix
|
||||
*/
|
||||
const CACHE_PREFIX = 'RateLimitBegin';
|
||||
|
||||
/**
|
||||
* Determines the key to use for saving the current rate
|
||||
*
|
||||
* @param string $itemkey Input key
|
||||
* @return string Result key
|
||||
*/
|
||||
protected function getCacheKey($itemkey)
|
||||
{
|
||||
$key = self::CACHE_PREFIX;
|
||||
|
||||
// Add global identifier
|
||||
if ($this->config()->get('lock_bypage')) {
|
||||
$key .= '_' . md5($itemkey);
|
||||
}
|
||||
|
||||
// Add user-specific identifier
|
||||
if ($this->config()->get('lock_byuserip') && Controller::has_curr()) {
|
||||
$ip = Controller::curr()->getRequest()->getIP();
|
||||
$key .= '_' . md5($ip);
|
||||
}
|
||||
|
||||
return $key;
|
||||
}
|
||||
|
||||
|
||||
public function getContent($key, $callback) {
|
||||
// Bypass rate limiting if flushing, or timeout isn't set
|
||||
$timeout = $this->config()->get('lock_timeout');
|
||||
if(isset($_GET['flush']) || !$timeout) {
|
||||
return parent::getContent($key, $callback);
|
||||
}
|
||||
|
||||
// Generate result with rate limiting enabled
|
||||
$limitKey = $this->getCacheKey($key);
|
||||
$cache = $this->getCache();
|
||||
if($lockedUntil = $cache->get($limitKey)) {
|
||||
if(time() < $lockedUntil) {
|
||||
// Politely inform visitor of limit
|
||||
$response = new HTTPResponse_Exception('Too Many Requests.', 429);
|
||||
$response->getResponse()->addHeader('Retry-After', 1 + $lockedUntil - time());
|
||||
throw $response;
|
||||
}
|
||||
}
|
||||
public function getContent($key, $callback)
|
||||
{
|
||||
// Bypass rate limiting if flushing, or timeout isn't set
|
||||
$timeout = $this->config()->get('lock_timeout');
|
||||
if (isset($_GET['flush']) || !$timeout) {
|
||||
return parent::getContent($key, $callback);
|
||||
}
|
||||
|
||||
// Generate result with rate limiting enabled
|
||||
$limitKey = $this->getCacheKey($key);
|
||||
$cache = $this->getCache();
|
||||
if ($lockedUntil = $cache->get($limitKey)) {
|
||||
if (time() < $lockedUntil) {
|
||||
// Politely inform visitor of limit
|
||||
$response = new HTTPResponse_Exception('Too Many Requests.', 429);
|
||||
$response->getResponse()->addHeader('Retry-After', 1 + $lockedUntil - time());
|
||||
throw $response;
|
||||
}
|
||||
}
|
||||
|
||||
$lifetime = Config::inst()->get(ContentFilter::class, 'cache_lifetime') ?: null;
|
||||
|
||||
// Apply rate limit
|
||||
$cache->set($limitKey, time() + $timeout, $lifetime);
|
||||
|
||||
// Generate results
|
||||
$result = parent::getContent($key, $callback);
|
||||
$lifetime = Config::inst()->get(ContentFilter::class, 'cache_lifetime') ?: null;
|
||||
|
||||
// Apply rate limit
|
||||
$cache->set($limitKey, time() + $timeout, $lifetime);
|
||||
|
||||
// Generate results
|
||||
$result = parent::getContent($key, $callback);
|
||||
|
||||
// Reset rate limit with optional cooldown
|
||||
if($cooldown = $this->config()->get('lock_cooldown')) {
|
||||
// Set cooldown on successful query execution
|
||||
$cache->set($limitKey, time() + $cooldown, $lifetime);
|
||||
} else {
|
||||
// Without cooldown simply disable lock
|
||||
$cache->delete($limitKey);
|
||||
}
|
||||
return $result;
|
||||
}
|
||||
// Reset rate limit with optional cooldown
|
||||
if ($cooldown = $this->config()->get('lock_cooldown')) {
|
||||
// Set cooldown on successful query execution
|
||||
$cache->set($limitKey, time() + $cooldown, $lifetime);
|
||||
} else {
|
||||
// Without cooldown simply disable lock
|
||||
$cache->delete($limitKey);
|
||||
}
|
||||
return $result;
|
||||
}
|
||||
}
|
||||
|
@ -13,206 +13,215 @@ use SilverStripe\Forms\LiteralField;
|
||||
use SilverStripe\SiteConfig\SiteConfig;
|
||||
use SilverStripe\CMS\Model\SiteTreeExtension;
|
||||
|
||||
class VersionFeed extends SiteTreeExtension {
|
||||
|
||||
private static $db = array(
|
||||
'PublicHistory' => 'Boolean(true)'
|
||||
);
|
||||
class VersionFeed extends SiteTreeExtension
|
||||
{
|
||||
|
||||
private static $db = array(
|
||||
'PublicHistory' => 'Boolean(true)'
|
||||
);
|
||||
|
||||
private static $defaults = array(
|
||||
'PublicHistory' => true
|
||||
);
|
||||
private static $defaults = array(
|
||||
'PublicHistory' => true
|
||||
);
|
||||
|
||||
public function updateFieldLabels(&$labels) {
|
||||
$labels['PublicHistory'] = _t('RSSHistory.LABEL', 'Make history public');
|
||||
}
|
||||
public function updateFieldLabels(&$labels)
|
||||
{
|
||||
$labels['PublicHistory'] = _t('RSSHistory.LABEL', 'Make history public');
|
||||
}
|
||||
|
||||
/**
|
||||
* Enable the allchanges feed
|
||||
*
|
||||
* @config
|
||||
* @var bool
|
||||
*/
|
||||
private static $allchanges_enabled = true;
|
||||
/**
|
||||
* Enable the allchanges feed
|
||||
*
|
||||
* @config
|
||||
* @var bool
|
||||
*/
|
||||
private static $allchanges_enabled = true;
|
||||
|
||||
/**
|
||||
* Allchanges feed limit of items.
|
||||
*
|
||||
* @config
|
||||
* @var int
|
||||
*/
|
||||
private static $allchanges_limit = 20;
|
||||
/**
|
||||
* Allchanges feed limit of items.
|
||||
*
|
||||
* @config
|
||||
* @var int
|
||||
*/
|
||||
private static $allchanges_limit = 20;
|
||||
|
||||
/**
|
||||
* Enables RSS feed for page-specific changes
|
||||
*
|
||||
* @config
|
||||
* @var bool
|
||||
*/
|
||||
private static $changes_enabled = true;
|
||||
/**
|
||||
* Enables RSS feed for page-specific changes
|
||||
*
|
||||
* @config
|
||||
* @var bool
|
||||
*/
|
||||
private static $changes_enabled = true;
|
||||
|
||||
/**
|
||||
* Changes feed limit of items.
|
||||
*
|
||||
* @config
|
||||
* @var int
|
||||
*/
|
||||
private static $changes_limit = 100;
|
||||
/**
|
||||
* Changes feed limit of items.
|
||||
*
|
||||
* @config
|
||||
* @var int
|
||||
*/
|
||||
private static $changes_limit = 100;
|
||||
|
||||
/**
|
||||
* 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",
|
||||
"\"SiteTree\".\"LastEdited\" DESC, \"SiteTree\".\"ID\" DESC",
|
||||
$qLimit
|
||||
);
|
||||
/**
|
||||
* 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",
|
||||
"\"SiteTree\".\"LastEdited\" DESC, \"SiteTree\".\"ID\" DESC",
|
||||
$qLimit
|
||||
);
|
||||
|
||||
// Process the list to add the comparisons.
|
||||
$changeList = new ArrayList();
|
||||
$previous = null;
|
||||
$count = 0;
|
||||
foreach ($versions as $version) {
|
||||
$changed = false;
|
||||
// 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)) {
|
||||
// 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);
|
||||
|
||||
// Produce the diff fields for use in the template.
|
||||
if ($version->Title != $previous->Title) {
|
||||
$diffTitle = Diff::compareHTML($version->Title, $previous->Title);
|
||||
$version->DiffTitle = DBField::create_field('HTMLText', null);
|
||||
$version->DiffTitle->setValue(
|
||||
sprintf(
|
||||
'<div><em>%s</em> ' . $diffTitle . '</div>',
|
||||
_t('RSSHistory.TITLECHANGED', 'Title has changed:')
|
||||
)
|
||||
);
|
||||
$changed = true;
|
||||
}
|
||||
|
||||
$version->DiffTitle = DBField::create_field('HTMLText', null);
|
||||
$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);
|
||||
|
||||
if ($version->Content != $previous->Content) {
|
||||
$diffContent = Diff::compareHTML($version->Content, $previous->Content);
|
||||
$version->DiffContent = DBField::create_field('HTMLText', null);
|
||||
$version->DiffContent->setValue('<div>'.$diffContent.'</div>');
|
||||
$changed = true;
|
||||
}
|
||||
|
||||
$version->DiffContent = DBField::create_field('HTMLText', null);
|
||||
$version->DiffContent->setValue('<div>'.$diffContent.'</div>');
|
||||
$changed = true;
|
||||
}
|
||||
// Copy the link so it can be cached.
|
||||
$version->GeneratedLink = $version->AbsoluteLink();
|
||||
}
|
||||
|
||||
// Copy the link so it can be cached.
|
||||
$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++;
|
||||
}
|
||||
|
||||
// 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;
|
||||
}
|
||||
|
||||
// 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 = DBField::create_field('HTMLText', null);
|
||||
$first->DiffContent->setValue('<div>' . $first->Content . '</div>');
|
||||
// Copy the link so it can be cached.
|
||||
$first->GeneratedLink = $first->AbsoluteLink();
|
||||
$changeList->push($first);
|
||||
}
|
||||
|
||||
// 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 = DBField::create_field('HTMLText', null);
|
||||
$first->DiffContent->setValue('<div>' . $first->Content . '</div>');
|
||||
// Copy the link so it can be cached.
|
||||
$first->GeneratedLink = $first->AbsoluteLink();
|
||||
$changeList->push($first);
|
||||
}
|
||||
return $changeList;
|
||||
}
|
||||
|
||||
return $changeList;
|
||||
}
|
||||
/**
|
||||
* Return a single diff representing this version.
|
||||
* Returns the initial version if there is nothing to compare to.
|
||||
*
|
||||
* @returns DataObject Object with relevant fields diffed.
|
||||
*/
|
||||
public function getDiff()
|
||||
{
|
||||
$changes = $this->getDiffList($this->owner->Version, 1);
|
||||
if ($changes && $changes->Count()) {
|
||||
return $changes->First();
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a single diff representing this version.
|
||||
* Returns the initial version if there is nothing to compare to.
|
||||
*
|
||||
* @returns DataObject Object with relevant fields diffed.
|
||||
*/
|
||||
public function getDiff() {
|
||||
$changes = $this->getDiffList($this->owner->Version, 1);
|
||||
if ($changes && $changes->Count()) {
|
||||
return $changes->First();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
/**
|
||||
* Compile a list of changes to the current page, excluding non-published and explicitly secured versions.
|
||||
*
|
||||
* @deprecated 2.0.0 Use VersionFeed::getDiffList instead
|
||||
*
|
||||
* @param int $highestVersion Top version number to consider.
|
||||
* @param boolean $fullHistory Set to true to get the full change history, set to false for a single diff.
|
||||
* @param int $limit Limit to the amount of items returned.
|
||||
*
|
||||
* @returns ArrayList List of cleaned records.
|
||||
*/
|
||||
public function getDiffedChanges($highestVersion = null, $fullHistory = true, $limit = 100)
|
||||
{
|
||||
return $this->getDiffList(
|
||||
$highestVersion,
|
||||
$fullHistory ? $limit : 1
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Compile a list of changes to the current page, excluding non-published and explicitly secured versions.
|
||||
*
|
||||
* @deprecated 2.0.0 Use VersionFeed::getDiffList instead
|
||||
*
|
||||
* @param int $highestVersion Top version number to consider.
|
||||
* @param boolean $fullHistory Set to true to get the full change history, set to false for a single diff.
|
||||
* @param int $limit Limit to the amount of items returned.
|
||||
*
|
||||
* @returns ArrayList List of cleaned records.
|
||||
*/
|
||||
public function getDiffedChanges($highestVersion = null, $fullHistory = true, $limit = 100) {
|
||||
return $this->getDiffList(
|
||||
$highestVersion,
|
||||
$fullHistory ? $limit : 1
|
||||
);
|
||||
}
|
||||
public function updateSettingsFields(FieldList $fields)
|
||||
{
|
||||
if (!Config::inst()->get(get_class(), 'changes_enabled')) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Add public history field.
|
||||
$fields->addFieldToTab('Root.Settings', $publicHistory = new FieldGroup(
|
||||
new CheckboxField('PublicHistory', $this->owner->fieldLabel('PublicHistory'))
|
||||
));
|
||||
|
||||
public function updateSettingsFields(FieldList $fields) {
|
||||
if(!Config::inst()->get(get_class(), 'changes_enabled')) return;
|
||||
|
||||
// Add public history field.
|
||||
$fields->addFieldToTab('Root.Settings', $publicHistory = new FieldGroup(
|
||||
new CheckboxField('PublicHistory', $this->owner->fieldLabel('PublicHistory')
|
||||
)));
|
||||
$warning = _t(
|
||||
'VersionFeed.Warning',
|
||||
"Publicising the history will also disclose the changes that have at the time been protected " .
|
||||
"from the public view."
|
||||
);
|
||||
|
||||
$warning = _t(
|
||||
'VersionFeed.Warning',
|
||||
"Publicising the history will also disclose the changes that have at the time been protected " .
|
||||
"from the public view."
|
||||
);
|
||||
$fields->addFieldToTab('Root.Settings', new LiteralField('PublicHistoryWarning', $warning), 'PublicHistory');
|
||||
|
||||
$fields->addFieldToTab('Root.Settings', new LiteralField('PublicHistoryWarning', $warning), 'PublicHistory');
|
||||
if ($this->owner->CanViewType!='Anyone') {
|
||||
$warning = _t(
|
||||
'VersionFeed.Warning2',
|
||||
"Changing access settings in such a way that this page or pages under it become publicly<br>" .
|
||||
"accessible may result in publicising all historical changes on these pages too. Please review<br>" .
|
||||
"this section's \"Public history\" settings to ascertain only intended information is disclosed."
|
||||
);
|
||||
|
||||
if ($this->owner->CanViewType!='Anyone') {
|
||||
$warning = _t(
|
||||
'VersionFeed.Warning2',
|
||||
"Changing access settings in such a way that this page or pages under it become publicly<br>" .
|
||||
"accessible may result in publicising all historical changes on these pages too. Please review<br>" .
|
||||
"this section's \"Public history\" settings to ascertain only intended information is disclosed."
|
||||
);
|
||||
$fields->addFieldToTab('Root.Settings', new LiteralField('PublicHistoryWarning2', $warning), 'CanViewType');
|
||||
}
|
||||
}
|
||||
|
||||
$fields->addFieldToTab('Root.Settings', new LiteralField('PublicHistoryWarning2', $warning), 'CanViewType');
|
||||
}
|
||||
}
|
||||
public function getSiteRSSLink()
|
||||
{
|
||||
// TODO: This link should be from the homepage, not this page.
|
||||
if (Config::inst()->get(get_class(), 'allchanges_enabled')
|
||||
&& SiteConfig::current_site_config()->AllChangesEnabled
|
||||
) {
|
||||
return $this->owner->Link('allchanges');
|
||||
}
|
||||
}
|
||||
|
||||
public function getSiteRSSLink() {
|
||||
// TODO: This link should be from the homepage, not this page.
|
||||
if(Config::inst()->get(get_class(), 'allchanges_enabled')
|
||||
&& SiteConfig::current_site_config()->AllChangesEnabled
|
||||
) {
|
||||
return $this->owner->Link('allchanges');
|
||||
}
|
||||
}
|
||||
|
||||
public function getDefaultRSSLink() {
|
||||
if(Config::inst()->get(get_class(), 'changes_enabled') && $this->owner->PublicHistory) {
|
||||
return $this->owner->Link('changes');
|
||||
}
|
||||
}
|
||||
public function getDefaultRSSLink()
|
||||
{
|
||||
if (Config::inst()->get(get_class(), 'changes_enabled') && $this->owner->PublicHistory) {
|
||||
return $this->owner->Link('changes');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -16,174 +16,184 @@ use SilverStripe\View\Requirements;
|
||||
use SilverStripe\Core\Extension;
|
||||
use SilverStripe\VersionFeed\Filters\ContentFilter;
|
||||
|
||||
class VersionFeedController extends Extension {
|
||||
class VersionFeedController extends Extension
|
||||
{
|
||||
|
||||
private static $allowed_actions = array(
|
||||
'changes',
|
||||
'allchanges'
|
||||
);
|
||||
|
||||
/**
|
||||
* Content handler
|
||||
*
|
||||
* @var ContentFilter
|
||||
*/
|
||||
protected $contentFilter;
|
||||
|
||||
/**
|
||||
* Sets the content filter
|
||||
*
|
||||
* @param ContentFilter $contentFilter
|
||||
*/
|
||||
public function setContentFilter(ContentFilter $contentFilter) {
|
||||
$this->contentFilter = $contentFilter;
|
||||
}
|
||||
|
||||
/**
|
||||
* Evaluates the result of the given callback
|
||||
*
|
||||
* @param string $key Unique key for this
|
||||
* @param callable $callback Callback for evaluating the content
|
||||
* @return mixed Result of $callback()
|
||||
*/
|
||||
protected function filterContent($key, $callback) {
|
||||
if($this->contentFilter) {
|
||||
return $this->contentFilter->getContent($key, $callback);
|
||||
} else {
|
||||
return call_user_func($callback);
|
||||
}
|
||||
}
|
||||
private static $allowed_actions = array(
|
||||
'changes',
|
||||
'allchanges'
|
||||
);
|
||||
|
||||
/**
|
||||
* Content handler
|
||||
*
|
||||
* @var ContentFilter
|
||||
*/
|
||||
protected $contentFilter;
|
||||
|
||||
/**
|
||||
* Sets the content filter
|
||||
*
|
||||
* @param ContentFilter $contentFilter
|
||||
*/
|
||||
public function setContentFilter(ContentFilter $contentFilter)
|
||||
{
|
||||
$this->contentFilter = $contentFilter;
|
||||
}
|
||||
|
||||
/**
|
||||
* Evaluates the result of the given callback
|
||||
*
|
||||
* @param string $key Unique key for this
|
||||
* @param callable $callback Callback for evaluating the content
|
||||
* @return mixed Result of $callback()
|
||||
*/
|
||||
protected function filterContent($key, $callback)
|
||||
{
|
||||
if ($this->contentFilter) {
|
||||
return $this->contentFilter->getContent($key, $callback);
|
||||
} else {
|
||||
return call_user_func($callback);
|
||||
}
|
||||
}
|
||||
|
||||
public function onAfterInit() {
|
||||
$this->linkToPageRSSFeed();
|
||||
$this->linkToAllSiteRSSFeed();
|
||||
}
|
||||
public function onAfterInit()
|
||||
{
|
||||
$this->linkToPageRSSFeed();
|
||||
$this->linkToAllSiteRSSFeed();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get page-specific changes in a RSS feed.
|
||||
*/
|
||||
public function changes() {
|
||||
// Check viewability of changes
|
||||
if(!Config::inst()->get(VersionFeed::class, 'changes_enabled')
|
||||
|| !$this->owner->PublicHistory
|
||||
|| $this->owner->Version == ''
|
||||
) {
|
||||
return $this->owner->httpError(404, 'Page history not viewable');
|
||||
}
|
||||
/**
|
||||
* Get page-specific changes in a RSS feed.
|
||||
*/
|
||||
public function changes()
|
||||
{
|
||||
// Check viewability of changes
|
||||
if (!Config::inst()->get(VersionFeed::class, 'changes_enabled')
|
||||
|| !$this->owner->PublicHistory
|
||||
|| $this->owner->Version == ''
|
||||
) {
|
||||
return $this->owner->httpError(404, 'Page history not viewable');
|
||||
}
|
||||
|
||||
// Cache the diffs to remove DOS possibility.
|
||||
$target = $this->owner;
|
||||
$key = implode('_', array('changes', $this->owner->ID, $this->owner->Version));
|
||||
$entries = $this->filterContent($key, function() use ($target) {
|
||||
return $target->getDiffList(null, Config::inst()->get(VersionFeed::class, 'changes_limit'));
|
||||
});
|
||||
// Cache the diffs to remove DOS possibility.
|
||||
$target = $this->owner;
|
||||
$key = implode('_', array('changes', $this->owner->ID, $this->owner->Version));
|
||||
$entries = $this->filterContent($key, function () use ($target) {
|
||||
return $target->getDiffList(null, Config::inst()->get(VersionFeed::class, 'changes_limit'));
|
||||
});
|
||||
|
||||
// Generate the output.
|
||||
$title = sprintf(_t('RSSHistory.SINGLEPAGEFEEDTITLE', 'Updates to %s page'), $this->owner->Title);
|
||||
$rss = new RSSFeed($entries, $this->owner->request->getURL(), $title, '', 'Title', '', null);
|
||||
$rss->setTemplate('Page_changes_rss');
|
||||
return $rss->outputToBrowser();
|
||||
}
|
||||
// Generate the output.
|
||||
$title = sprintf(_t('RSSHistory.SINGLEPAGEFEEDTITLE', 'Updates to %s page'), $this->owner->Title);
|
||||
$rss = new RSSFeed($entries, $this->owner->request->getURL(), $title, '', 'Title', '', null);
|
||||
$rss->setTemplate('Page_changes_rss');
|
||||
return $rss->outputToBrowser();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all changes from the site in a RSS feed.
|
||||
*/
|
||||
public function allchanges() {
|
||||
// Check viewability of allchanges
|
||||
if(!Config::inst()->get(VersionFeed::class, 'allchanges_enabled')
|
||||
|| !SiteConfig::current_site_config()->AllChangesEnabled
|
||||
) {
|
||||
return $this->owner->httpError(404, 'Global history not viewable');
|
||||
}
|
||||
/**
|
||||
* Get all changes from the site in a RSS feed.
|
||||
*/
|
||||
public function allchanges()
|
||||
{
|
||||
// Check viewability of allchanges
|
||||
if (!Config::inst()->get(VersionFeed::class, 'allchanges_enabled')
|
||||
|| !SiteConfig::current_site_config()->AllChangesEnabled
|
||||
) {
|
||||
return $this->owner->httpError(404, 'Global history not viewable');
|
||||
}
|
||||
|
||||
$limit = (int)Config::inst()->get(VersionFeed::class, 'allchanges_limit');
|
||||
$latestChanges = DB::query('
|
||||
SELECT * FROM "SiteTree_versions"
|
||||
$limit = (int)Config::inst()->get(VersionFeed::class, 'allchanges_limit');
|
||||
$latestChanges = DB::query('
|
||||
SELECT * FROM "SiteTree_Versions"
|
||||
WHERE "WasPublished" = \'1\'
|
||||
AND "CanViewType" IN (\'Anyone\', \'Inherit\')
|
||||
AND "ShowInSearch" = 1
|
||||
AND ("PublicHistory" IS NULL OR "PublicHistory" = \'1\')
|
||||
ORDER BY "LastEdited" DESC LIMIT ' . $limit
|
||||
);
|
||||
$lastChange = $latestChanges->record();
|
||||
$latestChanges->rewind();
|
||||
ORDER BY "LastEdited" DESC LIMIT ' . $limit);
|
||||
$lastChange = $latestChanges->record();
|
||||
$latestChanges->rewind();
|
||||
|
||||
if ($lastChange) {
|
||||
if ($lastChange) {
|
||||
// Cache the diffs to remove DOS possibility.
|
||||
$key = 'allchanges'
|
||||
. preg_replace('#[^a-zA-Z0-9_]#', '', $lastChange['LastEdited'])
|
||||
. (Member::currentUserID() ?: 'public');
|
||||
$changeList = $this->filterContent($key, function () use ($latestChanges) {
|
||||
$changeList = new ArrayList();
|
||||
$canView = array();
|
||||
foreach ($latestChanges as $record) {
|
||||
// Check if the page should be visible.
|
||||
// WARNING: although we are providing historical details, we check the current configuration.
|
||||
$id = $record['RecordID'];
|
||||
if (!isset($canView[$id])) {
|
||||
$page = SiteTree::get()->byID($id);
|
||||
$canView[$id] = $page && $page->canView(new Member());
|
||||
}
|
||||
if (!$canView[$id]) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Cache the diffs to remove DOS possibility.
|
||||
$key = 'allchanges'
|
||||
. preg_replace('#[^a-zA-Z0-9_]#', '', $lastChange['LastEdited'])
|
||||
. (Member::currentUserID() ?: 'public');
|
||||
$changeList = $this->filterContent($key, function() use ($latestChanges) {
|
||||
$changeList = new ArrayList();
|
||||
$canView = array();
|
||||
foreach ($latestChanges as $record) {
|
||||
|
||||
// Check if the page should be visible.
|
||||
// WARNING: although we are providing historical details, we check the current configuration.
|
||||
$id = $record['RecordID'];
|
||||
if(!isset($canView[$id])) {
|
||||
$page = SiteTree::get()->byID($id);
|
||||
$canView[$id] = $page && $page->canView(new Member());
|
||||
}
|
||||
if (!$canView[$id]) continue;
|
||||
// Get the diff to the previous version.
|
||||
$version = SiteTree::create($record);
|
||||
if ($diff = $version->getDiff()) {
|
||||
$changeList->push($diff);
|
||||
}
|
||||
}
|
||||
|
||||
// Get the diff to the previous version.
|
||||
$version = new Versioned_Version($record);
|
||||
if ($diff = $version->getDiff()) {
|
||||
$changeList->push($diff);
|
||||
}
|
||||
}
|
||||
return $changeList;
|
||||
});
|
||||
} else {
|
||||
$changeList = new ArrayList();
|
||||
}
|
||||
|
||||
return $changeList;
|
||||
});
|
||||
} else {
|
||||
$changeList = new ArrayList();
|
||||
}
|
||||
// Produce output
|
||||
$url = $this->owner->getRequest()->getURL();
|
||||
$rss = new RSSFeed($changeList, $url, $this->linkToAllSitesRSSFeedTitle(), '', 'Title', '', null);
|
||||
$rss->setTemplate('Page_allchanges_rss');
|
||||
return $rss->outputToBrowser();
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates and embeds the RSS header link for the page-specific version rss feed
|
||||
*/
|
||||
public function linkToPageRSSFeed()
|
||||
{
|
||||
if (!Config::inst()->get(VersionFeed::class, 'changes_enabled') || !$this->owner->PublicHistory) {
|
||||
return;
|
||||
}
|
||||
|
||||
RSSFeed::linkToFeed(
|
||||
$this->owner->Link('changes'),
|
||||
sprintf(
|
||||
_t('RSSHistory.SINGLEPAGEFEEDTITLE', 'Updates to %s page'),
|
||||
$this->owner->Title
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
// Produce output
|
||||
$rss = new RSSFeed($changeList, $this->owner->request->getURL(), $this->linkToAllSitesRSSFeedTitle(), '', 'Title', '', null);
|
||||
$rss->setTemplate('Page_allchanges_rss');
|
||||
return $rss->outputToBrowser();
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates and embeds the RSS header link for the page-specific version rss feed
|
||||
*/
|
||||
public function linkToPageRSSFeed() {
|
||||
if (!Config::inst()->get(VersionFeed::class, 'changes_enabled') || !$this->owner->PublicHistory) {
|
||||
return;
|
||||
}
|
||||
|
||||
RSSFeed::linkToFeed(
|
||||
$this->owner->Link('changes'),
|
||||
sprintf(
|
||||
_t('RSSHistory.SINGLEPAGEFEEDTITLE', 'Updates to %s page'),
|
||||
$this->owner->Title
|
||||
)
|
||||
);
|
||||
}
|
||||
/**
|
||||
* Generates and embeds the RSS header link for the global version rss feed
|
||||
*/
|
||||
public function linkToAllSiteRSSFeed()
|
||||
{
|
||||
if (!Config::inst()->get(VersionFeed::class, 'allchanges_enabled')
|
||||
|| !SiteConfig::current_site_config()->AllChangesEnabled
|
||||
) {
|
||||
return;
|
||||
}
|
||||
|
||||
// RSS feed to all-site changes.
|
||||
$title = Convert::raw2xml($this->linkToAllSitesRSSFeedTitle());
|
||||
$url = $this->owner->getSiteRSSLink();
|
||||
|
||||
/**
|
||||
* Generates and embeds the RSS header link for the global version rss feed
|
||||
*/
|
||||
public function linkToAllSiteRSSFeed() {
|
||||
if(!Config::inst()->get(VersionFeed::class, 'allchanges_enabled')
|
||||
|| !SiteConfig::current_site_config()->AllChangesEnabled
|
||||
) {
|
||||
return;
|
||||
}
|
||||
|
||||
// RSS feed to all-site changes.
|
||||
$title = Convert::raw2xml($this->linkToAllSitesRSSFeedTitle());
|
||||
$url = $this->owner->getSiteRSSLink();
|
||||
Requirements::insertHeadTags(
|
||||
'<link rel="alternate" type="application/rss+xml" title="' . $title .
|
||||
'" href="' . $url . '" />'
|
||||
);
|
||||
}
|
||||
|
||||
Requirements::insertHeadTags(
|
||||
'<link rel="alternate" type="application/rss+xml" title="' . $title .
|
||||
'" href="' . $url . '" />');
|
||||
}
|
||||
|
||||
public function linkToAllSitesRSSFeedTitle() {
|
||||
return sprintf(_t('RSSHistory.SITEFEEDTITLE', 'Updates to %s'), SiteConfig::current_site_config()->Title);
|
||||
}
|
||||
public function linkToAllSitesRSSFeedTitle()
|
||||
{
|
||||
return sprintf(_t('RSSHistory.SITEFEEDTITLE', 'Updates to %s'), SiteConfig::current_site_config()->Title);
|
||||
}
|
||||
}
|
||||
|
@ -2,11 +2,6 @@
|
||||
|
||||
namespace SilverStripe\VersionFeed;
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
use SilverStripe\Forms\FieldList;
|
||||
use SilverStripe\Core\Config\Config;
|
||||
use SilverStripe\VersionFeed\VersionFeed;
|
||||
@ -14,36 +9,40 @@ use SilverStripe\Forms\CheckboxField;
|
||||
use SilverStripe\Forms\FieldGroup;
|
||||
use SilverStripe\ORM\DataExtension;
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Allows global configuration of all changes
|
||||
*/
|
||||
class VersionFeedSiteConfig extends DataExtension {
|
||||
|
||||
private static $db = array(
|
||||
'AllChangesEnabled' => 'Boolean(true)'
|
||||
);
|
||||
class VersionFeedSiteConfig extends DataExtension
|
||||
{
|
||||
|
||||
private static $db = array(
|
||||
'AllChangesEnabled' => 'Boolean(true)'
|
||||
);
|
||||
|
||||
private static $defaults = array(
|
||||
'AllChangesEnabled' => true
|
||||
);
|
||||
private static $defaults = array(
|
||||
'AllChangesEnabled' => true
|
||||
);
|
||||
|
||||
public function updateFieldLabels(&$labels) {
|
||||
$labels['AllChangesEnabled'] = _t('VersionFeedSiteConfig.ALLCHANGESLABEL', 'Make global changes feed public');
|
||||
}
|
||||
|
||||
public function updateCMSFields(FieldList $fields) {
|
||||
if(!Config::inst()->get(VersionFeed::class, 'allchanges_enabled')) return;
|
||||
public function updateFieldLabels(&$labels)
|
||||
{
|
||||
$labels['AllChangesEnabled'] = _t('VersionFeedSiteConfig.ALLCHANGESLABEL', 'Make global changes feed public');
|
||||
}
|
||||
|
||||
public function updateCMSFields(FieldList $fields)
|
||||
{
|
||||
if (!Config::inst()->get(VersionFeed::class, 'allchanges_enabled')) {
|
||||
return;
|
||||
}
|
||||
|
||||
$fields->addFieldToTab('Root.Access',
|
||||
FieldGroup::create(new CheckboxField('AllChangesEnabled', $this->owner->fieldLabel('AllChangesEnabled')))
|
||||
->setTitle(_t('VersionFeedSiteConfig.ALLCHANGES', 'All page changes'))
|
||||
->setDescription(_t(
|
||||
'VersionFeed.Warning',
|
||||
"Publicising the history will also disclose the changes that have at the time been protected " .
|
||||
"from the public view."
|
||||
))
|
||||
);
|
||||
}
|
||||
$fields->addFieldToTab(
|
||||
'Root.Access',
|
||||
FieldGroup::create(new CheckboxField('AllChangesEnabled', $this->owner->fieldLabel('AllChangesEnabled')))
|
||||
->setTitle(_t('VersionFeedSiteConfig.ALLCHANGES', 'All page changes'))
|
||||
->setDescription(_t(
|
||||
'VersionFeed.Warning',
|
||||
"Publicising the history will also disclose the changes that have at the time been protected " .
|
||||
"from the public view."
|
||||
))
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -17,230 +17,242 @@ use SilverStripe\Dev\FunctionalTest;
|
||||
use SilverStripe\Control\Director;
|
||||
use Psr\SimpleCache\CacheInterface;
|
||||
|
||||
class VersionFeedFunctionalTest extends FunctionalTest
|
||||
{
|
||||
protected $usesDatabase = true;
|
||||
|
||||
protected $baseURI = 'http://www.fakesite.test';
|
||||
|
||||
class VersionFeedFunctionalTest extends FunctionalTest {
|
||||
protected $usesDatabase = true;
|
||||
|
||||
protected $baseURI = 'http://www.fakesite.test';
|
||||
protected static $required_extensions = array(
|
||||
'Page' => array(VersionFeed::class),
|
||||
'PageController' => array(VersionFeedController::class),
|
||||
);
|
||||
|
||||
protected static $required_extensions = array(
|
||||
'Page' => array(VersionFeed::class),
|
||||
'PageController' => array(VersionFeedController::class),
|
||||
);
|
||||
protected $userIP;
|
||||
|
||||
protected $userIP;
|
||||
protected function setUp()
|
||||
{
|
||||
Director::config()->set('alternate_base_url', $this->baseURI);
|
||||
|
||||
parent::setUp();
|
||||
|
||||
protected function setUp() {
|
||||
Director::config()->set('alternate_base_url', $this->baseURI);
|
||||
|
||||
parent::setUp();
|
||||
$cache = Injector::inst()->get(
|
||||
CacheInterface::class . '.VersionFeedController'
|
||||
);
|
||||
$cache->clear();
|
||||
|
||||
$cache = Injector::inst()->get(
|
||||
CacheInterface::class . '.VersionFeedController'
|
||||
);
|
||||
$cache->clear();
|
||||
|
||||
$this->userIP = isset($_SERVER['REMOTE_ADDR']) ? $_SERVER['REMOTE_ADDR'] : null;
|
||||
$this->userIP = isset($_SERVER['REMOTE_ADDR']) ? $_SERVER['REMOTE_ADDR'] : null;
|
||||
|
||||
// Enable history by default
|
||||
Config::modify()->set(VersionFeed::class, 'changes_enabled', true);
|
||||
Config::modify()->set(VersionFeed::class, 'allchanges_enabled', true);
|
||||
|
||||
// Disable caching and locking by default
|
||||
Config::modify()->set(CachedContentFilter::class, 'cache_enabled', false);
|
||||
Config::modify()->set(RateLimitFilter::class, 'lock_timeout', 0);
|
||||
Config::modify()->set(RateLimitFilter::class, 'lock_bypage', false);
|
||||
Config::modify()->set(RateLimitFilter::class, 'lock_byuserip', false);
|
||||
Config::modify()->set(RateLimitFilter::class, 'lock_cooldown', false);
|
||||
}
|
||||
// Disable caching and locking by default
|
||||
Config::modify()->set(CachedContentFilter::class, 'cache_enabled', false);
|
||||
Config::modify()->set(RateLimitFilter::class, 'lock_timeout', 0);
|
||||
Config::modify()->set(RateLimitFilter::class, 'lock_bypage', false);
|
||||
Config::modify()->set(RateLimitFilter::class, 'lock_byuserip', false);
|
||||
Config::modify()->set(RateLimitFilter::class, 'lock_cooldown', false);
|
||||
}
|
||||
|
||||
public function tearDown() {
|
||||
Director::config()->set('alternate_base_url', null);
|
||||
|
||||
$_SERVER['REMOTE_ADDR'] = $this->userIP;
|
||||
public function tearDown()
|
||||
{
|
||||
Director::config()->set('alternate_base_url', null);
|
||||
|
||||
$_SERVER['REMOTE_ADDR'] = $this->userIP;
|
||||
|
||||
parent::tearDown();
|
||||
}
|
||||
parent::tearDown();
|
||||
}
|
||||
|
||||
public function testPublicHistory() {
|
||||
$page = $this->createPageWithChanges(array('PublicHistory' => false));
|
||||
public function testPublicHistory()
|
||||
{
|
||||
$page = $this->createPageWithChanges(array('PublicHistory' => false));
|
||||
|
||||
$response = $this->get($page->RelativeLink('changes'));
|
||||
$this->assertEquals(404, $response->getStatusCode());
|
||||
$response = $this->get($page->RelativeLink('changes'));
|
||||
$this->assertEquals(404, $response->getStatusCode());
|
||||
|
||||
$response = $this->get($page->RelativeLink('allchanges'));
|
||||
$this->assertEquals(200, $response->getStatusCode());
|
||||
$xml = simplexml_load_string($response->getBody());
|
||||
$this->assertFalse((bool)$xml->channel->item);
|
||||
$response = $this->get($page->RelativeLink('allchanges'));
|
||||
$this->assertEquals(200, $response->getStatusCode());
|
||||
$xml = simplexml_load_string($response->getBody());
|
||||
$this->assertFalse((bool)$xml->channel->item);
|
||||
|
||||
$page = $this->createPageWithChanges(array('PublicHistory' => true));
|
||||
$page = $this->createPageWithChanges(array('PublicHistory' => true));
|
||||
|
||||
$response = $this->get($page->RelativeLink('changes'));
|
||||
$this->assertEquals(200, $response->getStatusCode());
|
||||
$xml = simplexml_load_string($response->getBody());
|
||||
$this->assertTrue((bool)$xml->channel->item);
|
||||
$response = $this->get($page->RelativeLink('changes'));
|
||||
$this->assertEquals(200, $response->getStatusCode());
|
||||
$xml = simplexml_load_string($response->getBody());
|
||||
$this->assertTrue((bool)$xml->channel->item);
|
||||
|
||||
$response = $this->get($page->RelativeLink('allchanges'));
|
||||
$this->assertEquals(200, $response->getStatusCode());
|
||||
$xml = simplexml_load_string($response->getBody());
|
||||
$this->assertTrue((bool)$xml->channel->item);
|
||||
}
|
||||
$response = $this->get($page->RelativeLink('allchanges'));
|
||||
$this->assertEquals(200, $response->getStatusCode());
|
||||
$xml = simplexml_load_string($response->getBody());
|
||||
$this->assertTrue((bool)$xml->channel->item);
|
||||
}
|
||||
|
||||
public function testRateLimiting() {
|
||||
// Re-enable locking just for this test
|
||||
Config::modify()->set(RateLimitFilter::class, 'lock_timeout', 20);
|
||||
Config::modify()->set(CachedContentFilter::class, 'cache_enabled', true);
|
||||
public function testRateLimiting()
|
||||
{
|
||||
// Re-enable locking just for this test
|
||||
Config::modify()->set(RateLimitFilter::class, 'lock_timeout', 20);
|
||||
Config::modify()->set(CachedContentFilter::class, 'cache_enabled', true);
|
||||
|
||||
$page1 = $this->createPageWithChanges(array('PublicHistory' => true, 'Title' => 'Page1'));
|
||||
$page2 = $this->createPageWithChanges(array('PublicHistory' => true, 'Title' => 'Page2'));
|
||||
$page1 = $this->createPageWithChanges(array('PublicHistory' => true, 'Title' => 'Page1'));
|
||||
$page2 = $this->createPageWithChanges(array('PublicHistory' => true, 'Title' => 'Page2'));
|
||||
|
||||
// Artifically set cache lock
|
||||
Config::modify()->set(RateLimitFilter::class, 'lock_byuserip', false);
|
||||
$cache = Injector::inst()->get(
|
||||
CacheInterface::class . '.VersionFeedController'
|
||||
);
|
||||
$cache->set(RateLimitFilter::CACHE_PREFIX, time() + 10);
|
||||
// Artifically set cache lock
|
||||
Config::modify()->set(RateLimitFilter::class, 'lock_byuserip', false);
|
||||
$cache = Injector::inst()->get(
|
||||
CacheInterface::class . '.VersionFeedController'
|
||||
);
|
||||
$cache->set(RateLimitFilter::CACHE_PREFIX, time() + 10);
|
||||
|
||||
// Test normal hit
|
||||
$response = $this->get($page1->RelativeLink('changes'));
|
||||
$this->assertEquals(429, $response->getStatusCode());
|
||||
$this->assertGreaterThan(0, $response->getHeader('Retry-After'));
|
||||
$response = $this->get($page2->RelativeLink('changes'));
|
||||
$this->assertEquals(429, $response->getStatusCode());
|
||||
$this->assertGreaterThan(0, $response->getHeader('Retry-After'));
|
||||
// Test normal hit
|
||||
$response = $this->get($page1->RelativeLink('changes'));
|
||||
$this->assertEquals(429, $response->getStatusCode());
|
||||
$this->assertGreaterThan(0, $response->getHeader('Retry-After'));
|
||||
$response = $this->get($page2->RelativeLink('changes'));
|
||||
$this->assertEquals(429, $response->getStatusCode());
|
||||
$this->assertGreaterThan(0, $response->getHeader('Retry-After'));
|
||||
|
||||
// Test page specific lock
|
||||
Config::modify()->set(RateLimitFilter::class, 'lock_bypage', true);
|
||||
$key = implode('_', array(
|
||||
'changes',
|
||||
$page1->ID,
|
||||
Versioned::get_versionnumber_by_stage(SiteTree::class, 'Live', $page1->ID, false)
|
||||
));
|
||||
$key = RateLimitFilter::CACHE_PREFIX . '_' . md5($key);
|
||||
$cache->set($key, time() + 10);
|
||||
$response = $this->get($page1->RelativeLink('changes'));
|
||||
$this->assertEquals(429, $response->getStatusCode());
|
||||
$this->assertGreaterThan(0, $response->getHeader('Retry-After'));
|
||||
$response = $this->get($page2->RelativeLink('changes'));
|
||||
$this->assertEquals(200, $response->getStatusCode());
|
||||
Config::modify()->set(RateLimitFilter::class, 'lock_bypage', false);
|
||||
// Test page specific lock
|
||||
Config::modify()->set(RateLimitFilter::class, 'lock_bypage', true);
|
||||
$key = implode('_', array(
|
||||
'changes',
|
||||
$page1->ID,
|
||||
Versioned::get_versionnumber_by_stage(SiteTree::class, 'Live', $page1->ID, false)
|
||||
));
|
||||
$key = RateLimitFilter::CACHE_PREFIX . '_' . md5($key);
|
||||
$cache->set($key, time() + 10);
|
||||
$response = $this->get($page1->RelativeLink('changes'));
|
||||
$this->assertEquals(429, $response->getStatusCode());
|
||||
$this->assertGreaterThan(0, $response->getHeader('Retry-After'));
|
||||
$response = $this->get($page2->RelativeLink('changes'));
|
||||
$this->assertEquals(200, $response->getStatusCode());
|
||||
Config::modify()->set(RateLimitFilter::class, 'lock_bypage', false);
|
||||
|
||||
// Test rate limit hit by IP
|
||||
Config::modify()->set(RateLimitFilter::class, 'lock_byuserip', true);
|
||||
$_SERVER['REMOTE_ADDR'] = '127.0.0.1';
|
||||
$cache->set(RateLimitFilter::CACHE_PREFIX . '_' . md5('127.0.0.1'), time() + 10);
|
||||
$response = $this->get($page1->RelativeLink('changes'));
|
||||
$this->assertEquals(429, $response->getStatusCode());
|
||||
$this->assertGreaterThan(0, $response->getHeader('Retry-After'));
|
||||
// Test rate limit hit by IP
|
||||
Config::modify()->set(RateLimitFilter::class, 'lock_byuserip', true);
|
||||
$_SERVER['REMOTE_ADDR'] = '127.0.0.1';
|
||||
$cache->set(RateLimitFilter::CACHE_PREFIX . '_' . md5('127.0.0.1'), time() + 10);
|
||||
$response = $this->get($page1->RelativeLink('changes'));
|
||||
$this->assertEquals(429, $response->getStatusCode());
|
||||
$this->assertGreaterThan(0, $response->getHeader('Retry-After'));
|
||||
|
||||
// Test rate limit doesn't hit other IP
|
||||
$_SERVER['REMOTE_ADDR'] = '127.0.0.20';
|
||||
$cache->set(RateLimitFilter::CACHE_PREFIX . '_' . md5('127.0.0.1'), time() + 10);
|
||||
$response = $this->get($page1->RelativeLink('changes'));
|
||||
$this->assertEquals(200, $response->getStatusCode());
|
||||
// Test rate limit doesn't hit other IP
|
||||
$_SERVER['REMOTE_ADDR'] = '127.0.0.20';
|
||||
$cache->set(RateLimitFilter::CACHE_PREFIX . '_' . md5('127.0.0.1'), time() + 10);
|
||||
$response = $this->get($page1->RelativeLink('changes'));
|
||||
$this->assertEquals(200, $response->getStatusCode());
|
||||
|
||||
// Restore setting
|
||||
Config::modify()->set(RateLimitFilter::class, 'lock_byuserip', false);
|
||||
Config::modify()->set(RateLimitFilter::class, 'lock_timeout', 0);
|
||||
Config::modify()->set(CachedContentFilter::class, 'cache_enabled', false);
|
||||
}
|
||||
// Restore setting
|
||||
Config::modify()->set(RateLimitFilter::class, 'lock_byuserip', false);
|
||||
Config::modify()->set(RateLimitFilter::class, 'lock_timeout', 0);
|
||||
Config::modify()->set(CachedContentFilter::class, 'cache_enabled', false);
|
||||
}
|
||||
|
||||
public function testContainsChangesForPageOnly() {
|
||||
$page1 = $this->createPageWithChanges(array('Title' => 'Page1'));
|
||||
$page2 = $this->createPageWithChanges(array('Title' => 'Page2'));
|
||||
public function testContainsChangesForPageOnly()
|
||||
{
|
||||
$page1 = $this->createPageWithChanges(array('Title' => 'Page1'));
|
||||
$page2 = $this->createPageWithChanges(array('Title' => 'Page2'));
|
||||
|
||||
$response = $this->get($page1->RelativeLink('changes'));
|
||||
$xml = simplexml_load_string($response->getBody());
|
||||
$titles = array_map(function($item) {return (string)$item->title;}, $xml->xpath('//item'));
|
||||
// TODO Unclear if this should contain the original version
|
||||
$this->assertContains('Changed: Page1', $titles);
|
||||
$this->assertNotContains('Changed: Page2', $titles);
|
||||
$response = $this->get($page1->RelativeLink('changes'));
|
||||
$xml = simplexml_load_string($response->getBody());
|
||||
$titles = array_map(function ($item) {
|
||||
return (string)$item->title;
|
||||
}, $xml->xpath('//item'));
|
||||
// TODO Unclear if this should contain the original version
|
||||
$this->assertContains('Changed: Page1', $titles);
|
||||
$this->assertNotContains('Changed: Page2', $titles);
|
||||
|
||||
$response = $this->get($page2->RelativeLink('changes'));
|
||||
$xml = simplexml_load_string($response->getBody());
|
||||
$titles = array_map(function($item) {return (string)$item->title;}, $xml->xpath('//item'));
|
||||
// TODO Unclear if this should contain the original version
|
||||
$this->assertNotContains('Changed: Page1', $titles);
|
||||
$this->assertContains('Changed: Page2', $titles);
|
||||
}
|
||||
$response = $this->get($page2->RelativeLink('changes'));
|
||||
$xml = simplexml_load_string($response->getBody());
|
||||
$titles = array_map(function ($item) {
|
||||
return (string)$item->title;
|
||||
}, $xml->xpath('//item'));
|
||||
// TODO Unclear if this should contain the original version
|
||||
$this->assertNotContains('Changed: Page1', $titles);
|
||||
$this->assertContains('Changed: Page2', $titles);
|
||||
}
|
||||
|
||||
public function testContainsAllChangesForAllPages() {
|
||||
$page1 = $this->createPageWithChanges(array('Title' => 'Page1'));
|
||||
$page2 = $this->createPageWithChanges(array('Title' => 'Page2'));
|
||||
public function testContainsAllChangesForAllPages()
|
||||
{
|
||||
$page1 = $this->createPageWithChanges(array('Title' => 'Page1'));
|
||||
$page2 = $this->createPageWithChanges(array('Title' => 'Page2'));
|
||||
|
||||
$response = $this->get($page1->RelativeLink('allchanges'));
|
||||
$xml = simplexml_load_string($response->getBody());
|
||||
$titles = array_map(function($item) {return (string)$item->title;}, $xml->xpath('//item'));
|
||||
$this->assertContains('Page1', $titles);
|
||||
$this->assertContains('Page2', $titles);
|
||||
}
|
||||
$response = $this->get($page1->RelativeLink('allchanges'));
|
||||
$xml = simplexml_load_string($response->getBody());
|
||||
$titles = array_map(function ($item) {
|
||||
return (string)$item->title;
|
||||
}, $xml->xpath('//item'));
|
||||
$this->assertContains('Page1', $titles);
|
||||
$this->assertContains('Page2', $titles);
|
||||
}
|
||||
|
||||
protected function createPageWithChanges($seed = null) {
|
||||
$page = new Page();
|
||||
protected function createPageWithChanges($seed = null)
|
||||
{
|
||||
$page = new Page();
|
||||
|
||||
$seed = array_merge(array(
|
||||
'Title' => 'My Title',
|
||||
'Content' => 'My Content'
|
||||
), $seed);
|
||||
$page->update($seed);
|
||||
$page->write();
|
||||
$page->publish('Stage', 'Live');
|
||||
$seed = array_merge(array(
|
||||
'Title' => 'My Title',
|
||||
'Content' => 'My Content'
|
||||
), $seed);
|
||||
$page->update($seed);
|
||||
$page->write();
|
||||
$page->publish('Stage', 'Live');
|
||||
|
||||
$page->update(array(
|
||||
'Title' => 'Changed: ' . $seed['Title'],
|
||||
'Content' => 'Changed: ' . $seed['Content'],
|
||||
));
|
||||
$page->write();
|
||||
$page->publish('Stage', 'Live');
|
||||
$page->update(array(
|
||||
'Title' => 'Changed: ' . $seed['Title'],
|
||||
'Content' => 'Changed: ' . $seed['Content'],
|
||||
));
|
||||
$page->write();
|
||||
$page->publish('Stage', 'Live');
|
||||
|
||||
$page->update(array(
|
||||
'Title' => 'Changed again: ' . $seed['Title'],
|
||||
'Content' => 'Changed again: ' . $seed['Content'],
|
||||
));
|
||||
$page->write();
|
||||
$page->publish('Stage', 'Live');
|
||||
$page->update(array(
|
||||
'Title' => 'Changed again: ' . $seed['Title'],
|
||||
'Content' => 'Changed again: ' . $seed['Content'],
|
||||
));
|
||||
$page->write();
|
||||
$page->publish('Stage', 'Live');
|
||||
|
||||
$page->update(array(
|
||||
'Title' => 'Unpublished: ' . $seed['Title'],
|
||||
'Content' => 'Unpublished: ' . $seed['Content'],
|
||||
));
|
||||
$page->write();
|
||||
$page->update(array(
|
||||
'Title' => 'Unpublished: ' . $seed['Title'],
|
||||
'Content' => 'Unpublished: ' . $seed['Content'],
|
||||
));
|
||||
$page->write();
|
||||
|
||||
return $page;
|
||||
}
|
||||
return $page;
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests response code for globally disabled feedss
|
||||
*/
|
||||
public function testFeedViewability() {
|
||||
/**
|
||||
* Tests response code for globally disabled feedss
|
||||
*/
|
||||
public function testFeedViewability()
|
||||
{
|
||||
|
||||
// Nested loop through each configuration
|
||||
foreach(array(true, false) as $publicHistory_Page) {
|
||||
$page = $this->createPageWithChanges(array('PublicHistory' => $publicHistory_Page, 'Title' => 'Page'));
|
||||
// Nested loop through each configuration
|
||||
foreach (array(true, false) as $publicHistory_Page) {
|
||||
$page = $this->createPageWithChanges(array('PublicHistory' => $publicHistory_Page, 'Title' => 'Page'));
|
||||
|
||||
// Test requests to 'changes' action
|
||||
foreach(array(true, false) as $publicHistory_Config) {
|
||||
Config::modify()->set(VersionFeed::class, 'changes_enabled', $publicHistory_Config);
|
||||
$expectedResponse = $publicHistory_Page && $publicHistory_Config ? 200 : 404;
|
||||
$response = $this->get($page->RelativeLink('changes'));
|
||||
$this->assertEquals($expectedResponse, $response->getStatusCode());
|
||||
}
|
||||
// Test requests to 'changes' action
|
||||
foreach (array(true, false) as $publicHistory_Config) {
|
||||
Config::modify()->set(VersionFeed::class, 'changes_enabled', $publicHistory_Config);
|
||||
$expectedResponse = $publicHistory_Page && $publicHistory_Config ? 200 : 404;
|
||||
$response = $this->get($page->RelativeLink('changes'));
|
||||
$this->assertEquals($expectedResponse, $response->getStatusCode());
|
||||
}
|
||||
|
||||
// Test requests to 'allchanges' action on each page
|
||||
foreach(array(true, false) as $allChanges_Config) {
|
||||
foreach(array(true, false) as $allChanges_SiteConfig) {
|
||||
Config::modify()->set(VersionFeed::class, 'allchanges_enabled', $allChanges_Config);
|
||||
$siteConfig = SiteConfig::current_site_config();
|
||||
$siteConfig->AllChangesEnabled = $allChanges_SiteConfig;
|
||||
$siteConfig->write();
|
||||
|
||||
$expectedResponse = $allChanges_Config && $allChanges_SiteConfig ? 200 : 404;
|
||||
$response = $this->get($page->RelativeLink('allchanges'));
|
||||
$this->assertEquals($expectedResponse, $response->getStatusCode());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// Test requests to 'allchanges' action on each page
|
||||
foreach (array(true, false) as $allChanges_Config) {
|
||||
foreach (array(true, false) as $allChanges_SiteConfig) {
|
||||
Config::modify()->set(VersionFeed::class, 'allchanges_enabled', $allChanges_Config);
|
||||
$siteConfig = SiteConfig::current_site_config();
|
||||
$siteConfig->AllChangesEnabled = $allChanges_SiteConfig;
|
||||
$siteConfig->write();
|
||||
|
||||
$expectedResponse = $allChanges_Config && $allChanges_SiteConfig ? 200 : 404;
|
||||
$response = $this->get($page->RelativeLink('allchanges'));
|
||||
$this->assertEquals($expectedResponse, $response->getStatusCode());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -2,7 +2,6 @@
|
||||
|
||||
namespace SilverStripe\VersionFeed\Tests;
|
||||
|
||||
|
||||
use Page;
|
||||
use SilverStripe\VersionFeed\VersionFeed;
|
||||
use SilverStripe\VersionFeed\VersionFeedController;
|
||||
@ -10,56 +9,58 @@ use SilverStripe\Dev\SapphireTest;
|
||||
use SilverStripe\CMS\Model\SiteTree;
|
||||
use SilverStripe\CMS\Controllers\ContentController;
|
||||
|
||||
class VersionFeedTest extends SapphireTest
|
||||
{
|
||||
|
||||
class VersionFeedTest extends SapphireTest {
|
||||
protected $usesDatabase = true;
|
||||
|
||||
protected $usesDatabase = true;
|
||||
protected static $required_extensions = [
|
||||
SiteTree::class => [VersionFeed::class],
|
||||
ContentController::class => [VersionFeedController::class],
|
||||
];
|
||||
|
||||
protected static $required_extensions = [
|
||||
SiteTree::class => [VersionFeed::class],
|
||||
ContentController::class => [VersionFeedController::class],
|
||||
];
|
||||
protected $illegalExtensions = [
|
||||
'SiteTree' => ['Translatable']
|
||||
];
|
||||
|
||||
protected $illegalExtensions = [
|
||||
'SiteTree' => ['Translatable']
|
||||
];
|
||||
public function testDiffedChangesExcludesRestrictedItems()
|
||||
{
|
||||
$this->markTestIncomplete();
|
||||
}
|
||||
|
||||
public function testDiffedChangesExcludesRestrictedItems() {
|
||||
$this->markTestIncomplete();
|
||||
}
|
||||
public function testDiffedChangesIncludesFullHistory()
|
||||
{
|
||||
$this->markTestIncomplete();
|
||||
}
|
||||
|
||||
public function testDiffedChangesIncludesFullHistory() {
|
||||
$this->markTestIncomplete();
|
||||
}
|
||||
public function testDiffedChangesTitle()
|
||||
{
|
||||
$page = new Page(['Title' => 'My Title']);
|
||||
$page->write();
|
||||
$page->publish('Stage', 'Live');
|
||||
|
||||
$page->Title = 'My Changed Title';
|
||||
$page->write();
|
||||
$page->publish('Stage', 'Live');
|
||||
|
||||
public function testDiffedChangesTitle() {
|
||||
$page = new Page(['Title' => 'My Title']);
|
||||
$page->write();
|
||||
$page->publish('Stage', 'Live');
|
||||
|
||||
$page->Title = 'My Changed Title';
|
||||
$page->write();
|
||||
$page->publish('Stage', 'Live');
|
||||
$page->Title = 'My Unpublished Changed Title';
|
||||
$page->write();
|
||||
|
||||
$page->Title = 'My Unpublished Changed Title';
|
||||
$page->write();
|
||||
// Strip spaces from test output because they're not reliably maintained by the HTML Tidier
|
||||
$cleanDiffOutput = function ($val) {
|
||||
return str_replace(' ', '', strip_tags($val));
|
||||
};
|
||||
|
||||
// Strip spaces from test output because they're not reliably maintained by the HTML Tidier
|
||||
$cleanDiffOutput = function($val) {
|
||||
return str_replace(' ','',strip_tags($val));
|
||||
};
|
||||
|
||||
$this->assertContains(
|
||||
str_replace(' ' ,'',_t('RSSHistory.TITLECHANGED', 'Title has changed:') . 'My Changed Title'),
|
||||
array_map($cleanDiffOutput, $page->getDiffList()->column('DiffTitle')),
|
||||
'Detects published title changes'
|
||||
);
|
||||
|
||||
$this->assertNotContains(
|
||||
str_replace(' ' ,'',_t('RSSHistory.TITLECHANGED', 'Title has changed:') . 'My Unpublished Changed Title'),
|
||||
array_map($cleanDiffOutput, $page->getDiffList()->column('DiffTitle')),
|
||||
'Ignores unpublished title changes'
|
||||
);
|
||||
}
|
||||
$this->assertContains(
|
||||
str_replace(' ', '', _t('RSSHistory.TITLECHANGED', 'Title has changed:') . 'My Changed Title'),
|
||||
array_map($cleanDiffOutput, $page->getDiffList()->column('DiffTitle')),
|
||||
'Detects published title changes'
|
||||
);
|
||||
|
||||
$this->assertNotContains(
|
||||
str_replace(' ', '', _t('RSSHistory.TITLECHANGED', 'Title has changed:') . 'My Unpublished Changed Title'),
|
||||
array_map($cleanDiffOutput, $page->getDiffList()->column('DiffTitle')),
|
||||
'Ignores unpublished title changes'
|
||||
);
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user