<?php

/**
 * Class file for user management action.
 *
 * THIS SOFTWARE IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR IMPLIED
 * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF
 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
 *
 * @package clearFusionCMS
 * @copyright Copyright (c) 2012 - 2016 clearFusionCMS. All rights reserved.
 * @link http://clearfusioncms.com
 */

/**
 * cfBackendRolesAction Class
 *
 * Action class to manage the roles and permissions within the system.
 *
 * @package clearFusionCMS
 */
class cfBackendRolesAction extends cfBackendControllerAction {
	/**
	 * Process the action.
	 */
	function process() {
		$this->testPermission('core.roles');
		$this->breadcrumbTrail->addCrumb(__('Roles', 'cfBackend'), $this->assemble('', 'roles'));

		$acMgr = new flAcMgr($this->db);

		// Get the role that we're currently editing
		$edRole = $this->request->getParam('*');
		$delRole = 0;
		$addRole = false;
		if($edRole !== null && count($edRole) == 1 && preg_match('/^\d+$/', $edRole[0]))
			$edRole = $edRole[0];
		else if($edRole !== null && count($edRole) == 2 && preg_match('/^\d+$/', $edRole[0]) && $edRole[1] == 'delete') {
			$delRole = $edRole[0];
			$edRole = 0;
		}
		else if($edRole !== null && count($edRole) == 1 && $edRole[0] == 'add') {
			$addRole = true;
			$edRole = 0;
		}
		else
			$edRole = 0;

		$edmsg = $this->session->flashGet('cfManagerOpSuccess', '');

		if($addRole) {
			// Build form for adding roles
			$formAdd = new flForm($this->request, 'addroleform', $this->assemble('', 'roles', array('add')), true);

			$newName = new flFormElementText($formAdd, 'newrolename');
			$newName->setLabel(__('New Role Name', 'cfBackend'))
				->setRequired()
				->addValidator(new flFormValidatorRegex('/^[a-z0-9_\.-]+$/', __('The role name may only contain a-z, 0-9, _, - and .', 'cfBackend')))
				->addValidator(new flFormValidatorRequired())
				->addValidator(new flFormValidatorMinLength(3))
				->addValidator(new flFormValidatorMaxLength(32));

			$newDesc = new flFormElementText($formAdd, 'newroledesc');
			$newDesc->setLabel(__('New Role Description', 'cfBackend'))
				->setRequired()
				->addValidator(new flFormValidatorRequired())
				->addValidator(new flFormValidatorMinLength(3))
				->addValidator(new flFormValidatorMaxLength(255));

			$permissions = $acMgr->getPermissions();
			$perms = array();
			foreach($permissions as $grp => $data) {
				$perms['group_' . $grp] = new flFormElementChecklist($formAdd, 'group_' . $grp, array(), $data['permissions']);
				$perms['group_' . $grp]->setLabel($data['groupname']);
			}

			$addSubmit = new flFormElementSubmit($formAdd, 'addrole');
			$addSubmit->setCaption(__('Add Role', 'cfBackend'))->setCSS('primary');

			$formAdd->focusElement($newName);

			$formAdd->groupElements('permissions', $perms)->setGroupLabel('permissions', __('Role Permissions', 'cfBackend'))
				->groupElements('submit', array($addSubmit), new flFormDecoratorList(new flFormDecoratorDl()));

			if($formAdd->isSubmitted() && $formAdd->isValid()) {
				if($id = $acMgr->createRole($newName->getValue(), $newDesc->getValue())) {

					// Set the permissions
					$audit = [];
					$set = [];
					foreach($perms as $elem) {
						$set = array_merge($set, $elem->getValue());
						foreach($elem->getValue() as $pid) {
							$audit[] = $acMgr->getPermissionDisplayName($pid);
						}
					}

					$acMgr->setRolePermissions($id, $set);

					cfUserAudit::newRole($id, $newDesc->getValue(), $audit);

					clearFusionCMS::getEventManager()->notify(new flEvent('system.roleAdded', array('id' => $id)));
					$this->session->flashSet('cfManagerOpSuccess', $this->alertMessage('Success', __('The new role has been created.', 'cfBackend')));
					$this->response->redirectTo($this->assemble('', 'roles', array($id), true));
				}
				else
					$edmsg = $this->alertMessage('Error', __('Unable to create new role.', 'cfBackend'));
			}

			$content = $this->renderBlock(__('Add Role', 'cfBackend'), $formAdd->render($this->tpl->getCodeBuffer()), '', $edmsg);
		}
		else if($edRole) {

			// Build the edit form
			$form = new flForm($this->request, 'edroleform', $this->assemble('', 'roles', array($edRole)), true);

			$roleInfo = $acMgr->getRoleInfo($edRole);
			$desc = new flFormElementText($form, 'roledesc', $roleInfo['displayname']);
			$desc->setLabel(__('Role Description', 'cfBackend'))
				->setRequired()
				->addValidator(new flFormValidatorRequired())
				->addValidator(new flFormValidatorMinLength(3))
				->addValidator(new flFormValidatorMaxLength(255));

			$name = new flFormElementText($form, 'rolename', $roleInfo['rolename']);
			$name->setLabel(__('Role Name', 'cfBackend'))
				->setRequired()
				->addValidator(new flFormValidatorRegex('/^[a-z0-9_\.-]+$/', __('The role name may only contain a-z, 0-9, _, - and .', 'cfBackend')))
				->addValidator(new flFormValidatorRequired())
				->addValidator(new flFormValidatorMinLength(3))
				->addValidator(new flFormValidatorMaxLength(32));

			$permissions = $acMgr->getPermissions();
			$prevPerms = [];
			$perms = [];
			if($edRole != 1) {
				foreach($permissions as $grp => $data) {
					$perms['group_' . $grp] = new flFormElementChecklist($form, 'group_' . $grp, $rolePerms = $acMgr->getRolePermissions($edRole, $grp), $data['permissions']);
					$perms['group_' . $grp]->setLabel($data['groupname']);

					foreach($rolePerms as $pid) {
						$prevPerms[$pid] = $acMgr->getPermissionDisplayName($pid);
					}
				}
			}

			$submit = new flFormElementSubmit($form, 'updaterole');
			$submit->setCaption(__('Update Role', 'cfBackend'))->setCSS('primary');

			if($edRole != 1) {
				$delRoleSub = new flFormElementSubmit($form, 'deleterole');
				$delRoleSub->setCaption(__('Delete Role', 'cfBackend'))
					->setCSS('danger');
			}

			$form->groupElements('role', array($name, $desc));

			if($edRole != 1)
				$form->groupElements('permissions', $perms)->setGroupLabel('permissions', __('Role Permissions', 'cfBackend'));

			$form->groupElements('submit', $edRole == 1 ? array($submit) : array($submit, $delRoleSub), new flFormDecoratorList(new flFormDecoratorDl()));

			$form->focusElement($name);

			if($form->isSubmitted() && $form->isValid()) {
				$acMgr->updateRole($edRole, $name->getValue(), $desc->getValue());

				if($edRole !== 1) {
					// Build permission list for role
					$audit = [];
					$set = [];
					foreach($perms as $elem) {
						$set = array_merge($set, $elem->getValue());
						foreach($elem->getValue() as $pid) {
							$audit[] = $acMgr->getPermissionDisplayName($pid);
						}
					}

					$acMgr->setRolePermissions($edRole, $set);

					cfUserAudit::updateRole($edRole, $desc->getValue(), $audit, $prevPerms);

					// If deleting role
					if(isset($delRoleSub) && $delRoleSub->getValue())
						$this->response->redirectTo($this->assemble('', 'roles', array($edRole, 'delete')));
				}

				$edmsg = $this->alertMessage('Success', __('The role has been updated.', 'cfBackend'));
			}
		}
		else if($delRole) {
			$form = new flForm($this->request, 'delroleform', $this->assemble('', 'roles', array($delRole, 'delete')), true);

			$roleInfo = $acMgr->getRoleInfo($delRole);
			$name = new flFormElementText($form, 'rolename', $roleInfo['displayname']);
			$name->setLabel(__('Delete Role', 'cfBackend'))
				->setDisabled()
				->setReadonly();

			$delSubmit = new flFormElementSubmit($form, 'deleterole');
			$delSubmit->setCaption(__('Permanently Delete Role', 'cfBackend'))
				->setCSS('danger')
				->setDescription($this->alertMessage('Warning', __('Deleting the role may cause some users to loose access to parts of the system, this action cannot be undone!', 'cfBackend')));

			if($form->isSubmitted() && $form->isValid()) {
				if($delRole != 1) {

					$permissions = $acMgr->getPermissions();
					$prevPerms = [];
					foreach($permissions as $grp => $data) {
						$rolePerms = $acMgr->getRolePermissions($delRole, $grp);
						foreach($rolePerms as $pid) {
							$prevPerms[$pid] = $acMgr->getPermissionDisplayName($pid);
						}
					}

					cfUserAudit::deleteRole($delRole, $roleInfo['displayname'], $prevPerms);

					clearFusionCMS::getEventManager()->notify(new flEvent('system.roleDeleted', array('id' => $delRole)));
					$acMgr->deleteRole($delRole);
				}
				$this->response->redirectTo($this->assemble('', 'roles', array(), true));
			}
		}

		$roleTree = new flWidgetTreeView();
		$roles = $acMgr->getRoles();
		foreach($roles as $id => $name) {
			$roleTree->addNode($n = new flWidgetTreeLinkNode($name, $this->assemble('', 'roles', array($id))))
				->setLastNodeActive($id == $edRole || $id == $delRole);
		}

		$roleRootTree = new flWidgetTreeView();
		$roleRootTree->addNode(new flWidgetTreeChildNode(__('Roles', 'cfBackend'), $roleTree, null));

		$tree = $roleRootTree->render();

		if($edRole || $delRole) {
			if(($content = $this->lockObject('cfUserRoles', $edRole ? $edRole : $delRole, true)) === true)
				$content = $this->renderBlock($edRole ? __('Edit Role', 'cfBackend') : __('Delete Role', 'cfBackend'), $form->render($this->tpl->getCodeBuffer()), '', $edmsg);
		}
		else if(!$addRole)
			$content = '';

		// Build floating action menu
		$fam = new flWidgetFloatingActionMenu();
		$fam->setCSS('[[+css]]')
			->addAction('add', __('Add Role', 'cfBackend'), $this->assemble('', 'roles', array('add')));

		$this->tpl->registerChunk('fam', $fam->render());

		$this->renderPage(__('Roles', 'cfBackend'), $content, $this->renderBlock(__('Roles', 'cfBackend'), $tree));
	}
}
