TextConflictHelper.php 6.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257
  1. <?php
  2. /**
  3. * Helper for displaying edit conflicts to users
  4. *
  5. * Copyright (C) 2017 Kunal Mehta <legoktm@member.fsf.org>
  6. *
  7. * This program is free software; you can redistribute it and/or modify
  8. * it under the terms of the GNU General Public License as published by
  9. * the Free Software Foundation; either version 2 of the License, or
  10. * (at your option) any later version.
  11. *
  12. * This program is distributed in the hope that it will be useful,
  13. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  14. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  15. * GNU General Public License for more details.
  16. *
  17. * You should have received a copy of the GNU General Public License along
  18. * with this program; if not, write to the Free Software Foundation, Inc.,
  19. * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
  20. * http://www.gnu.org/copyleft/gpl.html
  21. *
  22. * @file
  23. */
  24. namespace MediaWiki\EditPage;
  25. use Content;
  26. use ContentHandler;
  27. use Html;
  28. use IBufferingStatsdDataFactory;
  29. use OutputPage;
  30. use Title;
  31. /**
  32. * Helper for displaying edit conflicts in text content
  33. * models to users
  34. *
  35. * @since 1.31
  36. */
  37. class TextConflictHelper {
  38. /**
  39. * @var Title
  40. */
  41. protected $title;
  42. /**
  43. * @var null|string
  44. */
  45. public $contentModel = null;
  46. /**
  47. * @var null|string
  48. */
  49. public $contentFormat = null;
  50. /**
  51. * @var OutputPage
  52. */
  53. protected $out;
  54. /**
  55. * @var IBufferingStatsdDataFactory
  56. */
  57. protected $stats;
  58. /**
  59. * @var string Message key for submit button's label
  60. */
  61. protected $submitLabel;
  62. /**
  63. * @var string
  64. */
  65. protected $yourtext = '';
  66. /**
  67. * @var string
  68. */
  69. protected $storedversion = '';
  70. /**
  71. * @param Title $title
  72. * @param OutputPage $out
  73. * @param IBufferingStatsdDataFactory $stats
  74. * @param string $submitLabel
  75. */
  76. public function __construct( Title $title, OutputPage $out, IBufferingStatsdDataFactory $stats,
  77. $submitLabel
  78. ) {
  79. $this->title = $title;
  80. $this->out = $out;
  81. $this->stats = $stats;
  82. $this->submitLabel = $submitLabel;
  83. $this->contentModel = $title->getContentModel();
  84. $this->contentFormat = ContentHandler::getForModelID( $this->contentModel )->getDefaultFormat();
  85. }
  86. /**
  87. * @param string $yourtext
  88. * @param string $storedversion
  89. */
  90. public function setTextboxes( $yourtext, $storedversion ) {
  91. $this->yourtext = $yourtext;
  92. $this->storedversion = $storedversion;
  93. }
  94. /**
  95. * @param string $contentModel
  96. */
  97. public function setContentModel( $contentModel ) {
  98. $this->contentModel = $contentModel;
  99. }
  100. /**
  101. * @param string $contentFormat
  102. */
  103. public function setContentFormat( $contentFormat ) {
  104. $this->contentFormat = $contentFormat;
  105. }
  106. /**
  107. * Record a user encountering an edit conflict
  108. */
  109. public function incrementConflictStats() {
  110. $this->stats->increment( 'edit.failures.conflict' );
  111. // Only include 'standard' namespaces to avoid creating unknown numbers of statsd metrics
  112. if (
  113. $this->title->getNamespace() >= NS_MAIN &&
  114. $this->title->getNamespace() <= NS_CATEGORY_TALK
  115. ) {
  116. $this->stats->increment(
  117. 'edit.failures.conflict.byNamespaceId.' . $this->title->getNamespace()
  118. );
  119. }
  120. }
  121. /**
  122. * Record when a user has resolved an edit conflict
  123. */
  124. public function incrementResolvedStats() {
  125. $this->stats->increment( 'edit.failures.conflict.resolved' );
  126. // Only include 'standard' namespaces to avoid creating unknown numbers of statsd metrics
  127. if (
  128. $this->title->getNamespace() >= NS_MAIN &&
  129. $this->title->getNamespace() <= NS_CATEGORY_TALK
  130. ) {
  131. $this->stats->increment(
  132. 'edit.failures.conflict.resolved.byNamespaceId.' . $this->title->getNamespace()
  133. );
  134. }
  135. }
  136. /**
  137. * @return string HTML
  138. */
  139. public function getExplainHeader() {
  140. return Html::rawElement(
  141. 'div',
  142. [ 'class' => 'mw-explainconflict' ],
  143. $this->out->msg( 'explainconflict', $this->out->msg( $this->submitLabel )->text() )->parse()
  144. );
  145. }
  146. /**
  147. * HTML to build the textbox1 on edit conflicts
  148. *
  149. * @param array $customAttribs
  150. */
  151. public function getEditConflictMainTextBox( array $customAttribs = [] ) {
  152. $builder = new TextboxBuilder();
  153. $classes = $builder->getTextboxProtectionCSSClasses( $this->title );
  154. $attribs = [ 'tabindex' => 1 ];
  155. $attribs += $customAttribs;
  156. $attribs = $builder->mergeClassesIntoAttributes( $classes, $attribs );
  157. $attribs = $builder->buildTextboxAttribs(
  158. 'wpTextbox1',
  159. $attribs,
  160. $this->out->getUser(),
  161. $this->title
  162. );
  163. $this->out->addHTML(
  164. Html::textarea( 'wpTextbox1', $builder->addNewLineAtEnd( $this->storedversion ), $attribs )
  165. );
  166. }
  167. /**
  168. * Content to go in the edit form before textbox1
  169. *
  170. * @see EditPage::$editFormTextBeforeContent
  171. * @return string HTML
  172. */
  173. public function getEditFormHtmlBeforeContent() {
  174. return '';
  175. }
  176. /**
  177. * Content to go in the edit form after textbox1
  178. *
  179. * @see EditPage::$editFormTextAfterContent
  180. * @return string HTML
  181. */
  182. public function getEditFormHtmlAfterContent() {
  183. return '';
  184. }
  185. /**
  186. * Content to go in the edit form after the footers
  187. * (templates on this page, hidden categories, limit report)
  188. */
  189. public function showEditFormTextAfterFooters() {
  190. $this->out->wrapWikiMsg( '<h2>$1</h2>', "yourdiff" );
  191. $yourContent = $this->toEditContent( $this->yourtext );
  192. $storedContent = $this->toEditContent( $this->storedversion );
  193. $handler = ContentHandler::getForModelID( $this->contentModel );
  194. $diffEngine = $handler->createDifferenceEngine( $this->out );
  195. $diffEngine->setContent( $yourContent, $storedContent );
  196. $diffEngine->showDiff(
  197. $this->out->msg( 'yourtext' )->parse(),
  198. $this->out->msg( 'storedversion' )->text()
  199. );
  200. $this->out->wrapWikiMsg( '<h2>$1</h2>', "yourtext" );
  201. $builder = new TextboxBuilder();
  202. $attribs = $builder->buildTextboxAttribs(
  203. 'wpTextbox2',
  204. [ 'tabindex' => 6, 'readonly' ],
  205. $this->out->getUser(),
  206. $this->title
  207. );
  208. $this->out->addHTML(
  209. Html::textarea( 'wpTextbox2', $builder->addNewLineAtEnd( $this->yourtext ), $attribs )
  210. );
  211. }
  212. /**
  213. * @param string $text
  214. * @return Content
  215. */
  216. private function toEditContent( $text ) {
  217. return ContentHandler::makeContent(
  218. $text,
  219. $this->title,
  220. $this->contentModel,
  221. $this->contentFormat
  222. );
  223. }
  224. }