static function renderAutoEdit(&$parser) { // set defaults $formcontent = ''; $linkString = null; $linkType = 'span'; $summary = null; $classString = 'autoedit-trigger'; $inQueryArr = array(); $editTime = null; // parse parameters $params = func_get_args(); array_shift($params); // don't need the parser foreach ($params as $param) { $elements = explode('=', $param, 2); $key = trim($elements[0]); $value = count($elements) > 1 ? trim($elements[1]) : ''; switch ($key) { case 'link text': $linkString = $parser->recursiveTagParse($value); break; case 'link type': $linkType = $parser->recursiveTagParse($value); break; case 'reload': $classString .= ' reload'; break; case 'summary': $summary = $parser->recursiveTagParse($value); break; case 'query string': // Change HTML-encoded ampersands directly to // URL-encoded ampersands, so that the string // doesn't get split up on the '&'. $inQueryStr = str_replace('&', '%26', $value); parse_str($inQueryStr, $arr); $inQueryArr = SFUtils::array_merge_recursive_distinct($inQueryArr, $arr); break; case 'ok text': case 'error text': // do not parse ok text or error text yet. Will be parsed on api call $arr = array($key => $value); $inQueryArr = SFUtils::array_merge_recursive_distinct($inQueryArr, $arr); break; case 'target': case 'title': $value = $parser->recursiveTagParse($value); $arr = array($key => $value); $inQueryArr = SFUtils::array_merge_recursive_distinct($inQueryArr, $arr); $targetTitle = Title::newFromText($value); if ($targetTitle !== null) { $targetArticle = new Article($targetTitle); $targetArticle->clear(); $editTime = $targetArticle->getTimestamp(); } default: $value = $parser->recursiveTagParse($value); $arr = array($key => $value); $inQueryArr = SFUtils::array_merge_recursive_distinct($inQueryArr, $arr); } } // query string has to be turned into hidden inputs. if (!empty($inQueryArr)) { $query_components = explode('&', http_build_query($inQueryArr, '', '&')); foreach ($query_components as $query_component) { $var_and_val = explode('=', $query_component, 2); if (count($var_and_val) == 2) { $formcontent .= Html::hidden(urldecode($var_and_val[0]), urldecode($var_and_val[1])); } } } if ($linkString == null) { return null; } if ($linkType == 'button') { // Html::rawElement() before MW 1.21 or so drops the type attribute // do not use Html::rawElement() for buttons! $linkElement = '<button ' . Html::expandAttributes(array('type' => 'submit', 'class' => $classString)) . '>' . $linkString . '</button>'; } elseif ($linkType == 'link') { $linkElement = Html::rawElement('a', array('class' => $classString, 'href' => "#"), $linkString); } else { $linkElement = Html::rawElement('span', array('class' => $classString), $linkString); } if ($summary == null) { $summary = wfMessage('sf_autoedit_summary', "[[{$parser->getTitle()}]]")->text(); } $formcontent .= Html::hidden('wpSummary', $summary); if ($editTime !== null) { $formcontent .= Html::hidden('wpEdittime', $editTime); } $form = Html::rawElement('form', array('class' => 'autoedit-data'), $formcontent); // ensure loading of jQuery and style sheets self::loadScriptsForAutoEdit($parser); $output = Html::rawElement('div', array('class' => 'autoedit'), $linkElement . Html::rawElement('span', array('class' => "autoedit-result"), null) . $form); // return output HTML return $parser->insertStripItem($output, $parser->mStripState); }
/** * 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); } } }
/** * This method will try to store the data in mOptions. * * It will return true on success or an error message on failure. * The used form and target page will be available in mOptions after * execution of the method. * * This method also sets HTTP response headers according to the result. * * @param bool $prefillFromExisting If this is set, existing values in the page will be used to prefill the form. * @return true or an error message */ public function storeSemanticData($prefillFromExisting = true) { global $wgOut, $wgRequest; // If the wiki is read-only we might as well stop right away if (wfReadOnly()) { return $this->reportError(wfMsg('sf_autoedit_readonly', wfReadOnlyReason())); } // ensure 'form' key exists if (!array_key_exists('form', $this->mOptions)) { $this->mOptions['form'] = null; } // ensure 'target' key exists if (!array_key_exists('target', $this->mOptions)) { $this->mOptions['target'] = null; } // If we have no target article and no form we might as well stop right away if (!$this->mOptions['target'] && !$this->mOptions['form']) { return $this->reportError(wfMsg('sf_autoedit_notargetspecified')); } // check if form was specified if (!$this->mOptions['form']) { // If no form was specified, find the default one for // this page. $title = Title::newFromText($this->mOptions['target']); $form_names = SFFormLinker::getDefaultFormsForPage($title); // if no form can be found, return if (count($form_names) == 0) { return $this->reportError(wfMsg('sf_autoedit_noformfound')); } // if more than one form found, return if (count($form_names) > 1) { return $this->reportError(wfMsg('sf_autoedit_toomanyformsfound')); } // There should now be exactly one form. $this->mOptions['form'] = $form_names[0]; } // we only care for the form's body $wgOut->setArticleBodyOnly(true); $formedit = new SFFormEdit(); $data = array(); $oldRequest = $wgRequest; // Get the form definition and target page (if there is one), // as specified in the options string, then create the actual // HTML form from them, and call that form to modify or create // the page. if ($prefillFromExisting) { $wgRequest = new FauxRequest($this->mOptions, true); // get the Semantic Form if ($this->mOptions['target']) { $formedit->execute($this->mOptions['form'] . '/' . $this->mOptions['target']); } else { $formedit->execute($this->mOptions['form']); } // extract its data $form = $this->parseDataFromHTMLFrag($data, trim($wgOut->getHTML()), 'sfForm'); if (!$form) { // something went wrong $wgRequest = $oldRequest; return $this->reportError(wfMsg('sf_autoedit_nosemanticform', array($this->mOptions['target'], $this->mOptions['form']))); } } else { self::addToArray($data, "wpSave", "Save"); } // and modify as specified $data = SFUtils::array_merge_recursive_distinct($data, $this->mOptions); //////////////////////////////////////////////////////////////////////// // Store the modified form // $wgOut->clearHTML(); $wgRequest = new FauxRequest($data, true); // get the MW form if ($this->mOptions['target']) { $formedit->execute($this->mOptions['form'] . '/' . $this->mOptions['target'], false); } else { $formedit->execute($this->mOptions['form'], false); } $this->mOptions['form'] = $formedit->mForm; $this->mOptions['target'] = $formedit->mTarget; $wgRequest = $oldRequest; if ($formedit->mError) { return $this->reportError($formedit->mError); } else { if (!headers_sent()) { header("X-Location: " . $wgOut->getRedirect()); header("X-Form: " . $formedit->mForm); header("X-Target: " . $formedit->mTarget); } if ($this->isApiQuery()) { $this->getResult()->addValue(null, 'result', array('code' => '200', 'location' => $wgOut->getRedirect(), 'form' => $formedit->mForm, 'target' => $formedit->mTarget)); } } return true; }
/** * 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); } } }