Current File : /home/jvzmxxx/wiki1/extensions/Flow/includes/View.php
<?php

namespace Flow;

use ContextSource;
use Flow\Block\AbstractBlock;
use Flow\Exception\InvalidActionException;
use Flow\Model\Anchor;
use Flow\Model\HtmlRenderingInformation;
use Flow\Model\UUID;
use Flow\Model\Workflow;
use Html;
use Hooks;
use IContextSource;
use Message;
use OutputPage;
use Title;
use WebRequest;

class View extends ContextSource {
	/**
	 * @var UrlGenerator
	 */
	protected $urlGenerator;

	/**
	 * @var TemplateHelper
	 */
	protected $lightncandy;

	/**
	 * @var FlowActions
	 */
	protected $actions;

	function __construct(
		UrlGenerator $urlGenerator,
		TemplateHelper $lightncandy,
		IContextSource $requestContext,
		FlowActions $actions
	) {
		$this->urlGenerator = $urlGenerator;
		$this->lightncandy = $lightncandy;
		$this->setContext( $requestContext );
		$this->actions = $actions;
	}

	public function show( WorkflowLoader $loader, $action ) {
		$blocks = $loader->getBlocks();

		$parameters = $this->extractBlockParameters( $action, $blocks );
		foreach ( $loader->getBlocks() as $block ) {
			$block->init( $this, $action );
		}

		if ( $this->getRequest()->wasPosted() ) {
			$retval = $this->handleSubmit( $loader, $action, $parameters );
			// successful submission
			if ( $retval === true ) {
				$this->redirect( $loader->getWorkflow(), 'view' );
				return;
			// only render the returned subset of blocks
			} elseif ( is_array( $retval ) ) {
				$blocks = $retval;
			}
		}

		$apiResponse = $this->buildApiResponse( $loader, $blocks, $action, $parameters );

		$output = $this->getOutput();
		$output->enableOOUI();
		$this->addModules( $output, $action );
		// Please note that all blocks can set page title, which may cause them
		// to override one another's titles
		foreach ( $blocks as $block ) {
			$block->setPageTitle( $output );
		}


		$this->renderApiResponse( $apiResponse );
	}

	protected function addModules( OutputPage $out, $action ) {
		if ( $this->actions->hasValue( $action, 'modules' ) ) {
			$out->addModules( $this->actions->getValue( $action, 'modules' ) );
		} else {
			$out->addModules( array( 'ext.flow' ) );
		}

		if ( $this->actions->hasValue( $action, 'moduleStyles' ) ) {
			$out->addModuleStyles( $this->actions->getValue( $action, 'moduleStyles' ) );
		} else {
			$out->addModuleStyles( array(
				'mediawiki.ui',
				'mediawiki.ui.anchor',
				'mediawiki.ui.button',
				'mediawiki.ui.input',
				'mediawiki.ui.icon',
				'mediawiki.ui.text',
				'mediawiki.special.changeslist',
				'ext.flow.styles.base' ,
				'ext.flow.mediawiki.ui.form',
				'ext.flow.mediawiki.ui.text',
				'oojs-ui.styles.icons',
				'oojs-ui.styles.icons-layout',
				'oojs-ui.styles.icons-interactions',
				'ext.flow.board.styles',
				'ext.flow.board.topic.styles',
				'oojs-ui.styles.icons',
				'oojs-ui.styles.icons-alerts',
				'oojs-ui.styles.icons-content',
				'oojs-ui.styles.icons-layout',
				'oojs-ui.styles.icons-movement',
				'oojs-ui.styles.icons-indicators',
				'oojs-ui.styles.icons-editing-core',
				'oojs-ui.styles.icons-moderation',
				// Needed for pending texture while switching editors
				'oojs-ui.styles.textures'
			) );
		}

		// Add Parsoid modules if necessary
		Conversion\Utils::onFlowAddModules( $out );
		// Allow other extensions to add modules
		Hooks::run( 'FlowAddModules', array( $out ) );
	}

	protected function handleSubmit( WorkflowLoader $loader, $action, array $parameters ) {
		$this->getOutput()->enableClientCache( false );

		$blocksToCommit = $loader->handleSubmit( $this, $action, $parameters );
		if ( !$blocksToCommit ) {
			return false;
		}

		if ( !$this->getUser()->matchEditToken( $this->getRequest()->getVal( 'wpEditToken' ) ) ) {
			// this uses the above $blocksToCommit reference to only render the failed blocks
			foreach ( $blocksToCommit as $block ) {
				$block->addError( 'edit-token', $this->msg( 'sessionfailure' ) );
			}
			return $blocksToCommit;
		}

		$loader->commit( $blocksToCommit );
		return true;
	}

	protected function buildApiResponse( WorkflowLoader $loader, array $blocks, $action, array $parameters ) {
		$workflow = $loader->getWorkflow();
		$title = $workflow->getArticleTitle();
		$user = $this->getUser();
		$categories = array_keys( $title->getParentCategories() );
		$categoryObject = array();
		$linkedCategories = array();

		// Transform the raw category names into links
		foreach ( $categories as $value ) {
			$categoryTitle = Title::newFromText( $value );
			$categoryObject[ $value ] = array(
				'name' => $value,
				'exists' => $categoryTitle->exists()
			);
			$linkedCategories[] = \Linker::link( $categoryTitle, htmlspecialchars( $categoryTitle->getText() ) );
		}

		// @todo This and API should use same code
		$apiResponse = array(
			'title' => $title->getPrefixedText(),
			'categories' => $categoryObject,
			// We need to store the link to the Special:Categories page from the
			// back end php script, because there is no way in JS front end to
			// get the localized link of a special page
			'specialCategoryLink' => \SpecialPage::getTitleFor( 'Categories' )->getLocalURL(),
			'workflow' => $workflow->isNew() ? '' : $workflow->getId()->getAlphadecimal(),
			'blocks' => array(),
			'isWatched' => $user->isWatched( $title ),
			'watchable' => !$user->isAnon(),
			'links' => array(
				'watch-board' => array(
					'url' => $title->getLocalUrl( 'action=watch' ),
				),
				'unwatch-board' => array(
					'url' => $title->getLocalUrl( 'action=unwatch' ),
				),
			)
		);

		$editToken = $user->getEditToken();
		$wasPosted = $this->getRequest()->wasPosted();
		$topicListBlock = null;
		foreach ( $blocks as $block ) {
			if ( $wasPosted ? $block->canSubmit( $action ) : $block->canRender( $action ) ) {
				$apiResponse['blocks'][$block->getName()] = $block->renderApi( $parameters[$block->getName()] )
								+ array(
									'title' => $apiResponse['title'],
									'block-action-template' => $block->getTemplate( $action ),
									'editToken' => $editToken,
								);
				if ( $block->getName() == 'topiclist' ) {
					$topicListBlock = $block;
				}
			}
		}

		// Add category items to the header if they exist
		if ( count( $linkedCategories ) > 0 && isset( $apiResponse['blocks']['header'] ) ) {
			$apiResponse['blocks']['header']['categories'] = array(
				'link' => \Linker::link(
						\SpecialPage::getTitleFor( 'Categories' ),
						wfMessage( 'pagecategories' )->params( count( $linkedCategories ) )->text()
					) . wfMessage( 'colon-separator' )->text(),
				'items' => $linkedCategories
			);
		}

		if ( isset( $topicListBlock ) && isset( $parameters['topiclist'] ) ) {
			$apiResponse['toc'] = $topicListBlock->renderTocApi(
				$apiResponse['blocks']['topiclist'],
				$parameters['topiclist']
			);
		}

		if ( count( $apiResponse['blocks'] ) === 0 ) {
			throw new InvalidActionException( "No blocks accepted action: $action", 'invalid-action' );
		}

		array_walk_recursive( $apiResponse, function( &$value ) {
			if ( $value instanceof Anchor ) {
				$anchor = $value;
				$value = $value->toArray();

				// TODO: We're looking into another approach for this
				// using a parser function, so the URL doesn't have to be
				// fully qualified.
				// See https://bugzilla.wikimedia.org/show_bug.cgi?id=66746
				$value['url'] = $anchor->getFullURL();

			} elseif ( $value instanceof Message ) {
				$value = $value->text();
			} elseif ( $value instanceof UUID ) {
				$value = $value->getAlphadecimal();
			}
		} );

		return $apiResponse;
	}

	protected function renderApiResponse( array $apiResponse ) {
		// Render the flow-component wrapper
		if ( empty( $apiResponse['blocks'] ) ) {
			return array();
		}

		$out = $this->getOutput();

		$jsonBlobResponse = $apiResponse;

		// Temporary fix for T107170
		array_walk_recursive( $jsonBlobResponse, function ( &$value, $key ) {
			if ( stristr( $key, 'Token' ) !== false ) {
				$value = null;
			}
		} );

		// Add JSON blob for OOUI widgets
		$out->addJsConfigVars( 'wgFlowData', $jsonBlobResponse );

		$renderedBlocks = array();
		foreach ( $apiResponse['blocks'] as $block ) {
			// @todo find a better way to do this; potentially make all blocks their own components
			switch ( $block['type'] ) {
				case 'board-history':
					$flowComponent = 'boardHistory';
					$page = 'history';
					break;
				case 'topic':
					if ( $block['submitted']['action'] === 'history' ) {
						$page = 'history';
						$flowComponent = 'boardHistory';
					} else {
						$page = 'topic';
						$flowComponent = 'board';
					}
					break;
				default:
					$flowComponent = 'board';
					$page = 'board';
			}

			if ( isset( $block['errors'] ) ) {
				foreach( $block['errors'] as $error ) {
					if ( isset( $error['extra']['details'] ) &&
						$error['extra']['details'] instanceof HtmlRenderingInformation ) {

						$renderingInfo = $error['extra']['details'];

						$out->addHeadItems( $renderingInfo->getHeadItems() );
						$out->addModuleStyles( $renderingInfo->getModuleStyles() );
						$out->addModules( $renderingInfo->getModules() );
					}
				}
			}

			// Don't re-render a block type twice in one page
			if ( isset( $renderedBlocks[$flowComponent] ) ) {
				continue;
			}
			$renderedBlocks[$flowComponent] = true;

			// Get the block loop template
			$template = $this->lightncandy->getTemplate( 'flow_block_loop' );

			$classes = array( 'flow-component', "flow-$page-page" );

			// Always add mw-content-{ltr,rtl} class
			$title = Title::newFromText( $apiResponse['title'] );
			$classes[] = 'mw-content-' . $title->getPageViewLanguage()->getDir();

			$action = $this->getRequest()->getVal( 'action', 'view' );
			$classes[] = "flow-action-$action";

			// Output the component, with the rendered blocks inside it
			$out->addHTML( Html::rawElement(
				'div',
				array(
					'class'               => implode( ' ', $classes ),
					'data-flow-component' => $flowComponent,
					'data-flow-id'        => $apiResponse['workflow'],
				),
				$template( $apiResponse )
			) );
		}
	}

	protected function redirect( Workflow $workflow ) {
		$link = $this->urlGenerator->workflowLink(
			$workflow->getArticleTitle(),
			$workflow->getId()
		);
		$this->getOutput()->redirect( $link->getFullURL() );
	}

	/**
	 * Helper function extracts parameters from a WebRequest.
	 *
	 * @param string $action
	 * @param WebRequest $request
	 * @param AbstractBlock[] $blocks
	 * @return array
	 */
	public function extractBlockParameters( $action, array $blocks ) {
		$request = $this->getRequest();
		$result = array();
		// BC for old parameters enclosed in square brackets
		foreach ( $blocks as $block ) {
			$name = $block->getName();
			$result[$name] = $request->getArray( $name, array() );
		}
		// BC for topic_list renamed to topiclist
		if ( isset( $result['topiclist'] ) && !$result['topiclist'] ) {
			$result['topiclist'] = $request->getArray( 'topic_list', array() );
		}
		$globalData = array( 'action' => $action );
		foreach ( $request->getValues() as $name => $value ) {
			// between urls only allowing [-_.] as unencoded special chars and
			// php mangling all of those into '_', we have to split on '_'
			if ( false !== strpos( $name, '_' ) ) {
				list( $block, $var ) = explode( '_', $name, 2 );
				// flow_xxx is global data for all blocks
				if ( $block === 'flow' ) {
					$globalData[$var] = $value;
				} else {
					$result[$block][$var] = $value;
				}
			}
		}

		foreach ( $blocks as $block ) {
			$result[$block->getName()] += $globalData;
		}

		return $result;
	}
}