Completed
Push — master ( 59c431...219a45 )
by mw
36:38
created

SMWPropertyPage   B

Complexity

Total Complexity 37

Size/Duplication

Total Lines 320
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 11

Importance

Changes 0
Metric Value
dl 0
loc 320
rs 8.6
c 0
b 0
f 0
wmc 37
lcom 1
cbo 11

8 Methods

Rating   Name   Duplication   Size   Complexity  
A initParameters() 0 8 1
B getHtml() 0 17 5
B getIntroductoryText() 0 23 5
B getTopIndicator() 0 29 6
B getSubpropertyList() 0 51 6
B getPropertyValueList() 0 54 5
C subjectObjectList() 0 68 8
A doQuerySubjectListWithValue() 0 20 1
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 );
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...
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() {
0 ignored issues
show
Coding Style introduced by
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...
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();
0 ignored issues
show
Unused Code introduced by
$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...
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 );
0 ignored issues
show
Unused Code introduced by
$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...
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 ) );
0 ignored issues
show
Unused Code introduced by
$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...
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