Dominic.sauer has submitted this change and it was merged.
Change subject: Special pages now contain the lines they used to inherit
......................................................................
Special pages now contain the lines they used to inherit
Change-Id: I601c334f8faaa4651db6f21ba6f1ebaba4eeb27f
---
M WikidataQualityExternalValidation.php
A modules/SpecialExternalValidationPage.css
M specials/SpecialCrossCheck.php
M specials/SpecialExternalDbs.php
4 files changed, 589 insertions(+), 12 deletions(-)
Approvals:
Dominic.sauer: Verified; Looks good to me, approved
diff --git a/WikidataQualityExternalValidation.php
b/WikidataQualityExternalValidation.php
index e9caee9..12b5426 100644
--- a/WikidataQualityExternalValidation.php
+++ b/WikidataQualityExternalValidation.php
@@ -41,6 +41,13 @@
// Define API modules
$GLOBALS['wgAPIModules']['wdqacrosscheck'] =
'WikidataQuality\ExternalValidation\Api\CrossCheck';
+ // Define modules
+ $GLOBALS['wgResourceModules']['SpecialExternalValidationPage'] = array (
+ 'styles' => '/modules/SpecialExternalValidationPage.css',
+ 'localBasePath' => __DIR__,
+ 'remoteExtPath' => 'WikidataQualityExternalValidation'
+ );
+
// Define database table names
define( 'DUMP_DATA_TABLE', 'wdqa_external_data' );
define( 'DUMP_META_TABLE', 'wdqa_dump_information' );
diff --git a/modules/SpecialExternalValidationPage.css
b/modules/SpecialExternalValidationPage.css
new file mode 100644
index 0000000..d628c9c
--- /dev/null
+++ b/modules/SpecialExternalValidationPage.css
@@ -0,0 +1,83 @@
+/* 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;
+}
+
+/* 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/SpecialCrossCheck.php b/specials/SpecialCrossCheck.php
index ccd4ec0..12f6bc5 100755
--- a/specials/SpecialCrossCheck.php
+++ b/specials/SpecialCrossCheck.php
@@ -2,13 +2,28 @@
namespace WikidataQuality\ExternalValidation\Specials;
+use SpecialPage;
+use ValueFormatters\FormatterOptions;
+use Wikibase\Lib\EntityIdLabelFormatter;
+use Wikibase\Lib\EntityIdHtmlLinkFormatter;
+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 Html;
use JobQueueGroup;
use Linker;
use Traversable;
use Wikibase\DataModel\Entity\Entity;
use Wikibase\DataModel\Entity\EntityId;
-use Wikibase\Repo\WikibaseRepo;
+use DataValues\DataValue;
+use Doctrine\Instantiator\Exception\InvalidArgumentException;
+use Doctrine\Instantiator\Exception\UnexpectedValueException;
+use Wikibase\DataModel\Entity\EntityIdParsingException;
+use Countable;
+use Wikibase\DataModel\Entity\EntityIdValue;
use WikidataQuality\ExternalValidation\CheckForCrossCheckViolationsJob;
use
WikidataQuality\ExternalValidation\CrossCheck\Comparer\DataValueComparerFactory;
use WikidataQuality\ExternalValidation\CrossCheck\CrossChecker;
@@ -18,18 +33,88 @@
use
WikidataQuality\ExternalValidation\DumpMetaInformation\DumpMetaInformationLookup;
use WikidataQuality\Html\HtmlTable;
use WikidataQuality\Html\HtmlTableHeader;
-use WikidataQuality\Specials\SpecialCheckResultPage;
-class SpecialCrossCheck extends SpecialCheckResultPage {
+class SpecialCrossCheck extends SpecialPage {
/**
* @var CrossCheckInteractor
*/
private $crossCheckInteractor;
- public function __construct() {
- parent::__construct( 'CrossCheck' );
+ /**
+ * @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 = 'CrossCheck', $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 );
$claimGuidParser =
WikibaseRepo::getDefaultInstance()->getClaimGuidParser();
$dataValueComparerFactory = new DataValueComparerFactory(
$this->entityLookup );
@@ -43,6 +128,25 @@
$claimGuidParser,
$crossChecker
);
+ }
+
+ /**
+ * Returns array of modules that should be added
+ *
+ * @return array
+ */
+ protected function getModules() {
+ return array ( 'SpecialExternalValidationPage' );
+ }
+
+
+ /**
+ * @see SpecialPage::getGroupName
+ *
+ * @return string
+ */
+ function getGroupName() {
+ return 'wikidataquality';
}
/**
@@ -89,6 +193,302 @@
$this->doEvaluation( $entity, $results );
return $results;
+ }
+
+ /**
+ * @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->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 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' )
+ . $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 );
}
/**
@@ -156,14 +556,11 @@
}
protected function doEvaluation( $entity, $results ) {
- //TODO: Push (deferred) job(s) in queue
$checkTimeStamp = wfTimestamp( TS_MW );
$jobs = array();
$jobs[] = CheckForCrossCheckViolationsJob::newInsertNow( $entity,
$checkTimeStamp, $results );
$jobs[] = CheckForCrossCheckViolationsJob::newInsertDeferred( $entity,
$checkTimeStamp, 10 );
- $jobs[0]->run();
- $jobs[1]->run();
JobQueueGroup::singleton()->push( $jobs );
}
}
diff --git a/specials/SpecialExternalDbs.php b/specials/SpecialExternalDbs.php
index 36ab57a..488726a 100755
--- a/specials/SpecialExternalDbs.php
+++ b/specials/SpecialExternalDbs.php
@@ -2,6 +2,17 @@
namespace WikidataQuality\ExternalValidation\Specials;
+
+use SpecialPage;
+use ValueFormatters\FormatterOptions;
+use Wikibase\Lib\EntityIdLabelFormatter;
+use Wikibase\Lib\EntityIdHtmlLinkFormatter;
+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 DateInterval;
use DateTimeZone;
use Html;
@@ -11,14 +22,93 @@
use
WikidataQuality\ExternalValidation\DumpMetaInformation\DumpMetaInformationLookup;
use WikidataQuality\Html\HtmlTable;
use WikidataQuality\Html\HtmlTableCell;
-use WikidataQuality\Specials\SpecialWikidataQualityPage;
use DateTime;
-class SpecialExternalDbs extends SpecialWikidataQualityPage {
+class SpecialExternalDbs extends SpecialPage {
- public function __construct() {
- parent::__construct( 'ExternalDbs' );
+ /**
+ * @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 = 'ExternalDbs', $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 );
+ }
+
+ /**
+ * @see SpecialPage::getGroupName
+ *
+ * @return string
+ */
+ function getGroupName() {
+ return 'wikidataquality';
}
/**
--
To view, visit https://gerrit.wikimedia.org/r/209211
To unsubscribe, visit https://gerrit.wikimedia.org/r/settings
Gerrit-MessageType: merged
Gerrit-Change-Id: I601c334f8faaa4651db6f21ba6f1ebaba4eeb27f
Gerrit-PatchSet: 1
Gerrit-Project: mediawiki/extensions/WikidataQualityExternalValidation
Gerrit-Branch: v1
Gerrit-Owner: Jonaskeutel <[email protected]>
Gerrit-Reviewer: Dominic.sauer <[email protected]>
_______________________________________________
MediaWiki-commits mailing list
[email protected]
https://lists.wikimedia.org/mailman/listinfo/mediawiki-commits