isRegistered( $key ) ) { throw new PredefinedPropertyLabelMismatchException( "There is no predefined property with \"$key\"." ); } } $this->m_key = $key; $this->m_inverse = $inverse; } /** * @return integer */ public function getDIType() { return SMWDataItem::TYPE_PROPERTY; } /** * @return string */ public function getKey() { return $this->m_key; } /** * @return boolean */ public function isInverse() { return $this->m_inverse; } /** * @return string */ public function getSortKey() { return $this->m_key; } /** * Specifies whether values of this property should be shown in the * Factbox. A property may wish to prevent this if either * (1) its information is really dull, e.g. being a mere copy of * information that is obvious from other things that are shown, or * (2) the property is set in a hook after parsing, so that it is not * reliably available when Factboxes are displayed. If a property is * internal so it should never be observed by users, then it is better * to just not associate any translated label with it, so it never * appears anywhere. * * Examples of properties that are not shown include Modification date * (not available in time), and Has improper value for (errors are * shown directly on the page anyway). * * @return boolean */ public function isShown() { if ( $this->isUserDefined() ) { return true; } return PropertyRegistry::getInstance()->isVisible( $this->m_key ); } /** * Return true if this is a usual wiki property that is defined by a * wiki page, and not a property that is pre-defined in the wiki. * * @return boolean */ public function isUserDefined() { return $this->m_key{0} != '_'; } /** * Whether a user can freely use this property for value annotation or * not. * * @since 3.0 * * @return boolean */ public function isUserAnnotable() { // A user defined property is generally assumed to be unrestricted for // usage if ( $this->isUserDefined() ) { return true; } return PropertyRegistry::getInstance()->isAnnotable( $this->m_key ); } /** * Find a user-readable label for this property, or return '' if it is * a predefined property that has no label. For inverse properties, the * label starts with a "-". * * @return string */ public function getLabel() { $prefix = $this->m_inverse ? '-' : ''; if ( $this->isUserDefined() ) { return $prefix . str_replace( '_', ' ', $this->m_key ); } return $prefix . PropertyRegistry::getInstance()->findPropertyLabelById( $this->m_key ); } /** * @since 2.4 * * @return string */ public function getCanonicalLabel() { $prefix = $this->m_inverse ? '-' : ''; if ( $this->isUserDefined() ) { return $prefix . str_replace( '_', ' ', $this->m_key ); } return $prefix . PropertyRegistry::getInstance()->findCanonicalPropertyLabelById( $this->m_key ); } /** * Borrowing the skos:prefLabel definition where a preferred label is expected * to have only one label per given language (skos:altLabel can have many * alternative labels) * * An empty string signals that no preferred label is available in the current * user language. * * @since 2.5 * * @param string $languageCode * * @return string */ public function getPreferredLabel( $languageCode = '' ) { $label = PropertyRegistry::getInstance()->findPreferredPropertyLabelFromIdByLanguageCode( $this->m_key, $languageCode ); if ( $label !== '' ) { return ( $this->m_inverse ? '-' : '' ) . $label; } return ''; } /** * @since 2.4 * * @param string $interwiki */ public function setInterwiki( $interwiki ) { $this->interwiki = $interwiki; } /** * Get an object of type DIWikiPage that represents the page which * relates to this property, or null if no such page exists. The latter * can happen for special properties without user-readable label. * * It is possible to construct subobjects of the property's wikipage by * providing an optional subobject name. * * @param string $subobjectName * @return DIWikiPage|null */ public function getDiWikiPage( $subobjectName = '' ) { $dbkey = $this->m_key; if ( !$this->isUserDefined() ) { $dbkey = $this->getLabel(); } return $this->newDIWikiPage( $dbkey, $subobjectName ); } /** * @since 2.4 * * @param string $subobjectName * * @return DIWikiPage|null */ public function getCanonicalDiWikiPage( $subobjectName = '' ) { if ( $this->isUserDefined() ) { $dbkey = $this->m_key; } elseif ( $this->m_key === $this->findPropertyTypeID() ) { // If _dat as property [[Date::...]] refers directly to its _dat type // then use the en-label as canonical representation $dbkey = PropertyRegistry::getInstance()->findPropertyLabelFromIdByLanguageCode( $this->m_key, 'en' ); } else { $dbkey = PropertyRegistry::getInstance()->findCanonicalPropertyLabelById( $this->m_key ); } return $this->newDIWikiPage( $dbkey, $subobjectName ); } /** * @since 2.4 * * @return DIProperty */ public function getRedirectTarget() { if ( $this->m_inverse ) { return $this; } return ApplicationFactory::getInstance()->getStore()->getRedirectTarget( $this ); } /** * @deprecated since 3.0, use DIProperty::setPropertyValueType */ public function setPropertyTypeId( $valueType ) { return $this->setPropertyValueType( $valueType ); } /** * @since 3.0 * * @param string $valueType * * @return self * @throws DataTypeLookupException * @throws RuntimeException */ public function setPropertyValueType( $valueType ) { if ( !DataTypeRegistry::getInstance()->isRegistered( $valueType ) ) { throw new DataTypeLookupException( "{$valueType} is an unknown type id" ); } if ( $this->isUserDefined() && $this->propertyValueType === null ) { $this->propertyValueType = $valueType; return $this; } if ( !$this->isUserDefined() && $valueType === PropertyRegistry::getInstance()->getPropertyValueTypeById( $this->m_key ) ) { $this->propertyValueType = $valueType; return $this; } throw new RuntimeException( 'DataType cannot be altered for a predefined property' ); } /** * @deprecated since 3.0, use DIProperty::findPropertyValueType */ public function findPropertyTypeId() { return $this->findPropertyValueType(); } /** * Find the property's type ID, either by looking up its predefined ID * (if any) or by retrieving the relevant information from the store. * If no type is stored for a user defined property, the global default * type will be used. * * @since 3.0 * * @return string type ID */ public function findPropertyValueType() { if ( isset( $this->propertyValueType ) ) { return $this->propertyValueType; } if ( !$this->isUserDefined() ) { return $this->propertyValueType = PropertyRegistry::getInstance()->getPropertyValueTypeById( $this->m_key ); } $diWikiPage = new DIWikiPage( $this->getKey(), SMW_NS_PROPERTY, $this->interwiki ); $applicationFactory = ApplicationFactory::getInstance(); $typearray = $applicationFactory->getPropertySpecificationLookup()->getSpecification( $this, new self( '_TYPE' ) ); if ( is_array( $typearray ) && count( $typearray ) >= 1 ) { // some types given, pick one (hopefully unique) $typeDataItem = reset( $typearray ); if ( $typeDataItem instanceof SMWDIUri ) { $this->propertyValueType = $typeDataItem->getFragment(); } else { // This is important. If a page has an invalid assignment to "has type", no // value will be stored, so the elseif case below occurs. But if the value // is retrieved within the same run, then the error value for "has type" is // cached and thus this case occurs. This is why it is important to tolerate // this case -- it is not necessarily a DB error. $this->propertyValueType = $applicationFactory->getSettings()->get( 'smwgPDefaultType' ); } } else { // no type given $this->propertyValueType = $applicationFactory->getSettings()->get( 'smwgPDefaultType' ); } return $this->propertyValueType; } /** * @see DataItem::getSerialization * * @return string */ public function getSerialization() { return ( $this->m_inverse ? '-' : '' ) . $this->m_key; } /** * Create a data item from the provided serialization string and type * ID. * * @param string $serialization * * @return DIProperty */ public static function doUnserialize( $serialization ) { $inverse = false; if ( $serialization{0} == '-' ) { $serialization = substr( $serialization, 1 ); $inverse = true; } return new self( $serialization, $inverse ); } /** * @param SMWDataItem $di * * @return boolean */ public function equals( SMWDataItem $di ) { if ( $di->getDIType() !== SMWDataItem::TYPE_PROPERTY ) { return false; } return $di->getKey() === $this->m_key; } /** * Construct a property from a user-supplied label. The main difference * to the normal constructor of DIProperty is that it is checked * whether the label refers to a known predefined property. * Note that this function only gives access to the registry data that * DIProperty stores, but does not do further parsing of user input. * * To process wiki input, SMWPropertyValue should be used. * * @param $label string label for the property * @param $inverse boolean states if the inverse of the property is constructed * * @return DIProperty object */ public static function newFromUserLabel( $label, $inverse = false, $languageCode = false ) { if ( $label !== '' && $label{0} == '-' ) { $label = substr( $label, 1 ); $inverse = true; } // Special handling for when the user value contains a @LCODE marker if ( ( $annotatedLanguageCode = Localizer::getAnnotatedLanguageCodeFrom( $label ) ) !== false ) { $languageCode = $annotatedLanguageCode; } $id = false; $label = str_replace( '_', ' ', $label ); if ( $languageCode ) { $id = PropertyRegistry::getInstance()->findPropertyIdFromLabelByLanguageCode( $label, $languageCode ); } if ( $id !== false ) { return new self( $id, $inverse ); } $id = PropertyRegistry::getInstance()->findPropertyIdByLabel( $label ); if ( $id === false ) { return new self( str_replace( ' ', '_', $label ), $inverse ); } return new self( $id, $inverse ); } private function newDIWikiPage( $dbkey, $subobjectName ) { // If an inverse marker is present just omit the marker so a normal // property page link can be produced independent of its directionality if ( $dbkey !== '' && $dbkey{0} == '-' ) { $dbkey = substr( $dbkey, 1 ); } try { return new DIWikiPage( str_replace( ' ', '_', $dbkey ), SMW_NS_PROPERTY, $this->interwiki, $subobjectName ); } catch ( DataItemException $e ) { return null; } } }