static function formatLogEntry($type, $action, $title, $sk, $parameters) { switch ($action) { case 'merge': if ($parameters[0]) { $msg = 'lqt-log-action-merge-across'; } else { $msg = 'lqt-log-action-merge-down'; } break; default: $msg = 'lqt-log-action-' . $action; break; } $options = array('parseinline'); $forIRC = $sk === null; if ($forIRC) { global $wgContLang; $options['language'] = $wgContLang->getCode(); } $replacements = array_merge(array($title->getPrefixedText()), $parameters); $html = wfMsgExt($msg, $options, $replacements); if ($forIRC) { $html = StringUtils::delimiterReplace('<', '>', '', $html); } return $html; }
/** * adding the flag table to the comments form * * @global SFFormPrinter $sfgFormPrinter from SMW * @global Article $wgArticle * @param String $sHtml * @return boolean */ function onAddCommentsFormDiv(&$sHtml) { global $sfgFormPrinter, $wgArticle, $webplatformSectionCommentsSMW; $sHtml .= '<a id="comments-flag-link">' . wfMessage('comments-flag-link')->text() . '</a>'; $sHtml .= '<div id="comment-flags">'; $sFormName = $webplatformSectionCommentsSMW['form']; //$sPageName = 'Comments'; $oTitle = Title::newFromText($sFormName, SF_NS_FORM); $oArticle = new Article($oTitle, 0); $sFormDefinition = $oArticle->getContent(); $sFormDefinition = StringUtils::delimiterReplace('<noinclude>', '</noinclude>', '', $sFormDefinition); $aHtml = $sfgFormPrinter->formHTML($sFormDefinition, false, true, $oTitle->getArticleID(), $wgArticle->fetchContent()); //, $wgArticle->getTitle()->getArticleID(), $wgArticle->fetchContent(), $wgArticle->getTitle()->getText(), null ); $aMatches = array(); preg_match_all('#<table.*?</table>#is', $aHtml[0], $aMatches); $index = null; foreach ($aMatches[0] as $key => $value) { $bPos = strrpos($value, $webplatformSectionCommentsSMW['template'] . '['); if ($bPos !== false) { $index = $key; break; } } $sHtml .= $aMatches[0][$index]; $sHtml .= '</div>'; return true; }
static function formatLogEntry($type, $action, $title, $sk, $parameters) { $msg = "lqt-log-action-{$action}"; switch ($action) { case 'merge': if ($parameters[0]) { $msg = 'lqt-log-action-merge-across'; } else { $msg = 'lqt-log-action-merge-down'; } break; case 'move': $smt = new SpecialMoveThread(); $rightsCheck = $smt->checkUserRights($parameters[1] instanceof Title ? $parameters[1] : Title::newFromText($parameters[1]), $parameters[0] instanceof Title ? $parameters[0] : Title::newFromText($parameters[0])); if ($rightsCheck === true) { $parameters[] = Message::rawParam(Linker::link(SpecialPage::getTitleFor('MoveThread', $title), wfMessage('revertmove')->text(), array(), array('dest' => $parameters[0]))); } else { $parameters[] = ''; } break; default: // Give grep a chance to find the usages: // lqt-log-action-move, lqt-log-action-split, lqt-log-action-subjectedit, // lqt-log-action-resort, lqt-log-action-signatureedit $msg = "lqt-log-action-{$action}"; break; } array_unshift($parameters, $title->getPrefixedText()); $html = wfMessage($msg, $parameters); if ($sk === null) { return StringUtils::delimiterReplace('<', '>', '', $html->inContentLanguage()->parse()); } return $html->parse(); }
/** * Core parser tag hook function for 'pre'. * Text is treated roughly as 'nowiki' wrapped in an HTML 'pre' tag; * valid HTML attributes are passed on. * * @param string $text * @param array $attribs * @param Parser $parser * @return string HTML */ public static function pre($text, $attribs, $parser) { // Backwards-compatibility hack $content = StringUtils::delimiterReplace('<nowiki>', '</nowiki>', '$1', $text, 'i'); $attribs = Sanitizer::validateTagAttributes($attribs, 'pre'); return Xml::openElement('pre', $attribs) . Xml::escapeTagsOnly($content) . '</pre>'; }
function execute($query) { global $wgRequest, $wgOut; $wgOut->disable(); $this->setHeaders(); $page_name = $query; $title = Title::newFromText($page_name); if (is_null($title)) { return; } if (!$title->userCan('read')) { return; } $article = new Article($title); $page_text = $article->fetchContent(); // Remove <noinclude> sections and <includeonly> tags from text $page_text = StringUtils::delimiterReplace('<noinclude>', '</noinclude>', '', $page_text); $page_text = strtr($page_text, array('<includeonly>' => '', '</includeonly>' => '')); $orig_lines = explode("\n", $page_text); // ignore lines that are either blank or start with a semicolon $page_lines = array(); foreach ($orig_lines as $i => $line) { if ($line != '' && $line[0] != ';') { $page_lines[] = $line; } } $headers = EDUtils::getValuesFromCSVLine($page_lines[0]); $queried_headers = array(); foreach ($wgRequest->getValues() as $key => $value) { foreach ($headers as $header_index => $header_value) { $header_value = str_replace(' ', '_', $header_value); if ($key == $header_value) { $queried_headers[$header_index] = $value; } } } // include header in output $text = $page_lines[0]; foreach ($page_lines as $i => $line) { if ($i == 0) { continue; } $row_values = EDUtils::getValuesFromCSVLine($line); $found_match = true; foreach ($queried_headers as $i => $query_value) { $single_value = str_replace(' ', '_', $row_values[$i]); if ($single_value != $query_value) { $found_match = false; } } if ($found_match) { if ($text != '') { $text .= "\n"; } $text .= $line; } } print $text; }
/** * Core parser tag hook function for 'pre'. * Text is treated roughly as 'nowiki' wrapped in an HTML 'pre' tag; * valid HTML attributes are passed on. * * @param string $text * @param array $attribs * @param Parser $parser * @return string HTML */ public static function pre($text, $attribs, $parser) { // Backwards-compatibility hack $content = StringUtils::delimiterReplace('<nowiki>', '</nowiki>', '$1', $text, 'i'); $attribs = Sanitizer::validateTagAttributes($attribs, 'pre'); // We need to let both '"' and '&' through, // for strip markers and entities respectively. $content = str_replace(['>', '<'], ['>', '<'], $content); return Html::rawElement('pre', $attribs, $content); }
/** * Appends a preview of the actual form, when a page in the "Form" * namespace is previewed. * * @author Solitarius * @since 2.4 * * @param EditPage $editpage * @param WebRequest $request * * @return true */ public static function showFormPreview(EditPage $editpage, WebRequest $request) { global $wgOut, $sfgFormPrinter; wfDebug(__METHOD__ . ": enter.\n"); wfProfileIn(__METHOD__); // Exit if we're not in preview mode. if (!$editpage->preview) { wfProfileOut(__METHOD__); return true; } // Exit if we aren't in the "Form" namespace. if ($editpage->getArticle()->getTitle()->getNamespace() != SF_NS_FORM) { wfProfileOut(__METHOD__); return true; } $editpage->previewTextAfterContent .= Html::element('h2', null, wfMessage('sf-preview-header')->text()) . "\n" . '<div class="previewnote" style="font-weight: bold">' . $wgOut->parse(wfMessage('sf-preview-note')->text()) . "</div>\n<hr />\n"; $form_definition = StringUtils::delimiterReplace('<noinclude>', '</noinclude>', '', $editpage->textbox1); list($form_text, $javascript_text, $data_text, $form_page_title, $generated_page_name) = $sfgFormPrinter->formHTML($form_definition, null, false, null, null, "Semantic Forms form preview dummy title", null); SFUtils::addJavascriptAndCSS(); $editpage->previewTextAfterContent .= '<div style="margin-top: 15px">' . $form_text . "</div>"; wfProfileOut(__METHOD__); return true; }
private function printForm(&$parameters, WebRequest &$request) { global $wgOut, $sfgFormPrinter; // Prepare parameters for SFFormPrinter::formHTML // there is no ONE target page $targetTitle = null; // formDefinition $formName = $request->getText('form'); // if query string did not contain these variables, try the URL if ($formName === '') { $queryparts = explode('/', $parameters); $formName = isset($queryparts[0]) ? $queryparts[0] : null; // if the form name wasn't in the URL either, throw an error if (is_null($formName) || $formName === '') { throw new SPSException(SPSUtils::buildMessage('spserror-noformname')); } } $formTitle = Title::makeTitleSafe(SF_NS_FORM, $formName); if (!$formTitle->exists()) { throw new SPSException(SPSUtils::buildMessage('spserror-formunknown', $formName)); } $formArticle = new Article($formTitle); $formDefinition = StringUtils::delimiterReplace('<noinclude>', '</noinclude>', '', $formArticle->getContent()); // formSubmitted $formSubmitted = false; // pageContents $pageContents = null; // get 'preload' query value, if it exists if ($request->getCheck('preload')) { $pageContents = SFFormUtils::getPreloadedText($request->getVal('preload')); } else { // let other extensions preload the page, if they want wfRunHooks('sfEditFormPreloadText', array(&$pageContents, $targetTitle, $formTitle)); } // pageIsSource $pageIsSource = $pageContents != null; // pageNameFormula // parse the form to see if it has a 'page name' value set $matches; if (preg_match('/{{{info.*page name\\s*=\\s*(.*)}}}/m', $formDefinition, $matches)) { $pageNameElements = SFUtils::getFormTagComponents($matches[1]); $pageNameFormula = $pageNameElements[0]; } else { return 'sf_formedit_badurl'; } // get the iterator parameters $iteratorData = $this->buildIteratorParameters($request); // Call SFFormPrinter::formHTML list($formText, $javascriptText, $dataText, $formPageTitle, $generatedPageName) = $sfgFormPrinter->formHTML($formDefinition, $formSubmitted, $pageIsSource, $formArticle->getID(), $pageContents, '', $pageNameFormula); // Set Special page main header; // override the default title for this page if a title was specified in the form if ($formPageTitle != null) { $wgOut->setPageTitle($formPageTitle); } else { $wgOut->setPageTitle(SPSUtils::buildMessage('sf_formedit_createtitlenotarget', $formTitle->getText())); } $preFormHtml = ''; wfRunHooks('sfHTMLBeforeForm', array(&$targetTitle, &$preFormHtml)); $text = '<form name="createbox" id="sfForm" action="" method="post" class="createbox">' . $preFormHtml . "\n" . SFFormUtils::hiddenFieldHTML('iteratordata', $iteratorData) . $formText; SFUtils::addJavascriptAndCSS(); if (!empty($javascriptText)) { $wgOut->addScript(' <script type="text/javascript">' . "\n{$javascriptText}\n" . '</script>' . "\n"); } $wgOut->addHTML($text); return null; }
function getAllFieldsCargo($templateTitle) { $cargoFieldsOfTemplateParams = array(); $templateFields = array(); // First, get the table name, and fields, declared for this // template. $templatePageID = $templateTitle->getArticleID(); $tableSchemaString = CargoUtils::getPageProp($templatePageID, 'CargoFields'); // See if there even is DB storage for this template - if not, // exit. if (is_null($tableSchemaString)) { return null; } $tableSchema = CargoTableSchema::newFromDBString($tableSchemaString); $tableName = CargoUtils::getPageProp($templatePageID, 'CargoTableName'); // Then, match template params to Cargo table fields, by // parsing call(s) to #cargo_store. $templateText = SFUtils::getPageText($templateTitle); // Ignore 'noinclude' sections and 'includeonly' tags. $templateText = StringUtils::delimiterReplace('<noinclude>', '</noinclude>', '', $templateText); $templateText = strtr($templateText, array('<includeonly>' => '', '</includeonly>' => '')); // Let's find every #cargo_store tag. // Unfortunately, it doesn't seem possible to use a regexp // search for this, because it's hard to know which set of // double brackets represents the end of such a call. Instead, // we'll do some manual parsing. $cargoStoreLocations = array(); $curPos = 0; while (true) { $newPos = strpos($templateText, "#cargo_store:", $curPos); if ($newPos === false) { break; } $curPos = $newPos + 13; $cargoStoreLocations[] = $curPos; } $cargoStoreCalls = array(); foreach ($cargoStoreLocations as $locNum => $startPos) { $numUnclosedBrackets = 2; if ($locNum < count($cargoStoreLocations) - 1) { $lastPos = $cargoStoreLocations[$locNum + 1]; } else { $lastPos = strlen($templateText) - 1; } $curCargoStoreCall = ''; $curPos = $startPos; while ($curPos <= $lastPos) { $curChar = $templateText[$curPos]; $curCargoStoreCall .= $curChar; if ($curChar == '}') { $numUnclosedBrackets--; } elseif ($curChar == '{') { $numUnclosedBrackets++; } if ($numUnclosedBrackets == 0) { break; } $curPos++; } $cargoStoreCalls[] = $curCargoStoreCall; } foreach ($cargoStoreCalls as $cargoStoreCall) { if (preg_match_all('/([^|{]*?)=\\s*{{{([^|}]*)/mis', $cargoStoreCall, $matches)) { foreach ($matches[1] as $i => $cargoFieldName) { $templateParameter = trim($matches[2][$i]); $cargoFieldsOfTemplateParams[$templateParameter] = $cargoFieldName; } } } // Now, combine the two sets of information into an array of // SFTemplateFields objects. $fieldDescriptions = $tableSchema->mFieldDescriptions; foreach ($cargoFieldsOfTemplateParams as $templateParameter => $cargoField) { $templateField = SFTemplateField::create($templateParameter, $templateParameter); if (array_key_exists($cargoField, $fieldDescriptions)) { $fieldDescription = $fieldDescriptions[$cargoField]; $templateField->setCargoFieldData($tableName, $cargoField, $fieldDescription); } $templateFields[] = $templateField; } return $templateFields; }
/** * Depending on the requested action this method will try to store/preview * the data in mOptions or retrieve the edit form. * * The form and target page will be available in mOptions after execution of * the method. * * Errors and warnings are logged in the API result under the 'errors' key. * The general request status is maintained in mStatus. * * @global $wgRequest * @global $wgOut * @global $sfgFormPrinter * @throws MWException */ public function doAction() { global $wgOut, $wgRequest, $sfgFormPrinter; // if the wiki is read-only, do not save if (wfReadOnly()) { if ($this->mAction === self::ACTION_SAVE) { throw new MWException(wfMessage('sf_autoedit_readonly', wfReadOnlyReason())->parse()); } // even if not saving notify client anyway. Might want to dislay a notice $this->logMessage(wfMessage('sf_autoedit_readonly', wfReadOnlyReason())->parse(), self::NOTICE); } // find the title of the form to be used $formTitle = $this->getFormTitle(); // get the form content $formContent = StringUtils::delimiterReplace('<noinclude>', '</noinclude>', '', WikiPage::factory($formTitle)->getRawText()); // signals that the form was submitted // always true, else we would not be here $isFormSubmitted = $this->mAction === self::ACTION_SAVE || $this->mAction === self::ACTION_PREVIEW; // the article id of the form to be used $formArticleId = $formTitle->getArticleID(); // source of the data is a page $isPageSource = true; // the name of the target page; might be empty when using the one-step-process $targetName = $this->mOptions['target']; // if the target page was not specified, try finding the page name formula // (Why is this not done in SFFormPrinter::formHTML?) if ($targetName === '') { // parse the form to see if it has a 'page name' value set if (preg_match('/{{{info.*page name\\s*=\\s*(.*)}}}/m', $formContent, $matches)) { $pageNameElements = SFUtils::getFormTagComponents($matches[1]); $targetNameFormula = $pageNameElements[0]; } else { throw new MWException(wfMessage('sf_autoedit_notargetspecified')->parse()); } $targetTitle = null; } else { $targetNameFormula = null; $targetTitle = Title::newFromText($targetName); } $preloadContent = ''; // save $wgRequest for later restoration $oldRequest = $wgRequest; // preload data if not explicitly excluded and if the preload page exists if (!isset($this->mOptions['preload']) || $this->mOptions['preload'] !== false) { if (!isset($this->mOptions['preload']) || $this->mOptions['preload'] === true) { $preloadTitle = Title::newFromText($targetName); } else { $preloadTitle = Title::newFromText($this->mOptions['preload']); } if ($preloadTitle !== null && $preloadTitle->exists()) { // the content of the page that was specified to be used for preloading $preloadContent = WikiPage::factory($preloadTitle)->getRawText(); wfRunHooks('sfEditFormPreloadText', array(&$preloadContent, $targetTitle, $formTitle)); $isPageSource = true; // spoof $wgRequest for SFFormPrinter::formHTML $wgRequest = new FauxRequest($this->mOptions, true); // save wgOut for later restoration $oldOut = $wgOut; // spoof wgOut; if we took the general $wgOut some JS modules // might attach themselves twice and thus be called twice $wgOut = new OutputPage(RequestContext::getMain()); // call SFFormPrinter::formHTML to get at the form html of the existing page list($formHTML, $formJS, $targetContent, $form_page_title, $generatedTargetNameFormula) = $sfgFormPrinter->formHTML($formContent, $isFormSubmitted, $isPageSource, $formArticleId, $preloadContent, $targetName, $targetNameFormula); // restore wgOut $wgOut = $oldOut; // parse the data to be preloaded from the form html of the // existing page $data = $this->parseDataFromHTMLFrag($formHTML); // and merge/overwrite it with the new data $this->mOptions = SFUtils::array_merge_recursive_distinct($data, $this->mOptions); } else { if (isset($this->mOptions['preload'])) { $this->logMessage(wfMessage('sf_autoedit_invalidpreloadspecified', $this->mOptions['preload'])->parse(), self::WARNING); } } } // we already preloaded stuff for saving/previewing, do not do it again if ($this->mAction === self::ACTION_SAVE || $this->mAction === self::ACTION_PREVIEW) { $preloadContent = ''; $isPageSource = false; } // spoof wgRequest for SFFormPrinter::formHTML $wgRequest = new FauxRequest($this->mOptions, true); // get wikitext for submitted data and form list($formHTML, $formJS, $targetContent, $generatedFormName, $generatedTargetNameFormula) = $sfgFormPrinter->formHTML($formContent, $isFormSubmitted, $isPageSource, $formArticleId, $preloadContent, $targetName, $targetNameFormula); // restore original request $wgRequest = $oldRequest; if ($generatedFormName !== '') { $formTitle = Title::newFromText($generatedFormName); $this->mOptions['form'] = $formTitle->getText(); } $this->mOptions['formHTML'] = $formHTML; $this->mOptions['formJS'] = $formJS; if ($this->mAction === self::ACTION_SAVE || $this->mAction === self::ACTION_PREVIEW) { // if the target page was not specified, see if something was generated // from the target name formula if ($this->mOptions['target'] === '') { // if no name was generated, we can not save => give up if ($generatedTargetNameFormula === '') { throw new MWException(wfMessage('sf_autoedit_notargetspecified')->parse()); } $this->mOptions['target'] = $this->generateTargetName($generatedTargetNameFormula); } // Lets other code process additional form-definition syntax wfRunHooks('sfWritePageData', array($this->mOptions['form'], Title::newFromText($this->mOptions['target']), &$targetContent)); $editor = $this->setupEditPage($targetContent); // perform the requested action if ($this->mAction === self::ACTION_PREVIEW) { $this->doPreview($editor); } else { $this->doStore($editor); } } else { if ($this->mAction === self::ACTION_FORMEDIT) { $this->doFormEdit($formHTML, $formJS); } } }
/** * Pick apart some CSS and check it for forbidden or unsafe structures. * Returns a sanitized string. This sanitized string will have * character references and escape sequences decoded and comments * stripped (unless it is itself one valid comment, in which case the value * will be passed through). If the input is just too evil, only a comment * complaining about evilness will be returned. * * Currently URL references, 'expression', 'tps' are forbidden. * * NOTE: Despite the fact that character references are decoded, the * returned string may contain character references given certain * clever input strings. These character references must * be escaped before the return value is embedded in HTML. * * @param string $value * @return string */ static function checkCss($value) { // Decode character references like { $value = Sanitizer::decodeCharReferences($value); // Decode escape sequences and line continuation // See the grammar in the CSS 2 spec, appendix D. // This has to be done AFTER decoding character references. // This means it isn't possible for this function to return // unsanitized escape sequences. It is possible to manufacture // input that contains character references that decode to // escape sequences that decode to character references, but // it's OK for the return value to contain character references // because the caller is supposed to escape those anyway. static $decodeRegex; if (!$decodeRegex) { $space = '[\\x20\\t\\r\\n\\f]'; $nl = '(?:\\n|\\r\\n|\\r|\\f)'; $backslash = '\\\\'; $decodeRegex = "/ {$backslash}\n\t\t\t\t(?:\n\t\t\t\t\t({$nl}) | # 1. Line continuation\n\t\t\t\t\t([0-9A-Fa-f]{1,6}){$space}? | # 2. character number\n\t\t\t\t\t(.) | # 3. backslash cancelling special meaning\n\t\t\t\t\t() | # 4. backslash at end of string\n\t\t\t\t)/xu"; } $value = preg_replace_callback($decodeRegex, array(__CLASS__, 'cssDecodeCallback'), $value); // Normalize Halfwidth and Fullwidth Unicode block that IE6 might treat as ascii $value = preg_replace_callback('/[!-[]-z]/u', function ($matches) { $cp = utf8ToCodepoint($matches[0]); if ($cp === false) { return ''; } return chr($cp - 65248); // ASCII range \x21-\x7A }, $value); // Convert more characters IE6 might treat as ascii // U+0280, U+0274, U+207F, U+029F, U+026A, U+207D, U+208D $value = str_replace(array('ʀ', 'ɴ', 'ⁿ', 'ʟ', 'ɪ', '⁽', '₍'), array('r', 'n', 'n', 'l', 'i', '(', '('), $value); // Let the value through if it's nothing but a single comment, to // allow other functions which may reject it to pass some error // message through. if (!preg_match('! ^ \\s* /\\* [^*\\/]* \\*/ \\s* $ !x', $value)) { // Remove any comments; IE gets token splitting wrong // This must be done AFTER decoding character references and // escape sequences, because those steps can introduce comments // This step cannot introduce character references or escape // sequences, because it replaces comments with spaces rather // than removing them completely. $value = StringUtils::delimiterReplace('/*', '*/', ' ', $value); // Remove anything after a comment-start token, to guard against // incorrect client implementations. $commentPos = strpos($value, '/*'); if ($commentPos !== false) { $value = substr($value, 0, $commentPos); } } // S followed by repeat, iteration, or prolonged sound marks, // which IE will treat as "ss" $value = preg_replace('/s(?: \\xE3\\x80\\xB1 | # U+3031 \\xE3\\x82\\x9D | # U+309D \\xE3\\x83\\xBC | # U+30FC \\xE3\\x83\\xBD | # U+30FD \\xEF\\xB9\\xBC | # U+FE7C \\xEF\\xB9\\xBD | # U+FE7D \\xEF\\xBD\\xB0 # U+FF70 )/ix', 'ss', $value); // Reject problematic keywords and control characters if (preg_match('/[\\000-\\010\\013\\016-\\037\\177]/', $value)) { return '/* invalid control char */'; } elseif (preg_match('! expression | filter\\s*: | accelerator\\s*: | -o-link\\s*: | -o-link-source\\s*: | -o-replace\\s*: | url\\s*\\( | image\\s*\\( | image-set\\s*\\( !ix', $value)) { return '/* insecure input */'; } return $value; }
public function formSerialize($form_def = '', $source_is_page = false, $existing_page_content = null, $page_title = null) { // public function formSerialize($form_def, $source_is_page, $existing_page_content = null, $page_title = null, $page_name_formula = null) { global $wgRequest, $wgUser, $wgParser; global $sfgTabIndex; // used to represent the current tab index in the form global $sfgFieldNum; // used for setting various HTML IDs global $sfgJSValidationCalls; // array of Javascript calls to determine if page can be saved # define a var for all fields $__fields = array(); // initialize some variables $sfgTabIndex = 1; $sfgFieldNum = 1; $source_page_matches_this_form = false; $form_page_title = NULL; // $form_is_partial is true if: // (a) 'partial' == 1 in the arguments // (b) 'partial form' is found in the form definition // in the latter case, it may remain false until close to the end of // the parsing, so we have to assume that it will become a possibility $form_is_partial = false; $new_text = ""; // if we have existing content and we're not in an active replacement // situation, preserve the original content. We do this because we want // to pass the original content on IF this is a partial form // TODO: A better approach here would be to pass the revision id of the // existing page content through the replace value, which would // minimize the html traffic and would allow us to do a concurrent // update check. For now, we pass it through the hidden text field... if (!$wgRequest->getCheck('partial')) { $original_page_content = $existing_page_content; } else { $original_page_content = null; if ($wgRequest->getCheck('free_text')) { $existing_page_content = $wgRequest->getVal('free_text'); $form_is_partial = true; } } // disable all form elements if user doesn't have edit permission - // two different checks are needed, because editing permissions can be // set in different ways // HACK - sometimes we don't know the page name in advance, but we still // need to set a title here for testing permissions if ($page_title == '') { $this->mPageTitle = Title::newFromText("Semantic Forms permissions test"); } else { $this->mPageTitle = Title::newFromText($page_title); } if ($wgUser->isAllowed('edit') && $this->mPageTitle->userCanEdit()) { $form_is_disabled = false; $form_text = ""; // show "Your IP address will be recorded" warning if user is // anonymous - wikitext for bolding has to be replaced with HTML if ($wgUser->isAnon()) { $anon_edit_warning = preg_replace("/'''(.*)'''/", "<strong>\$1</strong>", wfMsg('anoneditwarning')); $form_text .= "<p>{$anon_edit_warning}</p>\n"; } } else { $form_is_disabled = true; // display a message to the user explaining why they can't edit the // page - borrowed heavily from EditPage.php if ($wgUser->isAnon()) { $skin = $wgUser->getSkin(); $loginTitle = SpecialPage::getTitleFor('Userlogin'); $loginLink = $skin->makeKnownLinkObj($loginTitle, wfMsgHtml('loginreqlink')); $form_text = wfMsgWikiHtml('whitelistedittext', $loginLink); } else { $form_text = wfMsg('protectedpagetext'); } } $javascript_text = ""; $sfgJSValidationCalls = array(); $fields_javascript_text = ""; // Remove <noinclude> sections and <includeonly> tags from form definition $form_def = StringUtils::delimiterReplace('<noinclude>', '</noinclude>', '', $form_def); $form_def = strtr($form_def, array('<includeonly>' => '', '</includeonly>' => '')); // parse wiki-text // add '<nowiki>' tags around every triple-bracketed form definition // element, so that the wiki parser won't touch it - the parser will // remove the '<nowiki>' tags, leaving us with what we need global $sfgDisableWikiTextParsing; if (!$sfgDisableWikiTextParsing) { $form_def = "__NOEDITSECTION__" . strtr($form_def, array('{{{' => '<nowiki>{{{', '}}}' => '}}}</nowiki>')); $wgParser->mOptions = new ParserOptions(); $wgParser->mOptions->initialiseFromUser($wgUser); $form_def = $wgParser->parse($form_def, $this->mPageTitle, $wgParser->mOptions)->getText(); } // turn form definition file into an array of sections, one for each // template definition (plus the first section) $form_def_sections = array(); $start_position = 0; $section_start = 0; $free_text_was_included = false; $free_text_preload_page = null; $all_values_for_template = array(); // unencode and HTML-encoded representations of curly brackets and // pipes - this is a hack to allow for forms to include templates // that themselves contain form elements - the escaping is needed // to make sure that those elements don't get parsed too early $form_def = str_replace(array('{', '|', '}'), array('{', '|', '}'), $form_def); // and another hack - replace the 'free text' standard input with // a field declaration to get it to be handled as a field $form_def = str_replace('standard input|free text', 'field|<freetext>', $form_def); while ($brackets_loc = strpos($form_def, "{{{", $start_position)) { $brackets_end_loc = strpos($form_def, "}}}", $brackets_loc); $bracketed_string = substr($form_def, $brackets_loc + 3, $brackets_end_loc - ($brackets_loc + 3)); $tag_components = explode('|', $bracketed_string); $tag_title = trim($tag_components[0]); if ($tag_title == 'for template' || $tag_title == 'end template') { // create a section for everything up to here $section = substr($form_def, $section_start, $brackets_loc - $section_start); $form_def_sections[] = $section; $section_start = $brackets_loc; } $start_position = $brackets_loc + 1; } // end while $form_def_sections[] = trim(substr($form_def, $section_start)); // cycle through form definition file (and possibly an existing article // as well), finding template and field declarations and replacing them // with form elements, either blank or pre-populated, as appropriate $all_fields = array(); $data_text = ""; $template_name = ""; $allow_multiple = false; $instance_num = 0; $all_instances_printed = false; $strict_parsing = false; // initialize list of choosers (dropdowns with available templates) $choosers = array(); for ($section_num = 0; $section_num < count($form_def_sections); $section_num++) { $tif = new SFTemplateInForm(); $start_position = 0; $template_text = ""; // the append is there to ensure that the original array doesn't get // modified; is it necessary? $section = " " . $form_def_sections[$section_num]; while ($brackets_loc = strpos($section, '{{{', $start_position)) { $brackets_end_loc = strpos($section, "}}}", $brackets_loc); $bracketed_string = substr($section, $brackets_loc + 3, $brackets_end_loc - ($brackets_loc + 3)); $tag_components = explode('|', $bracketed_string); $tag_title = trim($tag_components[0]); // ===================================================== // for template processing // ===================================================== if ($tag_title == 'for template') { $old_template_name = $template_name; $template_name = trim($tag_components[1]); $tif->template_name = $template_name; $query_template_name = str_replace(' ', '_', $template_name); // also replace periods with underlines, since that's what // POST does to strings anyway $query_template_name = str_replace('.', '_', $query_template_name); $chooser_name = false; $chooser_caption = false; // cycle through the other components for ($i = 2; $i < count($tag_components); $i++) { $component = $tag_components[$i]; if ($component == 'multiple') { $allow_multiple = true; } if ($component == 'strict') { $strict_parsing = true; } $sub_components = explode('=', $component); if (count($sub_components) == 2) { if ($sub_components[0] == 'label') { $template_label = $sub_components[1]; } elseif ($sub_components[0] == 'chooser') { $allow_multiple = true; $chooser_name = $sub_components[1]; } elseif ($sub_components[0] == 'chooser caption') { $chooser_caption = $sub_components[1]; } } } // if this is the first instance, add the label in the form if ($old_template_name != $template_name && isset($template_label)) { // add a placeholder to the form text so the fieldset can be // hidden if chooser support demands it if ($chooser_name !== false) { $form_text .= "<fieldset [[placeholder]] haschooser=true>\n"; } else { $form_text .= "<fieldset>\n"; } $form_text .= "<legend>{$template_label}</legend>\n"; } $template_text .= "{{" . $tif->template_name; # reads all the fields of the template definition page $all_fields = $tif->getAllFields(); // remove template tag $section = substr_replace($section, '', $brackets_loc, $brackets_end_loc + 3 - $brackets_loc); $template_instance_query_values = $wgRequest->getArray($query_template_name); // if we are editing a page, and this template can be found more than // once in that page, and multiple values are allowed, repeat this // section $existing_template_text = null; // replace underlines with spaces in template name, to allow for // searching on either $search_template_str = str_replace('_', ' ', $tif->template_name); if ($source_is_page || $form_is_partial) { if ($allow_multiple) { // find instances of this template in the page - // if there's at least one, re-parse this section of the // definition form for the subsequent template instances in // this page; if there's none, don't include fields at all. // there has to be a more efficient way to handle multiple // instances of templates, one that doesn't involve re-parsing // the same tags, but I don't know what it is. if (stripos(str_replace('_', ' ', $existing_page_content), '{{' . $search_template_str) !== false) { $instance_num++; } else { $all_instances_printed = true; } } // get the first instance of this template on the page being edited, // even if there are more if (($start_char = stripos(str_replace('_', ' ', $existing_page_content), '{{' . $search_template_str)) !== false) { $fields_start_char = $start_char + 2 + strlen($search_template_str); // skip ahead to the first real character while (in_array($existing_page_content[$fields_start_char], array(' ', '\\n', '|'))) { $fields_start_char++; } $template_contents = array('0' => ''); // cycle through template call, splitting it up by pipes ('|'), // except when that pipe is part of a piped link $field = ""; $uncompleted_square_brackets = 0; $uncompleted_curly_brackets = 2; $template_ended = false; for ($i = $fields_start_char; !$template_ended && $i < strlen($existing_page_content); $i++) { $c = $existing_page_content[$i]; if ($c == '[') { $uncompleted_square_brackets++; } elseif ($c == ']' && $uncompleted_square_brackets > 0) { $uncompleted_square_brackets--; } elseif ($c == '{') { $uncompleted_curly_brackets++; } elseif ($c == '}' && $uncompleted_curly_brackets > 0) { $uncompleted_curly_brackets--; } // handle an end to a field and/or template declaration $template_ended = $uncompleted_curly_brackets == 0 && $uncompleted_square_brackets == 0; $field_ended = $c == '|' && $uncompleted_square_brackets == 0; if ($template_ended || $field_ended) { // if this was the last character in the template, remove // the closing curly brackets if ($template_ended) { $field = substr($field, 0, -1); } // either there's an equals sign near the beginning or not - // handling is similar in either way; if there's no equals // sign, the index of this field becomes the key $sub_fields = explode('=', $field, 2); if (count($sub_fields) > 1) { $template_contents[trim($sub_fields[0])] = trim($sub_fields[1]); } else { $template_contents[] = trim($sub_fields[0]); } $field = ''; } else { $field .= $c; } } $existing_template_text = substr($existing_page_content, $start_char, $i - $start_char); // now remove this template from the text being edited // if this is a partial form, establish a new insertion point if ($existing_page_content && $form_is_partial && $wgRequest->getCheck('partial')) { // if something already exists, set the new insertion point // to its position; otherwise just let it lie if (strpos($existing_page_content, $existing_template_text) !== false) { $existing_page_content = str_replace('{{{insertionpoint}}}', '', $existing_page_content); $existing_page_content = str_replace($existing_template_text, '{{{insertionpoint}}}', $existing_page_content); } } else { $existing_page_content = str_replace($existing_template_text, '', $existing_page_content); } // if this is not a multiple-instance template, and we've found // a match in the source page, there's a good chance that this // page was created with this form - note that, so we don't // send the user a warning // (multiple-instance templates have a greater chance of // getting repeated from one form to the next) if (!$allow_multiple) { $source_page_matches_this_form = true; } } } // if the input is from the form (meaning the user has hit one // of the bottom row of buttons), and we're dealing with a // multiple template, get the values for this instance of this // template, then delete them from the array, so we can get the // next group next time - the next() command for arrays doesn't // seem to work here if (!$source_is_page && $allow_multiple && $wgRequest) { $all_instances_printed = true; if ($old_template_name != $template_name) { $all_values_for_template = $wgRequest->getArray($query_template_name); } if ($all_values_for_template) { $cur_key = key($all_values_for_template); // skip the input coming in from the "starter" div if ($cur_key == 'num') { unset($all_values_for_template[$cur_key]); $cur_key = key($all_values_for_template); } if ($template_instance_query_values = current($all_values_for_template)) { $all_instances_printed = false; unset($all_values_for_template[$cur_key]); } } } // save the template name $field = array(); $field['tmpl_name'] = $tif->template_name; // ===================================================== // end template processing // ===================================================== } elseif ($tag_title == 'end template') { // remove this tag, reset some variables, and close off form HTML tag $section = substr_replace($section, '', $brackets_loc, $brackets_end_loc + 3 - $brackets_loc); $template_name = null; if (isset($template_label)) { $form_text .= "</fieldset>\n"; unset($template_label); } $allow_multiple = false; $all_instances_printed = false; $instance_num = 0; // if the hiding placeholder is still around, this fieldset should // be hidden because it is empty and choosers are being used. So, // hide it. $form_text = str_replace("[[placeholder]]", "style='display:none'", $form_text); $__fields["template" . count($__fields)] = $field; // ===================================================== // field processing // ===================================================== } elseif ($tag_title == 'field') { $field_name = trim($tag_components[1]); // cycle through the other components $is_mandatory = false; $is_hidden = false; $is_restricted = false; $is_uploadable = false; $is_list = false; $input_type = null; $field_args = array(); $default_value = ""; $possible_values = null; $preload_page = null; for ($i = 2; $i < count($tag_components); $i++) { $component = trim($tag_components[$i]); if ($component == 'mandatory') { $is_mandatory = true; } elseif ($component == 'hidden') { $is_hidden = true; } elseif ($component == 'restricted') { $is_restricted = true; } elseif ($component == 'uploadable') { $field_args['is_uploadable'] = true; } elseif ($component == 'list') { $is_list = true; } elseif ($component == 'autocomplete') { $field_args['autocomplete'] = true; } elseif ($component == 'no autocomplete') { $field_args['no autocomplete'] = true; } elseif ($component == 'remote autocompletion') { $field_args['remote autocompletion'] = true; } else { $sub_components = explode('=', $component); if (count($sub_components) == 2) { if ($sub_components[0] == 'input type') { $input_type = $sub_components[1]; } elseif ($sub_components[0] == 'default') { $default_value = $sub_components[1]; } elseif ($sub_components[0] == 'preload') { // free text field has special handling if ($field_name == 'free text' || ($field_name = '<freetext>')) { $free_text_preload_page = $sub_components[1]; } else { // this variable is not used $preload_page = $sub_components[1]; } } elseif ($sub_components[0] == 'values') { $possible_values = explode(',', $sub_components[1]); } elseif ($sub_components[0] == 'values from category') { $possible_values = SFUtils::getAllPagesForCategory($sub_components[1], 10); } elseif ($sub_components[0] == 'values from concept') { $possible_values = SFUtils::getAllPagesForConcept($sub_components[1]); } else { $field_args[$sub_components[0]] = $sub_components[1]; } // for backwards compatibility if ($sub_components[0] == 'autocomplete on' && $sub_components[1] == null) { $field_args['no autocomplete'] = true; } } } } $field_args['part_of_multiple'] = $allow_multiple; // get the value from the request, if it's there, and if it's not // an array $escaped_field_name = str_replace("'", "\\'", $field_name); if (isset($template_instance_query_values) && $template_instance_query_values != null && array_key_exists($escaped_field_name, $template_instance_query_values)) { $field_query_val = $template_instance_query_values[$escaped_field_name]; if ($field_query_val && !is_array($field_query_val)) { $cur_value = $field_query_val; } } else { $cur_value = ''; } if ($cur_value && !is_array($cur_value)) { # no escaping needed // $cur_value = str_replace('"', '"', $cur_value); } if ($cur_value == null) { // set to default value specified in the form, if it's there $cur_value = $default_value; } // if the user is starting to edit a page, and that page contains // the current template being processed, get the current template // field's value in the existing page if ($source_is_page && !empty($existing_template_text)) { if (isset($template_contents[$field_name])) { $cur_value = $template_contents[$field_name]; } else { $cur_value = ''; } if ($cur_value) { # no escaping needed // $cur_value = str_replace('"', '"', $cur_value); } } // handle the free text field - if it was declared as // "field|free text" (a deprecated usage), it has to be outside // of a template if ($template_name == '' && $field_name == 'free text' || $field_name == '<freetext>') { // add placeholders for the free text in both the form and // the page, using <free_text> tags - once all the free text // is known (at the end), it will get substituted in if ($is_hidden) { $new_text = SFFormUtils::hiddenFieldHTML('free_text', '<free_text>'); } else { if (!array_key_exists('rows', $field_args)) { $field_args['rows'] = 5; } if (!array_key_exists('cols', $field_args)) { $field_args['cols'] = 80; } $sfgTabIndex++; $sfgFieldNum++; list($new_text, $new_javascript_text) = SFFormInputs::textAreaHTML('<free_text>', 'free_text', false, $form_is_disabled || $is_restricted, $field_args); $fields_javascript_text .= $new_javascript_text; } $free_text_was_included = true; // add a similar placeholder to the data text $data_text .= "<free_text>\n"; } if ($template_name == '') { $section = substr_replace($section, $new_text, $brackets_loc, $brackets_end_loc + 3 - $brackets_loc); } else { if (is_array($cur_value)) { // first, check if it's a list if (array_key_exists('is_list', $cur_value) && $cur_value['is_list'] == true) { $cur_value_in_template = ""; if (array_key_exists('delimiter', $field_args)) { $delimiter = $field_args['delimiter']; } else { $delimiter = ","; } foreach ($cur_value as $key => $val) { if ($key !== "is_list") { if ($cur_value_in_template != "") { $cur_value_in_template .= $delimiter . " "; } $cur_value_in_template .= $val; } } } else { // otherwise: // if it has 1 or 2 elements, assume it's a checkbox; if it has // 3 elements, assume it's a date // - this handling will have to get more complex if other // possibilities get added if (count($cur_value) == 1) { // manually load SMW's message values here, in case they // didn't get loaded before wfLoadExtensionMessages('SemanticMediaWiki'); $words_for_false = explode(',', wfMsgForContent('smw_false_words')); // for each language, there's a series of words that are // equal to false - get the word in the series that matches // "no"; generally, that's the third word $index_of_no = 2; if (count($words_for_false) > $index_of_no) { $no = ucwords($words_for_false[$index_of_no]); } elseif (count($words_for_false) == 0) { $no = "0"; // some safe value if no words are found } else { $no = ucwords($words_for_false[0]); } $cur_value_in_template = $no; } elseif (count($cur_value) == 2) { wfLoadExtensionMessages('SemanticMediaWiki'); $words_for_true = explode(',', wfMsgForContent('smw_true_words')); // get the value in the 'true' series that tends to be "yes", // and go with that one - generally, that's the third word $index_of_yes = 2; if (count($words_for_true) > $index_of_yes) { $yes = ucwords($words_for_true[$index_of_yes]); } elseif (count($words_for_true) == 0) { $yes = "1"; // some safe value if no words are found } else { $yes = ucwords($words_for_true[0]); } $cur_value_in_template = $yes; // if it's 3 or greater, assume it's a date or datetime } elseif (count($cur_value) >= 3) { $month = $cur_value['month']; $day = $cur_value['day']; if ($day != '') { global $wgAmericanDates; if ($wgAmericanDates == false) { // pad out day to always be two digits $day = str_pad($day, 2, "0", STR_PAD_LEFT); } } $year = $cur_value['year']; if (isset($cur_value['hour'])) { $hour = $cur_value['hour']; } if (isset($cur_value['minute'])) { $minute = $cur_value['minute']; } if (isset($cur_value['second'])) { $second = $cur_value['second']; } if (isset($cur_value['ampm24h'])) { $ampm24h = $cur_value['ampm24h']; } if (isset($cur_value['timezone'])) { $timezone = $cur_value['timezone']; } if ($month != '' && $day != '' && $year != '') { // special handling for American dates - otherwise, just // the standard year/month/day (where month is a number) global $wgAmericanDates; if ($wgAmericanDates == true) { $cur_value_in_template = "{$month} {$day}, {$year}"; } else { $cur_value_in_template = "{$year}/{$month}/{$day}"; } // include whatever time information we have if (isset($hour)) { $cur_value_in_template .= " " . str_pad(intval(substr($hour, 0, 2)), 2, '0', STR_PAD_LEFT) . ":" . str_pad(intval(substr($minute, 0, 2)), 2, '0', STR_PAD_LEFT); } if (isset($second)) { $cur_value_in_template .= ":" . str_pad(intval(substr($second, 0, 2)), 2, '0', STR_PAD_LEFT); } if (isset($ampm24h)) { $cur_value_in_template .= " {$ampm24h}"; } if (isset($timezone)) { $cur_value_in_template .= " {$timezone}"; } } else { $cur_value_in_template = ""; } } } } else { // value is not an array $cur_value_in_template = $cur_value; } if ($query_template_name == null || $query_template_name == '') { $input_name = $field_name; } elseif ($allow_multiple) { // 'num' will get replaced by an actual index, either in PHP // or in Javascript, later on $input_name = $query_template_name . '[num][' . $field_name . ']'; } else { $input_name = $query_template_name . '[' . $field_name . ']'; } // disable this field if either the whole form is disabled, or // it's a restricted field and user doesn't have sysop privileges $is_disabled = $form_is_disabled || $is_restricted && (!$wgUser || !$wgUser->isAllowed('editrestrictedfields')); // create an SFFormTemplateField instance based on all the // parameters in the form definition, and any information from // the template definition (contained in the $all_fields parameter) # creation of a form field from the definition page $possible_values['_element'] = "value"; $form_field = $this->createFromDefinitionForSerialization($field_name, $input_name, $is_mandatory, $is_hidden, $is_uploadable, $possible_values, $is_disabled, $is_list, $input_type, $field_args, $all_fields, $strict_parsing); // if this is not part of a 'multiple' template, incrememt the // global tab index (used for correct tabbing) if (!$field_args['part_of_multiple']) { $sfgTabIndex++; } // increment the global field number regardless $sfgFieldNum++; // if the field is a date field, and its default value was set // to 'now', and it has no current value, set $cur_value to be // the current date if ($default_value == 'now' && ($cur_value == '' || $cur_value == 'now')) { if ($input_type == 'date' || $input_type == 'datetime' || $input_type == 'datetime with timezone' || $input_type == '' && $form_field->template_field->field_type == 'Date') { $cur_time = time(); $year = date("Y", $cur_time); $month = date("n", $cur_time); $day = date("j", $cur_time); global $wgAmericanDates, $sfg24HourTime; if ($wgAmericanDates == true) { $month_names = SFFormUtils::getMonthNames(); $month_name = $month_names[$month - 1]; $cur_value_in_template = "{$month_name} {$day}, {$year}"; } else { $cur_value_in_template = "{$year}/{$month}/{$day}"; } if ($input_type == 'datetime' || $input_type == 'datetime with timezone') { if ($sfg24HourTime) { $hour = str_pad(intval(substr(date("G", $cur_time), 0, 2)), 2, '0', STR_PAD_LEFT); } else { $hour = str_pad(intval(substr(date("g", $cur_time), 0, 2)), 2, '0', STR_PAD_LEFT); } $minute = str_pad(intval(substr(date("i", $cur_time), 0, 2)), 2, '0', STR_PAD_LEFT); $second = str_pad(intval(substr(date("s", $cur_time), 0, 2)), 2, '0', STR_PAD_LEFT); if ($sfg24HourTime) { $cur_value_in_template .= " {$hour}:{$minute}:{$second}"; } else { $ampm = date("A", $cur_time); $cur_value_in_template .= " {$hour}:{$minute}:{$second} {$ampm}"; } } if ($input_type == 'datetime with timezone') { $timezone = date("T", $cur_time); $cur_value_in_template .= " {$timezone}"; } } } // if the field is a text field, and its default value was set // to 'current user', and it has no current value, set $cur_value // to be the current user if ($default_value == 'current user' && ($cur_value == '' || $cur_value == 'current user')) { if ($input_type == 'text' || $input_type == '') { $cur_value_in_template = $wgUser->getName(); $cur_value = $cur_value_in_template; } } # field + field value $form_field->cur_value = $cur_value; # possible_values hack $__tmpValues = $form_field->template_field->possible_values; $form_field->template_field->possible_values = array(); if ($__tmpValues != NULL) { foreach ($__tmpValues as $key => $value) { $form_field->template_field->possible_values["value" . $key] = $value; } } $field["field" . count($field)] = $this->toArrayForSerialize($form_field); $new_text = "dummy"; // set only in order to break if ($new_text) { $section = substr_replace($section, $new_text, $brackets_loc, $brackets_end_loc + 3 - $brackets_loc); } else { $start_position = $brackets_end_loc; } } } else { // tag is not one of the three allowed values // ignore tag $start_position = $brackets_end_loc; } // end if } // end while } // end for // get free text, and add to page data, as well as retroactively // inserting it into the form // If $form_is_partial is true then either: // (a) we're processing a replacement (param 'partial' == 1) // (b) we're sending out something to be replaced (param 'partial' is missing) if ($form_is_partial) { if (!$wgRequest->getCheck('partial')) { $free_text = $original_page_content; $form_text .= SFFormUtils::hiddenFieldHTML('partial', 1); } else { $free_text = null; $existing_page_content = preg_replace('/²\\{(.*?)\\}²/s', '{{\\1}}', $existing_page_content); $existing_page_content = preg_replace('/\\{\\{\\{insertionpoint\\}\\}\\}/', '', $existing_page_content); $existing_page_content = Sanitizer::safeEncodeAttribute($existing_page_content); } } elseif ($source_is_page) { // if the page is the source, free_text will just be whatever in the // page hasn't already been inserted into the form $free_text = trim($existing_page_content); // or get it from a form submission } elseif ($wgRequest->getCheck('free_text')) { $free_text = $wgRequest->getVal('free_text'); if (!$free_text_was_included) { $data_text .= "<free_text>"; } // or get it from the form definition } elseif ($free_text_preload_page != null) { $free_text = SFFormUtils::getPreloadedText($free_text_preload_page); } else { $free_text = null; } # the free text is set here // if the FCKeditor extension is installed, use that for the free text input global $wgFCKEditorDir; if ($wgFCKEditorDir) { $showFCKEditor = SFFormUtils::getShowFCKEditor(); $free_text = htmlspecialchars($free_text); if ($showFCKEditor & RTE_VISIBLE) { $free_text = SFFormUtils::prepareTextForFCK($free_text); } } else { $showFCKEditor = 0; $free_text = Sanitizer::safeEncodeAttribute($free_text); } // now that we have it, substitute free text into the form and page $form_text = str_replace('<free_text>', $free_text, $form_text); $data_text = str_replace('<free_text>', $free_text, $data_text); # return the fields return $__fields; }
/** * Strips a text string of wikitext for use in a section anchor * * Accepts a text string and then removes all wikitext from the * string and leaves only the resultant text (i.e. the result of * [[User:WikiSysop|Sysop]] would be "Sysop" and the result of * [[User:WikiSysop]] would be "User:WikiSysop") - this is intended * to create valid section anchors by mimicing the output of the * parser when headings are parsed. * * @param string $text text string to be stripped of wikitext * for use in a Section anchor * @return string Filtered text string */ public function stripSectionName($text) { # Strip internal link markup $text = preg_replace('/\\[\\[:?([^[|]+)\\|([^[]+)\\]\\]/', '$2', $text); $text = preg_replace('/\\[\\[:?([^[]+)\\|?\\]\\]/', '$1', $text); # Strip external link markup # @todo FIXME: Not tolerant to blank link text # I.E. [https://www.mediawiki.org] will render as [1] or something depending # on how many empty links there are on the page - need to figure that out. $text = preg_replace('/\\[(?i:' . $this->mUrlProtocols . ')([^ ]+?) ([^[]+)\\]/', '$2', $text); # Parse wikitext quotes (italics & bold) $text = $this->doQuotes($text); # Strip HTML tags $text = StringUtils::delimiterReplace('<', '>', '', $text); return $text; }
/** * Get the fields of the template, along with the semantic property * attached to each one (if any), by parsing the text of the template. */ function getAllFields() { global $wgContLang; $templateFields = array(); $fieldNamesArray = array(); // The way this works is that fields are found and then stored // in an array based on their location in the template text, so // that they can be returned in the order in which they appear // in the template, not the order in which they were found. // Some fields can be found more than once (especially if // they're part of an "#if" statement), so they're only // recorded the first time they're found. $template_title = Title::makeTitleSafe(NS_TEMPLATE, $this->mTemplateName); $template_article = null; if (isset($template_title)) { $template_article = new Article($template_title, 0); } if (isset($template_article)) { $templateText = $template_article->getContent(); // Ignore 'noinclude' sections and 'includeonly' tags. $templateText = StringUtils::delimiterReplace('<noinclude>', '</noinclude>', '', $templateText); $templateText = strtr($templateText, array('<includeonly>' => '', '</includeonly>' => '')); // First, look for "arraymap" parser function calls // that map a property onto a list. if ($ret = preg_match_all('/{{#arraymap:{{{([^|}]*:?[^|}]*)[^\\[]*\\[\\[([^:]*:?[^:]*)::/mis', $templateText, $matches)) { foreach ($matches[1] as $i => $field_name) { if (!in_array($field_name, $fieldNamesArray)) { $propertyName = $matches[2][$i]; $this->handlePropertySettingInTemplate($field_name, $propertyName, true, $templateFields, $templateText); $fieldNamesArray[] = $field_name; } } } elseif ($ret === false) { // There was an error in the preg_match_all() // call - let the user know about it. if (preg_last_error() == PREG_BACKTRACK_LIMIT_ERROR) { print 'Semantic Forms error: backtrace limit exceeded during parsing! Please increase the value of <a href="http://www.php.net/manual/en/pcre.configuration.php#ini.pcre.backtrack-limit">pcre.backtrack-limit</a> in the PHP settings.'; } } // Second, look for normal property calls. if (preg_match_all('/\\[\\[([^:|\\[\\]]*:*?[^:|\\[\\]]*)::{{{([^\\]\\|}]*).*?\\]\\]/mis', $templateText, $matches)) { foreach ($matches[1] as $i => $propertyName) { $field_name = trim($matches[2][$i]); if (!in_array($field_name, $fieldNamesArray)) { $propertyName = trim($propertyName); $this->handlePropertySettingInTemplate($field_name, $propertyName, false, $templateFields, $templateText); $fieldNamesArray[] = $field_name; } } } // Then, get calls to #set and #set_internal // (thankfully, they have basically the same syntax). if (preg_match_all('/#(set|set_internal):(.*?}}})\\s*}}/mis', $templateText, $matches)) { foreach ($matches[2] as $match) { if (preg_match_all('/([^|{]*?)=\\s*{{{([^|}]*)/mis', $match, $matches2)) { foreach ($matches2[1] as $i => $propertyName) { $fieldName = trim($matches2[2][$i]); if (!in_array($fieldName, $fieldNamesArray)) { $propertyName = trim($propertyName); $this->handlePropertySettingInTemplate($fieldName, $propertyName, false, $templateFields, $templateText); $fieldNamesArray[] = $fieldName; } } } } } // Then, get calls to #declare. if (preg_match_all('/#declare:(.*?)}}/mis', $templateText, $matches)) { foreach ($matches[1] as $match) { $setValues = explode('|', $match); foreach ($setValues as $valuePair) { $keyAndVal = explode('=', $valuePair); if (count($keyAndVal) == 2) { $propertyName = trim($keyAndVal[0]); $fieldName = trim($keyAndVal[1]); if (!in_array($fieldName, $fieldNamesArray)) { $this->handlePropertySettingInTemplate($fieldName, $propertyName, false, $templateFields, $templateText); $fieldNamesArray[] = $fieldName; } } } } } // Finally, get any non-semantic fields defined. if (preg_match_all('/{{{([^|}]*)/mis', $templateText, $matches)) { foreach ($matches[1] as $fieldName) { $fieldName = trim($fieldName); if (!empty($fieldName) && !in_array($fieldName, $fieldNamesArray)) { $cur_pos = stripos($templateText, $fieldName); $templateFields[$cur_pos] = SFTemplateField::create($fieldName, $wgContLang->ucfirst($fieldName)); $fieldNamesArray[] = $fieldName; } } } } ksort($templateFields); return $templateFields; }
function compute($vars) { $parameters = $this->mParameters; $result = null; switch ($this->mMethod) { case 'diff': $text1Var = $parameters['oldtext-var']; $text2Var = $parameters['newtext-var']; $text1 = $vars->getVar($text1Var)->toString(); $text2 = $vars->getVar($text2Var)->toString(); $result = wfDiff($text1, $text2); $result = trim(preg_replace("/^\\\\ No newline at end of file\n/m", '', $result)); break; case 'diff-split': $diff = $vars->getVar($parameters['diff-var'])->toString(); $line_prefix = $parameters['line-prefix']; $diff_lines = explode("\n", $diff); $interest_lines = array(); foreach ($diff_lines as $line) { if (substr($line, 0, 1) === $line_prefix) { $interest_lines[] = substr($line, strlen($line_prefix)); } } $result = $interest_lines; break; case 'links-from-wikitext': // This should ONLY be used when sharing a parse operation with the edit. $article = $parameters['article']; if ($article) { $textVar = $parameters['text-var']; $new_text = $vars->getVar($textVar)->toString(); $editInfo = $article->prepareTextForEdit($new_text); $links = array_keys($editInfo->output->getExternalLinks()); $result = $links; break; } // Otherwise fall back to database // Otherwise fall back to database case 'links-from-wikitext-nonedit': case 'links-from-wikitext-or-database': $article = self::articleFromTitle($parameters['namespace'], $parameters['title']); if ($vars->getVar('context')->toString() == 'filter') { $links = $this->getLinksFromDB($article); wfDebug("AbuseFilter: loading old links from DB\n"); } else { wfDebug("AbuseFilter: loading old links from Parser\n"); $textVar = $parameters['text-var']; $wikitext = $vars->getVar($textVar)->toString(); $editInfo = $this->parseNonEditWikitext($wikitext, $article); $links = array_keys($editInfo->output->getExternalLinks()); } $result = $links; break; case 'link-diff-added': case 'link-diff-removed': $oldLinkVar = $parameters['oldlink-var']; $newLinkVar = $parameters['newlink-var']; $oldLinks = $vars->getVar($oldLinkVar)->toString(); $newLinks = $vars->getVar($newLinkVar)->toString(); $oldLinks = explode("\n", $oldLinks); $newLinks = explode("\n", $newLinks); if ($this->mMethod == 'link-diff-added') { $result = array_diff($newLinks, $oldLinks); } if ($this->mMethod == 'link-diff-removed') { $result = array_diff($oldLinks, $newLinks); } break; case 'parse-wikitext': // Should ONLY be used when sharing a parse operation with the edit. $article = $parameters['article']; if ($article) { $textVar = $parameters['wikitext-var']; $new_text = $vars->getVar($textVar)->toString(); $editInfo = $article->prepareTextForEdit($new_text); $newHTML = $editInfo->output->getText(); // Kill the PP limit comments. Ideally we'd just remove these by not setting the // parser option, but then we can't share a parse operation with the edit, which is bad. $result = preg_replace('/<!--\\s*NewPP limit report[^>]*-->\\s*$/si', '', $newHTML); break; } // Otherwise fall back to database // Otherwise fall back to database case 'parse-wikitext-nonedit': $article = self::articleFromTitle($parameters['namespace'], $parameters['title']); $textVar = $parameters['wikitext-var']; $text = $vars->getVar($textVar)->toString(); $editInfo = $this->parseNonEditWikitext($text, $article); $result = $editInfo->output->getText(); break; case 'strip-html': $htmlVar = $parameters['html-var']; $html = $vars->getVar($htmlVar)->toString(); $result = StringUtils::delimiterReplace('<', '>', '', $html); break; case 'load-recent-authors': $cutOff = $parameters['cutoff']; $title = Title::makeTitle($parameters['namespace'], $parameters['title']); if (!$title->exists()) { $result = ''; break; } $dbr = wfGetDB(DB_SLAVE); $res = $dbr->select('revision', 'DISTINCT rev_user_text', array('rev_page' => $title->getArticleId(), 'rev_timestamp<' . $dbr->addQuotes($dbr->timestamp($cutOff))), __METHOD__, array('ORDER BY' => 'rev_timestamp DESC', 'LIMIT' => 10)); $users = array(); foreach ($res as $row) { $users[] = $row->rev_user_text; } $result = $users; break; case 'get-page-restrictions': $action = $parameters['action']; $title = Title::makeTitle($parameters['namespace'], $parameters['title']); $rights = $title->getRestrictions($action); $rights = count($rights) ? $rights : array(); $result = $rights; break; case 'simple-user-accessor': $user = $parameters['user']; $method = $parameters['method']; if (!$user) { throw new MWException('No user parameter given.'); } $obj = self::userObjectFromName($user); if (!$obj) { throw new MWException("Invalid username {$user}"); } $result = call_user_func(array($obj, $method)); break; case 'user-age': $user = $parameters['user']; $asOf = $parameters['asof']; $obj = self::userObjectFromName($user); if ($obj->getId() == 0) { $result = 0; break; } $registration = $obj->getRegistration(); $result = wfTimestamp(TS_UNIX, $asOf) - wfTimestampOrNull(TS_UNIX, $registration); break; case 'user-groups': $user = $parameters['user']; $obj = self::userObjectFromName($user); $result = $obj->getEffectiveGroups(); break; case 'length': $s = $vars->getVar($parameters['length-var'])->toString(); $result = strlen($s); break; case 'subtract': $v1 = $vars->getVar($parameters['val1-var'])->toFloat(); $v2 = $vars->getVar($parameters['val2-var'])->toFloat(); $result = $v1 - $v2; break; case 'revision-text-by-id': $rev = Revision::newFromId($parameters['revid']); $result = $rev->getText(); break; case 'revision-text-by-timestamp': $timestamp = $parameters['timestamp']; $title = Title::makeTitle($parameters['namespace'], $parameters['title']); $dbr = wfGetDB(DB_SLAVE); $rev = Revision::loadFromTimestamp($dbr, $title, $timestamp); if ($rev) { $result = $rev->getText(); } else { $result = ''; } break; default: if (wfRunHooks('AbuseFilter-computeVariable', array($this->mMethod, $vars))) { throw new AFPException('Unknown variable compute type ' . $this->mMethod); } } return $result instanceof AFPData ? $result : AFPData::newFromPHPVar($result); }
/** * Depending on the requested action this method will try to store/preview * the data in mOptions or retrieve the edit form. * * The form and target page will be available in mOptions after execution of * the method. * * Errors and warnings are logged in the API result under the 'errors' key. * The general request status is maintained in mStatus. * * @global $wgRequest * @global $wgOut * @global SFFormPrinter $sfgFormPrinter * @throws MWException */ public function doAction() { global $wgOut, $wgParser, $wgRequest, $sfgFormPrinter; // if the wiki is read-only, do not save if (wfReadOnly()) { if ($this->mAction === self::ACTION_SAVE) { throw new MWException(wfMessage('sf_autoedit_readonly', wfReadOnlyReason())->parse()); } // even if not saving notify client anyway. Might want to dislay a notice $this->logMessage(wfMessage('sf_autoedit_readonly', wfReadOnlyReason())->parse(), self::NOTICE); } // find the title of the form to be used $formTitle = $this->getFormTitle(); // get the form content $formContent = StringUtils::delimiterReplace('<noinclude>', '</noinclude>', '', $this->getTextForPage($formTitle)); // signals that the form was submitted // always true, else we would not be here $isFormSubmitted = $this->mAction === self::ACTION_SAVE || $this->mAction === self::ACTION_PREVIEW || $this->mAction === self::ACTION_DIFF; // the article id of the form to be used $formArticleId = $formTitle->getArticleID(); // the name of the target page; might be empty when using the one-step-process $targetName = $this->mOptions['target']; // if the target page was not specified, try finding the page name formula // (Why is this not done in SFFormPrinter::formHTML?) if ($targetName === '') { // Parse the form to see if it has a 'page name' value set. if (preg_match('/{{{\\s*info.*page name\\s*=\\s*(.*)}}}/msU', $formContent, $matches)) { $pageNameElements = SFUtils::getFormTagComponents(trim($matches[1])); $targetNameFormula = $pageNameElements[0]; } else { throw new MWException(wfMessage('sf_autoedit_notargetspecified')->parse()); } $targetTitle = null; } else { $targetNameFormula = null; $targetTitle = Title::newFromText($targetName); } $preloadContent = ''; // save $wgRequest for later restoration $oldRequest = $wgRequest; $pageExists = false; // preload data if not explicitly excluded and if the preload page exists if (!isset($this->mOptions['preload']) || $this->mOptions['preload'] !== false) { if (isset($this->mOptions['preload']) && is_string($this->mOptions['preload'])) { $preloadTitle = Title::newFromText($this->mOptions['preload']); } else { $preloadTitle = Title::newFromText($targetName); } if ($preloadTitle !== null && $preloadTitle->exists()) { // the content of the page that was specified to be used for preloading $preloadContent = $this->getTextForPage($preloadTitle); $pageExists = true; } else { if (isset($this->mOptions['preload'])) { $this->logMessage(wfMessage('sf_autoedit_invalidpreloadspecified', $this->mOptions['preload'])->parse(), self::WARNING); } } } // Allow extensions to set/change the preload text, for new // pages. if (!$pageExists) { Hooks::run('sfEditFormPreloadText', array(&$preloadContent, $targetTitle, $formTitle)); } // Flag to keep track of formHTML() runs. $formHtmlHasRun = false; if ($preloadContent !== '') { // @HACK - we need to set this for the preload to take // effect in the form. $pageExists = true; // Spoof $wgRequest for SFFormPrinter::formHTML(). if (isset($_SESSION)) { $wgRequest = new FauxRequest($this->mOptions, true, $_SESSION); } else { $wgRequest = new FauxRequest($this->mOptions, true); } // Call SFFormPrinter::formHTML() to get at the form // HTML of the existing page. list($formHTML, $formJS, $targetContent, $form_page_title, $generatedTargetNameFormula) = $sfgFormPrinter->formHTML($formContent, $isFormSubmitted, $pageExists, $formArticleId, $preloadContent, $targetName, $targetNameFormula); // Parse the data to be preloaded from the form HTML of // the existing page. $data = $this->parseDataFromHTMLFrag($formHTML); // ...and merge/overwrite it with the new data. $this->mOptions = SFUtils::array_merge_recursive_distinct($data, $this->mOptions); } // We already preloaded stuff for saving/previewing - // do not do this again. if ($isFormSubmitted && !$wgRequest->getCheck('partial')) { $preloadContent = ''; $pageExists = false; } else { // Source of the data is a page. $pageExists = is_a($targetTitle, 'Title') && $targetTitle->exists(); } // Spoof $wgRequest for SFFormPrinter::formHTML(). if (isset($_SESSION)) { $wgRequest = new FauxRequest($this->mOptions, true, $_SESSION); } else { $wgRequest = new FauxRequest($this->mOptions, true); } // get wikitext for submitted data and form list($formHTML, $formJS, $targetContent, $generatedFormName, $generatedTargetNameFormula) = $sfgFormPrinter->formHTML($formContent, $isFormSubmitted, $pageExists, $formArticleId, $preloadContent, $targetName, $targetNameFormula); // Restore original request. $wgRequest = $oldRequest; if ($generatedFormName !== '') { $formTitle = Title::newFromText($generatedFormName); $this->mOptions['formtitle'] = $formTitle->getText(); } $this->mOptions['formHTML'] = $formHTML; $this->mOptions['formJS'] = $formJS; if ($isFormSubmitted) { // If the target page was not specified, see if // something was generated from the target name formula. if ($this->mOptions['target'] === '') { // If no name was generated, we cannot save => give up if ($generatedTargetNameFormula === '') { throw new MWException(wfMessage('sf_autoedit_notargetspecified')->parse()); } $this->mOptions['target'] = $this->generateTargetName($generatedTargetNameFormula); } // Lets other code process additional form-definition syntax Hooks::run('sfWritePageData', array($this->mOptions['form'], Title::newFromText($this->mOptions['target']), &$targetContent)); $editor = $this->setupEditPage($targetContent); // Perform the requested action. if ($this->mAction === self::ACTION_PREVIEW) { $this->doPreview($editor); } else { if ($this->mAction === self::ACTION_DIFF) { $this->doDiff($editor); } else { $this->doStore($editor); } } } else { if ($this->mAction === self::ACTION_FORMEDIT) { $parserOutput = $wgParser->getOutput(); if (method_exists($wgOut, 'addParserOutputMetadata')) { $wgOut->addParserOutputMetadata($parserOutput); } else { $wgOut->addParserOutputNoText($parserOutput); } $this->doFormEdit($formHTML, $formJS); } } }
/** * Parse the form definition and return it */ public static function getFormDefinition(Parser $parser, $form_def = null, $form_id = null) { if ($form_id !== null) { $cachedDef = self::getFormDefinitionFromCache($form_id, $parser); if ($cachedDef !== null) { return $cachedDef; } } if ($form_id !== null) { $form_title = Title::newFromID($form_id); $form_def = SFUtils::getPageText($form_title); } elseif ($form_def == null) { // No id, no text -> nothing to do return ''; } // Remove <noinclude> sections and <includeonly> tags from form definition $form_def = StringUtils::delimiterReplace('<noinclude>', '</noinclude>', '', $form_def); $form_def = strtr($form_def, array('<includeonly>' => '', '</includeonly>' => '')); // We need to replace all SF tags in the form definition by strip items. But we can not just use // the Parser strip state because the Parser would during parsing replace all strip items and then // mangle them into HTML code. So we have to use our own. Which means we also can not just use // Parser::insertStripItem() (see below). $rnd = wfRandomString(32); // This regexp will find any SF triple braced tags (including correct handling of contained braces), i.e. // {{{field|foo|default={{Bar}}}}} is not a problem. When used with preg_match and friends, $matches[0] will // contain the whole SF tag, $matches[1] will contain the tag without the enclosing triple braces. $regexp = '#\\{\\{\\{((?>[^\\{\\}]+)|(\\{((?>[^\\{\\}]+)|(?-2))*\\}))*\\}\\}\\}#'; $items = array(); // replace all SF tags by strip markers $form_def = preg_replace_callback($regexp, function (array $matches) use(&$items, $rnd) { $markerIndex = count($items); $items[] = $matches[0]; return "{$rnd}-item-{$markerIndex}-{$rnd}"; }, $form_def); // parse wiki-text if (isset($parser->mInParse) && $parser->mInParse === true) { $form_def = $parser->recursiveTagParse($form_def); $output = $parser->getOutput(); } else { $title = is_object($parser->getTitle()) ? $parser->getTitle() : new Title(); $output = $parser->parse($form_def, $title, $parser->getOptions()); $form_def = $output->getText(); } $form_def = preg_replace_callback("/{$rnd}-item-(\\d+)-{$rnd}/", function (array $matches) use($items) { $markerIndex = (int) $matches[1]; return $items[$markerIndex]; }, $form_def); if ($output->getCacheTime() == -1) { $form_article = Article::newFromID($form_id); self::purgeCache($form_article); wfDebug("Caching disabled for form definition {$form_id}\n"); } elseif ($form_id !== null) { self::cacheFormDefinition($form_id, $form_def, $parser); } return $form_def; }
/** * Take a fragment of (potentially invalid) HTML and return * a version with any tags removed, encoded suitably for literal * inclusion in an attribute value. * * @param string $text HTML fragment * @return string */ static function stripAllTags($text) { # Actual <tags> $text = StringUtils::delimiterReplace('<', '>', '', $text); # Normalize &entities and whitespace $text = Sanitizer::normalizeAttributeValue($text); # Will be placed into "double-quoted" attributes, # make sure remaining bits are safe. $text = str_replace(array('<', '>', '"'), array('<', '>', '"'), $text); return $text; }
function wiki_get_template($widgetName, &$widgetCode, &$smarty_obj) { $widgetTitle = Title::newFromText($widgetName, NS_WIDGET); if ($widgetTitle && $widgetTitle->exists()) { $widgetArticle = new Article($widgetTitle); $widgetCode = $widgetArticle->getContent(); // Remove <noinclude> sections and <includeonly> tags from form definition $widgetCode = StringUtils::delimiterReplace('<noinclude>', '</noinclude>', '', $widgetCode); $widgetCode = strtr($widgetCode, array('<includeonly>' => '', '</includeonly>' => '')); return true; } else { return false; } }
static function printForm(&$form_name, &$target_name, $alt_forms = array(), $redirectOnError = false) { global $wgOut, $wgRequest, $wgUser, $sfgFormPrinter; // initialize some variables $target_title = null; $page_name_formula = null; $form_title = Title::makeTitleSafe(SF_NS_FORM, $form_name); // If the given form is not a valid title, bail out. if (!$form_title) { return 'sf_formedit_badurl'; } $form_article = new Article($form_title, 0); $form_definition = $form_article->getContent(); // If the form page is a redirect, use the other form // instead. if ($form_title->isRedirect()) { $form_title = Title::newFromRedirectRecurse($form_definition); $form_article = new Article($form_title, 0); $form_definition = $form_article->getContent(); } $form_definition = StringUtils::delimiterReplace('<noinclude>', '</noinclude>', '', $form_definition); if (is_null($target_name)) { $target_name = ''; } if ($target_name === '') { // parse the form to see if it has a 'page name' value set $matches; if (preg_match('/{{{info.*page name\\s*=\\s*(.*)}}}/m', $form_definition, $matches)) { $page_name_elements = SFUtils::getFormTagComponents($matches[1]); $page_name_formula = $page_name_elements[0]; } elseif (count($alt_forms) == 0) { return 'sf_formedit_badurl'; } } else { $target_title = Title::newFromText($target_name); if ($target_title && $target_title->exists()) { if ($wgRequest->getVal('query') == 'true') { $page_contents = null; //$page_is_source = false; } else { // If page already exists and 'redlink' // is in the query string, redirect to // the actual page, just like // MediaWiki does it. if ($wgRequest->getBool('redlink')) { $wgOut->redirect($target_title->getFullURL()); wfProfileOut(__METHOD__); return; } $target_article = new Article($target_title, 0); $page_contents = $target_article->getContent(); //$page_is_source = true; } } else { $target_name = str_replace('_', ' ', $target_name); } } if (!$form_title || !$form_title->exists()) { if (count($alt_forms) > 0) { $text = '<div class="infoMessage">' . wfMsg('sf_formedit_altformsonly') . ' ' . self::printAltFormsList($alt_forms, $form_name) . "</div>\n"; } else { $text = Html::rawElement('p', array('class' => 'error'), wfMsgExt('sf_formstart_badform', 'parseinline', SFUtils::linkText(SF_NS_FORM, $form_name))) . "\n"; } } elseif ($target_name === '' && $page_name_formula === '') { $text = Html::element('p', array('class' => 'error'), wfMsg('sf_formedit_badurl')) . "\n"; } else { $save_page = $wgRequest->getCheck('wpSave'); $preview_page = $wgRequest->getCheck('wpPreview'); $diff_page = $wgRequest->getCheck('wpDiff'); $form_submitted = $save_page || $preview_page || $diff_page; // get 'preload' query value, if it exists if (!$form_submitted) { if ($wgRequest->getCheck('preload')) { $page_is_source = true; $page_contents = SFFormUtils::getPreloadedText($wgRequest->getVal('preload')); } else { // let other extensions preload the page, if they want wfRunHooks('sfEditFormPreloadText', array(&$page_contents, $target_title, $form_title)); $page_is_source = $page_contents != null; } } else { $page_is_source = false; $page_contents = null; } list($form_text, $javascript_text, $data_text, $form_page_title, $generated_page_name) = $sfgFormPrinter->formHTML($form_definition, $form_submitted, $page_is_source, $form_article->getID(), $page_contents, $target_name, $page_name_formula); // Before we do anything else, set the form header // title - this needs to be done after formHTML() is // called, because otherwise it doesn't take hold // for some reason if the form is disabled. if (empty($target_title)) { $s = wfMsg('sf_formedit_createtitlenotarget', $form_title->getText()); } elseif ($target_title->exists()) { $s = wfMsg('sf_formedit_edittitle', $form_title->getText(), $target_title->getPrefixedText()); } else { $s = wfMsg('sf_formedit_createtitle', $form_title->getText(), $target_title->getPrefixedText()); } $wgOut->setPageTitle($s); if ($form_submitted) { if (!is_null($page_name_formula) && $page_name_formula !== '') { $target_name = $generated_page_name; // prepend a super-page, if one was specified if ($wgRequest->getCheck('super_page')) { $target_name = $wgRequest->getVal('super_page') . '/' . $target_name; } // prepend a namespace, if one was specified if ($wgRequest->getCheck('namespace')) { $target_name = $wgRequest->getVal('namespace') . ':' . $target_name; } // replace "unique number" tag with one // that won't get erased by the next line $target_name = preg_replace('/<unique number(.*)>/', '{num\\1}', $target_name, 1); // if any formula stuff is still in the // name after the parsing, just remove it $target_name = StringUtils::delimiterReplace('<', '>', '', $target_name); // now run the parser on it global $wgParser; // ...but first, replace spaces back // with underlines, in case a magic word // or parser function name contains // underlines - hopefully this won't // cause problems of its own $target_name = str_replace(' ', '_', $target_name); $target_name = $wgParser->preprocess($target_name, $wgOut->getTitle(), ParserOptions::newFromUser(null)); $title_number = ""; $isRandom = false; $randomNumHasPadding = false; $randomNumDigits = 6; if (strpos($target_name, '{num') !== false) { // Random number if (preg_match('/{num;random(;(0)?([1-9][0-9]*))?}/', $target_name, $matches)) { $isRandom = true; $randomNumHasPadding = array_key_exists(2, $matches); $randomNumDigits = array_key_exists(3, $matches) ? $matches[3] : $randomNumDigits; $title_number = self::makeRandomNumber($randomNumDigits, $randomNumHasPadding); } else { // get unique number start value // from target name; if it's not // there, or it's not a positive // number, start it out as blank preg_match('/{num.*start[_]*=[_]*([^;]*).*}/', $target_name, $matches); if (count($matches) == 2 && is_numeric($matches[1]) && $matches[1] >= 0) { // the "start" value" $title_number = $matches[1]; } } // set target title $target_title = Title::newFromText(preg_replace('/{num.*}/', $title_number, $target_name)); // if title exists already // cycle through numbers for // this tag until we find one // that gives a nonexistent page // title while ($target_title->exists()) { if ($isRandom) { $title_number = self::makeRandomNumber($randomNumDigits, $randomNumHasPadding); } elseif ($title_number == "") { $title_number = 2; } else { $title_number = str_pad($title_number + 1, strlen($title_number), '0', STR_PAD_LEFT); } $target_title = Title::newFromText(preg_replace('/{num.*}/', $title_number, $target_name)); } $target_name = $target_title->getPrefixedText(); } else { $target_title = Title::newFromText($target_name); } } if (is_null($target_title)) { if ($target_name) { return array('sf_formstart_badtitle', array($target_name)); } else { return 'sf_formedit_emptytitle'; } } if ($save_page) { $permErrors = $target_title->getUserPermissionsErrors('edit', $wgUser); if ($permErrors) { // just return the first error and let them fix it one by one return array_shift($permErrors); } // Set up all the variables for the // page save. $data = array('wpTextbox1' => $data_text, 'wpSummary' => $wgRequest->getVal('wpSummary'), 'wpStarttime' => $wgRequest->getVal('wpStarttime'), 'wpEdittime' => $wgRequest->getVal('wpEdittime'), 'wpEditToken' => $wgUser->isLoggedIn() ? $wgUser->editToken() : EDIT_TOKEN_SUFFIX, 'wpSave' => '', 'action' => 'submit'); if ($wgRequest->getCheck('wpMinoredit')) { $data['wpMinoredit'] = true; } if ($wgRequest->getCheck('wpWatchthis')) { $data['wpWatchthis'] = true; } $request = new FauxRequest($data, true); // Find existing article if it exists, // or create a new one. $article = new Article($target_title, 0); $editor = new EditPage($article); $editor->importFormData($request); // Try to save the page! $resultDetails = array(); $saveResult = $editor->internalAttemptSave($resultDetails); // Return value was made an object in MW 1.19 if (is_object($saveResult)) { $saveResultCode = $saveResult->value; } else { $saveResultCode = $saveResult; } if (($saveResultCode == EditPage::AS_HOOK_ERROR || $saveResultCode == EditPage::AS_HOOK_ERROR_EXPECTED) && $redirectOnError) { $wgOut->clearHTML(); $wgOut->setArticleBodyOnly(true); // Lets other code process additional form-definition syntax wfRunHooks('sfWritePageData', array($form_name, $target_title, &$data_text)); $text = SFUtils::printRedirectForm($target_title, $data_text, $wgRequest->getVal('wpSummary'), $save_page, $preview_page, $diff_page, $wgRequest->getCheck('wpMinoredit'), $wgRequest->getCheck('wpWatchthis'), $wgRequest->getVal('wpStarttime'), $wgRequest->getVal('wpEdittime')); } else { if ($saveResultCode == EditPage::AS_SUCCESS_UPDATE || $saveResultCode == EditPage::AS_SUCCESS_NEW_ARTICLE) { $wgOut->redirect($target_title->getFullURL()); } return SFUtils::processEditErrors($saveResultCode); } } else { // Lets other code process additional form-definition syntax wfRunHooks('sfWritePageData', array($form_name, $target_title, &$data_text)); $text = SFUtils::printRedirectForm($target_title, $data_text, $wgRequest->getVal('wpSummary'), $save_page, $preview_page, $diff_page, $wgRequest->getCheck('wpMinoredit'), $wgRequest->getCheck('wpWatchthis'), $wgRequest->getVal('wpStarttime'), $wgRequest->getVal('wpEdittime')); // extract its data } } else { // override the default title for this page if // a title was specified in the form if ($form_page_title != null) { if ($target_name === '') { $wgOut->setPageTitle($form_page_title); } else { $wgOut->setPageTitle("{$form_page_title}: {$target_title->getPrefixedText()}"); } } $text = ""; if (count($alt_forms) > 0) { $text .= '<div class="infoMessage">' . wfMsg('sf_formedit_altforms') . ' '; $text .= self::printAltFormsList($alt_forms, $target_name); $text .= "</div>\n"; } $text .= '<form name="createbox" id="sfForm" method="post" class="createbox">'; $pre_form_html = ''; wfRunHooks('sfHTMLBeforeForm', array(&$target_title, &$pre_form_html)); $text .= $pre_form_html; $text .= $form_text; } } SFUtils::addJavascriptAndCSS(); if (!empty($javascript_text)) { $wgOut->addScript(' <script type="text/javascript">' . "\n{$javascript_text}\n" . '</script>' . "\n"); } $wgOut->addHTML($text); return null; }
/** * @param $vars AbuseFilterVariableHolder * @return AFPData|array|int|mixed|null|string * @throws MWException * @throws AFPException */ function compute($vars) { $parameters = $this->mParameters; $result = null; if (!wfRunHooks('AbuseFilter-interceptVariable', array($this->mMethod, $vars, $parameters, &$result))) { return $result instanceof AFPData ? $result : AFPData::newFromPHPVar($result); } switch ($this->mMethod) { case 'diff': $text1Var = $parameters['oldtext-var']; $text2Var = $parameters['newtext-var']; $text1 = $vars->getVar($text1Var)->toString() . "\n"; $text2 = $vars->getVar($text2Var)->toString() . "\n"; $result = wfDiff($text1, $text2); break; case 'diff-split': $diff = $vars->getVar($parameters['diff-var'])->toString(); $line_prefix = $parameters['line-prefix']; $diff_lines = explode("\n", $diff); $interest_lines = array(); foreach ($diff_lines as $line) { if (substr($line, 0, 1) === $line_prefix) { $interest_lines[] = substr($line, strlen($line_prefix)); } } $result = $interest_lines; break; case 'links-from-wikitext': // This should ONLY be used when sharing a parse operation with the edit. /* @var WikiPage $article */ $article = $parameters['article']; if ($article !== null && (!defined('MW_SUPPORTS_CONTENTHANDLER') || $article->getContentModel() === CONTENT_MODEL_WIKITEXT)) { $textVar = $parameters['text-var']; // XXX: Use prepareContentForEdit. But we need a Content object for that. $new_text = $vars->getVar($textVar)->toString(); $content = ContentHandler::makeContent($new_text, $article->getTitle()); $editInfo = $article->prepareContentForEdit($content); $links = array_keys($editInfo->output->getExternalLinks()); $result = $links; break; } // Otherwise fall back to database // Otherwise fall back to database case 'links-from-wikitext-nonedit': case 'links-from-wikitext-or-database': // TODO: use Content object instead, if available! In any case, use WikiPage, not Article. $article = self::articleFromTitle($parameters['namespace'], $parameters['title']); if ($vars->getVar('context')->toString() == 'filter') { $links = $this->getLinksFromDB($article); wfDebug("AbuseFilter: loading old links from DB\n"); } elseif (!defined('MW_SUPPORTS_CONTENTHANDLER') || $article->getContentModel() === CONTENT_MODEL_WIKITEXT) { wfDebug("AbuseFilter: loading old links from Parser\n"); $textVar = $parameters['text-var']; $wikitext = $vars->getVar($textVar)->toString(); $editInfo = $this->parseNonEditWikitext($wikitext, $article); $links = array_keys($editInfo->output->getExternalLinks()); } else { // TODO: Get links from Content object. But we don't have the content object. // And for non-text content, $wikitext is usually not going to be a valid // serialization, but rather some dummy text for filtering. $links = array(); } $result = $links; break; case 'link-diff-added': case 'link-diff-removed': $oldLinkVar = $parameters['oldlink-var']; $newLinkVar = $parameters['newlink-var']; $oldLinks = $vars->getVar($oldLinkVar)->toString(); $newLinks = $vars->getVar($newLinkVar)->toString(); $oldLinks = explode("\n", $oldLinks); $newLinks = explode("\n", $newLinks); if ($this->mMethod == 'link-diff-added') { $result = array_diff($newLinks, $oldLinks); } if ($this->mMethod == 'link-diff-removed') { $result = array_diff($oldLinks, $newLinks); } break; case 'parse-wikitext': // Should ONLY be used when sharing a parse operation with the edit. $article = $parameters['article']; if ($article !== null && (!defined('MW_SUPPORTS_CONTENTHANDLER') || $article->getContentModel() === CONTENT_MODEL_WIKITEXT)) { $textVar = $parameters['wikitext-var']; // XXX: Use prepareContentForEdit. But we need a Content object for that. $new_text = $vars->getVar($textVar)->toString(); $editInfo = $article->prepareTextForEdit($new_text); if (isset($parameters['pst']) && $parameters['pst']) { $result = $editInfo->pstContent->serialize($editInfo->format); } else { $newHTML = $editInfo->output->getText(); // Kill the PP limit comments. Ideally we'd just remove these by not setting the // parser option, but then we can't share a parse operation with the edit, which is bad. $result = preg_replace('/<!--\\s*NewPP limit report[^>]*-->\\s*$/si', '', $newHTML); } break; } // Otherwise fall back to database // Otherwise fall back to database case 'parse-wikitext-nonedit': // TODO: use Content object instead, if available! In any case, use WikiPage, not Article. $article = self::articleFromTitle($parameters['namespace'], $parameters['title']); $textVar = $parameters['wikitext-var']; if (!defined('MW_SUPPORTS_CONTENTHANDLER') || $article->getContentModel() === CONTENT_MODEL_WIKITEXT) { if (isset($parameters['pst']) && $parameters['pst']) { // $textVar is already PSTed when it's not loaded from an ongoing edit. $result = $vars->getVar($textVar)->toString(); } else { $text = $vars->getVar($textVar)->toString(); $editInfo = $this->parseNonEditWikitext($text, $article); $result = $editInfo->output->getText(); } } else { // TODO: Parser Output from Content object. But we don't have the content object. // And for non-text content, $wikitext is usually not going to be a valid // serialization, but rather some dummy text for filtering. $result = ''; } break; case 'strip-html': $htmlVar = $parameters['html-var']; $html = $vars->getVar($htmlVar)->toString(); $result = StringUtils::delimiterReplace('<', '>', '', $html); break; case 'load-recent-authors': $cutOff = $parameters['cutoff']; $title = Title::makeTitle($parameters['namespace'], $parameters['title']); if (!$title->exists()) { $result = ''; break; } $dbr = wfGetDB(DB_SLAVE); $res = $dbr->select('revision', 'DISTINCT rev_user_text', array('rev_page' => $title->getArticleID(), 'rev_timestamp<' . $dbr->addQuotes($dbr->timestamp($cutOff))), __METHOD__, array('ORDER BY' => 'rev_timestamp DESC', 'LIMIT' => 10)); $users = array(); foreach ($res as $row) { $users[] = $row->rev_user_text; } $result = $users; break; case 'get-page-restrictions': $action = $parameters['action']; $title = Title::makeTitle($parameters['namespace'], $parameters['title']); $rights = $title->getRestrictions($action); $rights = count($rights) ? $rights : array(); $result = $rights; break; case 'simple-user-accessor': $user = $parameters['user']; $method = $parameters['method']; if (!$user) { throw new MWException('No user parameter given.'); } $obj = self::getUserObject($user); if (!$obj) { throw new MWException("Invalid username {$user}"); } $result = call_user_func(array($obj, $method)); break; case 'user-age': $user = $parameters['user']; $asOf = $parameters['asof']; $obj = self::getUserObject($user); if ($obj->getId() == 0) { $result = 0; break; } $registration = $obj->getRegistration(); $result = wfTimestamp(TS_UNIX, $asOf) - wfTimestampOrNull(TS_UNIX, $registration); break; case 'user-groups': // Deprecated but needed by old log entries $user = $parameters['user']; $obj = self::getUserObject($user); $result = $obj->getEffectiveGroups(); break; case 'length': $s = $vars->getVar($parameters['length-var'])->toString(); $result = strlen($s); break; case 'subtract': $v1 = $vars->getVar($parameters['val1-var'])->toFloat(); $v2 = $vars->getVar($parameters['val2-var'])->toFloat(); $result = $v1 - $v2; break; case 'revision-text-by-id': $rev = Revision::newFromId($parameters['revid']); $result = AbuseFilter::revisionToString($rev); break; case 'revision-text-by-timestamp': $timestamp = $parameters['timestamp']; $title = Title::makeTitle($parameters['namespace'], $parameters['title']); $dbr = wfGetDB(DB_SLAVE); $rev = Revision::loadFromTimestamp($dbr, $title, $timestamp); $result = AbuseFilter::revisionToString($rev); break; default: if (wfRunHooks('AbuseFilter-computeVariable', array($this->mMethod, $vars, $parameters, &$result))) { throw new AFPException('Unknown variable compute type ' . $this->mMethod); } } return $result instanceof AFPData ? $result : AFPData::newFromPHPVar($result); }
/** * Parse the form definition and return it */ public static function getFormDefinition(&$parser, &$form_def = null, &$form_id = null) { $cachedDef = self::getFormDefinitionFromCache($form_id, $parser); if ($cachedDef) { return $cachedDef; } if ($form_id !== null) { $form_article = Article::newFromID($form_id); $form_def = $form_article->getContent(); } elseif ($form_def == null) { // No id, no text -> nothing to do return ''; } // Remove <noinclude> sections and <includeonly> tags from form definition $form_def = StringUtils::delimiterReplace('<noinclude>', '</noinclude>', '', $form_def); $form_def = strtr($form_def, array('<includeonly>' => '', '</includeonly>' => '')); // add '<nowiki>' tags around every triple-bracketed form // definition element, so that the wiki parser won't touch // it - the parser will remove the '<nowiki>' tags, leaving // us with what we need $form_def = "__NOEDITSECTION__" . strtr($form_def, array('{{{' => '<nowiki>{{{', '}}}' => '}}}</nowiki>')); $title = is_object($parser->getTitle()) ? $parser->getTitle() : new Title(); // parse wiki-text $output = $parser->parse($form_def, $title, $parser->getOptions()); $form_def = $output->getText(); self::cacheFormDefinition($form_id, $parser, $output); return $form_def; }
/** * Take a fragment of (potentially invalid) HTML and return * a version with any tags removed, encoded as plain text. * * Warning: this return value must be further escaped for literal * inclusion in HTML output as of 1.10! * * @param string $text HTML fragment * @return string */ static function stripAllTags($text) { # Actual <tags> $text = StringUtils::delimiterReplace('<', '>', '', $text); # Normalize &entities and whitespace $text = self::decodeCharReferences($text); $text = self::normalizeWhitespace($text); return $text; }
public static function wiki_get_template($widgetName, &$widgetCode, &$smarty_obj) { global $wgWidgetsUseFlaggedRevs; $widgetTitle = Title::newFromText($widgetName, NS_WIDGET); if ($widgetTitle && $widgetTitle->exists()) { if ($wgWidgetsUseFlaggedRevs) { $flaggedWidgetArticle = FlaggedArticle::getTitleInstance($widgetTitle); $flaggedWidgetArticleRevision = $flaggedWidgetArticle->getStableRev(); if ($flaggedWidgetArticleRevision) { $widgetCode = $flaggedWidgetArticleRevision->getRevText(); } else { $widgetCode = ''; } } else { $widgetArticle = new Article($widgetTitle, 0); $widgetCode = $widgetArticle->getContent(); } // Remove <noinclude> sections and <includeonly> tags from form definition $widgetCode = StringUtils::delimiterReplace('<noinclude>', '</noinclude>', '', $widgetCode); $widgetCode = strtr($widgetCode, array('<includeonly>' => '', '</includeonly>' => '')); return true; } else { return false; } }
public static function stripHTML($text) { return StringUtils::delimiterReplace('<', '>', '', $text); }
function getAllFieldsCargo($templateTitle) { $cargoFieldsOfTemplateParams = array(); $templateFields = array(); // First, get the table name, and fields, declared for this // template. $templatePageID = $templateTitle->getArticleID(); $tableSchemaString = CargoUtils::getPageProp($templatePageID, 'CargoFields'); // See if there even is DB storage for this template - if not, // exit. if (is_null($tableSchemaString)) { return null; } $tableSchema = CargoTableSchema::newFromDBString($tableSchemaString); $tableName = CargoUtils::getPageProp($templatePageID, 'CargoTableName'); // Then, match template params to Cargo table fields, by // parsing call(s) to #cargo_store. $templateText = SFUtils::getPageText($templateTitle); // Ignore 'noinclude' sections and 'includeonly' tags. $templateText = StringUtils::delimiterReplace('<noinclude>', '</noinclude>', '', $templateText); $templateText = strtr($templateText, array('<includeonly>' => '', '</includeonly>' => '')); if (preg_match_all('/#cargo_store:(.*?}})\\s*}}/mis', $templateText, $matches)) { foreach ($matches[1] as $match) { if (preg_match_all('/([^|{]*?)=\\s*{{{([^|}]*)/mis', $match, $matches2)) { foreach ($matches2[1] as $i => $cargoFieldName) { $templateParameter = trim($matches2[2][$i]); $cargoFieldsOfTemplateParams[$templateParameter] = $cargoFieldName; } } } } // Now, combine the two sets of information into an array of // SFTemplateFields objects. $fieldDescriptions = $tableSchema->mFieldDescriptions; foreach ($cargoFieldsOfTemplateParams as $templateParameter => $cargoField) { $templateField = SFTemplateField::create($templateParameter, $templateParameter); if (array_key_exists($cargoField, $fieldDescriptions)) { $fieldDescription = $fieldDescriptions[$cargoField]; $templateField->setCargoFieldData($tableName, $cargoField, $fieldDescription); } $templateFields[] = $templateField; } return $templateFields; }