PageRenderTime 24ms CodeModel.GetById 14ms RepoModel.GetById 0ms app.codeStats 0ms

/extensions/SemanticForms/includes/SF_FormLinker.php

https://github.com/ChuguluGames/mediawiki-svn
PHP | 344 lines | 229 code | 27 blank | 88 comment | 57 complexity | 38c10245eb085239412b2743a9a12232 MD5 | raw file
  1. <?php
  2. /**
  3. * Gets the form(s) used to edit a page, both for existing pages and for
  4. * not-yet-created, red-linked pages. This class uses its own in-memory
  5. * caching to try to minimize the number of calls to the Semantic
  6. * MediaWiki data store.
  7. *
  8. * @author Yaron Koren
  9. * @file
  10. * @ingroup SF
  11. */
  12. if ( !defined( 'MEDIAWIKI' ) ) die();
  13. class SFFormLinker {
  14. const DEFAULT_FORM = 1;
  15. const ALTERNATE_FORM = 2;
  16. const PAGE_DEFAULT_FORM = 3;
  17. const AUTO_CREATE_FORM = 4;
  18. // An in-memory cache of data already retrieved for the current page.
  19. static $mLinkedForms = array();
  20. static $mLinkedPages = array();
  21. static $mLinkedPagesRetrieved = false;
  22. /**
  23. * Gets the set of all properties that point to this page, anywhere
  24. * in the wiki.
  25. */
  26. static function getIncomingProperties( $title ) {
  27. $store = smwfGetStore();
  28. // SMW 1.6+
  29. if ( class_exists( 'SMWDataItem' ) ) {
  30. $value = SMWDIWikiPage::newFromTitle( $title );
  31. } else {
  32. $title_text = SFUtils::titleString( $title );
  33. $value = SMWDataValueFactory::newTypeIDValue( '_wpg', $title_text );
  34. }
  35. $properties = $store->getInProperties( $value );
  36. $propertyNames = array();
  37. foreach( $properties as $property ) {
  38. // SMW 1.6+
  39. if ( $property instanceof SMWDIProperty ) {
  40. $property_name = $property->getKey();
  41. } else {
  42. $property_name = $property->getWikiValue();
  43. }
  44. if ( !empty( $property_name ) ) {
  45. $propertyNames[] = $property_name;
  46. }
  47. }
  48. return $propertyNames;
  49. }
  50. /**
  51. * Gets the properties pointing from the current page to this one.
  52. */
  53. static function getPagePropertiesOfPage( $title ) {
  54. if ( self::$mLinkedPagesRetrieved ) {
  55. return;
  56. }
  57. $store = smwfGetStore();
  58. if ( class_exists( 'SMWDataItem' ) ) {
  59. $value = SMWDIWikiPage::newFromTitle( $title );
  60. } else {
  61. $value = $title;
  62. }
  63. $data = $store->getSemanticData( $value );
  64. foreach ( $data->getProperties() as $property ) {
  65. $propertyValues = $data->getPropertyValues( $property );
  66. foreach ( $propertyValues as $propertyValue ) {
  67. $linkedPageName = null;
  68. if ( $propertyValue instanceof SMWDIWikiPage ) {
  69. $propertyName = $property->getKey();
  70. $linkedPageName = $propertyValue->getDBkey();
  71. } elseif ( $propertyValue instanceof SMWWikiPageValue ) {
  72. $propertyName = $property->getWikiValue();
  73. $linkedPageName = $propertyValue->getWikiValue();
  74. }
  75. if ( !is_null( $linkedPageName ) ) {
  76. if ( array_key_exists( $linkedPageName, self::$mLinkedPages ) ) {
  77. self::$mLinkedPages[$linkedPageName][] = $propertyName;
  78. } else {
  79. self::$mLinkedPages[$linkedPageName] = array( $propertyName );
  80. }
  81. }
  82. }
  83. }
  84. self::$mLinkedPagesRetrieved = true;
  85. }
  86. /**
  87. * Gets the forms specified, if any, of either type "default form",
  88. * "alternate form", or "default form for page", for a specific page
  89. * (which should be a category, property, or namespace page)
  90. */
  91. static function getFormsThatPagePointsTo( $page_name, $page_namespace, $form_connection_type ) {
  92. if ( $page_name == NULL ) {
  93. return array();
  94. }
  95. // Check if we've already gotten the set of forms for this
  96. // combination of page and "form connection type" (default,
  97. // alternate or "creates pages with"). If so, use that -
  98. // otherwise, prepare the array so that we can add this
  99. // data to it.
  100. $page_key = "$page_namespace:$page_name";
  101. if ( array_key_exists( $page_key, self::$mLinkedForms ) ) {
  102. if ( array_key_exists( $form_connection_type, self::$mLinkedForms[$page_key] ) ) {
  103. return self::$mLinkedForms[$page_key][$form_connection_type];
  104. } else {
  105. // Do nothing - an entry with this key will
  106. // be added at the end of this method.
  107. }
  108. } else {
  109. self::$mLinkedForms[$page_key] = array();
  110. }
  111. if ( $form_connection_type == self::DEFAULT_FORM ) {
  112. $prop_smw_id = '_SF_DF';
  113. $backup_prop_smw_id = '_SF_DF_BACKUP';
  114. } elseif ( $form_connection_type == self::ALTERNATE_FORM ) {
  115. $prop_smw_id = '_SF_AF';
  116. $backup_prop_smw_id = '_SF_AF_BACKUP';
  117. } elseif ( $form_connection_type == self::PAGE_DEFAULT_FORM ) {
  118. $prop_smw_id = '_SF_PDF';
  119. $backup_prop_smw_id = '_SF_PDF_BACKUP';
  120. } elseif ( $form_connection_type == self::AUTO_CREATE_FORM ) {
  121. $prop_smw_id = '_SF_CP';
  122. $backup_prop_smw_id = '_SF_CP_BACKUP';
  123. } else {
  124. return array();
  125. }
  126. global $sfgContLang;
  127. $store = smwfGetStore();
  128. $subject = Title::makeTitleSafe( $page_namespace, $page_name );
  129. $form_names = SFUtils::getSMWPropertyValues( $store, $subject, $prop_smw_id );
  130. // If we're using a non-English language, check for the English
  131. // string as well.
  132. if ( ! class_exists( 'SF_LanguageEn' ) || ! $sfgContLang instanceof SF_LanguageEn ) {
  133. $backup_form_names = SFUtils::getSMWPropertyValues( $store, $subject, $backup_prop_smw_id );
  134. $form_names = array_merge( $form_names, $backup_form_names );
  135. }
  136. // Add this data to the "cache".
  137. self::$mLinkedForms[$page_key][$form_connection_type] = $form_names;
  138. return $form_names;
  139. }
  140. /**
  141. * Automatically creates a page that's red-linked from the page being
  142. * viewed, if there's a property pointing from anywhere to that page
  143. * that's defined with the 'Creates pages with form' special property
  144. */
  145. static function createLinkedPage( $title, $incoming_properties ) {
  146. // if we're in a 'special' page, just exit - this is to prevent
  147. // constant additions being made from the 'Special:RecentChanges'
  148. // page, which shows pages that were previously deleted as red
  149. // links, even if they've since been recreated. The same might
  150. // hold true for other special pages.
  151. global $wgTitle;
  152. if ( empty( $wgTitle ) )
  153. return false;
  154. if ( $wgTitle->getNamespace() == NS_SPECIAL )
  155. return false;
  156. foreach ( $incoming_properties as $property_name ) {
  157. $auto_create_forms = self::getFormsThatPagePointsTo( $property_name, SMW_NS_PROPERTY, self::AUTO_CREATE_FORM );
  158. if ( count( $auto_create_forms ) > 0 ) {
  159. global $sfgFormPrinter;
  160. $form_name = $auto_create_forms[0];
  161. $form_title = Title::makeTitleSafe( SF_NS_FORM, $form_name );
  162. $form_article = new Article( $form_title );
  163. $form_definition = $form_article->getContent();
  164. list ( $form_text, $javascript_text, $data_text, $form_page_title, $generated_page_name ) =
  165. $sfgFormPrinter->formHTML( $form_definition, false, false, null, null, 'Some very long page name that will hopefully never get created ABCDEF123', null );
  166. $params = array();
  167. global $wgUser;
  168. $params['user_id'] = $wgUser->getId();
  169. $params['page_text'] = $data_text;
  170. $job = new SFCreatePageJob( $title, $params );
  171. Job::batchInsert( array( $job ) );
  172. return true;
  173. }
  174. }
  175. return false;
  176. }
  177. /**
  178. * Helper function for formEditLink() - gets the 'default form' and
  179. * 'alternate form' properties for a page, and creates the
  180. * corresponding Special:FormEdit link, if any such properties are
  181. * defined
  182. */
  183. static function getFormEditLinkForPage( $target_page_title, $page_name, $page_namespace ) {
  184. $default_forms = self::getFormsThatPagePointsTo( $page_name, $page_namespace, self::DEFAULT_FORM );
  185. $alt_forms = self::getFormsThatPagePointsTo( $page_name, $page_namespace, self::ALTERNATE_FORM );
  186. if ( ( count( $default_forms ) == 0 ) && ( count( $alt_forms ) == 0 ) ) {
  187. return null;
  188. }
  189. $fe = SpecialPage::getPage( 'FormEdit' );
  190. $fe_url = $fe->getTitle()->getLocalURL();
  191. if ( count( $default_forms ) > 0 ) {
  192. $form_edit_url = $fe_url . "/" . $default_forms[0] . "/" . SFUtils::titleURLString( $target_page_title );
  193. } else {
  194. $form_edit_url = $fe_url . "/" . SFUtils::titleURLString( $target_page_title );
  195. }
  196. foreach ( $alt_forms as $i => $alt_form ) {
  197. $form_edit_url .= ( strpos( $form_edit_url, "?" ) ) ? "&" : "?";
  198. $form_edit_url .= "alt_form[$i]=$alt_form";
  199. }
  200. return $form_edit_url;
  201. }
  202. /**
  203. * Returns the URL for the Special:FormEdit page for a specific page,
  204. * given its default and alternate form(s) - we can't just point to
  205. * '&action=formedit', because that one doesn't reflect alternate forms
  206. */
  207. static function formEditLink( $title, $incoming_properties ) {
  208. // Get all properties pointing to this page, and if
  209. // getFormEditLinkForPage() returns a value with any of
  210. // them, return that.
  211. foreach ( $incoming_properties as $property_name ) {
  212. if ( $form_edit_link = self::getFormEditLinkForPage( $title, $property_name, SMW_NS_PROPERTY ) ) {
  213. return $form_edit_link;
  214. }
  215. }
  216. // If that didn't work, check if this page's namespace
  217. // has a default form specified.
  218. $namespace_name = $title->getNsText();
  219. if ( '' === $namespace_name ) {
  220. // If it's in the main (blank) namespace, check for the
  221. // file named with the word for "Main" in this language.
  222. SFUtils::loadMessages();
  223. $namespace_name = wfMsgForContent( 'sf_blank_namespace' );
  224. }
  225. if ( $form_edit_link = self::getFormEditLinkForPage( $title, $namespace_name, NS_PROJECT ) ) {
  226. return $form_edit_link;
  227. }
  228. // If nothing found still, return null.
  229. return null;
  230. }
  231. /**
  232. * Sets the URL for form-based creation of a nonexistent (broken-linked,
  233. * AKA red-linked) page
  234. */
  235. static function setBrokenLink( $linker, $target, $options, $text, &$attribs, &$ret ) {
  236. // If it's not a broken (red) link, exit.
  237. if ( !in_array( 'broken', $options ) ) {
  238. return true;
  239. }
  240. // If the link is to a special page, exit.
  241. if ( $target->getNamespace() == NS_SPECIAL ) {
  242. return true;
  243. }
  244. global $sfgRedLinksCheckOnlyLocalProps;
  245. if ( $sfgRedLinksCheckOnlyLocalProps ) {
  246. if ( $linker instanceof DummyLinker ) {
  247. global $wgTitle;
  248. $curTitle = $wgTitle;
  249. } else {
  250. $curTitle = $linker->getTitle();
  251. }
  252. self::getPagePropertiesOfPage( $curTitle );
  253. $targetName = $target->getText();
  254. if ( array_key_exists( $targetName, self::$mLinkedPages ) ) {
  255. $incoming_properties = self::$mLinkedPages[$targetName];
  256. } else {
  257. $incoming_properties = array();
  258. }
  259. } else {
  260. $incoming_properties = self::getIncomingProperties( $target );
  261. }
  262. self::createLinkedPage( $target, $incoming_properties );
  263. $link = self::formEditLink( $target, $incoming_properties );
  264. if ( $link != '' ) {
  265. $attribs['href'] = $link;
  266. }
  267. return true;
  268. }
  269. /**
  270. * Get the form(s) used to edit this page - either:
  271. * - the default form(s) for the page itself, if there are any; or
  272. * - the default form(s) for a category that this article belongs to,
  273. * if there are any; or
  274. * - the default form(s) for the article's namespace, if there are any.
  275. */
  276. static function getDefaultFormsForPage( $title ) {
  277. // See if the page itself has a default form (or forms), and
  278. // return it/them if so.
  279. $default_forms = self::getFormsThatPagePointsTo( $title->getText(), $title->getNamespace(), self::PAGE_DEFAULT_FORM );
  280. if ( count( $default_forms ) > 0 ) {
  281. return $default_forms;
  282. }
  283. // If this is not a category page, look for a default form
  284. // for its parent category or categories.
  285. $namespace = $title->getNamespace();
  286. if ( NS_CATEGORY !== $namespace ) {
  287. $default_forms = array();
  288. $categories = SFUtils::getCategoriesForPage( $title );
  289. foreach ( $categories as $category ) {
  290. $default_forms = array_merge( $default_forms, self::getFormsThatPagePointsTo( $category, NS_CATEGORY, self::DEFAULT_FORM ) );
  291. }
  292. if ( count( $default_forms ) > 0 ) {
  293. return $default_forms;
  294. }
  295. }
  296. // All that's left is checking for the namespace. If this is
  297. // a subpage, exit out - default forms for namespaces don't
  298. // apply to subpages.
  299. if ( $title->isSubpage() ) {
  300. return array();
  301. }
  302. // If we're still here, just return the default form for the
  303. // namespace, which may well be null.
  304. if ( NS_MAIN === $namespace ) {
  305. // If it's in the main (blank) namespace, check for the
  306. // file named with the word for "Main" in this language.
  307. SFUtils::loadMessages();
  308. $namespace_label = wfMsgForContent( 'sf_blank_namespace' );
  309. } else {
  310. global $wgContLang;
  311. $namespace_labels = $wgContLang->getNamespaces();
  312. $namespace_label = $namespace_labels[$namespace];
  313. }
  314. $default_forms = self::getFormsThatPagePointsTo( $namespace_label, NS_PROJECT, self::DEFAULT_FORM );
  315. return $default_forms;
  316. }
  317. }