<?php
/**
 * @file api/v1/site/PKPSiteHandler.inc.php
 *
 * Copyright (c) 2014-2021 Simon Fraser University
 * Copyright (c) 2000-2021 John Willinsky
 * Distributed under the GNU GPL v3. For full terms see the file docs/COPYING.
 *
 * @class PKPSiteHandler
 * @ingroup api_v1_users
 *
 * @brief Base class to handle API requests for the site object.
 */

import('lib.pkp.classes.handler.APIHandler');

class PKPSiteHandler extends APIHandler {
	/** @var string One of the SCHEMA_... constants */
	public $schemaName = SCHEMA_SITE;

	/**
	 * @copydoc APIHandler::__construct()
	 */
	public function __construct() {
		$this->_handlerPath = 'site';
		$roles = [ROLE_ID_SITE_ADMIN];
		$this->_endpoints = array(
			'GET' => array(
				array(
					'pattern' => $this->getEndpointPattern(),
					'handler' => array($this, 'get'),
					'roles' => $roles,
				),
				array(
					'pattern' => $this->getEndpointPattern() . '/theme',
					'handler' => array($this, 'getTheme'),
					'roles' => $roles,
				),
			),
			'PUT' => array(
				array(
					'pattern' => $this->getEndpointPattern(),
					'handler' => array($this, 'edit'),
					'roles' => $roles,
				),
				array(
					'pattern' => $this->getEndpointPattern() . '/theme',
					'handler' => array($this, 'editTheme'),
					'roles' => $roles,
				),
			),
		);
		parent::__construct();
	}

	/**
	 * @copydoc PKPHandler::authorize
	 */
	public function authorize($request, &$args, $roleAssignments) {
		import('lib.pkp.classes.security.authorization.PolicySet');
		$rolePolicy = new PolicySet(COMBINING_PERMIT_OVERRIDES);

		import('lib.pkp.classes.security.authorization.RoleBasedHandlerOperationPolicy');
		foreach($roleAssignments as $role => $operations) {
			$rolePolicy->addPolicy(new RoleBasedHandlerOperationPolicy($request, $role, $operations));
		}
		$this->addPolicy($rolePolicy);

		return parent::authorize($request, $args, $roleAssignments);
	}

	/**
	 * Get the site
	 *
	 * @param $slimRequest Request Slim request object
	 * @param $response Response object
	 * @param array $args arguments
	 *
	 * @return Response
	 */
	public function get($slimRequest, $response, $args) {
		$request = $this->getRequest();

		$siteProps = Services::get('site')
			->getFullProperties($request->getSite(), [
				'request' => $request,
			]);

		return $response->withJson($siteProps, 200);
	}

	/**
	 * Get the active theme on the site
	 *
	 * @param $slimRequest Request Slim request object
	 * @param $response Response object
	 * @param array $args arguments
	 *
	 * @return Response
	 */
	public function getTheme($slimRequest, $response, $args) {
		$site = $this->getRequest()->getSite();

		$allThemes = PluginRegistry::loadCategory('themes', true);
		$activeTheme = null;
		foreach ($allThemes as $theme) {
			if ($site->getData('themePluginPath') === $theme->getDirName()) {
				$activeTheme = $theme;
				break;
			}
		}

		if (!$activeTheme) {
			return $response->withStatus(404)->withJsonError('api.themes.404.themeUnavailable');
		}

		$data = array_merge(
			$activeTheme->getOptionValues(CONTEXT_ID_NONE),
			['themePluginPath' => $theme->getDirName()]
		);

		ksort($data);

		return $response->withJson($data, 200);
	}

	/**
	 * Edit the site
	 *
	 * @param $slimRequest Request Slim request object
	 * @param $response Response object
	 * @param array $args arguments
	 *
	 * @return Response
	 */
	public function edit($slimRequest, $response, $args) {
		$request = $this->getRequest();
		$site = $request->getSite();
		$siteService = Services::get('site');

		$params = $this->convertStringsToSchema(SCHEMA_SITE, $slimRequest->getParsedBody());

		$errors = $siteService->validate($params, $site->getSupportedLocales(), $site->getPrimaryLocale());

		if (!empty($errors)) {
			return $response->withStatus(400)->withJson($errors);
		}
		$site = $siteService->edit($site, $params, $request);

		$siteProps = $siteService->getFullProperties($site, array(
			'request' => $request,
			'slimRequest' 	=> $slimRequest
		));

		return $response->withJson($siteProps, 200);
	}

	/**
	 * Edit the active theme and theme options on the site
	 *
	 * @param $slimRequest Request Slim request object
	 * @param $response Response object
	 * @param array $args arguments
	 *
	 * @return Response
	 */
	public function editTheme($slimRequest, $response, $args) {
		$request = $this->getRequest();
		$site = $request->getSite();
		$siteService = Services::get('site');

		$params = $slimRequest->getParsedBody();

		// Validate the themePluginPath and allow themes to perform their own validation
		$themePluginPath = empty($params['themePluginPath']) ? null : $params['themePluginPath'];
		if ($themePluginPath !== $site->getData('themePluginPath')) {
			$errors = $siteService->validate(
				['themePluginPath' => $themePluginPath],
				$site->getSupportedLocales(),
				$site->getPrimaryLocale()
			);
			if (!empty($errors)) {
				return $response->withJson($errors, 400);
			}
			$newSite = $siteService->edit($site, ['themePluginPath' => $themePluginPath], $request);
		}

		// Get the appropriate theme plugin
		$allThemes = PluginRegistry::loadCategory('themes', true);
		$selectedTheme = null;
		foreach ($allThemes as $theme) {
			if ($themePluginPath === $theme->getDirName()) {
				$selectedTheme = $theme;
				break;
			}
		}

		// Run the theme's init() method if a new theme has been selected
		if (isset($newSite)) {
			$selectedTheme->init();
		}

		$errors = $selectedTheme->validateOptions($params, $themePluginPath, CONTEXT_ID_NONE, $request);
		if (!empty($errors)) {
			return $response->withJson($errors, 400);
		}

		// Only accept params that are defined in the theme options
		$options = $selectedTheme->getOptionsConfig();
		foreach ($options as $optionName => $optionConfig) {
			if (!array_key_exists($optionName, $params)) {
				continue;
			}
			$selectedTheme->saveOption($optionName, $params[$optionName], CONTEXT_ID_NONE);
		}

		// Clear the template cache so that new settings can take effect
		$templateMgr = TemplateManager::getManager(Application::get()->getRequest());
		$templateMgr->clearTemplateCache();
		$templateMgr->clearCssCache();

		$data = array_merge(
			$selectedTheme->getOptionValues(CONTEXT_ID_NONE),
			['themePluginPath' => $themePluginPath]
		);

		ksort($data);

		return $response->withJson($data, 200);
	}
}
