<?php
/**
 * Pimcore
 *
 * This source file is subject to the GNU General Public License version 3 (GPLv3)
 * For the full copyright and license information, please view the LICENSE.md and gpl-3.0.txt
 * files that are distributed with this source code.
 *
 * @copyright  Copyright (c) 2009-2016 pimcore GmbH (http://www.pimcore.org)
 * @license    http://www.pimcore.org/license     GNU General Public License version 3 (GPLv3)
 */

use Pimcore\Tool; 
use Pimcore\Model\User;
use Pimcore\Model\Element;
use Pimcore\Model\Object;

class Admin_UserController extends \Pimcore\Controller\Action\Admin {

    public function init() {
        parent::init();

        // check permissions
        $notRestrictedActions = array("get-current-user", "update-current-user", "get-available-permissions", "get-minimal", "get-image", "upload-current-user-image");
        if (!in_array($this->getParam("action"), $notRestrictedActions)) {
            $this->checkPermission("users");
        }
    }

    public function treeGetChildsByIdAction() {

        $list = new User\Listing();
        $list->setCondition("parentId = ?", intval($this->getParam("node")));
        $list->setOrder("ASC");
        $list->setOrderKey("name");
        $list->load();

        $users = array();
        if(is_array($list->getUsers())){
            foreach ($list->getUsers() as $user) {
                if($user->getId() && $user->getName() != "system") {
                    $users[] = $this->getTreeNodeConfig($user);
                }
            }
        }
        $this->_helper->json($users);
    }

    protected function getTreeNodeConfig($user) {
        $tmpUser = array(
            "id" => $user->getId(),
            "text" => $user->getName(),
            "elementType" => "user",
            "type" => $user->getType(),
            "qtipCfg" => array(
                "title" => "ID: " . $user->getId()
            )
        );

        // set type specific settings
        if ($user instanceof User\Folder) {
            $tmpUser["leaf"] = false;
            $tmpUser["iconCls"] = "pimcore_icon_folder";
            $tmpUser["expanded"] = true;
            $tmpUser["allowChildren"] = true;

            if($user->hasChilds()) {
                $tmpUser["expanded"] = false;
            }
        }
        else {
            $tmpUser["leaf"] = true;
            $tmpUser["iconCls"] = "pimcore_icon_user";
            if (!$user->getActive()) {
                $tmpUser["cls"] = " pimcore_unpublished";
            }
            $tmpUser["allowChildren"] = false;
            $tmpUser["admin"] = $user->isAdmin();

        }

        return $tmpUser;
    }

    public function addAction() {

        $this->protectCSRF();

        try {
            $type = $this->getParam("type");;
            $className = User\Service::getClassNameForType($type);
            $user = $className::create(array(
                "parentId" => intval($this->getParam("parentId")),
                "name" => trim($this->getParam("name")),
                "password" => "",
                "active" => $this->getParam("active")
            ));

            if ($this->getParam("rid")) {

                $rid = $this->getParam("rid");
                $rObject = $className::getById($rid);
                if ($rObject) {
                    if ($type == "user" || $type == "role") {
                        $user->setParentId($rObject->getParentId());
                        if ($rObject->getClasses()) {
                            $user->setClasses(implode(',', $rObject->getClasses()));
                        }
                        if ($rObject->getDocTypes()) {
                            $user->setDocTypes(implode(',', $rObject->getDocTypes()));
                        }

                        $keys = array("asset", "document", "object");
                        foreach ($keys as $key) {
                            $getter = "getWorkspaces" . ucfirst($key);
                            $setter = "setWorkspaces" . ucfirst($key);
                            $workspaces = $rObject->$getter();
                            $clonedWorkspaces = array();
                            if (is_array($workspaces)) {
                                foreach($workspaces as $workspace) {
                                    $vars = get_object_vars($workspace);
                                    $workspaceClass = "\\Pimcore\\Model\\User\\Workspace\\" . ucfirst($key);
                                    $newWorkspace = new $workspaceClass();
                                    foreach ($vars as $varKey => $varValue) {
                                        $newWorkspace->$varKey = $varValue;
                                    }
                                    $newWorkspace->setUserId($user->getId());
                                    $clonedWorkspaces[] = $newWorkspace;
                                }
                            }

                            $user->$setter($clonedWorkspaces);
                        }

                        $user->setPermissions($rObject->getPermissions());

                        if ($type == "user") {
                            $user->setAdmin(false);
                            if($this->getUser()->isAdmin()) {
                                $user->setAdmin($rObject->getAdmin());
                            }
                            $user->setActive($rObject->getActive());
                            $user->setRoles($rObject->getRoles());
                            $user->setWelcomeScreen($rObject->getWelcomescreen());
                            $user->setMemorizeTabs($rObject->getMemorizeTabs());
                            $user->setCloseWarning($rObject->getCloseWarning());
                        }

                        $user->save();
                    }
                }
            }
            $this->_helper->json(array(
                "success" => true,
                "id" => $user->getId()
            ));
        } catch (\Exception $e) {
            $this->_helper->json(array("success" => false, "message" => $e->getMessage()));
        }

        $this->_helper->json(false);
    }


    protected function populateChildNodes($node, &$currentList) {
        $currentUser = \Pimcore\Tool\Admin::getCurrentUser();
        $list = new User\Listing();
        $list->setCondition("parentId = ?", $node->getId());
        $list->setOrder("ASC");
        $list->setOrderKey("name");
        $list->load();

        $childList = $list->getUsers();
        if(is_array($childList)) {
            foreach ($childList as $user) {
                if ($user->getId() == $currentUser->getId()) {
                    throw new Exception("Cannot delete current user");
                }
                if ($user->getId() && $currentUser->getId() && $user->getName() != "system") {
                    $currentList[] = $user;
                    $this->populateChildNodes($user, $currentList);
                }
            }
        }
        return $currentList;
    }

    public function deleteAction() {
        $user = User\AbstractUser::getById(intval($this->getParam("id")));

        // only admins are allowed to delete admins and folders
        // because a folder might contain an admin user, so it is simply not allowed for users with the "users" permission
        if(($user instanceof User\Folder && !$this->getUser()->isAdmin()) || ($user instanceof User && $user->isAdmin() && !$this->getUser()->isAdmin())) {
            throw new \Exception("You are not allowed to delete this user");
        } else {
            if ($user instanceof User\Folder) {

                $list = array($user);
                $this->populateChildNodes($user, $list);
                $listCount = count($list);
                for ($i = $listCount - 1; $i >= 0; $i--) {
                    // iterate over the list from the so that nothing can get "lost"
                    $user = $list[$i];
                    $user->delete();
                }
            } else {
                $user->delete();
            }
        }

        $this->_helper->json(array("success" => true));
    }

    public function updateAction() {

        $this->protectCSRF();

        $user = User\AbstractUser::getById(intval($this->getParam("id")));

        if($user instanceof User && $user->isAdmin() && !$this->getUser()->isAdmin()) {
            throw new \Exception("Only admin users are allowed to modify admin users");
        }

        if($this->getParam("data")) {
            $values = \Zend_Json::decode($this->getParam("data"));

            if (!empty($values["password"])) {
                $values["password"] = Tool\Authentication::getPasswordHash($user->getName(),$values["password"]);
            }

            // check if there are permissions transmitted, if so reset them all to false (they will be set later)
            foreach($values as $key => $value) {
                if(strpos($key, "permission_") === 0) {
                    if(method_exists($user, "setAllAclToFalse")) {
                        $user->setAllAclToFalse();
                    }
                    break;
                }
            }

            $user->setValues($values);

            // only admins are allowed to create admin users
            // if the logged in user isn't an admin, set admin always to false
            if(!$this->getUser()->isAdmin() && $user instanceof User) {
                if($user instanceof User) {
                    $user->setAdmin(false);
                }
            }

            // check for permissions
            $availableUserPermissionsList = new User\Permission\Definition\Listing();
            $availableUserPermissions = $availableUserPermissionsList->load();

            foreach($availableUserPermissions as $permission) {
                if(isset($values["permission_" . $permission->getKey()])) {
                    $user->setPermission($permission->getKey(), (bool) $values["permission_" . $permission->getKey()]);
                }
            }

            // check for workspaces
            if($this->getParam("workspaces")) {
                $workspaces = \Zend_Json::decode($this->getParam("workspaces"));
                foreach ($workspaces as $type => $spaces) {

                    $newWorkspaces = array();
                    foreach ($spaces as $space) {

                        $element = Element\Service::getElementByPath($type, $space["path"]);
                        if($element) {
                            $className = "\\Pimcore\\Model\\User\\Workspace\\" . ucfirst($type);
                            $workspace = new $className();
                            $workspace->setValues($space);

                            $workspace->setCid($element->getId());
                            $workspace->setCpath($element->getFullPath());
                            $workspace->setUserId($user->getId());

                            $newWorkspaces[] = $workspace;
                        }
                    }
                    $user->{"setWorkspaces" . ucfirst($type)}($newWorkspaces);
                }
            }
        }

        $user->save();

        $this->_helper->json(array("success" => true));
    }

    public function getAction() {

        if(intval($this->getParam("id")) < 1) {
            $this->_helper->json(array("success" => false));
        }

        $user = User::getById(intval($this->getParam("id")));

        if($user->isAdmin() && !$this->getUser()->isAdmin()) {
            throw new \Exception("Only admin users are allowed to modify admin users");
        }

        // workspaces
        $types = array("asset","document","object");
        foreach ($types as $type) {
            $workspaces = $user->{"getWorkspaces" . ucfirst($type)}();
            foreach ($workspaces as $workspace) {
                $el = Element\Service::getElementById($type, $workspace->getCid());
                if($el) {
                    // direct injection => not nice but in this case ok ;-)
                    $workspace->path = $el->getFullPath();
                }
            }
        }

        // object <=> user dependencies
        $userObjects = Object\Service::getObjectsReferencingUser($user->getId());
        $userObjectData = array();

        foreach ($userObjects as $o) {
            $hasHidden = false;
            if ($o->isAllowed("list")) {
                $userObjectData[] = array(
                    "path" => $o->getFullPath(),
                    "id" => $o->getId(),
                    "subtype" => $o->getClass()->getName()
                );
            } else {
                $hasHidden = true;
            }
        }

        // get available permissions
        $availableUserPermissionsList = new User\Permission\Definition\Listing();
        $availableUserPermissions = $availableUserPermissionsList->load();

        // get available roles
        $roles = array();
        $list = new User\Role\Listing();
        $list->setCondition("`type` = ?", array("role"));
        $list->load();

        $roles = array();
        if(is_array($list->getItems())){
            foreach ($list->getItems() as $role) {
                $roles[] = array($role->getId(), $role->getName());
            }
        }

        // unset confidential informations
        $userData = object2array($user);
        $contentLanguages = Tool\Admin::reorderWebsiteLanguages($user, Tool::getValidLanguages());
        $userData["contentLanguages"] = $contentLanguages;
        unset($userData["password"]);

        $conf = \Pimcore\Config::getSystemConfig();
        $this->_helper->json(array(
            "success" => true,
            "wsenabled" => $conf->webservice->enabled,
            "user" => $userData,
            "roles" => $roles,
            "permissions" => $user->generatePermissionList(),
            "availablePermissions" => $availableUserPermissions,
            "objectDependencies" => array(
                "hasHidden" => $hasHidden,
                "dependencies" => $userObjectData
            )
        ));
    }

    public function getMinimalAction() {
        $user = User::getById(intval($this->getParam("id")));
        $user->setPassword(null);

        $minimalUserData['id'] = $user->getId();
        $minimalUserData['admin'] = $user->isAdmin();
        $minimalUserData['active'] = $user->isActive();
        $minimalUserData['permissionInfo']['assets'] = $user->isAllowed("assets");
        $minimalUserData['permissionInfo']['documents'] = $user->isAllowed("documents");
        $minimalUserData['permissionInfo']['objects'] = $user->isAllowed("objects");

        $this->_helper->json($minimalUserData);
    }

    public function uploadCurrentUserImageAction() {

        $user = $this->getUser();
        if ($user != null) {
            if ($user->getId() == $this->getParam("id")) {
                $this->uploadImageAction();
            } else {
                \Logger::warn("prevented save current user, because ids do not match. ");
                $this->_helper->json(false);
            }
        } else {
            $this->_helper->json(false);
        }
    }


    public function updateCurrentUserAction() {

        $this->protectCSRF();

        $user = $this->getUser();
        if ($user != null) {
            if ($user->getId() == $this->getParam("id")) {
                $values = \Zend_Json::decode($this->getParam("data"));

                unset($values["name"]);
                unset($values["id"]);
                unset($values["admin"]);
                unset($values["permissions"]);
                unset($values["roles"]);
                unset($values["active"]);

                if (!empty($values["new_password"])) {
                    $oldPasswordCheck = false;


                    if(empty($values["old_password"])) {
                        // if the user want to reset the password, the old password isn't required
                        $oldPasswordCheck = Tool\Session::useSession(function($adminSession) use ($oldPasswordCheck) {
                            if($adminSession->password_reset) {
                                return true;
                            }
                            return false;
                        });

                    } else {
                        // the password has to match
                        $checkUser = Tool\Authentication::authenticatePlaintext($user->getName(), $values["old_password"]);
                        if($checkUser) {
                            $oldPasswordCheck = true;
                        }
                    }

                    if($oldPasswordCheck && $values["new_password"] == $values["retype_password"]) {
                        $values["password"] = Tool\Authentication::getPasswordHash($user->getName(),$values["new_password"]);
                    } else {
                        $this->_helper->json(array("success" => false, "message" => "password_cannot_be_changed"));
                    }
                }

                $user->setValues($values);
                $user->save();
                $this->_helper->json(array("success" => true));
            } else {
                \Logger::warn("prevented save current user, because ids do not match. ");
                $this->_helper->json(false);
            }
        } else {
            $this->_helper->json(false);
        }
    }

    public function getCurrentUserAction() {

        header("Content-Type: text/javascript");

        $user = $this->getUser();

        $list = new User\Permission\Definition\Listing();
        $definitions = $list->load();

        foreach ($definitions as $definition) {
            $user->setPermission($definition->getKey(), $user->isAllowed($definition->getKey()));
        }

        // unset confidential informations
        $userData = object2array($user);
        $contentLanguages = Tool\Admin::reorderWebsiteLanguages($user, Tool::getValidLanguages());
        $userData["contentLanguages"] = $contentLanguages;
        unset($userData["password"]);

        echo "pimcore.currentuser = " . \Zend_Json::encode($userData);
        exit;
    }


    /* ROLES */

    public function roleTreeGetChildsByIdAction() {

        $list = new User\Role\Listing();
        $list->setCondition("parentId = ?", intval($this->getParam("node")));
        $list->load();

        $roles = array();
        if(is_array($list->getItems())){
            foreach ($list->getItems() as $role) {
                $roles[] = $this->getRoleTreeNodeConfig($role);
            }
        }
        $this->_helper->json($roles);
    }

    protected function getRoleTreeNodeConfig($role) {
        $tmpUser = array(
            "id" => $role->getId(),
            "text" => $role->getName(),
            "elementType" => "role",
            "qtipCfg" => array(
                "title" => "ID: " . $role->getId()
            )
        );

        // set type specific settings
        if ($role instanceof User\Role\Folder) {
            $tmpUser["leaf"] = false;
            $tmpUser["iconCls"] = "pimcore_icon_folder";
            $tmpUser["expanded"] = true;
            $tmpUser["allowChildren"] = true;

            if($role->hasChilds()) {
                $tmpUser["expanded"] = false;
            }
        }
        else {
            $tmpUser["leaf"] = true;
            $tmpUser["iconCls"] = "pimcore_icon_roles";
            $tmpUser["allowChildren"] = false;

        }

        return $tmpUser;
    }

    public function roleGetAction() {
        $role = User\Role::getById(intval($this->getParam("id")));

        // workspaces
        $types = array("asset","document","object");
        foreach ($types as $type) {
            $workspaces = $role->{"getWorkspaces" . ucfirst($type)}();
            foreach ($workspaces as $workspace) {
                $el = Element\Service::getElementById($type, $workspace->getCid());
                if($el) {
                    // direct injection => not nice but in this case ok ;-)
                    $workspace->path = $el->getFullPath();
                }
            }
        }

        // get available permissions
        $availableUserPermissionsList = new User\Permission\Definition\Listing();
        $availableUserPermissions = $availableUserPermissionsList->load();

        $this->_helper->json(array(
            "success" => true,
            "role" => $role,
            "permissions" => $role->generatePermissionList(),
            "classes" => $role->getClasses(),
            "docTypes" => $role->getDocTypes(),
            "availablePermissions" => $availableUserPermissions
        ));
    }

    public function uploadImageAction() {

        if($this->getParam("id")) {
            if($this->getUser()->getId() != $this->getParam("id")) {
                $this->checkPermission("users");
            }
            $id = $this->getParam("id");
        } else {
            $id = $this->getUser()->getId();
        }

        $userObj = User::getById($id);

        if($userObj->isAdmin() && !$this->getUser()->isAdmin()) {
            throw new \Exception("Only admin users are allowed to modify admin users");
        }

        $userObj->setImage($_FILES["Filedata"]["tmp_name"]);


        $this->_helper->json(array(
            "success" => true
        ), false);

        // set content-type to text/html, otherwise (when application/json is sent) chrome will complain in
        // Ext.form.Action.Submit and mark the submission as failed
        $this->getResponse()->setHeader("Content-Type", "text/html");
    }

    public function getImageAction() {

        if($this->getParam("id")) {
            if($this->getUser()->getId() != $this->getParam("id")) {
                $this->checkPermission("users");
            }
            $id = $this->getParam("id");
        } else {
            $id = $this->getUser()->getId();
        }

        $userObj = User::getById($id);
        $thumb = $userObj->getImage();

        header("Content-type: image/png", true);

        while(@ob_end_flush());
        flush();
        readfile($thumb);
        exit;
    }

    public function getTokenLoginLinkAction() {

        $user = User::getById($this->getParam("id"));

        if($user->isAdmin() && !$this->getUser()->isAdmin()) {
            throw new \Exception("Only admin users are allowed to login as an admin user");
        }

        if($user) {
            $token = Tool\Authentication::generateToken($user->getName(), $user->getPassword());
            $r = $this->getRequest();
            $link = $r->getScheme() . "://" . $r->getHttpHost() . "/admin/login/login/?username=" . $user->getName() . "&token=" . $token;

            $this->_helper->json(array(
                "link" => $link
            ));
        }
    }

    public function searchAction() {

        $q = "%" . $this->getParam("query") . "%";

        $list = new User\Listing();
        $list->setCondition("name LIKE ? OR firstname LIKE ? OR lastname LIKE ? OR email LIKE ? OR id = ?", [$q, $q, $q, $q, intval($this->getParam("query"))]);
        $list->setOrder("ASC");
        $list->setOrderKey("name");
        $list->load();

        $users = array();
        if(is_array($list->getUsers())){
            foreach ($list->getUsers() as $user) {
                if($user instanceof User && $user->getId() && $user->getName() != "system") {
                    $users[] = [
                        "id" => $user->getId(),
                        "name" => $user->getName(),
                        "email" => $user->getEmail(),
                        "firstname" => $user->getFirstname(),
                        "lastname" => $user->getLastname(),
                    ];
                }
            }
        }
        $this->_helper->json([
            "success" => true,
            "users" => $users
        ]);
    }
}
