Jonaskeutel has uploaded a new change for review.
https://gerrit.wikimedia.org/r/209213
Change subject: special page now has explanation text
......................................................................
special page now has explanation text
the layout is work in progress and will be finalized after Lydia saw it
Change-Id: I2e8a4260f8d912ccb8f6a9fdc13ff217239297ac
---
M WikidataQualityConstraints.php
M i18n/en.json
M i18n/qqq.json
A modules/SpecialConstraintReportPage.css
M specials/SpecialConstraintReport.php
5 files changed, 525 insertions(+), 8 deletions(-)
git pull
ssh://gerrit.wikimedia.org:29418/mediawiki/extensions/WikidataQualityConstraints
refs/changes/13/209213/1
diff --git a/WikidataQualityConstraints.php b/WikidataQualityConstraints.php
index d7e1095..e7ccc58 100644
--- a/WikidataQualityConstraints.php
+++ b/WikidataQualityConstraints.php
@@ -37,6 +37,13 @@
// Initialize special pages
$GLOBALS['wgSpecialPages']['ConstraintReport'] =
'WikidataQuality\ConstraintReport\Specials\SpecialConstraintReport';
+ // Define modules
+ $GLOBALS['wgResourceModules']['SpecialConstraintReportPage'] = array (
+ 'styles' => '/modules/SpecialConstraintReportPage.css',
+ 'localBasePath' => __DIR__,
+ 'remoteExtPath' => 'WikidataQualityConstraints'
+ );
+
// Define database table names
define( 'CONSTRAINT_TABLE', 'wdqa_constraints' );
diff --git a/i18n/en.json b/i18n/en.json
index e33e49e..c14e30a 100644
--- a/i18n/en.json
+++ b/i18n/en.json
@@ -3,6 +3,9 @@
"authors": "BP2014N1"
},
"wikidataquality-constraintreport": "Constraint report",
+ "wikidataquality-constraintreport-explanation-part-one": "This special page
performs constraints checks on every entity you want. The entities are fetched
from the live system so every constraint violation you fix there will be
instantly removed from this list.",
+ "wikidataquality-constraintreport-explanation-part-two": "The constraints
are parsed from the property talk pages once a week, so if you
add/delete/modify a constraint it might take up to a week until this gets taken
into account by this constraint report. There is currently work in progress to
migrate the constraints to statements on properties, enabling this special page
to do live-checks.",
+ "wikidataquality-constraintreport-explanation-heading": "Explanation",
"wikidataquality-constraintreport-instructions": "Enter an entity ID and let
it be checked against the constraints.",
"wikidataquality-constraintreport-instructions-example": "Try any ID,
ideally an item ID, and look at the results.",
"wikidataquality-constraintreport-empty-result": "There are no constraint
defined on this entity.",
diff --git a/i18n/qqq.json b/i18n/qqq.json
index 66c31a0..73ee18a 100644
--- a/i18n/qqq.json
+++ b/i18n/qqq.json
@@ -8,6 +8,9 @@
]
},
"wikidataquality-constraintreport": "{{doc-special|ConstraintReport}}",
+ "wikidataquality-constraintreport-explanation-heading": "Heading for
the explanation box.",
+ "wikidataquality-constraintreport-explanation-part-one": "Explanation
(part one), what this special page does and how it gets its data.",
+ "wikidataquality-constraintreport-explanation-part-one": "Explanation
(part two), what this special page does and how it gets its data.",
"wikidataquality-constraintreport-instructions": "Instructions for
starting a check against the constraints.",
"wikidataquality-constraintreport-instructions-example": "Example, what
to enter to get good results for constraint reports.",
"wikidataquality-constraintreport-empty-result": "Message that appears,
when there are no constraints defined for the given entity.",
diff --git a/modules/SpecialConstraintReportPage.css
b/modules/SpecialConstraintReportPage.css
new file mode 100644
index 0000000..b244643
--- /dev/null
+++ b/modules/SpecialConstraintReportPage.css
@@ -0,0 +1,93 @@
+/* Entity id form */
+.wbq-checkresult-form {
+ margin-top: 10px;
+ margin-bottom: 20px;
+}
+
+.wbq-checkresult-form-entity-id {
+ width: 120px;
+}
+
+.wbq-checkresult-form-submit {
+ margin-left: 5px;
+}
+
+/* Notices */
+.wbq-checkresult-notice {
+ font-style: italic;
+}
+
+.wbq-checkresult-notice-error {
+ font-weight: bold;
+ color: #BA0000;
+}
+
+/* Explanation infobox */
+.wbq-explanation {
+ width: 25%;
+ float: right;
+ padding: 1em;
+ padding-top: 0;
+ border: 1px solid black;
+ margin-left: 1em;
+}
+
+/* Statuses */
+.wbq-status {
+ font-weight: bold;
+}
+
+.wbq-status-success {
+ color: #008000;
+}
+
+.wbq-status-partial-success {
+ color: #6CB500;
+}
+
+.wbq-status-warning {
+ color: #E6B800;
+}
+
+.wbq-status-error {
+ color: #BA0000;
+}
+
+.wbq-status-unknown {
+ color: #404040;
+}
+
+/* Tooltip */
+.wbq-tooltip-indicator:before {
+ content: '[?]';
+ color: #CCC;
+ font-weight: 600;
+}
+
+[tooltip]:before {
+ /* needed - do not touch */
+ content: attr(tooltip);
+ position: absolute;
+ opacity: 0;
+
+ /* customizable */
+ transition: all 0.20s ease;
+ padding: 5px;
+ border: 1px solid #AAA;
+ border-radius: 5px;
+ box-shadow: 2px 2px 1px #CCC;
+}
+
+[tooltip]:hover:before {
+ /* needed - do not touch */
+ opacity: 1;
+
+ /* customizable */
+ background: #F2F2F2;
+ margin-top: -35px;
+ margin-left: -5px;
+}
+
+[tooltip]:not([tooltip-persistent]):before {
+ pointer-events: none;
+}
\ No newline at end of file
diff --git a/specials/SpecialConstraintReport.php
b/specials/SpecialConstraintReport.php
index 1c81115..bea69ea 100755
--- a/specials/SpecialConstraintReport.php
+++ b/specials/SpecialConstraintReport.php
@@ -2,23 +2,37 @@
namespace WikidataQuality\ConstraintReport\Specials;
+use SpecialPage;
+use ValueFormatters\FormatterOptions;
+use Wikibase\Lib\EntityIdHtmlLinkFormatter;
+use Wikibase\Lib\EntityIdLabelFormatter;
+use Wikibase\Lib\EntityIdLinkFormatter;
+use Wikibase\Lib\HtmlUrlFormatter;
+use Wikibase\Lib\LanguageNameLookup;
+use Wikibase\Lib\SnakFormatter;
+use Wikibase\Lib\Store\LanguageLabelDescriptionLookup;
+use Wikibase\Repo\WikibaseRepo;
use DataValues;
use DataValues\DataValue;
use Html;
-use Wikibase\DataModel;
+use Doctrine\Instantiator\Exception\InvalidArgumentException;
+use Doctrine\Instantiator\Exception\UnexpectedValueException;
use Wikibase\DataModel\Entity\Entity;
use Wikibase\DataModel\Entity\EntityId;
+use Wikibase\DataModel\Entity\EntityIdParsingException;
+use Traversable;
+use Countable;
+use Wikibase\DataModel\Entity\EntityIdValue;
+use Wikibase\DataModel;
use Wikibase\DataModel\Entity\ItemId;
use Wikibase\DataModel\Entity\PropertyId;
use Wikibase\Lib\Store\EntityTitleLookup;
-use Wikibase\Repo\WikibaseRepo;
use WikidataQuality\ConstraintReport\CheckForConstraintViolationsJob;
use WikidataQuality\ConstraintReport\ConstraintCheck\CheckerMapBuilder;
use
WikidataQuality\ConstraintReport\ConstraintCheck\DelegatingConstraintChecker;
use
WikidataQuality\ConstraintReport\ConstraintCheck\Helper\ConstraintReportHelper;
use WikidataQuality\Html\HtmlTable;
use WikidataQuality\Html\HtmlTableHeader;
-use WikidataQuality\Specials\SpecialCheckResultPage;
use JobQueueGroup;
@@ -31,7 +45,7 @@
* @author BP2014N1
* @license GNU GPL v2+
*/
-class SpecialConstraintReport extends SpecialCheckResultPage {
+class SpecialConstraintReport extends SpecialPage {
/**
* Maximum number of displayed values for parameters with multiple ones.
@@ -53,10 +67,235 @@
*/
private $entityTitleLookup;
- public function __construct() {
- parent::__construct( 'ConstraintReport' );
+ /**
+ * @var \Wikibase\DataModel\Entity\EntityIdParser
+ */
+ protected $entityIdParser;
+
+ /**
+ * @var \Wikibase\Lib\Store\EntityLookup
+ */
+ protected $entityLookup;
+
+ /**
+ * @var \ValueFormatters\ValueFormatter
+ */
+ protected $dataValueFormatter;
+
+ /**
+ * @var EntityIdLabelFormatter
+ */
+ protected $entityIdLabelFormatter;
+
+ /**
+ * @var EntityIdLinkFormatter
+ */
+ protected $entityIdLinkFormatter;
+
+ /**
+ * @var EntityIdHtmlLinkFormatter
+ */
+ protected $entityIdHtmlLinkFormatter;
+
+ /**
+ * @var HtmlUrlFormatter
+ */
+ protected $htmlUrlFormatter;
+
+ /**
+ * @param string $name
+ * @param string $restriction
+ * @param bool $listed
+ * @param bool $function
+ * @param string $file
+ * @param bool $includable
+ */
+ public function __construct( $name = 'ConstraintReport', $restriction =
'', $listed = true, $function = false, $file = '', $includable = false ) {
+ parent::__construct( $name, $restriction, $listed, $function,
$file, $includable );
+
+ $repo = WikibaseRepo::getDefaultInstance();
+
+ // Get entity lookup
+ $this->entityLookup = $repo->getEntityLookup();
+
+ // Get entity id parser
+ $this->entityIdParser = $repo->getEntityIdParser();
+
+ // Get value formatter
+ $formatterOptions = new FormatterOptions();
+ $formatterOptions->setOption( SnakFormatter::OPT_LANG,
$this->getLanguage()->getCode() );
+ $this->dataValueFormatter =
$repo->getValueFormatterFactory()->getValueFormatter(
SnakFormatter::FORMAT_HTML, $formatterOptions );
+
+ // Get entity id link formatters
+ $entityTitleLookup = $repo->getEntityTitleLookup();
+ $labelLookup = new LanguageLabelDescriptionLookup(
$repo->getTermLookup(), $this->getLanguage()->getCode() );
+ $this->entityIdLabelFormatter = new EntityIdLabelFormatter(
$labelLookup );
+ $this->entityIdLinkFormatter = new EntityIdLinkFormatter(
$entityTitleLookup );
+ $this->entityIdHtmlLinkFormatter = new
EntityIdHtmlLinkFormatter(
+ $labelLookup,
+ $entityTitleLookup,
+ new LanguageNameLookup()
+ );
+
+ // Get url formatter
+ $formatterOptions = new FormatterOptions();
+ $this->htmlUrlFormatter = new HtmlUrlFormatter(
$formatterOptions );
$this->entityTitleLookup =
WikibaseRepo::getDefaultInstance()->getEntityTitleLookup();
+ }
+
+ /**
+ * @see SpecialPage::execute
+ *
+ * @param string|null $subPage
+ */
+ public function execute( $subPage ) {
+ $out = $this->getOutput();
+
+ $postRequest = $this->getContext()->getRequest()->getVal(
'entityId' );
+ if ( $postRequest ) {
+ $out->redirect( $this->getPageTitle( strtoupper(
$postRequest ) )->getLocalURL() );
+ return;
+ }
+
+ $out->addModules( $this->getModules() );
+
+ $this->setHeaders();
+
+ $out->addHTML(
+ $this->getExplanationText()
+ . $this->getInstructionsText()
+ . $this->buildEntityIdForm()
+ );
+
+ if ( !$subPage ) {
+ return;
+ }
+
+ if ( !is_string( $subPage ) ) {
+ throw new InvalidArgumentException( '$subPage must be
string.' );
+ }
+
+ try {
+ $entityId = $this->entityIdParser->parse( $subPage );
+ $entity = $this->entityLookup->getEntity( $entityId );
+ } catch ( EntityIdParsingException $e ) {
+ $out->addHTML(
+ $this->buildNotice( $this->msg(
'wikidataquality-checkresult-invalid-entity-id' )->text(), true )
+ );
+ return;
+ }
+
+ if ( !$entity ) {
+ $out->addHTML(
+ $this->buildNotice( $this->msg(
'wikidataquality-checkresult-not-existent-entity' )->text(), true )
+ );
+ return;
+ }
+
+ $results = $this->executeCheck( $entity );
+
+ if ( !is_array( $results ) ) {
+ if ( !( $results instanceof Traversable && $results
instanceof Countable ) ) {
+ throw new UnexpectedValueException(
'SpecialCheckResultPage::executeCheck has to return an array or traversable and
countable object.' );
+ }
+ }
+
+ if ( $results && count( $results ) > 0 ) {
+ $out->addHTML(
+ $this->buildResultHeader( $entityId )
+ . $this->buildSummary( $results )
+ . $this->buildResultTable( $entityId, $results )
+ );
+ } else {
+ $out->addHTML(
+ $this->buildResultHeader( $entityId )
+ . $this->buildNotice(
$this->getEmptyResultText() )
+ );
+ }
+ }
+
+ /**
+ * Returns html text of the entity id form
+ *
+ * @return string
+ */
+ protected function buildEntityIdForm() {
+ return
+ Html::openElement(
+ 'form',
+ array (
+ 'class' => 'wbq-checkresult-form',
+ 'action' =>
$this->getPageTitle()->getLocalURL(),
+ 'method' => 'post'
+ )
+ )
+ . Html::input(
+ 'entityId',
+ '',
+ 'text',
+ array (
+ 'class' =>
'wbq-checkresult-form-entity-id',
+ 'placeholder' => $this->msg(
'wikidataquality-checkresult-form-entityid-placeholder' )->text()
+ )
+ )
+ . Html::input(
+ 'submit',
+ $this->msg(
'wikidataquality-checkresult-form-submit-label' )->text(),
+ 'submit',
+ array (
+ 'class' => 'wbq-checkresult-form-submit'
+ )
+ )
+ . Html::closeElement( 'form' );
+ }
+
+ /**
+ * Builds notice with given message. Optionally notice can be handles
as error by settings $error to true
+ *
+ * @param string $message
+ * @param bool $error
+ *
+ * @return string
+ */
+ protected function buildNotice( $message, $error = false ) {
+ if ( !is_string( $message ) ) {
+ throw new InvalidArgumentException( '$message must be
string.' );
+ }
+ if ( !is_bool( $error ) ) {
+ throw new InvalidArgumentException( '$error must be
bool.' );
+ }
+
+ $cssClasses = 'wbq-checkresult-notice';
+ if ( $error ) {
+ $cssClasses .= ' wbq-checkresult-notice-error';
+ }
+
+ return
+ Html::element(
+ 'p',
+ array (
+ 'class' => $cssClasses
+ ),
+ $message );
+ }
+
+ /**
+ * Returns array of modules that should be added
+ *
+ * @return array
+ */
+ protected function getModules() {
+ return array ( 'SpecialConstraintReportPage' );
+ }
+
+ /**
+ * @see SpecialPage::getGroupName
+ *
+ * @return string
+ */
+ function getGroupName() {
+ return 'wikidataquality';
}
/**
@@ -69,8 +308,6 @@
}
/**
- * @see SpecialCheckResultPage::getInstructionsText
- *
* @return string
*/
protected function getInstructionsText() {
@@ -78,6 +315,19 @@
$this->msg(
'wikidataquality-constraintreport-instructions' )->text()
. Html::element( 'br' )
. $this->msg(
'wikidataquality-constraintreport-instructions-example' )->text();
+ }
+
+ protected function getExplanationText() {
+ return
+ Html::openElement( 'div', array( 'class' =>
'wbq-explanation') )
+ . Html::openElement( 'h2' )
+ . $this->msg(
'wikidataquality-constraintreport-explanation-heading' )
+ . Html::closeElement( 'h2' )
+ . $this->msg(
'wikidataquality-constraintreport-explanation-part-one' )
+ . Html::element( 'br' )
+ . Html::element( 'br' )
+ . $this->msg(
'wikidataquality-constraintreport-explanation-part-two' )
+ . Html::closeElement( 'div' );
}
/**
@@ -184,6 +434,167 @@
}
/**
+ * Returns html text of the result header
+ *
+ * @param EntityId $entityId
+ *
+ * @return string
+ */
+ protected function buildResultHeader( EntityId $entityId ) {
+ $entityLink = sprintf( '%s (%s)',
+
$this->entityIdHtmlLinkFormatter->formatEntityId( $entityId ),
+
$entityId->getSerialization() );
+
+ return
+ Html::openElement( 'h3', array( 'class' => 'wbq-clear'
) ) //TODO delete if not wished
+ . $this->msg(
'wikidataquality-checkresult-result-headline', $entityLink )->text()
+ . Html::closeElement( 'h3' );
+ }
+
+ /**
+ * Builds summary from given results
+ *
+ * @param array|Traversable $results
+ *
+ * @return string
+ */
+ protected function buildSummary( $results ) {
+ $statuses = array ();
+ foreach ( $results as $result ) {
+ $status = strtolower( $result->getStatus() );
+ if ( array_key_exists( $status, $statuses ) ) {
+ $statuses[ $status ]++;
+ } else {
+ $statuses[ $status ] = 1;
+ }
+ }
+
+ $statusElements = array ();
+ foreach ( $statuses as $status => $count ) {
+ if ( $count > 0 ) {
+ $statusElements[ ] =
+ $this->formatStatus( $status )
+ . ": "
+ . $count;
+ }
+ }
+ $summary =
+ Html::openElement( 'p' )
+ . implode( ', ', $statusElements )
+ . Html::closeElement( 'p' );
+
+ return $summary;
+ }
+
+ /**
+ * Builds a html div element with given content and a tooltip with
given tooltip content
+ * If $tooltipContent is null, no tooltip will be created
+ *
+ * @param string $content
+ * @param string $tooltipContent
+ *
+ * @return string
+ */
+ protected function buildTooltipElement( $content, $tooltipContent ) {
+ if ( !is_string( $content ) ) {
+ throw new InvalidArgumentException( '$content has to be
string.' );
+ }
+ if ( $tooltipContent && ( !is_string( $tooltipContent ) ) ) {
+ throw new InvalidArgumentException( '$tooltipContent,
if provided, has to be string.' );
+ }
+
+ if ( empty( $tooltipContent ) ) {
+ return $content;
+ }
+
+ $tooltipIndicator = Html::element(
+ 'span',
+ array (
+ 'class' => 'wbq-tooltip-indicator'
+ )
+ );
+
+ return
+ Html::openElement(
+ 'span',
+ array (
+ 'tooltip' => $tooltipContent
+ )
+ )
+ . sprintf( '%s %s', $content, $tooltipIndicator )
+ . Html::closeElement( 'span' );
+ }
+
+ /**
+ * Formats given status to html
+ *
+ * @param string $status
+ *
+ * @return string
+ */
+ protected function formatStatus( $status ) {
+ if ( !is_string( $status ) ) {
+ throw new InvalidArgumentException( '$status has to be
string.' );
+ }
+
+ $messageName = "wikidataquality-checkresult-status-" .
strtolower( $status );
+ $message = $this->msg( $messageName )->text();
+
+ $statusMapping = $this->getStatusMapping();
+ if ( array_key_exists( $status, $statusMapping ) ) {
+ $genericStatus = $statusMapping[ $status ];
+ } else {
+ $genericStatus = 'unknown';
+ }
+
+ $formattedStatus =
+ Html::element(
+ 'span',
+ array (
+ 'class' => 'wbq-status wbq-status-' .
$genericStatus
+ ),
+ $message
+ );
+
+ return $formattedStatus;
+ }
+
+ /**
+ * Parses data values to human-readable string
+ *
+ * @param DataValue|array $dataValues
+ * @param bool $linking
+ * @param string $separator
+ *
+ * @return string
+ */
+ protected function formatDataValues( $dataValues, $linking = true,
$separator = ', ' ) {
+ if ( $dataValues instanceof DataValue ) {
+ $dataValues = array ( $dataValues );
+ } elseif ( !is_array( $dataValues ) ) {
+ throw new InvalidArgumentException( '$dataValues has to
be instance of DataValue or an array of DataValues.' );
+ }
+
+ $formattedDataValues = array ();
+ foreach ( $dataValues as $dataValue ) {
+ if ( !( $dataValue instanceof DataValue ) ) {
+ throw new InvalidArgumentException(
'$dataValues has to be instance of DataValue or an array of DataValues.' );
+ }
+ if ( $dataValue instanceof EntityIdValue ) {
+ if ( $linking ) {
+ $formattedDataValues[ ] =
$this->entityIdHtmlLinkFormatter->formatEntityId( $dataValue->getEntityId() );
+ } else {
+ $formattedDataValues[ ] =
$this->entityIdLabelFormatter->formatEntityId( $dataValue->getEntityId() );
+ }
+ } else {
+ $formattedDataValues[ ] =
$this->dataValueFormatter->format( $dataValue );
+ }
+ }
+
+ return implode( $separator, $formattedDataValues );
+ }
+
+ /**
* Returns html link to given entity with anchor to specified property.
*
* @param EntityId $entityId
--
To view, visit https://gerrit.wikimedia.org/r/209213
To unsubscribe, visit https://gerrit.wikimedia.org/r/settings
Gerrit-MessageType: newchange
Gerrit-Change-Id: I2e8a4260f8d912ccb8f6a9fdc13ff217239297ac
Gerrit-PatchSet: 1
Gerrit-Project: mediawiki/extensions/WikidataQualityConstraints
Gerrit-Branch: v1
Gerrit-Owner: Jonaskeutel <[email protected]>
_______________________________________________
MediaWiki-commits mailing list
[email protected]
https://lists.wikimedia.org/mailman/listinfo/mediawiki-commits