Completed
Push — master ( 2472c5...a814e8 )
by mw
35:03
created

includes/articlepages/SMW_PropertyPage.php (9 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
13
/**
14
 * Implementation of MediaWiki's Article that shows additional information on
15
 * property pages. Very similar to CategoryPage, but with different printout
16
 * that also displays values for each subject with the given property.
17
 *
18
 * @ingroup SMW
19
 *
20
 * @author Markus Krötzsch
21
 */
22
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...
23
24
	/**
25
	 * @see SMWOrderedListPage::initParameters()
26
	 * @note We use a smaller limit here; property pages might become large.
27
	 */
28
	protected function initParameters() {
29
		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...
30
		$this->limit = $smwgPropertyPagingLimit;
31
		$this->mProperty = DIProperty::newFromUserLabel( $this->mTitle->getText() );
32
		$this->store = ApplicationFactory::getInstance()->getStore();
33
		$this->propertyValue = DataValueFactory::getInstance()->newDataItemValue( $this->mProperty );
0 ignored issues
show
Deprecated Code introduced by
The method SMW\DataValueFactory::newDataItemValue() has been deprecated with message: since 2.4, use DataValueFactory::newDataValueByItem

This method has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the method will be removed from the class and what other method or class to use instead.

Loading history...
34
		return true;
35
	}
36
37
	/**
38
	 * Returns the HTML which is added to $wgOut after the article text.
39
	 *
40
	 * @return string
41
	 */
42
	protected function getHtml() {
43
44
		if ( !$this->store->getRedirectTarget( $this->mProperty )->equals( $this->mProperty ) ) {
45
			return '';
46
		}
47
48
		$dv = DataValueFactory::getInstance()->newDataValueByItem(
49
			$this->mProperty
50
		);
51
52
		$title = $dv->getFormattedLabel( DataValueFormatter::WIKI_LONG );
53
		$this->getContext()->getOutput()->setPageTitle( $title );
54
55
		$list = $this->getSubpropertyList() . $this->getPropertyValueList();
56
		$result = ( $list !== '' ? Html::element( 'div', array( 'id' => 'smwfootbr' ) ) . $list : '' );
57
58
		return $result;
59
	}
60
61
	/**
62
	 * Returns an introductory text for a predefined property
63
	 *
64
	 * @note In order to enable a more detailed description for a specific
65
	 * predefined property a concatenated message key can be used (e.g
66
	 * 'smw-pa-property-predefined' + <internal property key> => '_asksi' )
67
	 *
68
	 * @since 1.9
69
	 *
70
	 * @return string
71
	 */
72
	protected function getIntroductoryText() {
73
74
		$dv = DataValueFactory::getInstance()->newDataValueByItem(
75
			$this->mProperty
76
		);
77
78
		$propertyName = $dv->getFormattedLabel();
79
		$message = '';
80
81
		if ( $this->mProperty->isUserDefined() ) {
82
			return $message;
83
		}
84
85
		$key = $this->mProperty->getKey();
86
87
		if ( ( $messageKey = PropertyRegistry::getInstance()->findPropertyDescriptionMsgKeyById( $key ) ) !== '' ) {
88
			$messageKeyLong = $messageKey . '-long';
89
		} else {
90
			$messageKey = 'smw-pa-property-predefined' . strtolower( $key );
91
			$messageKeyLong = 'smw-pa-property-predefined-long' . strtolower( $key );
92
		}
93
94
		$message .= wfMessage( $messageKey )->exists() ? wfMessage( $messageKey, $propertyName )->parse() : wfMessage( 'smw-pa-property-predefined-default', $propertyName )->parse();
95
		$message .= wfMessage( $messageKeyLong )->exists() ? ' ' . wfMessage( $messageKeyLong )->parse() : '';
96
		$message .= ' ' . wfMessage( 'smw-pa-property-predefined-common' )->parse();
97
98
		return Html::rawElement( 'div', array( 'class' => 'smw-property-predefined-intro plainlinks' ), $message );
99
	}
100
101
	protected function getTopIndicator() {
102
103
		$propertyName = htmlspecialchars( $this->mTitle->getText() );
104
		$usageCountHtml = '';
105
106
		$requestOptions = new RequestOptions();
107
		$requestOptions->setLimit( 1 );
108
		$requestOptions->addStringCondition( $propertyName, StringCondition::COND_EQ );
109
110
		$cachedLookupList = $this->store->getPropertiesSpecial( $requestOptions );
111
		$usageList = $cachedLookupList->fetchList();
112
113
		if ( $usageList && $usageList !== array() ) {
114
			$usage = end( $usageList );
115
			$usageCount = $usage[1];
116
			$usageCountHtml = Html::rawElement(
117
				'div', array(
118
					'title' => $this->getContext()->getLanguage()->timeanddate( $cachedLookupList->getTimestamp() ),
119
					'class' => 'smw-page-indicator usage-count' . ( $usageCount < 25000 ? ( $usageCount > 5000 ? ' moderate' : '' ) : ' high' ) ),
120
				$usageCount
121
			);
122
		}
123
124
		return Html::rawElement( 'div', array(), Html::rawElement(
125
				'div', array(
126
				'class' => 'smw-page-indicator property-type',
127
				'title' => wfMessage( 'smw-page-indicator-type-info', $this->mProperty->isUserDefined() )->parse()
128
			), ( $this->mProperty->isUserDefined() ? 'U' : 'S' )
129
		) . $usageCountHtml );
130
	}
131
132
	/**
133
	 * Get the HTML for displaying subproperties of this property. This list
134
	 * is usually short and we implement no additional navigation.
135
	 *
136
	 * @return string
137
	 */
138
	protected function getSubpropertyList() {
0 ignored issues
show
getSubpropertyList 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...
139
140
		$more = false;
141
		$requestOptions = new RequestOptions();
142
		$requestOptions->sort = true;
143
		$requestOptions->ascending = true;
144
145
		// +1 look-ahead
146
		$requestOptions->setLimit( $GLOBALS['smwgSubPropertyListLimit'] + 1 );
147
		$subproperties = $this->store->getPropertySubjects( new DIProperty( '_SUBP' ), $this->getDataItem(), $requestOptions );
148
149
		// Pop the +1 look-ahead from the list
150
		if ( count( $subproperties ) > $GLOBALS['smwgSubPropertyListLimit'] ) {
151
			array_pop( $subproperties );
152
			$more = true;
153
		}
154
155
		$result = '';
156
		$resultCount = count( $subproperties );
157
158
		if ( $more ) {
159
			$message = Html::rawElement(
160
				'span',
161
				array( 'class' => 'plainlinks' ),
162
				wfMessage( 'smw-subpropertylist-count-with-restricted-note', $resultCount, $GLOBALS['smwgSubPropertyListLimit'] )->parse()
163
			);
164
		} else {
165
			$message = wfMessage( 'smw-subpropertylist-count', $resultCount )->text();
166
		}
167
168
		if ( $resultCount > 0 ) {
169
			$titleText = htmlspecialchars( $this->mTitle->getText() );
170
			$result .= "<div id=\"mw-subcategories\">\n<h2>" . wfMessage( 'smw_subproperty_header', $titleText )->text() . "</h2>\n<p>";
171
172
			if ( !$this->mProperty->isUserDefined() ) {
173
				$result .= wfMessage( 'smw_isspecprop' )->text() . ' ';
174
			}
175
176
			$result .= $message . "</p>"  ."\n";
177
178
			if ( $resultCount < 6 ) {
179
				$result .= SMWPageLister::getShortList( 0, $resultCount, $subproperties, null );
180
			} else {
181
				$result .= SMWPageLister::getColumnList( 0, $resultCount, $subproperties, null );
182
			}
183
184
			$result .= "\n</div>";
185
		}
186
187
		return $result;
188
	}
189
190
	/**
191
	 * Get the HTML for displaying values of this property, based on the
192
	 * current from/until and limit settings.
193
	 *
194
	 * @return string
195
	 */
196
	protected function getPropertyValueList() {
197
		global $smwgPropertyPagingLimit, $wgRequest;
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...
198
199
		 // limit==0: configuration setting to disable this completely
200
		if ( $this->limit < 1 ) {
201
			return '';
202
		}
203
204
		$diWikiPages = array();
0 ignored issues
show
$diWikiPages 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...
205
		$options = SMWPageLister::getRequestOptions( $this->limit, $this->from, $this->until );
206
207
		$options->limit = $wgRequest->getVal( 'limit', $smwgPropertyPagingLimit );
208
		$options->offset = $wgRequest->getVal( 'offset', '0' );
209
210
		if ( ( $value = $wgRequest->getVal( 'value', '' ) ) !== '' ) {
211
			$diWikiPages = $this->doQuerySubjectListWithValue( $value, $options );
212
		} else {
213
			$diWikiPages = $this->store->getAllPropertySubjects( $this->mProperty, $options );
214
		}
215
216
		if ( !$options->ascending ) {
217
			$diWikiPages = array_reverse( $diWikiPages );
218
		}
219
220
		$result = '';
221
222
		if ( count( $diWikiPages ) > 0 ) {
223
			$pageLister = new SMWPageLister( $diWikiPages, null, $this->limit, $this->from, $this->until );
224
225
			$this->mTitle->setFragment( '#SMWResults' ); // Make navigation point to the result list.
226
			$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...
227
228
			$dvWikiPage = DataValueFactory::getInstance()->newDataValueByItem(
229
				$this->mProperty
230
			);
231
232
			// Allow the DV formatter to access a specific language code
233
			$dvWikiPage->setOption(
234
				DataValue::OPT_USER_LANGUAGE,
235
				Localizer::getInstance()->getUserLanguage()->getCode()
236
			);
237
238
			$titleText = htmlspecialchars( $dvWikiPage->getWikiValue() );
239
			$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...
240
241
			$result .= "<a name=\"SMWResults\"></a><div id=\"mw-pages\">\n" .
242
			           '<h2>' . wfMessage( 'smw_attribute_header', $titleText )->text() . "</h2>\n<p>";
243
244
			$result .= $this->getNavigationLinks( 'smw_attributearticlecount', $diWikiPages, $smwgPropertyPagingLimit ) .
245
			           $this->subjectObjectList( $diWikiPages ) . "\n</div>";
246
		}
247
248
		return $result;
249
	}
250
251
	/**
252
	 * Format $diWikiPages chunked by letter in a table that shows subject
253
	 * articles in one column and object articles/values in the other one.
254
	 *
255
	 * @param $diWikiPages array
256
	 * @return string
257
	 */
258
	protected function subjectObjectList( array $diWikiPages ) {
259
		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...
260
261
		$ac = count( $diWikiPages );
262
263
		if ( $ac > $this->limit ) {
264
			if ( $this->until !== '' ) {
265
				$start = 1;
266
			} else {
267
				$start = 0;
268
				$ac = $ac - 1;
269
			}
270
		} else {
271
			$start = 0;
272
		}
273
274
		$r = '<table class="property-page-results" style="width: 100%;" cellspacing="0" cellpadding="0">';
275
		$prev_start_char = 'None';
276
277
		for ( $index = $start; $index < $ac; $index++ ) {
278
			$diWikiPage = $diWikiPages[$index];
279
			$dvWikiPage = DataValueFactory::getInstance()->newDataValueByItem( $diWikiPage, null );
280
281
			$sortkey = $this->store->getWikiPageSortKey( $diWikiPage );
282
			$start_char = $wgContLang->convert( $wgContLang->firstChar( $sortkey ) );
283
284
			// Header for index letters
285
			if ( $start_char != $prev_start_char ) {
286
				$r .= '<tr class="header-row" ><th class="smwpropname"><div class="header-title">' . htmlspecialchars( $start_char ) . "</div></th><th></th></tr>\n";
287
				$prev_start_char = $start_char;
288
			}
289
290
			// Property name
291
			$searchlink = SMWInfolink::newBrowsingLink( '+', $dvWikiPage->getWikiValue() );
292
			$r .= '<tr class="value-row" ><td class="smwpropname">' . $dvWikiPage->getShortHTMLText( smwfGetLinker() ) .
293
			      '&#160;' . $searchlink->getHTML( smwfGetLinker() ) . '</td><td class="smwprops">';
294
295
			// Property values
296
			$ropts = new RequestOptions();
297
			$ropts->limit = $smwgMaxPropertyValues + 1;
298
			$values = $this->store->getPropertyValues( $diWikiPage, $this->mProperty, $ropts );
299
			$i = 0;
300
301
			foreach ( $values as $di ) {
302
				if ( $i != 0 ) {
303
					$r .= ', ';
304
				}
305
				$i++;
306
307
				if ( $i < $smwgMaxPropertyValues + 1 ) {
308
					$dv = DataValueFactory::getInstance()->newDataValueByItem( $di, $this->mProperty );
309
310
					$dv->setOutputFormat( 'LOCL' );
311
312
					$r .= $dv->getShortHTMLText( smwfGetLinker() ) . $dv->getInfolinkText( SMW_OUTPUT_HTML, smwfGetLinker() );
313
				} else {
314
					$searchlink = SMWInfolink::newInversePropertySearchLink( '…', $dvWikiPage->getWikiValue(), $this->mTitle->getText() );
315
					$r .= $searchlink->getHTML( smwfGetLinker() );
316
				}
317
			}
318
319
			$r .= "</td></tr>\n";
320
		}
321
322
		$r .= '</table>';
323
324
		return $r;
325
	}
326
327
	private function doQuerySubjectListWithValue( $value, $options ) {
328
329
		$applicationFactory = ApplicationFactory::getInstance();
330
331
		$dataValue = $applicationFactory->getDataValueFactory()->newDataValueByProperty( $this->mProperty );
332
		$dataValue->setOption( DataValue::OPT_QUERY_CONTEXT, true );
333
		$dataValue->setUserValue( $value );
334
		$queryFactory = $applicationFactory->getQueryFactory();
335
336
		$description = $queryFactory->newDescriptionFactory()->newFromDataValue(
337
			$dataValue
338
		);
339
340
		$query = $queryFactory->newQuery( $description );
341
		$query->setLimit( $options->limit );
342
		$query->setOffset( $options->offset );
343
		$query->setSortKeys( array( '' => 'asc' ) );
344
345
		return $this->store->getQueryResult( $query )->getResults();
346
	}
347
348
}
349