Completed
Push — master ( 3abc67...80e892 )
by mw
207:38 queued 172:37
created

includes/articlepages/SMW_PropertyPage.php (6 issues)

Upgrade to new PHP Analysis Engine

These results are based on our legacy PHP analysis, consider migrating to our new PHP analysis engine instead. Learn more

1
<?php
2
3
use SMW\ApplicationFactory;
4
use SMW\DataValueFactory;
5
use SMW\Localizer;
6
use SMW\RequestOptions;
7
use SMW\StringCondition;
8
use SMW\PropertyRegistry;
9
use SMWDataValue as DataValue;
10
use SMW\DataValues\ValueFormatters\DataValueFormatter;
11
use SMW\DIProperty;
12
use SMW\Content\PropertyPageMessageHtmlBuilder;
13
use SMW\PropertySpecificationReqExaminer;
14
15
/**
16
 * Implementation of MediaWiki's Article that shows additional information on
17
 * property pages. Very similar to CategoryPage, but with different printout
18
 * that also displays values for each subject with the given property.
19
 *
20
 * @ingroup SMW
21
 *
22
 * @author Markus Krötzsch
23
 */
24
class SMWPropertyPage extends SMWOrderedListPage {
0 ignored issues
show
Coding Style Compatibility introduced by
PSR1 recommends that each class must be in a namespace of at least one level to avoid collisions.

You can fix this by adding a namespace to your class:

namespace YourVendor;

class YourClass { }

When choosing a vendor namespace, try to pick something that is not too generic to avoid conflicts with other libraries.

Loading history...
25
26
	/**
27
	 * @see SMWOrderedListPage::initParameters()
28
	 * @note We use a smaller limit here; property pages might become large.
29
	 */
30
	protected function initParameters() {
31
		global $smwgPropertyPagingLimit;
0 ignored issues
show
Compatibility Best Practice introduced by
Use of global functionality is not recommended; it makes your code harder to test, and less reusable.

Instead of relying on global state, we recommend one of these alternatives:

1. Pass all data via parameters

function myFunction($a, $b) {
    // Do something
}

2. Create a class that maintains your state

class MyClass {
    private $a;
    private $b;

    public function __construct($a, $b) {
        $this->a = $a;
        $this->b = $b;
    }

    public function myFunction() {
        // Do something
    }
}
Loading history...
32
		$this->limit = $smwgPropertyPagingLimit;
33
		$this->mProperty = DIProperty::newFromUserLabel( $this->mTitle->getText() );
34
		$this->store = ApplicationFactory::getInstance()->getStore();
35
		$this->propertyValue = DataValueFactory::getInstance()->newDataItemValue( $this->mProperty );
36
		return true;
37
	}
38
39
	/**
40
	 * Returns the HTML which is added to $wgOut after the article text.
41
	 *
42
	 * @return string
43
	 */
44
	protected function getHtml() {
0 ignored issues
show
getHtml uses the super-global variable $GLOBALS which is generally not recommended.

Instead of super-globals, we recommend to explicitly inject the dependencies of your class. This makes your code less dependent on global state and it becomes generally more testable:

// Bad
class Router
{
    public function generate($path)
    {
        return $_SERVER['HOST'].$path;
    }
}

// Better
class Router
{
    private $host;

    public function __construct($host)
    {
        $this->host = $host;
    }

    public function generate($path)
    {
        return $this->host.$path;
    }
}

class Controller
{
    public function myAction(Request $request)
    {
        // Instead of
        $page = isset($_GET['page']) ? intval($_GET['page']) : 1;

        // Better (assuming you use the Symfony2 request)
        $page = $request->query->get('page', 1);
    }
}
Loading history...
45
46
		if ( !$this->store->getRedirectTarget( $this->mProperty )->equals( $this->mProperty ) ) {
47
			return '';
48
		}
49
50
		$dv = DataValueFactory::getInstance()->newDataValueByItem(
51
			$this->mProperty
52
		);
53
54
		$title = $dv->getFormattedLabel( DataValueFormatter::WIKI_LONG );
55
		$this->getContext()->getOutput()->setPageTitle( $title );
56
57
		$requestOptions = new RequestOptions();
58
		$requestOptions->sort = true;
59
		$requestOptions->ascending = true;
60
61
		// +1 look ahead
62
		$requestOptions->setLimit( $GLOBALS['smwgRedirectPropertyListLimit'] + 1 );
63
64
		$list = $this->getPropertyList(
65
			new DIProperty( '_REDI' ),
66
			$requestOptions,
67
			$GLOBALS['smwgRedirectPropertyListLimit'],
68
			'smw-propertylist-redirect'
69
		);
70
71
		$requestOptions->setLimit( $GLOBALS['smwgSubPropertyListLimit'] + 1 );
72
73
		$list .= $this->getPropertyList(
74
			new DIProperty( '_SUBP' ),
75
			$requestOptions,
76
			$GLOBALS['smwgSubPropertyListLimit'],
77
			'smw-propertylist-subproperty'
78
		);
79
80
		$list .= $this->getPropertyValueList();
81
82
		$result = ( $list !== '' ? Html::element( 'div', array( 'id' => 'smwfootbr' ) ) . $list : '' );
83
84
		return $result;
85
	}
86
87
	/**
88
	 * @since 1.9
89
	 *
90
	 * @return string
91
	 */
92
	protected function getIntroductoryText() {
93
94
		$propertySpecificationReqExaminer = new PropertySpecificationReqExaminer(
95
			$this->store
96
		);
97
98
		$propertySpecificationReqExaminer->setEditProtectionRight(
99
			ApplicationFactory::getInstance()->getSettings()->get( 'smwgEditProtectionRight' )
100
		);
101
102
		$propertyPageMessageHtmlBuilder = new PropertyPageMessageHtmlBuilder(
103
			$this->store,
104
			$propertySpecificationReqExaminer
105
		);
106
107
		$propertyPageMessageHtmlBuilder->hasEditProtection(
108
			ApplicationFactory::getInstance()->singleton( 'EditProtectionValidator' )->hasEditProtection( $this->mTitle )
109
		);
110
111
		return $propertyPageMessageHtmlBuilder->createMessageBody( $this->mProperty );
112
	}
113
114
	protected function getTopIndicators() {
115
116
		$propertyName = htmlspecialchars( $this->mTitle->getText() );
117
		$usageCountHtml = '';
118
119
		$requestOptions = new RequestOptions();
120
		$requestOptions->setLimit( 1 );
121
		$requestOptions->addStringCondition( $propertyName, StringCondition::COND_EQ );
122
123
		$cachedLookupList = $this->store->getPropertiesSpecial( $requestOptions );
124
		$usageList = $cachedLookupList->fetchList();
125
126
		if ( $usageList && $usageList !== array() ) {
127
			$usage = end( $usageList );
128
			$usageCount = $usage[1];
129
			$usageCountHtml = Html::rawElement(
130
				'div', array(
131
					'title' => wfMessage( 'smw-property-indicator-last-count-update', $this->getContext()->getLanguage()->timeanddate( $cachedLookupList->getTimestamp() ) )->text(),
132
					'class' => 'smw-property-page-indicator usage-count' . ( $usageCount < 25000 ? ( $usageCount > 5000 ? ' moderate' : '' ) : ' high' ) ),
133
				$usageCount
134
			);
135
		}
136
137
		$type = Html::rawElement(
138
				'div',
139
				array(
140
					'class' => 'smw-property-page-indicator property-type',
141
					'title' => wfMessage( 'smw-property-indicator-type-info', $this->mProperty->isUserDefined() )->parse()
142
			), ( $this->mProperty->isUserDefined() ? 'U' : 'S' )
143
		);
144
145
		return array(
146
			'smw-prop-count' => $usageCountHtml,
147
			'smw-prop-type' => $type
148
		);
149
	}
150
151
	/**
152
	 * Get the HTML for displaying subproperties of this property. This list
153
	 * is usually short and we implement no additional navigation.
154
	 *
155
	 * @return string
156
	 */
157
	protected function getPropertyList( $property, $requestOptions, $listLimit, $header ) {
158
159
		$propertyList =  $this->store->getPropertySubjects(
160
			$property,
161
			$this->getDataItem(),
162
			$requestOptions
163
		);
164
165
		$more = false;
166
167
		// Pop the +1 look ahead from the list
168
		if ( count( $propertyList ) > $listLimit ) {
169
			array_pop( $propertyList );
170
			$more = true;
171
		}
172
173
		$result = '';
174
		$resultCount = count( $propertyList );
175
176
		if ( $more ) {
177
			$message = Html::rawElement(
178
				'span',
179
				array( 'class' => 'plainlinks' ),
180
				wfMessage( 'smw-propertylist-count-with-restricted-note', $resultCount, $listLimit )->parse()
181
			);
182
		} else {
183
			$message = wfMessage( 'smw-propertylist-count', $resultCount )->text();
184
		}
185
186
		if ( $resultCount > 0 ) {
187
			$titleText = htmlspecialchars( $this->mTitle->getText() );
188
			$result .= "<div id=\"{$header}\">" . Html::rawElement( 'h2' , array(), wfMessage( $header . '-header', $titleText )->text() ) . "\n<p>";
189
190
			if ( !$this->mProperty->isUserDefined() ) {
191
				$result .= wfMessage( 'smw_isspecprop' )->text() . ' ';
192
			}
193
194
			$result .= $message . "</p>";
195
196
			if ( $resultCount < 6 ) {
197
				$result .= SMWPageLister::getShortList( 0, $resultCount, $propertyList, null );
198
			} else {
199
				$result .= SMWPageLister::getColumnList( 0, $resultCount, $propertyList, null );
200
			}
201
202
			$result .= "\n</div>";
203
		}
204
205
		return $result;
206
	}
207
208
	/**
209
	 * Get the HTML for displaying values of this property, based on the
210
	 * current from/until and limit settings.
211
	 *
212
	 * @return string
213
	 */
214
	protected function getPropertyValueList() {
215
		global $smwgPropertyPagingLimit, $wgRequest;
216
217
		 // limit==0: configuration setting to disable this completely
218
		if ( $this->limit < 1 ) {
219
			return '';
220
		}
221
222
		$diWikiPages = array();
223
		$options = SMWPageLister::getRequestOptions( $this->limit, $this->from, $this->until );
224
225
		$options->limit = $wgRequest->getVal( 'limit', $smwgPropertyPagingLimit );
226
		$options->offset = $wgRequest->getVal( 'offset', '0' );
227
228
		if ( ( $value = $wgRequest->getVal( 'value', '' ) ) !== '' ) {
229
			$diWikiPages = $this->doQuerySubjectListWithValue( $value, $options );
230
		} else {
231
			$diWikiPages = $this->store->getAllPropertySubjects( $this->mProperty, $options );
232
		}
233
234
		if ( !$options->ascending ) {
235
			$diWikiPages = array_reverse( $diWikiPages );
236
		}
237
238
		$result = '';
239
240
		if ( count( $diWikiPages ) > 0 ) {
241
			$pageLister = new SMWPageLister( $diWikiPages, null, $this->limit, $this->from, $this->until );
242
243
			$this->mTitle->setFragment( '#SMWResults' ); // Make navigation point to the result list.
244
			$navigation = $pageLister->getNavigationLinks( $this->mTitle );
0 ignored issues
show
$navigation is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
245
246
			$dvWikiPage = DataValueFactory::getInstance()->newDataValueByItem(
247
				$this->mProperty
248
			);
249
250
			// Allow the DV formatter to access a specific language code
251
			$dvWikiPage->setOption(
252
				DataValue::OPT_USER_LANGUAGE,
253
				Localizer::getInstance()->getUserLanguage()->getCode()
254
			);
255
256
			$titleText = htmlspecialchars( $dvWikiPage->getWikiValue() );
257
			$resultNumber = min( $this->limit, count( $diWikiPages ) );
0 ignored issues
show
$resultNumber is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
258
259
			$result .= "<a name=\"SMWResults\"></a><div id=\"mw-pages\">\n" .
260
			           '<h2>' . wfMessage( 'smw_attribute_header', $titleText )->text() . "</h2>\n<p>";
261
262
			$result .= $this->getNavigationLinks( 'smw_attributearticlecount', $diWikiPages, $smwgPropertyPagingLimit ) .
263
			           $this->subjectObjectList( $diWikiPages ) . "\n</div>";
264
		}
265
266
		return $result;
267
	}
268
269
	/**
270
	 * Format $diWikiPages chunked by letter in a table that shows subject
271
	 * articles in one column and object articles/values in the other one.
272
	 *
273
	 * @param $diWikiPages array
274
	 * @return string
275
	 */
276
	protected function subjectObjectList( array $diWikiPages ) {
277
		global $wgContLang, $smwgMaxPropertyValues;
0 ignored issues
show
Compatibility Best Practice introduced by
Use of global functionality is not recommended; it makes your code harder to test, and less reusable.

Instead of relying on global state, we recommend one of these alternatives:

1. Pass all data via parameters

function myFunction($a, $b) {
    // Do something
}

2. Create a class that maintains your state

class MyClass {
    private $a;
    private $b;

    public function __construct($a, $b) {
        $this->a = $a;
        $this->b = $b;
    }

    public function myFunction() {
        // Do something
    }
}
Loading history...
278
279
		$ac = count( $diWikiPages );
280
281
		if ( $ac > $this->limit ) {
282
			if ( $this->until !== '' ) {
283
				$start = 1;
284
			} else {
285
				$start = 0;
286
				$ac = $ac - 1;
287
			}
288
		} else {
289
			$start = 0;
290
		}
291
292
		$r = '<table class="property-page-results" style="width: 100%;" cellspacing="0" cellpadding="0">';
293
		$prev_start_char = 'None';
294
295
		for ( $index = $start; $index < $ac; $index++ ) {
296
			$diWikiPage = $diWikiPages[$index];
297
			$dvWikiPage = DataValueFactory::getInstance()->newDataValueByItem( $diWikiPage, null );
298
299
			$sortkey = $this->store->getWikiPageSortKey( $diWikiPage );
300
			$start_char = $wgContLang->convert( $wgContLang->firstChar( $sortkey ) );
301
302
			// Header for index letters
303
			if ( $start_char != $prev_start_char ) {
304
				$r .= '<tr class="header-row" ><th class="smwpropname"><div class="header-title">' . htmlspecialchars( $start_char ) . "</div></th><th></th></tr>\n";
305
				$prev_start_char = $start_char;
306
			}
307
308
			// Property name
309
			$searchlink = SMWInfolink::newBrowsingLink( '+', $dvWikiPage->getWikiValue() );
310
			$r .= '<tr class="value-row" ><td class="smwpropname">' . $dvWikiPage->getShortHTMLText( smwfGetLinker() ) .
311
			      '&#160;' . $searchlink->getHTML( smwfGetLinker() ) . '</td><td class="smwprops">';
312
313
			// Property values
314
			$ropts = new RequestOptions();
315
			$ropts->limit = $smwgMaxPropertyValues + 1;
316
			$values = $this->store->getPropertyValues( $diWikiPage, $this->mProperty, $ropts );
317
			$i = 0;
318
319
			foreach ( $values as $di ) {
320
				if ( $i != 0 ) {
321
					$r .= ', ';
322
				}
323
				$i++;
324
325
				if ( $i < $smwgMaxPropertyValues + 1 ) {
326
					$dv = DataValueFactory::getInstance()->newDataValueByItem( $di, $this->mProperty );
327
328
					$dv->setOutputFormat( 'LOCL' );
329
330
					$r .= $dv->getShortHTMLText( smwfGetLinker() ) . $dv->getInfolinkText( SMW_OUTPUT_HTML, smwfGetLinker() );
331
				} else {
332
					$searchlink = SMWInfolink::newInversePropertySearchLink( '…', $dvWikiPage->getWikiValue(), $this->mTitle->getText() );
333
					$r .= $searchlink->getHTML( smwfGetLinker() );
334
				}
335
			}
336
337
			$r .= "</td></tr>\n";
338
		}
339
340
		$r .= '</table>';
341
342
		return $r;
343
	}
344
345
	private function doQuerySubjectListWithValue( $value, $options ) {
346
347
		$applicationFactory = ApplicationFactory::getInstance();
348
349
		$dataValue = $applicationFactory->getDataValueFactory()->newDataValueByProperty( $this->mProperty );
350
		$dataValue->setOption( DataValue::OPT_QUERY_CONTEXT, true );
351
		$dataValue->setUserValue( $value );
352
		$queryFactory = $applicationFactory->getQueryFactory();
353
354
		$description = $queryFactory->newDescriptionFactory()->newFromDataValue(
355
			$dataValue
356
		);
357
358
		$query = $queryFactory->newQuery( $description );
359
		$query->setLimit( $options->limit );
360
		$query->setOffset( $options->offset );
361
		$query->setSortKeys( array( '' => 'asc' ) );
362
363
		return $this->store->getQueryResult( $query )->getResults();
364
	}
365
366
}
367