Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

NEW Migrate code from other modules #204

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .eslintrc.js
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
module.exports = require('@silverstripe/eslint-config/.eslintrc');
3 changes: 2 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
.sass-cache
.DS_Store
.DS_Store
node_modules
1 change: 1 addition & 0 deletions .nvmrc
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
18
1 change: 1 addition & 0 deletions .stylelintrc.js
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
module.exports = require('@silverstripe/eslint-config/.stylelintrc');
9 changes: 9 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,11 @@ composer require silverstripe/reports
This module contains the API's for building Reports that are displayed in the
Silverstripe backend.

There are also a few CMS reports that comes out of the box:
- A "Users, Groups and Permissions" report allowing administrators to get a quick overview of who has access to the CMS.
- A "Site-wide content report" report allowing CMS users to get a quick overview of content across the site.
- An "External broken links report" allowing users with permissions to track broken external links.

## Troubleshooting

The reports section will not show up in the CMS if:
Expand All @@ -31,6 +36,10 @@ SilverStripe\Reports\Report:
```
Note that some reports may have overridden the `getCount` method, and for those reports this may not apply.

## Included reports

This module comes with a few customisable reports out of the box. Details on how to customise these reports can be found in the [documentation] section(./docs/en/index.md).

## Links ##

* [License](./LICENSE)
9 changes: 9 additions & 0 deletions _config/externallinks.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
---
Name: reports-externallinksdependencies
---
SilverStripe\Core\Injector\Injector:
SilverStripe\Reports\ExternalLinks\Tasks\LinkChecker: SilverStripe\Reports\ExternalLinks\Tasks\CurlLinkChecker
Psr\SimpleCache\CacheInterface.CurlLinkChecker:
factory: SilverStripe\Core\Cache\CacheFactory
constructor:
namespace: 'curllinkchecker'
6 changes: 6 additions & 0 deletions _config/securityreport.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
---
Name: reports-securityreportconfig
---
SilverStripe\Security\Member:
extensions:
- SilverStripe\Reports\SecurityReport\Extensions\MemberReportExtension
8 changes: 8 additions & 0 deletions _config/sitewidecontent-report.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
---
Name: reports-sitewidecontenttaxonomy
Only:
moduleexists: silverstripe/taxonomy
---
SilverStripe\Reports\SitewideContentReport\Reports\SitewideContentReport:
extensions:
- SilverStripe\Reports\SiteWideContentReport\Extensions\SitewideContentTaxonomy
6 changes: 6 additions & 0 deletions babel.config.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
{
"presets": [
"@babel/preset-env",
"@babel/preset-react"
]
}
29 changes: 29 additions & 0 deletions behat.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
default:
suites:
reports:
paths:
- "%paths.modules.reports%/tests/behat/features"
contexts:
- SilverStripe\Admin\Tests\Behat\Context\AdminContext
- SilverStripe\BehatExtension\Context\BasicContext
- SilverStripe\BehatExtension\Context\EmailContext
- SilverStripe\BehatExtension\Context\LoginContext
- SilverStripe\Framework\Tests\Behaviour\CmsFormsContext
- SilverStripe\Framework\Tests\Behaviour\CmsUiContext
- SilverStripe\Reports\Tests\Behat\Context\FeatureContext
- SilverStripe\Reports\Tests\Behat\Context\FixtureContext
-
SilverStripe\Reports\Tests\Behat\Context\FixtureContext:
- "%paths.modules.reports%/tests/behat/files/"

extensions:
SilverStripe\BehatExtension\MinkExtension:
default_session: facebook_web_driver
javascript_session: facebook_web_driver
facebook_web_driver:
browser: chrome
wd_host: "http://127.0.0.1:9515"

SilverStripe\BehatExtension\Extension:
screenshot_path: "%paths.base%/artifacts/screenshots"
bootstrap_file: vendor/silverstripe/framework/tests/behat/serve-bootstrap.php
1 change: 1 addition & 0 deletions client/dist/js/BrokenExternalLinksReport.js

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions client/dist/js/ReportAdmin.Tree.js

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions client/dist/js/ReportAdmin.js

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions client/dist/styles/BrokenExternalLinksReport.css
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
.external-links-report__create-report,.external-links-report__report-progress{margin-top:20px}
1 change: 1 addition & 0 deletions client/dist/styles/sitewidecontentreport.css
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
.gridfieldbasiccontentreport.field{border-bottom:1px solid #d2d5d8;margin:24px 0}.gridfieldbasiccontentreport.field:last-of-type{border-bottom-style:none}
148 changes: 148 additions & 0 deletions client/src/js/BrokenExternalLinksReport.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,148 @@
/* global jQuery */
(function ($) {
// eslint-disable-next-line no-shadow
$.entwine('ss', ($) => {
$('.external-links-report__create-report').entwine({
PollTimeout: null,
ButtonIsLoading: false,
ReloadContent: false,

onclick(e) {
e.preventDefault();

this.buttonLoading();
this.start();
},

onmatch() {
// poll the current job and update the front end status
this.poll();
},

start() {
const self = this;
// initiate a new job
$('.external-links-report__report-progress')
.empty()
.text('Running report 0%');

$.ajax({
url: 'admin/externallinks/start',
async: true,
timeout: 3000,
success() {
self.setReloadContent(true);
self.poll();
},
error() {
self.buttonReset();
}
});
},

/**
* Get the "create report" button selector
*
* @return {Object}
*/
getButton() {
return $('.external-links-report__create-report');
},

/**
* Sets the button into a loading state. See LeftAndMain.js.
*/
buttonLoading() {
if (this.getButtonIsLoading()) {
return;
}
this.setButtonIsLoading(true);

const $button = this.getButton();

// set button to "submitting" state
$button.addClass('btn--loading loading');
$button.attr('disabled', true);

if ($button.is('button')) {
$button.append($(
'<div class="btn__loading-icon">' +
'<span class="btn__circle btn__circle--1" />' +
'<span class="btn__circle btn__circle--2" />' +
'<span class="btn__circle btn__circle--3" />' +
'</div>'));

$button.css(`${$button.outerWidth()}px`);
}
},

/**
* Reset the button back to its original state after loading. See LeftAndMain.js.
*/
buttonReset() {
this.setButtonIsLoading(false);

const $button = this.getButton();

$button.removeClass('btn--loading loading');
$button.attr('disabled', false);
$button.find('.btn__loading-icon').remove();
$button.css('width', 'auto');
},

poll() {
const self = this;
this.buttonLoading();

$.ajax({
url: 'admin/externallinks/getJobStatus',
async: true,
success(data) {
// No report, so let user create one
if (!data || (typeof data === 'object' && data.length < 1)) {
self.buttonReset();
return;
}

// Parse data
const completed = data.Completed ? data.Completed : 0;
const total = data.Total ? data.Total : 0;

// If complete status
if (data.Status === 'Completed') {
if (self.getReloadContent()) {
$('.cms-container').loadPanel(document.location.href, null, {}, true, false);
self.setReloadContent(false);
}
$('.external-links-report__report-progress')
.text(`Report finished ${completed}/${total}`);

self.buttonReset();
return;
}

// If incomplete update status
if (completed < total) {
const percent = (completed / total) * 100;
$('.external-links-report__report-progress')
.text(`Running report ${completed}/${total} (${percent.toFixed(2)}%)`);
}

// Ensure the regular poll method is run
// kill any existing timeout
if (self.getPollTimeout() !== null) {
clearTimeout(self.getPollTimeout());
}

self.setPollTimeout(setTimeout(() => {
$('.external-links-report__create-report').poll();
}, 1000));
},
error() {
self.buttonReset();
}
});
}
});
});
}(jQuery));
File renamed without changes.
File renamed without changes.
4 changes: 4 additions & 0 deletions client/src/styles/BrokenExternalLinksReport.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
.external-links-report__create-report,
.external-links-report__report-progress {
margin-top: 20px;
}
8 changes: 8 additions & 0 deletions client/src/styles/sitewidecontentreport.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
.gridfieldbasiccontentreport.field {
border-bottom: 1px solid #D2D5D8;
margin: 24px 0;
}

.gridfieldbasiccontentreport.field:last-of-type {
border-bottom-style: none;
}
67 changes: 67 additions & 0 deletions code/ExternalLinks/Controllers/CMSExternalLinksController.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
<?php

namespace SilverStripe\Reports\ExternalLinks\Controllers;

use SilverStripe\Admin\AdminController;
use SilverStripe\Control\HTTPResponse;
use SilverStripe\Reports\ExternalLinks\Model\BrokenExternalPageTrackStatus;
use SilverStripe\Reports\ExternalLinks\Jobs\CheckExternalLinksJob;
use SilverStripe\Reports\ExternalLinks\Tasks\CheckExternalLinksTask;
use Symbiote\QueuedJobs\Services\QueuedJobService;
use SilverStripe\PolyExecution\PolyOutput;

class CMSExternalLinksController extends AdminController
{
private static ?string $url_segment = 'externallinks';

private static string|array $required_permission_codes = [
'CMS_ACCESS_CMSMain',
];

private static $allowed_actions = [
'getJobStatus',
'start'
];

/**
* Respond to Ajax requests for info on a running job
*/
public function getJobStatus(): HTTPResponse
{
$this->getResponse()->addHeader('X-Content-Type-Options', 'nosniff');
$track = BrokenExternalPageTrackStatus::get_latest();
if ($track) {
return $this->jsonSuccess(200, [
'TrackID' => $track->ID,
'Status' => $track->Status,
'Completed' => $track->getCompletedPages(),
'Total' => $track->getTotalPages()
]);
}
return $this->jsonSuccess(200, []);
}

/**
* Starts a broken external link check
*/
public function start(): HTTPResponse
{
// return if the a job is already running
$status = BrokenExternalPageTrackStatus::get_latest();
if ($status && $status->Status == 'Running') {
return $this->jsonSuccess(200, []);
}

// Create a new job
if (class_exists(QueuedJobService::class)) {
// Force the creation of a new run
BrokenExternalPageTrackStatus::create_status();
$checkLinks = new CheckExternalLinksJob();
singleton(QueuedJobService::class)->queueJob($checkLinks);
} else {
$task = CheckExternalLinksTask::create();
$task->runLinksCheck(PolyOutput::create(PolyOutput::FORMAT_HTML));
}
return $this->jsonSuccess(200, []);
}
}
46 changes: 46 additions & 0 deletions code/ExternalLinks/Jobs/CheckExternalLinksJob.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
<?php

namespace SilverStripe\Reports\ExternalLinks\Jobs;

use Symbiote\QueuedJobs\Services\AbstractQueuedJob;
use Symbiote\QueuedJobs\Services\QueuedJob;
use SilverStripe\Reports\ExternalLinks\Tasks\CheckExternalLinksTask;
use SilverStripe\PolyExecution\PolyOutput;

if (!class_exists(AbstractQueuedJob::class)) {
return;
}

/**
* A Job for running a external link check for published pages
*
*/
class CheckExternalLinksJob extends AbstractQueuedJob implements QueuedJob
{
public function getTitle()
{
return _t(__CLASS__ . '.TITLE', 'Checking for external broken links');
}

public function getJobType()
{
return QueuedJob::QUEUED;
}

public function getSignature()
{
return md5(get_class($this));
}

/**
* Check an individual page
*/
public function process()
{
$task = CheckExternalLinksTask::create();
$track = $task->runLinksCheck(PolyOutput::create(PolyOutput::FORMAT_ANSI), 1);
$this->currentStep = $track->CompletedPages;
$this->totalSteps = $track->TotalPages;
$this->isComplete = $track->Status === 'Completed';
}
}
Loading