Run the upgrader & linting tools
An initial (untested) run at a proper upgrade. Progress commit.
This commit is contained in:
parent
e6ddf0fe47
commit
1ed9a23a71
|
@ -2,14 +2,14 @@
|
||||||
|
|
||||||
namespace SilverStripe\ExternalLinks\Controllers;
|
namespace SilverStripe\ExternalLinks\Controllers;
|
||||||
|
|
||||||
use Controller;
|
use SilverStripe\Control\HTTP;
|
||||||
use HTTP;
|
use SilverStripe\ExternalLinks\Model\BrokenExternalPageTrackStatus;
|
||||||
use BrokenExternalPageTrackStatus;
|
use SilverStripe\ExternalLinks\Jobs\CheckExternalLinksJob;
|
||||||
use CheckExternalLinksJob;
|
use SilverStripe\ExternalLinks\Tasks\CheckExternalLinksTask;
|
||||||
use CheckExternalLinksTask;
|
use SilverStripe\Control\Controller;
|
||||||
|
|
||||||
|
class CMSExternalLinks_Controller extends Controller
|
||||||
class CMSExternalLinks_Controller extends Controller {
|
{
|
||||||
|
|
||||||
private static $allowed_actions = array('getJobStatus', 'start');
|
private static $allowed_actions = array('getJobStatus', 'start');
|
||||||
|
|
||||||
|
@ -18,7 +18,8 @@ class CMSExternalLinks_Controller extends Controller {
|
||||||
*
|
*
|
||||||
* @return string JSON string detailing status of the job
|
* @return string JSON string detailing status of the job
|
||||||
*/
|
*/
|
||||||
public function getJobStatus() {
|
public function getJobStatus()
|
||||||
|
{
|
||||||
// Set headers
|
// Set headers
|
||||||
HTTP::set_cache_age(0);
|
HTTP::set_cache_age(0);
|
||||||
HTTP::add_cache_headers($this->response);
|
HTTP::add_cache_headers($this->response);
|
||||||
|
@ -29,22 +30,27 @@ class CMSExternalLinks_Controller extends Controller {
|
||||||
|
|
||||||
// Format status
|
// Format status
|
||||||
$track = BrokenExternalPageTrackStatus::get_latest();
|
$track = BrokenExternalPageTrackStatus::get_latest();
|
||||||
if($track) return json_encode(array(
|
if ($track) {
|
||||||
|
return json_encode(array(
|
||||||
'TrackID' => $track->ID,
|
'TrackID' => $track->ID,
|
||||||
'Status' => $track->Status,
|
'Status' => $track->Status,
|
||||||
'Completed' => $track->getCompletedPages(),
|
'Completed' => $track->getCompletedPages(),
|
||||||
'Total' => $track->getTotalPages()
|
'Total' => $track->getTotalPages()
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Starts a broken external link check
|
* Starts a broken external link check
|
||||||
*/
|
*/
|
||||||
public function start() {
|
public function start()
|
||||||
|
{
|
||||||
// return if the a job is already running
|
// return if the a job is already running
|
||||||
$status = BrokenExternalPageTrackStatus::get_latest();
|
$status = BrokenExternalPageTrackStatus::get_latest();
|
||||||
if ($status && $status->Status == 'Running') return;
|
if ($status && $status->Status == 'Running') {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
// Create a new job
|
// Create a new job
|
||||||
if (class_exists('QueuedJobService')) {
|
if (class_exists('QueuedJobService')) {
|
||||||
|
|
|
@ -4,38 +4,44 @@ namespace SilverStripe\ExternalLinks\Jobs;
|
||||||
|
|
||||||
use AbstractQueuedJob;
|
use AbstractQueuedJob;
|
||||||
use QueuedJob;
|
use QueuedJob;
|
||||||
use CheckExternalLinksTask;
|
|
||||||
|
|
||||||
|
use SilverStripe\ExternalLinks\Tasks\CheckExternalLinksTask;
|
||||||
|
|
||||||
if(!class_exists('AbstractQueuedJob')) return;
|
if (!class_exists('AbstractQueuedJob')) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A Job for running a external link check for published pages
|
* A Job for running a external link check for published pages
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
class CheckExternalLinksJob extends AbstractQueuedJob implements QueuedJob {
|
class CheckExternalLinksJob extends AbstractQueuedJob implements QueuedJob
|
||||||
|
{
|
||||||
|
|
||||||
public function getTitle() {
|
public function getTitle()
|
||||||
|
{
|
||||||
return _t('CheckExternalLiksJob.TITLE', 'Checking for external broken links');
|
return _t('CheckExternalLiksJob.TITLE', 'Checking for external broken links');
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getJobType() {
|
public function getJobType()
|
||||||
|
{
|
||||||
return QueuedJob::QUEUED;
|
return QueuedJob::QUEUED;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getSignature() {
|
public function getSignature()
|
||||||
|
{
|
||||||
return md5(get_class($this));
|
return md5(get_class($this));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Check an individual page
|
* Check an individual page
|
||||||
*/
|
*/
|
||||||
public function process() {
|
public function process()
|
||||||
|
{
|
||||||
$task = CheckExternalLinksTask::create();
|
$task = CheckExternalLinksTask::create();
|
||||||
$track = $task->runLinksCheck(1);
|
$track = $task->runLinksCheck(1);
|
||||||
$this->currentStep = $track->CompletedPages;
|
$this->currentStep = $track->CompletedPages;
|
||||||
$this->totalSteps = $track->TotalPages;
|
$this->totalSteps = $track->TotalPages;
|
||||||
$this->isComplete = $track->Status === 'Completed';
|
$this->isComplete = $track->Status === 'Completed';
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,11 +2,13 @@
|
||||||
|
|
||||||
namespace SilverStripe\ExternalLinks\Model;
|
namespace SilverStripe\ExternalLinks\Model;
|
||||||
|
|
||||||
use DataObject;
|
use SilverStripe\ExternalLinks\Model\BrokenExternalPageTrack;
|
||||||
use Member;
|
use SilverStripe\ExternalLinks\Model\BrokenExternalPageTrackStatus;
|
||||||
use Permission;
|
use SilverStripe\Security\Member;
|
||||||
use Config;
|
use SilverStripe\Security\Permission;
|
||||||
|
use SilverStripe\Core\Config\Config;
|
||||||
|
use SilverStripe\Control\HTTPResponse;
|
||||||
|
use SilverStripe\ORM\DataObject;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Represents a single link checked for a single run that is broken
|
* Represents a single link checked for a single run that is broken
|
||||||
|
@ -14,7 +16,8 @@ use Config;
|
||||||
* @method BrokenExternalPageTrack Track()
|
* @method BrokenExternalPageTrack Track()
|
||||||
* @method BrokenExternalPageTrackStatus Status()
|
* @method BrokenExternalPageTrackStatus Status()
|
||||||
*/
|
*/
|
||||||
class BrokenExternalLink extends DataObject {
|
class BrokenExternalLink extends DataObject
|
||||||
|
{
|
||||||
|
|
||||||
private static $db = array(
|
private static $db = array(
|
||||||
'Link' => 'Varchar(2083)', // 2083 is the maximum length of a URL in Internet Explorer.
|
'Link' => 'Varchar(2083)', // 2083 is the maximum length of a URL in Internet Explorer.
|
||||||
|
@ -22,8 +25,8 @@ class BrokenExternalLink extends DataObject {
|
||||||
);
|
);
|
||||||
|
|
||||||
private static $has_one = array(
|
private static $has_one = array(
|
||||||
'Track' => 'BrokenExternalPageTrack',
|
'Track' => BrokenExternalPageTrack::class,
|
||||||
'Status' => 'BrokenExternalPageTrackStatus'
|
'Status' => BrokenExternalPageTrackStatus::class
|
||||||
);
|
);
|
||||||
|
|
||||||
private static $summary_fields = array(
|
private static $summary_fields = array(
|
||||||
|
@ -40,15 +43,18 @@ class BrokenExternalLink extends DataObject {
|
||||||
/**
|
/**
|
||||||
* @return SiteTree
|
* @return SiteTree
|
||||||
*/
|
*/
|
||||||
public function Page() {
|
public function Page()
|
||||||
|
{
|
||||||
return $this->Track()->Page();
|
return $this->Track()->Page();
|
||||||
}
|
}
|
||||||
|
|
||||||
public function canEdit($member = false) {
|
public function canEdit($member = false)
|
||||||
|
{
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function canView($member = false) {
|
public function canView($member = false)
|
||||||
|
{
|
||||||
$member = $member ? $member : Member::currentUser();
|
$member = $member ? $member : Member::currentUser();
|
||||||
$codes = array('content-authors', 'administrators');
|
$codes = array('content-authors', 'administrators');
|
||||||
return Permission::checkMember($member, $codes);
|
return Permission::checkMember($member, $codes);
|
||||||
|
@ -59,13 +65,13 @@ class BrokenExternalLink extends DataObject {
|
||||||
*
|
*
|
||||||
* @return string
|
* @return string
|
||||||
*/
|
*/
|
||||||
public function getHTTPCodeDescription() {
|
public function getHTTPCodeDescription()
|
||||||
|
{
|
||||||
$code = $this->HTTPCode;
|
$code = $this->HTTPCode;
|
||||||
if(empty($code)) {
|
if (empty($code)) {
|
||||||
// Assume that $code = 0 means there was no response
|
// Assume that $code = 0 means there was no response
|
||||||
$description = _t('BrokenExternalLink.NOTAVAILABLE', 'Server Not Available');
|
$description = _t('BrokenExternalLink.NOTAVAILABLE', 'Server Not Available');
|
||||||
} elseif(
|
} elseif (($descriptions = Config::inst()->get(HTTPResponse::class, 'status_codes'))
|
||||||
($descriptions = Config::inst()->get('SS_HTTPResponse', 'status_codes'))
|
|
||||||
&& isset($descriptions[$code])
|
&& isset($descriptions[$code])
|
||||||
) {
|
) {
|
||||||
$description = $descriptions[$code];
|
$description = $descriptions[$code];
|
||||||
|
@ -75,5 +81,3 @@ class BrokenExternalLink extends DataObject {
|
||||||
return sprintf("%d (%s)", $code, $description);
|
return sprintf("%d (%s)", $code, $description);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -2,33 +2,37 @@
|
||||||
|
|
||||||
namespace SilverStripe\ExternalLinks\Model;
|
namespace SilverStripe\ExternalLinks\Model;
|
||||||
|
|
||||||
use DataObject;
|
use SilverStripe\CMS\Model\SiteTree;
|
||||||
use Versioned;
|
use SilverStripe\ExternalLinks\Model\BrokenExternalPageTrackStatus;
|
||||||
|
use SilverStripe\ExternalLinks\Model\BrokenExternalLink;
|
||||||
|
use SilverStripe\Versioned\Versioned;
|
||||||
|
use SilverStripe\ORM\DataObject;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Represents a track for a single page
|
* Represents a track for a single page
|
||||||
*/
|
*/
|
||||||
class BrokenExternalPageTrack extends DataObject {
|
class BrokenExternalPageTrack extends DataObject
|
||||||
|
{
|
||||||
|
|
||||||
private static $db = array(
|
private static $db = array(
|
||||||
'Processed' => 'Boolean'
|
'Processed' => 'Boolean'
|
||||||
);
|
);
|
||||||
|
|
||||||
private static $has_one = array(
|
private static $has_one = array(
|
||||||
'Page' => 'SiteTree',
|
'Page' => SiteTree::class,
|
||||||
'Status' => 'BrokenExternalPageTrackStatus'
|
'Status' => BrokenExternalPageTrackStatus::class
|
||||||
);
|
);
|
||||||
|
|
||||||
private static $has_many = array(
|
private static $has_many = array(
|
||||||
'BrokenLinks' => 'BrokenExternalLink'
|
'BrokenLinks' => BrokenExternalLink::class
|
||||||
);
|
);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return SiteTree
|
* @return SiteTree
|
||||||
*/
|
*/
|
||||||
public function Page() {
|
public function Page()
|
||||||
return Versioned::get_by_stage('SiteTree', 'Stage')
|
{
|
||||||
|
return Versioned::get_by_stage(SiteTree::class, 'Stage')
|
||||||
->byID($this->PageID);
|
->byID($this->PageID);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,9 +2,11 @@
|
||||||
|
|
||||||
namespace SilverStripe\ExternalLinks\Model;
|
namespace SilverStripe\ExternalLinks\Model;
|
||||||
|
|
||||||
use DataObject;
|
use SilverStripe\ExternalLinks\Model\BrokenExternalPageTrack;
|
||||||
use Versioned;
|
use SilverStripe\ExternalLinks\Model\BrokenExternalLink;
|
||||||
|
use SilverStripe\CMS\Model\SiteTree;
|
||||||
|
use SilverStripe\Versioned\Versioned;
|
||||||
|
use SilverStripe\ORM\DataObject;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Represents the status of a track run
|
* Represents the status of a track run
|
||||||
|
@ -14,7 +16,8 @@ use Versioned;
|
||||||
* @property int $TotalPages Get total pages count
|
* @property int $TotalPages Get total pages count
|
||||||
* @property int $CompletedPages Get completed pages count
|
* @property int $CompletedPages Get completed pages count
|
||||||
*/
|
*/
|
||||||
class BrokenExternalPageTrackStatus extends DataObject {
|
class BrokenExternalPageTrackStatus extends DataObject
|
||||||
|
{
|
||||||
|
|
||||||
private static $db = array(
|
private static $db = array(
|
||||||
'Status' => 'Enum("Completed, Running", "Running")',
|
'Status' => 'Enum("Completed, Running", "Running")',
|
||||||
|
@ -22,8 +25,8 @@ class BrokenExternalPageTrackStatus extends DataObject {
|
||||||
);
|
);
|
||||||
|
|
||||||
private static $has_many = array(
|
private static $has_many = array(
|
||||||
'TrackedPages' => 'BrokenExternalPageTrack',
|
'TrackedPages' => BrokenExternalPageTrack::class,
|
||||||
'BrokenLinks' => 'BrokenExternalLink'
|
'BrokenLinks' => BrokenExternalLink::class
|
||||||
);
|
);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -31,7 +34,8 @@ class BrokenExternalPageTrackStatus extends DataObject {
|
||||||
*
|
*
|
||||||
* @return self
|
* @return self
|
||||||
*/
|
*/
|
||||||
public static function get_latest() {
|
public static function get_latest()
|
||||||
|
{
|
||||||
return self::get()
|
return self::get()
|
||||||
->sort('ID', 'DESC')
|
->sort('ID', 'DESC')
|
||||||
->first();
|
->first();
|
||||||
|
@ -42,20 +46,24 @@ class BrokenExternalPageTrackStatus extends DataObject {
|
||||||
*
|
*
|
||||||
* @return DataList
|
* @return DataList
|
||||||
*/
|
*/
|
||||||
public function getIncompletePageList() {
|
public function getIncompletePageList()
|
||||||
|
{
|
||||||
$pageIDs = $this
|
$pageIDs = $this
|
||||||
->getIncompleteTracks()
|
->getIncompleteTracks()
|
||||||
->column('PageID');
|
->column('PageID');
|
||||||
if($pageIDs) return Versioned::get_by_stage('SiteTree', 'Stage')
|
if ($pageIDs) {
|
||||||
|
return Versioned::get_by_stage(SiteTree::class, 'Stage')
|
||||||
->byIDs($pageIDs);
|
->byIDs($pageIDs);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the list of incomplete BrokenExternalPageTrack
|
* Get the list of incomplete BrokenExternalPageTrack
|
||||||
*
|
*
|
||||||
* @return DataList
|
* @return DataList
|
||||||
*/
|
*/
|
||||||
public function getIncompleteTracks() {
|
public function getIncompleteTracks()
|
||||||
|
{
|
||||||
return $this
|
return $this
|
||||||
->TrackedPages()
|
->TrackedPages()
|
||||||
->filter('Processed', 0);
|
->filter('Processed', 0);
|
||||||
|
@ -64,14 +72,16 @@ class BrokenExternalPageTrackStatus extends DataObject {
|
||||||
/**
|
/**
|
||||||
* Get total pages count
|
* Get total pages count
|
||||||
*/
|
*/
|
||||||
public function getTotalPages() {
|
public function getTotalPages()
|
||||||
|
{
|
||||||
return $this->TrackedPages()->count();
|
return $this->TrackedPages()->count();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get completed pages count
|
* Get completed pages count
|
||||||
*/
|
*/
|
||||||
public function getCompletedPages() {
|
public function getCompletedPages()
|
||||||
|
{
|
||||||
return $this
|
return $this
|
||||||
->TrackedPages()
|
->TrackedPages()
|
||||||
->filter('Processed', 1)
|
->filter('Processed', 1)
|
||||||
|
@ -83,7 +93,8 @@ class BrokenExternalPageTrackStatus extends DataObject {
|
||||||
*
|
*
|
||||||
* @return self
|
* @return self
|
||||||
*/
|
*/
|
||||||
public static function get_or_create() {
|
public static function get_or_create()
|
||||||
|
{
|
||||||
// Check the current status
|
// Check the current status
|
||||||
$status = self::get_latest();
|
$status = self::get_latest();
|
||||||
if ($status && $status->Status == 'Running') {
|
if ($status && $status->Status == 'Running') {
|
||||||
|
@ -99,13 +110,14 @@ class BrokenExternalPageTrackStatus extends DataObject {
|
||||||
*
|
*
|
||||||
* @return self
|
* @return self
|
||||||
*/
|
*/
|
||||||
public static function create_status() {
|
public static function create_status()
|
||||||
|
{
|
||||||
// If the script is to be started create a new status
|
// If the script is to be started create a new status
|
||||||
$status = self::create();
|
$status = self::create();
|
||||||
$status->updateJobInfo('Creating new tracking object');
|
$status->updateJobInfo('Creating new tracking object');
|
||||||
|
|
||||||
// Setup all pages to test
|
// Setup all pages to test
|
||||||
$pageIDs = Versioned::get_by_stage('SiteTree', 'Stage')
|
$pageIDs = Versioned::get_by_stage(SiteTree::class, 'Stage')
|
||||||
->column('ID');
|
->column('ID');
|
||||||
foreach ($pageIDs as $pageID) {
|
foreach ($pageIDs as $pageID) {
|
||||||
$trackPage = BrokenExternalPageTrack::create();
|
$trackPage = BrokenExternalPageTrack::create();
|
||||||
|
@ -117,7 +129,8 @@ class BrokenExternalPageTrackStatus extends DataObject {
|
||||||
return $status;
|
return $status;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function updateJobInfo($message) {
|
public function updateJobInfo($message)
|
||||||
|
{
|
||||||
$this->JobInfo = $message;
|
$this->JobInfo = $message;
|
||||||
$this->write();
|
$this->write();
|
||||||
}
|
}
|
||||||
|
@ -125,7 +138,8 @@ class BrokenExternalPageTrackStatus extends DataObject {
|
||||||
/**
|
/**
|
||||||
* Self check status
|
* Self check status
|
||||||
*/
|
*/
|
||||||
public function updateStatus() {
|
public function updateStatus()
|
||||||
|
{
|
||||||
if ($this->CompletedPages == $this->TotalPages) {
|
if ($this->CompletedPages == $this->TotalPages) {
|
||||||
$this->Status = 'Completed';
|
$this->Status = 'Completed';
|
||||||
$this->updateJobInfo('Setting to completed');
|
$this->updateJobInfo('Setting to completed');
|
||||||
|
|
|
@ -2,13 +2,12 @@
|
||||||
|
|
||||||
namespace SilverStripe\ExternalLinks\Reports;
|
namespace SilverStripe\ExternalLinks\Reports;
|
||||||
|
|
||||||
use SS_Report;
|
use SilverStripe\Core\Convert;
|
||||||
use Convert;
|
use SilverStripe\ExternalLinks\Model\BrokenExternalPageTrackStatus;
|
||||||
use BrokenExternalPageTrackStatus;
|
use SilverStripe\ORM\ArrayList;
|
||||||
use ArrayList;
|
use SilverStripe\View\Requirements;
|
||||||
use Requirements;
|
use SilverStripe\Forms\LiteralField;
|
||||||
use LiteralField;
|
use SilverStripe\Reports\Report;
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Content side-report listing pages with external broken links
|
* Content side-report listing pages with external broken links
|
||||||
|
@ -16,23 +15,26 @@ use LiteralField;
|
||||||
* @subpackage content
|
* @subpackage content
|
||||||
*/
|
*/
|
||||||
|
|
||||||
class BrokenExternalLinksReport extends SS_Report {
|
class BrokenExternalLinksReport extends Report
|
||||||
|
{
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the report title
|
* Returns the report title
|
||||||
*
|
*
|
||||||
* @return string
|
* @return string
|
||||||
*/
|
*/
|
||||||
public function title() {
|
public function title()
|
||||||
|
{
|
||||||
return _t('ExternalBrokenLinksReport.EXTERNALBROKENLINKS', "External broken links report");
|
return _t('ExternalBrokenLinksReport.EXTERNALBROKENLINKS', "External broken links report");
|
||||||
}
|
}
|
||||||
|
|
||||||
public function columns() {
|
public function columns()
|
||||||
|
{
|
||||||
return array(
|
return array(
|
||||||
"Created" => "Checked",
|
"Created" => "Checked",
|
||||||
'Link' => array(
|
'Link' => array(
|
||||||
'title' => 'External Link',
|
'title' => 'External Link',
|
||||||
'formatting' => function($value, $item) {
|
'formatting' => function ($value, $item) {
|
||||||
return sprintf(
|
return sprintf(
|
||||||
'<a target="_blank" href="%s">%s</a>',
|
'<a target="_blank" href="%s">%s</a>',
|
||||||
Convert::raw2att($item->Link),
|
Convert::raw2att($item->Link),
|
||||||
|
@ -43,7 +45,7 @@ class BrokenExternalLinksReport extends SS_Report {
|
||||||
'HTTPCodeDescription' => 'HTTP Error Code',
|
'HTTPCodeDescription' => 'HTTP Error Code',
|
||||||
"Title" => array(
|
"Title" => array(
|
||||||
"title" => 'Page link is on',
|
"title" => 'Page link is on',
|
||||||
'formatting' => function($value, $item) {
|
'formatting' => function ($value, $item) {
|
||||||
$page = $item->Page();
|
$page = $item->Page();
|
||||||
return sprintf(
|
return sprintf(
|
||||||
'<a href="%s">%s</a>',
|
'<a href="%s">%s</a>',
|
||||||
|
@ -60,17 +62,22 @@ class BrokenExternalLinksReport extends SS_Report {
|
||||||
* in {@link GridFieldExportButton} generateExportFileData method.
|
* in {@link GridFieldExportButton} generateExportFileData method.
|
||||||
* @return array
|
* @return array
|
||||||
*/
|
*/
|
||||||
public function getColumns() {
|
public function getColumns()
|
||||||
|
{
|
||||||
return $this->columns();
|
return $this->columns();
|
||||||
}
|
}
|
||||||
|
|
||||||
public function sourceRecords() {
|
public function sourceRecords()
|
||||||
|
{
|
||||||
$track = BrokenExternalPageTrackStatus::get_latest();
|
$track = BrokenExternalPageTrackStatus::get_latest();
|
||||||
if ($track) return $track->BrokenLinks();
|
if ($track) {
|
||||||
|
return $track->BrokenLinks();
|
||||||
|
}
|
||||||
return new ArrayList();
|
return new ArrayList();
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getCMSFields() {
|
public function getCMSFields()
|
||||||
|
{
|
||||||
Requirements::javascript('externallinks/javascript/BrokenExternalLinksReport.js');
|
Requirements::javascript('externallinks/javascript/BrokenExternalLinksReport.js');
|
||||||
$fields = parent::getCMSFields();
|
$fields = parent::getCMSFields();
|
||||||
|
|
||||||
|
|
|
@ -2,18 +2,23 @@
|
||||||
|
|
||||||
namespace SilverStripe\ExternalLinks\Tasks;
|
namespace SilverStripe\ExternalLinks\Tasks;
|
||||||
|
|
||||||
use BuildTask;
|
|
||||||
use Debug;
|
|
||||||
use BrokenExternalPageTrack;
|
|
||||||
use DOMNode;
|
use DOMNode;
|
||||||
use BrokenExternalLink;
|
|
||||||
use Config;
|
|
||||||
use BrokenExternalPageTrackStatus;
|
|
||||||
use Injector;
|
|
||||||
use DB;
|
|
||||||
|
|
||||||
|
|
||||||
class CheckExternalLinksTask extends BuildTask {
|
|
||||||
|
|
||||||
|
|
||||||
|
use SilverStripe\Dev\Debug;
|
||||||
|
use SilverStripe\ExternalLinks\Model\BrokenExternalPageTrack;
|
||||||
|
use SilverStripe\ExternalLinks\Model\BrokenExternalLink;
|
||||||
|
use SilverStripe\Core\Config\Config;
|
||||||
|
use SilverStripe\ExternalLinks\Model\BrokenExternalPageTrackStatus;
|
||||||
|
use SilverStripe\Core\Injector\Injector;
|
||||||
|
use SilverStripe\ORM\DB;
|
||||||
|
use SilverStripe\Dev\BuildTask;
|
||||||
|
|
||||||
|
class CheckExternalLinksTask extends BuildTask
|
||||||
|
{
|
||||||
|
|
||||||
private static $dependencies = array(
|
private static $dependencies = array(
|
||||||
'LinkChecker' => '%$LinkChecker'
|
'LinkChecker' => '%$LinkChecker'
|
||||||
|
@ -40,11 +45,15 @@ class CheckExternalLinksTask extends BuildTask {
|
||||||
*
|
*
|
||||||
* @param string $message
|
* @param string $message
|
||||||
*/
|
*/
|
||||||
protected function log($message) {
|
protected function log($message)
|
||||||
if(!$this->silent) Debug::message($message);
|
{
|
||||||
|
if (!$this->silent) {
|
||||||
|
Debug::message($message);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public function run($request) {
|
public function run($request)
|
||||||
|
{
|
||||||
$this->runLinksCheck();
|
$this->runLinksCheck();
|
||||||
}
|
}
|
||||||
/**
|
/**
|
||||||
|
@ -52,21 +61,24 @@ class CheckExternalLinksTask extends BuildTask {
|
||||||
*
|
*
|
||||||
* @param bool $silent
|
* @param bool $silent
|
||||||
*/
|
*/
|
||||||
public function setSilent($silent) {
|
public function setSilent($silent)
|
||||||
|
{
|
||||||
$this->silent = $silent;
|
$this->silent = $silent;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param LinkChecker $linkChecker
|
* @param LinkChecker $linkChecker
|
||||||
*/
|
*/
|
||||||
public function setLinkChecker(LinkChecker $linkChecker) {
|
public function setLinkChecker(LinkChecker $linkChecker)
|
||||||
|
{
|
||||||
$this->linkChecker = $linkChecker;
|
$this->linkChecker = $linkChecker;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return LinkChecker
|
* @return LinkChecker
|
||||||
*/
|
*/
|
||||||
public function getLinkChecker() {
|
public function getLinkChecker()
|
||||||
|
{
|
||||||
return $this->linkChecker;
|
return $this->linkChecker;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -76,17 +88,20 @@ class CheckExternalLinksTask extends BuildTask {
|
||||||
* @param BrokenExternalPageTrack $pageTrack
|
* @param BrokenExternalPageTrack $pageTrack
|
||||||
* @param DOMNode $link
|
* @param DOMNode $link
|
||||||
*/
|
*/
|
||||||
protected function checkPageLink(BrokenExternalPageTrack $pageTrack, DOMNode $link) {
|
protected function checkPageLink(BrokenExternalPageTrack $pageTrack, DOMNode $link)
|
||||||
|
{
|
||||||
$class = $link->getAttribute('class');
|
$class = $link->getAttribute('class');
|
||||||
$href = $link->getAttribute('href');
|
$href = $link->getAttribute('href');
|
||||||
$markedBroken = preg_match('/\b(ss-broken)\b/', $class);
|
$markedBroken = preg_match('/\b(ss-broken)\b/', $class);
|
||||||
|
|
||||||
// Check link
|
// Check link
|
||||||
$httpCode = $this->linkChecker->checkLink($href);
|
$httpCode = $this->linkChecker->checkLink($href);
|
||||||
if($httpCode === null) return; // Null link means uncheckable, such as an internal link
|
if ($httpCode === null) {
|
||||||
|
return; // Null link means uncheckable, such as an internal link
|
||||||
|
}
|
||||||
|
|
||||||
// If this code is broken then mark as such
|
// If this code is broken then mark as such
|
||||||
if($foundBroken = $this->isCodeBroken($httpCode)) {
|
if ($foundBroken = $this->isCodeBroken($httpCode)) {
|
||||||
// Create broken record
|
// Create broken record
|
||||||
$brokenLink = new BrokenExternalLink();
|
$brokenLink = new BrokenExternalLink();
|
||||||
$brokenLink->Link = $href;
|
$brokenLink->Link = $href;
|
||||||
|
@ -97,8 +112,10 @@ class CheckExternalLinksTask extends BuildTask {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check if we need to update CSS class, otherwise return
|
// Check if we need to update CSS class, otherwise return
|
||||||
if($markedBroken == $foundBroken) return;
|
if ($markedBroken == $foundBroken) {
|
||||||
if($foundBroken) {
|
return;
|
||||||
|
}
|
||||||
|
if ($foundBroken) {
|
||||||
$class .= ' ss-broken';
|
$class .= ' ss-broken';
|
||||||
} else {
|
} else {
|
||||||
$class = preg_replace('/\s*\b(ss-broken)\b\s*/', ' ', $class);
|
$class = preg_replace('/\s*\b(ss-broken)\b\s*/', ' ', $class);
|
||||||
|
@ -112,13 +129,18 @@ class CheckExternalLinksTask extends BuildTask {
|
||||||
* @param int $httpCode
|
* @param int $httpCode
|
||||||
* @return bool True if this is a broken code
|
* @return bool True if this is a broken code
|
||||||
*/
|
*/
|
||||||
protected function isCodeBroken($httpCode) {
|
protected function isCodeBroken($httpCode)
|
||||||
|
{
|
||||||
// Null represents no request attempted
|
// Null represents no request attempted
|
||||||
if($httpCode === null) return false;
|
if ($httpCode === null) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
// do we have any whitelisted codes
|
// do we have any whitelisted codes
|
||||||
$ignoreCodes = Config::inst()->get('CheckExternalLinks', 'IgnoreCodes');
|
$ignoreCodes = Config::inst()->get('CheckExternalLinks', 'IgnoreCodes');
|
||||||
if(is_array($ignoreCodes) && in_array($httpCode, $ignoreCodes)) return false;
|
if (is_array($ignoreCodes) && in_array($httpCode, $ignoreCodes)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
// Check if code is outside valid range
|
// Check if code is outside valid range
|
||||||
return $httpCode < 200 || $httpCode > 302;
|
return $httpCode < 200 || $httpCode > 302;
|
||||||
|
@ -130,13 +152,16 @@ class CheckExternalLinksTask extends BuildTask {
|
||||||
* @param int $limit Limit to number of pages to run, or null to run all
|
* @param int $limit Limit to number of pages to run, or null to run all
|
||||||
* @return BrokenExternalPageTrackStatus
|
* @return BrokenExternalPageTrackStatus
|
||||||
*/
|
*/
|
||||||
public function runLinksCheck($limit = null) {
|
public function runLinksCheck($limit = null)
|
||||||
|
{
|
||||||
// Check the current status
|
// Check the current status
|
||||||
$status = BrokenExternalPageTrackStatus::get_or_create();
|
$status = BrokenExternalPageTrackStatus::get_or_create();
|
||||||
|
|
||||||
// Calculate pages to run
|
// Calculate pages to run
|
||||||
$pageTracks = $status->getIncompleteTracks();
|
$pageTracks = $status->getIncompleteTracks();
|
||||||
if($limit) $pageTracks = $pageTracks->limit($limit);
|
if ($limit) {
|
||||||
|
$pageTracks = $pageTracks->limit($limit);
|
||||||
|
}
|
||||||
|
|
||||||
// Check each page
|
// Check each page
|
||||||
foreach ($pageTracks as $pageTrack) {
|
foreach ($pageTracks as $pageTrack) {
|
||||||
|
@ -148,11 +173,13 @@ class CheckExternalLinksTask extends BuildTask {
|
||||||
$page = $pageTrack->Page();
|
$page = $pageTrack->Page();
|
||||||
$this->log("Checking {$page->Title}");
|
$this->log("Checking {$page->Title}");
|
||||||
$htmlValue = Injector::inst()->create('HTMLValue', $page->Content);
|
$htmlValue = Injector::inst()->create('HTMLValue', $page->Content);
|
||||||
if (!$htmlValue->isValid()) continue;
|
if (!$htmlValue->isValid()) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
// Check each link
|
// Check each link
|
||||||
$links = $htmlValue->getElementsByTagName('a');
|
$links = $htmlValue->getElementsByTagName('a');
|
||||||
foreach($links as $link) {
|
foreach ($links as $link) {
|
||||||
$this->checkPageLink($pageTrack, $link);
|
$this->checkPageLink($pageTrack, $link);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -164,7 +191,7 @@ class CheckExternalLinksTask extends BuildTask {
|
||||||
// Once all links have been created for this page update HasBrokenLinks
|
// Once all links have been created for this page update HasBrokenLinks
|
||||||
$count = $pageTrack->BrokenLinks()->count();
|
$count = $pageTrack->BrokenLinks()->count();
|
||||||
$this->log("Found {$count} broken links");
|
$this->log("Found {$count} broken links");
|
||||||
if($count) {
|
if ($count) {
|
||||||
// Bypass the ORM as syncLinkTracking does not allow you to update HasBrokenLink to true
|
// Bypass the ORM as syncLinkTracking does not allow you to update HasBrokenLink to true
|
||||||
DB::query(sprintf(
|
DB::query(sprintf(
|
||||||
'UPDATE "SiteTree" SET "HasBrokenLink" = 1 WHERE "ID" = \'%d\'',
|
'UPDATE "SiteTree" SET "HasBrokenLink" = 1 WHERE "ID" = \'%d\'',
|
||||||
|
@ -178,7 +205,8 @@ class CheckExternalLinksTask extends BuildTask {
|
||||||
return $status;
|
return $status;
|
||||||
}
|
}
|
||||||
|
|
||||||
private function updateCompletedPages($trackID = 0) {
|
private function updateCompletedPages($trackID = 0)
|
||||||
|
{
|
||||||
$noPages = BrokenExternalPageTrack::get()
|
$noPages = BrokenExternalPageTrack::get()
|
||||||
->filter(array(
|
->filter(array(
|
||||||
'TrackID' => $trackID,
|
'TrackID' => $trackID,
|
||||||
|
@ -191,9 +219,10 @@ class CheckExternalLinksTask extends BuildTask {
|
||||||
return $noPages;
|
return $noPages;
|
||||||
}
|
}
|
||||||
|
|
||||||
private function updateJobInfo($message) {
|
private function updateJobInfo($message)
|
||||||
|
{
|
||||||
$track = BrokenExternalPageTrackStatus::get_latest();
|
$track = BrokenExternalPageTrackStatus::get_latest();
|
||||||
if($track) {
|
if ($track) {
|
||||||
$track->JobInfo = $message;
|
$track->JobInfo = $message;
|
||||||
$track->write();
|
$track->write();
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,18 +4,19 @@ namespace SilverStripe\ExternalLinks\Tasks;
|
||||||
|
|
||||||
use SS_Cache;
|
use SS_Cache;
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Check links using curl
|
* Check links using curl
|
||||||
*/
|
*/
|
||||||
class CurlLinkChecker implements LinkChecker {
|
class CurlLinkChecker implements LinkChecker
|
||||||
|
{
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Return cache
|
* Return cache
|
||||||
*
|
*
|
||||||
* @return Zend_Cache_Frontend
|
* @return Zend_Cache_Frontend
|
||||||
*/
|
*/
|
||||||
protected function getCache() {
|
protected function getCache()
|
||||||
|
{
|
||||||
return SS_Cache::factory(
|
return SS_Cache::factory(
|
||||||
__CLASS__,
|
__CLASS__,
|
||||||
'Output',
|
'Output',
|
||||||
|
@ -29,18 +30,23 @@ class CurlLinkChecker implements LinkChecker {
|
||||||
* @param string $href URL to check
|
* @param string $href URL to check
|
||||||
* @return int HTTP status code, or null if not checkable (not a link)
|
* @return int HTTP status code, or null if not checkable (not a link)
|
||||||
*/
|
*/
|
||||||
public function checkLink($href) {
|
public function checkLink($href)
|
||||||
|
{
|
||||||
// Skip non-external links
|
// Skip non-external links
|
||||||
if(!preg_match('/^https?[^:]*:\/\//', $href)) return null;
|
if (!preg_match('/^https?[^:]*:\/\//', $href)) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
// Check if we have a cached result
|
// Check if we have a cached result
|
||||||
$cacheKey = md5($href);
|
$cacheKey = md5($href);
|
||||||
$result = $this->getCache()->load($cacheKey);
|
$result = $this->getCache()->load($cacheKey);
|
||||||
if($result !== false) return $result;
|
if ($result !== false) {
|
||||||
|
return $result;
|
||||||
|
}
|
||||||
|
|
||||||
// No cached result so just request
|
// No cached result so just request
|
||||||
$handle = curl_init($href);
|
$handle = curl_init($href);
|
||||||
curl_setopt($handle, CURLOPT_RETURNTRANSFER, TRUE);
|
curl_setopt($handle, CURLOPT_RETURNTRANSFER, true);
|
||||||
curl_setopt($handle, CURLOPT_CONNECTTIMEOUT, 5);
|
curl_setopt($handle, CURLOPT_CONNECTTIMEOUT, 5);
|
||||||
curl_setopt($handle, CURLOPT_TIMEOUT, 10);
|
curl_setopt($handle, CURLOPT_TIMEOUT, 10);
|
||||||
curl_exec($handle);
|
curl_exec($handle);
|
||||||
|
|
|
@ -2,13 +2,11 @@
|
||||||
|
|
||||||
namespace SilverStripe\ExternalLinks\Tasks;
|
namespace SilverStripe\ExternalLinks\Tasks;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Provides an interface for checking that a link is valid
|
* Provides an interface for checking that a link is valid
|
||||||
*/
|
*/
|
||||||
interface LinkChecker {
|
interface LinkChecker
|
||||||
|
{
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Determine the http status code for a given link
|
* Determine the http status code for a given link
|
||||||
|
|
|
@ -1,6 +1,16 @@
|
||||||
<?php
|
<?php
|
||||||
|
|
||||||
class ExternalLinksTest extends SapphireTest {
|
use SilverStripe\ExternalLinks\Tasks\LinkChecker;
|
||||||
|
use SilverStripe\Core\Injector\Injector;
|
||||||
|
use SilverStripe\ExternalLinks\Tasks\CheckExternalLinksTask;
|
||||||
|
use SilverStripe\ExternalLinks\Model\BrokenExternalPageTrackStatus;
|
||||||
|
use SilverStripe\i18n\i18n;
|
||||||
|
use SilverStripe\Reports\Report;
|
||||||
|
use SilverStripe\ExternalLinks\Reports\BrokenExternalLinksReport;
|
||||||
|
use SilverStripe\Dev\SapphireTest;
|
||||||
|
|
||||||
|
class ExternalLinksTest extends SapphireTest
|
||||||
|
{
|
||||||
|
|
||||||
protected static $fixture_file = 'ExternalLinksTest.yml';
|
protected static $fixture_file = 'ExternalLinksTest.yml';
|
||||||
|
|
||||||
|
@ -12,7 +22,8 @@ class ExternalLinksTest extends SapphireTest {
|
||||||
'SiteTree' => array('Translatable')
|
'SiteTree' => array('Translatable')
|
||||||
);
|
);
|
||||||
|
|
||||||
public function setUpOnce() {
|
public function setUpOnce()
|
||||||
|
{
|
||||||
if (class_exists('Phockito')) {
|
if (class_exists('Phockito')) {
|
||||||
Phockito::include_hamcrest(false);
|
Phockito::include_hamcrest(false);
|
||||||
}
|
}
|
||||||
|
@ -20,7 +31,8 @@ class ExternalLinksTest extends SapphireTest {
|
||||||
parent::setUpOnce();
|
parent::setUpOnce();
|
||||||
}
|
}
|
||||||
|
|
||||||
public function setUp() {
|
public function setUp()
|
||||||
|
{
|
||||||
parent::setUp();
|
parent::setUp();
|
||||||
|
|
||||||
// Check dependencies
|
// Check dependencies
|
||||||
|
@ -30,7 +42,7 @@ class ExternalLinksTest extends SapphireTest {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Mock link checker
|
// Mock link checker
|
||||||
$checker = Phockito::mock('LinkChecker');
|
$checker = Phockito::mock(LinkChecker::class);
|
||||||
Phockito::when($checker)
|
Phockito::when($checker)
|
||||||
->checkLink('http://www.working.com')
|
->checkLink('http://www.working.com')
|
||||||
->return(200);
|
->return(200);
|
||||||
|
@ -71,10 +83,11 @@ class ExternalLinksTest extends SapphireTest {
|
||||||
->checkLink(Hamcrest_Matchers::anything()) // anything else is 404
|
->checkLink(Hamcrest_Matchers::anything()) // anything else is 404
|
||||||
->return(404);
|
->return(404);
|
||||||
|
|
||||||
Injector::inst()->registerService($checker, 'LinkChecker');
|
Injector::inst()->registerService($checker, LinkChecker::class);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testLinks() {
|
public function testLinks()
|
||||||
|
{
|
||||||
// Run link checker
|
// Run link checker
|
||||||
$task = CheckExternalLinksTask::create();
|
$task = CheckExternalLinksTask::create();
|
||||||
$task->setSilent(true); // Be quiet during the test!
|
$task->setSilent(true); // Be quiet during the test!
|
||||||
|
@ -87,7 +100,7 @@ class ExternalLinksTest extends SapphireTest {
|
||||||
$this->assertEquals(5, $status->CompletedPages);
|
$this->assertEquals(5, $status->CompletedPages);
|
||||||
|
|
||||||
// Check all pages have had the correct HTML adjusted
|
// Check all pages have had the correct HTML adjusted
|
||||||
for($i = 1; $i <= 5; $i++) {
|
for ($i = 1; $i <= 5; $i++) {
|
||||||
$page = $this->objFromFixture('ExternalLinksTestPage', 'page'.$i);
|
$page = $this->objFromFixture('ExternalLinksTestPage', 'page'.$i);
|
||||||
$this->assertNotEmpty($page->Content);
|
$this->assertNotEmpty($page->Content);
|
||||||
$this->assertEquals(
|
$this->assertEquals(
|
||||||
|
@ -136,13 +149,17 @@ class ExternalLinksTest extends SapphireTest {
|
||||||
/**
|
/**
|
||||||
* Test that broken links appears in the reports list
|
* Test that broken links appears in the reports list
|
||||||
*/
|
*/
|
||||||
public function testReportExists() {
|
public function testReportExists()
|
||||||
$reports = SS_Report::get_reports();
|
{
|
||||||
|
$reports = Report::get_reports();
|
||||||
$reportNames = array();
|
$reportNames = array();
|
||||||
foreach($reports as $report) {
|
foreach ($reports as $report) {
|
||||||
$reportNames[] = $report->class;
|
$reportNames[] = $report->class;
|
||||||
}
|
}
|
||||||
$this->assertContains('BrokenExternalLinksReport',$reportNames,
|
$this->assertContains(
|
||||||
'BrokenExternalLinksReport is in reports list');
|
BrokenExternalLinksReport::class,
|
||||||
|
$reportNames,
|
||||||
|
'BrokenExternalLinksReport is in reports list'
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,7 @@
|
||||||
<?php
|
<?php
|
||||||
|
|
||||||
|
use SilverStripe\Dev\TestOnly;
|
||||||
|
|
||||||
class ExternalLinksTestPage extends Page implements TestOnly
|
class ExternalLinksTestPage extends Page implements TestOnly
|
||||||
{
|
{
|
||||||
private static $db = array(
|
private static $db = array(
|
||||||
|
|
Loading…
Reference in New Issue