Dominic.sauer has submitted this change and it was merged.

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(-)

Approvals:
  Dominic.sauer: Verified; Looks good to me, approved



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: merged
Gerrit-Change-Id: I2e8a4260f8d912ccb8f6a9fdc13ff217239297ac
Gerrit-PatchSet: 1
Gerrit-Project: mediawiki/extensions/WikidataQualityConstraints
Gerrit-Branch: v1
Gerrit-Owner: Jonaskeutel <jonas.keu...@student.hpi.de>
Gerrit-Reviewer: Dominic.sauer <dominic.sa...@yahoo.de>
Gerrit-Reviewer: Siebrand <siebr...@kitano.nl>

_______________________________________________
MediaWiki-commits mailing list
MediaWiki-commits@lists.wikimedia.org
https://lists.wikimedia.org/mailman/listinfo/mediawiki-commits

Reply via email to