Completed
Push — master ( 314506...335380 )
by mw
100:54 queued 62:54
created

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