<?php
class Plugg_User_Plugin extends Plugg_Plugin implements Plugg_Widget_Widget, Plugg_User_Tab, Plugg_User_Widget, Plugg_User_Menu
{
    const FIELD_TYPE_REGISTERABLE = 1;
    const FIELD_TYPE_EDITABLE = 2;
    const FIELD_TYPE_VIEWABLE = 4;
    const FIELD_TYPE_ALL = 7;
    const FIELD_TYPE_REGISTERABLE_REQUIRED = 9;
    const FIELD_TYPE_EDITABLE_REQUIRED = 18;
    const FIELD_TYPE_VIEWABLE_REQUIRED = 36;
    const FIELD_TYPE_ALL_REQUIRED = 63;
    const FIELD_CONFIGURABLE = 64;

    const TAB_TYPE_PUBLIC = 1;
    const TAB_TYPE_PRIVATE = 2;
    const TAB_TYPE_PUBLIC_ACTIVE = 3;
    const TAB_TYPE_PRIVATE_ACTIVE = 4;

    const WIDGET_TYPE_PUBLIC = 1;
    const WIDGET_TYPE_PRIVATE = 2;
    const WIDGET_TYPE_MINI = 4;

    const MENU_TYPE_EDITABLE = 1;
    const MENU_TYPE_NONEDITABLE = 2;

    const AUTOLOGIN_COOKIE = 'plugg_user_autologin';

    const QUEUE_TYPE_REGISTER = 0;
    const QUEUE_TYPE_REGISTERAUTH = 1;
    const QUEUE_TYPE_EDITEMAIL = 2;
    const QUEUE_TYPE_REQUESTPASSWORD = 3;

    const FRIENDREQUEST_STATUS_PENDING = 0;
    const FRIENDREQUEST_STATUS_REJECTED = 1;
    const FRIENDREQUEST_STATUS_ACCEPTED = 2;
    const FRIENDREQUEST_STATUS_CONFIRMED = 3;

    public function hasCurrentUser()
    {
        if ($this->getParam('enableAutologin')) {
            if (isset($_COOKIE[self::AUTOLOGIN_COOKIE])) {
                if (($cookie = explode(':', $_COOKIE[self::AUTOLOGIN_COOKIE])) &&
                    ($user_id = $cookie[0]) &&
                    ($password = $cookie[1]) &&
                    ($session = $this->_getAutologinSession($user_id)) &&
                    $session->expires > time() &&
                    ($identity = $session->User) &&
                    sha1($session->salt . $identity->getPassword()) == $password
                ) {
                    $session->last_ip = getip();
                    $session->last_ua = $_SERVER['HTTP_USER_AGENT'];
                    $session->commit();
                    $user = new Sabai_User($identity, true);
                    $user->startSession();
                    return $user;
                }
                // Invalidate cookie
                $this->_setAutologinCookie('', time() - 3600);
            }
        }
        return false;
    }

    private function _getAutologinSession($userId)
    {
        return $this->getModel()->Autologin->criteria()->userid_is($userId)->fetch(1, 0)->getNext();
    }

    public function createAutologinSession($user)
    {
        $user_id = $user->getId();
        if (!$session = $this->_getAutologinSession($user_id)) {
            $session = $this->getModel()->create('Autologin');
            $session->salt = md5(uniqid(mt_rand(), true));
            $session->assignUser($user);
            $session->markNew();
        } else {
            if ($this->getParam('limitSingleAutologinSession')) {
                // Update salt so that only the requested PC holds a valid autologin cookie
                $session->salt = md5(uniqid(mt_rand(), true));
            }
        }
        $expires = time() + 86400 * intval($this->getParam('autologinSessionLifetime'));
        $session->expires = $expires;
        $session->last_ip = getip();
        $session->last_ua = $_SERVER['HTTP_USER_AGENT'];
        if (!$session->commit()) {
            return false;
        }
        $password = sha1($session->salt . $user->getIdentity()->getPassword());
        $this->_setAutologinCookie("$user_id:$password", $expires);
        return true;
    }

    private function _setAutologinCookie($value, $expires)
    {
        $path = '/';
        if ($parsed = parse_url($this->config->get('siteUrl'))) {
            $path = $parsed['path'];
        }
        setcookie(self::AUTOLOGIN_COOKIE, $value, $expires, $path, '', false, true);
    }

    public function getManagerPlugin()
    {
        if ($manager_name = $this->getParam('userManagerPlugin')) {
            return $this->pluginManager->getPlugin($manager_name);
        }
        return false;
    }

    public function onPluggInit()
    {
        // Use alternate user account plugin?
        if ($plugin_name = $this->getParam('userManagerPlugin')) {
            if ($plugin_handle = $this->pluginManager->getPluginHandle($plugin_name)) {
                $db = $this->getDB();
                $this->locator->addProviderClass(
                    'UserIdentityFetcher',
                    array('pluginHandle' => $plugin_handle, 'DB' => $db),
                    'Plugg_User_IdentityFetcher',
                    $this->_path . '/IdentityFetcher.php'
                );
            }
        }
    }

    public function onPluggRun($controller)
    {
        require $this->_path . '/Filter.php';
        require_once 'Sabai/Handle/Instance.php';
        $controller->prependFilter(new Sabai_Handle_Instance(new Plugg_User_Filter()));
    }

    public function onPluggMainRoutes($routes)
    {
        parent::_onPluggMainRoutes($routes);
    }

    public function onPluggAdminRoutes($routes)
    {
        parent::_onPluggAdminRoutes($routes);
    }

    public function onUserPluginConfigured($pluginEntity, $originalParams)
    {
        $params = $pluginEntity->getParams();
        if ($params['userManagerPlugin'] !== $originalParams['userManagerPlugin']) {
            $this->pluginManager->dispatch(
                'UserManagerPluginChanged',
                array($originalParams['userManagerPlugin'], $params['userManagerPlugin'])
            );
        }
    }

    public function onUserTabInstalled($pluginEntity, $plugin)
    {
        if ($tabs = $plugin->userTabGetNames()) {
            $this->_createPluginUserTabs($pluginEntity->name, $tabs);
        }
    }

    public function onUserTabUninstalled($pluginEntity, $plugin)
    {
        $this->_deletePluginUserFeature($pluginEntity->name, 'Tab');
        $this->clearAllCache();
    }

    public function onUserTabUpgraded($pluginEntity, $plugin)
    {
        if (!$tabs = $plugin->userTabGetNames()) {
            $this->_deletePluginUserFeature($pluginEntity->name, 'Tab');
        } else {
            $tabs_already_installed = array();
            foreach ($this->getModel()->Tab->criteria()->plugin_is($pluginEntity->name)->fetch() as $current_tab) {
                if (in_array($current_tab->name, $tabs)) {
                    $tabs_already_installed[] = $current_tab->name;
                } else {
                    $current_tab->markRemoved();
                }
            }
            $this->_createPluginUserTabs($pluginEntity->name, array_diff($tabs, $tabs_already_installed));
        }

        $this->clearAllCache();
    }

    public function onUserFieldInstalled($pluginEntity, $plugin)
    {
        if ($fields = $plugin->userFieldGetNames()) {
            $this->_createPluginUserFields($pluginEntity->name, $fields);
            $this->clearAllCache();
        }
    }

    public function onUserFieldUninstalled($pluginEntity, $plugin)
    {
        $this->_deletePluginUserFeature($pluginEntity->name, 'Field');
        $this->clearAllCache();
    }

    public function onUserFieldUpgraded($pluginEntity, $plugin)
    {
        if (!$fields = $plugin->userFieldGetNames()) {
            $this->_deletePluginUserFeature($pluginEntity->name, 'Field');
        } else {
            $fields_already_installed = array();
            foreach ($this->getModel()->Field->criteria()->plugin_is($pluginEntity->name)->fetch() as $current_field) {
                if (in_array($current_field->name, $fields)) {
                    $fields_already_installed[] = $current_field->name;
                } else {
                    $current_field->markRemoved();
                }
            }
            $this->_createPluginUserFields($pluginEntity->name, array_diff($fields, $fields_already_installed));
        }

        $this->clearAllCache();
    }

    public function onUserAuthenticatorInstalled($pluginEntity, $plugin)
    {
        if ($auths = $plugin->userAuthGetName()) {
            $this->_createPluginUserAuths($pluginEntity->name, $auths);
            $this->clearAllCache();
        }
    }

    public function onUserAuthenticatorUninstalled($pluginEntity, $plugin)
    {
        $this->_deletePluginUserFeature($pluginEntity->name, 'Auth');
        $this->clearAllCache();
    }

    public function onUserAuthenticatorUpgraded($pluginEntity, $plugin)
    {
        if (!$auths = $plugin->userAuthGetName()) {
            $this->_deletePluginUserFeature($pluginEntity->name, 'Auth');
        } else {
            $auths_already_installed = array();
            foreach ($this->getModel()->Auth->criteria()->plugin_is($pluginEntity->name)->fetch() as $current_auth) {
                if (in_array($current_auth->name, $auths)) {
                    $auths_already_installed[] = $current_auth->name;
                } else {
                    $current_auth->markRemoved();
                }
            }
            $this->_createPluginUserAuths($pluginEntity->name, array_diff($auths, $auths_already_installed));
        }

        $this->clearAllCache();
    }

    public function onUserWidgetInstalled($pluginEntity, $plugin)
    {
        if ($widgets = $plugin->userWidgetGetNames()) {
            $this->_createPluginUserWidgets($pluginEntity->name, $widgets);
            $this->clearAllCache();
        }
    }

    public function onUserWidgetUninstalled($pluginEntity, $plugin)
    {
        $this->_deletePluginUserFeature($pluginEntity->name, 'Widget');
        $this->clearAllCache();
    }

    public function onUserWidgetUpgraded($pluginEntity, $plugin)
    {
        if (!$widgets = $plugin->userWidgetGetNames()) {
            $this->_deletePluginUserFeature($pluginEntity->name, 'Widget');
        } else {
            $widgets_already_installed = array();
            foreach ($this->getModel()->Widget->criteria()->plugin_is($pluginEntity->name)->fetch() as $current_widget) {
                if (in_array($current_widget->name, $widgets)) {
                    $widgets_already_installed[] = $current_widget->name;
                } else {
                    $current_widget->markRemoved();
                }
            }
            $this->_createPluginUserWidgets($pluginEntity->name, array_diff($widgets, $widgets_already_installed));
        }

        $this->clearAllCache();
    }

    public function onUserMenuInstalled($pluginEntity, $plugin)
    {
        if ($menus = $plugin->userMenuGetNames()) {
            $this->_createPluginUserMenus($pluginEntity->name, $menus);
            $this->clearAllCache();
        }
    }

    public function onUserMenuUninstalled($pluginEntity, $plugin)
    {
        $this->_deletePluginUserFeature($pluginEntity->name, 'Menu');
        $this->clearAllCache();
    }

    public function onUserMenuUpgraded($pluginEntity, $plugin)
    {
        if (!$menus = $plugin->userMenuGetNames()) {
            $this->_deletePluginUserFeature($pluginEntity->name, 'Menu');
        } else {
            $menus_already_installed = array();
            foreach ($this->getModel()->Menu->criteria()->plugin_is($pluginEntity->name)->fetch() as $current_menu) {
                if (in_array($current_menu->name, $menus)) {
                    $menus_already_installed[] = $current_menu->name;
                } else {
                    $current_menu->markRemoved();
                }
            }
            $this->_createPluginUserMenus($pluginEntity->name, array_diff($menus, $menus_already_installed));
        }

        $this->clearAllCache();
    }

    private function _clearRolePermissionsCache()
    {
        $this->getCache()->remove('permissions');
    }

    public function clearMenuDataCache()
    {
        $this->getCache()->remove('menu_data');
    }

    function clearAllCache()
    {
        $this->getCache()->clean();
    }

    private function _createPluginUserTabs($pluginName, $tabs)
    {
        $model = $this->getModel();
        foreach ($tabs as $tab_name => $tab_title) {
            if (empty($tab_name)) continue;
            $tab = $model->create('Tab');
            $tab->name = $tab_name;
            if (is_array($tab_title)) {
                $tab->title = $tab_title[0];
                if (isset($tab_title[1])) {
                    if (in_array($tab_title[1], array(self::TAB_TYPE_PRIVATE, self::TAB_TYPE_PRIVATE_ACTIVE))) {
                        $tab->private = 1;
                    }
                    $tab->type = $tab_title[1];
                } else {
                    $tab->type = self::TAB_TYPE_PUBLIC;
                }
            } else {
                $tab->title = $tab_title;
                $tab->type = self::TAB_TYPE_PUBLIC;
            }
            $tab->plugin = $pluginName;
            $tab->active = 1;
            $tab->markNew();
        }

        return $model->commit();
    }

    /**
     * Plugins that are installed prior to the User plugin can call this method upon
     * the UserPluginInstalled event to manually register user tabs.
     *
     * @param string $pluginName
     * @param array $tabs
     * @return bool
     */
    public function createPluginUserTabs($pluginName, $tabs)
    {
        return $this->_createPluginUserTabs($pluginName, $tabs);
    }

    private function _createPluginUserFields($pluginName, $fields)
    {
        $model = $this->getModel();
        foreach ($fields as $field_name => $field_title) {
            if (empty($field_name)) continue;
            $field = $model->create('Field');
            $field->name = $field_name;
            if (is_array($field_title)) {
                $field->title = $field_title[0];
                $field->type = isset($field_title[1]) ? intval($field_title[1]) : self::FIELD_TYPE_ALL;
            } else {
                $field->title = $field_title;
                $field->type = self::FIELD_TYPE_ALL;
            }
            if ($field->isType(self::FIELD_TYPE_EDITABLE)) {
                $field->editable = 1;
            }
            if ($field->isType(self::FIELD_TYPE_VIEWABLE)) {
                $field->viewable = 1;
            }
            if ($field->isType(self::FIELD_CONFIGURABLE)) {
                $field->configurable = 1;
            }
            $field->plugin = $pluginName;
            $field->active = 1;
            $field->markNew();
        }

        return $model->commit();
    }

    /**
     * Plugins that are installed prior to the User plugin can call this method upon
     * the UserPluginInstalled event to manually register user fields.
     *
     * @param string $pluginName
     * @param array $fields
     * @return bool
     */
    public function createPluginUserFields($pluginName, $fields)
    {
        return $this->_createPluginUserFields($pluginName, $fields);
    }

    private function _createPluginUserAuths($pluginName, $auths)
    {
        $model = $this->getModel();
        foreach ($auths as $auth_name => $auth_title) {
            if (empty($auth_name)) continue;
            $auth = $model->create('Auth');
            $auth->name = $auth_name;
            $auth->title = $auth_title;
            $auth->plugin = $pluginName;
            $auth->active = 1;
            $auth->markNew();
        }
        return $model->commit();
    }

    private function _createPluginUserWidgets($pluginName, $widgets)
    {
        $model = $this->getModel();
        foreach ($widgets as $widget_name => $widget_title) {
            if (empty($widget_name)) continue;
            $widget = $model->create('Widget');
            $widget->name = $widget_name;
            if (is_array($widget_title)) {
                $widget->title = $widget_title[0];
                if (isset($widget_title[1])) {
                    if ($widget_title[1] & self::WIDGET_TYPE_PRIVATE) {
                        $widget->private = 1;
                    } else {
                        // Make sure the widget is of type public
                        if (!$widget_title[1] & self::WIDGET_TYPE_PUBLIC) {
                            $widget_title[1] += self::WIDGET_TYPE_PUBLIC;
                        }
                    }
                    if ($widget_title[1] & self::WIDGET_TYPE_MINI) {
                        $widget->mini = 1;
                    }
                    $widget->type = $widget_title[1];
                } else {
                    $widget->type = self::WIDGET_TYPE_PUBLIC;
                }
            } else {
                $widget->title = $widget_title;
                $widget->type = self::WIDGET_TYPE_PUBLIC;
            }
            $widget->plugin = $pluginName;
            $widget->active = 1;
            $widget->markNew();
        }
        return $model->commit();
    }

    private function _createPluginUserMenus($pluginName, $menus)
    {
        $model = $this->getModel();
        foreach ($menus as $menu_name => $menu_title) {
            $menu = $model->create('Menu');
            $menu->name = $menu_name;
            $menu->plugin = $pluginName;
            $menu->active = 1;
            $menu->type = self::MENU_TYPE_EDITABLE;
            $menu->markNew();
            if (is_array($menu_title)) {
                $menu->title = $menu_title[0];
                if (isset($menu_title[1])) {
                    $menu->type = $menu_title[1];
                }
            } else {
                $menu->title = $menu_title;
            }
        }
        return $model->commit();
    }

    private function _deletePluginUserFeature($pluginName, $featureName)
    {
        $model = $this->getModel();
        foreach ($model->$featureName->criteria()->plugin_is($pluginName)->fetch() as $entity) {
            $entity->markRemoved();
        }
        return $model->commit();
    }

    public function onUserAdminRolePermissions($permissions)
    {
        $permissions[$this->_library] = array(
            'user profile view any' => $this->_("View other user's profile"),
            'user profile edit any' => $this->_("Edit other user's profile"),
            'user profile delete own' => $this->_('Delete own user profile'),
            'user profile delete any' => $this->_("Delete other user's profile"),
            'user email edit own' => $this->_('Edit own user email'),
            'user email edit any' => $this->_("Edit other user's email"),
            'user image edit own' => $this->_('Edit own user image'),
            'user image edit any' => $this->_("Edit other user's image"),
            'user tab view any private' => $this->_("View other user's private tab contents"),
            'user widget view any private' => $this->_("View other user's private widget contents"),
            'user friend manage any' => $this->_("Manage other user's friends data"),
        );
        if (!empty($this->_param['allowViewAnyUser'])) unset($permissions[$this->_library]['user profile view any']);
    }

    public function widgetGetNames()
    {
        return array('account', 'login');
    }

    public function widgetGetTitle($widgetName)
    {
        switch ($widgetName) {
            case 'account':
                return $this->_('Your Account');
            case 'login':
                return $this->_('Login');
        }
    }

    public function widgetGetContent(Sabai_User $user, Sabai_Template_PHP $template, $widgetName)
    {
        switch ($widgetName) {
            case 'account':
                if ($user->isAuthenticated()) {
                    return $this->_renderAccountWidget($user, $template);
                }
                break;
            case 'login':
                if (!$user->isAuthenticated()) {
                    return $template->render('plugg_user_widget_login.tpl', array());
                }
                break;
        }
    }

    public function _renderAccountWidget($user, $template)
    {
        $menu_data = $this->_getMenuData();
        $menus = array();
        foreach ($menu_data as $_menu) {
            $plugin_name = $_menu['plugin'];
            $menu_name = $_menu['name'];
            if (!isset($_SESSION['Plugg_User_Plugin'][$plugin_name][$menu_name])) {
                if (($plugin = $this->pluginManager->getPlugin($plugin_name)) &&
                    ($link_text = $plugin->userMenuGetLinkText($menu_name, $_menu['title'], $user))
                ) {
                    $_SESSION['Plugg_User_Plugin'][$plugin_name][$menu_name] = array(
                        'text' => $link_text,
                        'url' => $plugin->userMenuGetLinkUrl($menu_name, $user),
                    );
                } else {
                    $_SESSION['Plugg_User_Plugin'][$plugin_name][$menu_name] = false;
                }
            }
            $menus[] = $_SESSION['Plugg_User_Plugin'][$plugin_name][$menu_name];
        }
        return $template->render('plugg_user_widget_account.tpl', array('menus' => $menus));
    }

    public function clearMenuInSession($pluginName, $menuName)
    {
        unset($_SESSION['Plugg_User_Plugin'][$pluginName][$menuName]);
    }

    public function onUserLoginSuccess($user)
    {
        $stat = $this->_getStatByIdentity($user->getIdentity());
        $stat->last_login = time();
        $stat->commit();
    }

    public function onUserLogoutSuccess($user)
    {
        // Invalidate autologin cookie
        $this->_setAutologinCookie('', time() - 3600);
    }

    public function onUserRegisterSuccess($identity)
    {
        // Init stat data for the user
        $stat = $this->_getStatByIdentity($identity);
        foreach (array('last_edit', 'last_edit_email', 'last_edit_password', 'last_edit_image') as $stat_type) {
            $stat->$stat_type = time();
        }
        $stat->commit();

        // Send user registration complete email to site admin
        $replacements = array(
            '{SITE_NAME}' => $site_name = $this->config->get('siteName'),
            '{SITE_URL}' => $this->config->get('siteUrl'),
            '{USER_NAME}' => $identity->getUsername(),
            '{USER_LINK}' => $this->url->create(array('base' => '/user', 'path' => '/' . $identity->getId())),
        );
        $subject = sprintf($this->_('New user registration at %s'), $site_name);
        $body = strtr($this->getParam('registerCompleteEmail'), $replacements);

        $this->pluginManager
            ->getPlugin('mail')
            ->getSender()
            ->mailSend($this->config->get('siteEmail'), $subject, $body);
    }

    public function onUserIdentityEditSuccess($identity)
    {
        $stat = $this->_getStatByIdentity($identity);
        $stat->last_edit = time();
        $stat->commit();
    }

    public function onUserIdentityEditEmailSuccess($identity)
    {
        $stat = $this->_getStatByIdentity($identity);
        $stat->last_edit_email = time();
        $stat->commit();
    }

    public function onUserIdentityEditPasswordSuccess($identity)
    {
        $stat = $this->_getStatByIdentity($identity);
        $stat->last_edit_password = time();
        $stat->commit();
    }

    public function onUserIdentityEditImageSuccess($identity)
    {
        $stat = $this->_getStatByIdentity($identity);
        $stat->last_edit_image = time();
        $stat->commit();
    }

    private function _getStatByIdentity(&$identity)
    {
        $id = $identity->getId();
        $model = $this->getModel();
        if (!$stat = $model->Stat->fetchByUser($id)->getNext()) {
            $stat = $model->create('Stat');
            $stat->userid = $id;
            $stat->markNew();
        }
        return $stat;
    }

    public function onUserIdentityDeleteSuccess($identity)
    {
        $id = $identity->getId();
        $model = $this->getModel();
        $model->getGateway('Stat')->deleteByCriteria($model->createCriteria('Stat')->userid_is($id));
        $model->getGateway('Extra')->deleteByCriteria($model->createCriteria('Extra')->userid_is($id));
        $model->getGateway('Authdata')->deleteByCriteria($model->createCriteria('Authdata')->userid_is($id));
        $model->getGateway('Autologin')->deleteByCriteria($model->createCriteria('Autologin')->userid_is($id));
        $model->getGateway('Queue')->deleteByCriteria($model->createCriteria('Queue')->identity_id_is($id));
        $model->getGateway('Friendrequest')->deleteByCriteria($model->createCriteria('Freindrequest')->userid_is($id));
        $model->getGateway('Friend')->deleteByCriteria($model->createCriteria('Friend')->userid_is($id)->or_()->with_is($id));
    }

    public function userTabGetNames()
    {
        return array(
            'autologin' => array($this->_('AutoLogin'), self::TAB_TYPE_PRIVATE),
            'friends' => array($this->_('Friends'), self::TAB_TYPE_PUBLIC_ACTIVE)
        );
    }

    public function userTabGetNicename($tabName)
    {
        switch ($tabName) {
            case 'autologin':
                return $this->_('AutoLogin');
            case 'friends':
                return $this->_('Friends');
        }
    }

    public function userTabGetContent($tabName, Sabai_Request_Web $request, Sabai_User $user, Sabai_Template_PHP $template, $tabId, Sabai_User_Identity $identity)
    {
        switch ($tabName) {
            case 'autologin':
                return $this->_renderAutologinUserTab($request, $user, $template, $tabId, $identity);
            case 'friends':
                if ($this->getParam('useFriendsFeature')) {
                    return $this->_renderFriendsUserTab($request, $user, $template, $tabId, $identity);
                }
                break;
        }
    }

    private function _renderAutologinUserTab($request, $user, $template, $tabId, $identity)
    {
        $id = $identity->getId();
        return $template->render('plugg_user_user_tab_autologin.tpl', array(
            'identity' => $identity,
            'autologins' => $this->getModel()->Autologin->criteria()->userid_is($id)->fetch(),
            'tab_id' => $tabId)
        );
    }

    private function _renderFriendsUserTab($request, $user, $template, $tabId, $identity)
    {
        $id = $identity->getId();
        $model = $this->getModel();
        $friends_pages = $model->Friend->paginateByUser($id, 30);
        $friends_page = $friends_pages->getValidPage($request->getAsInt('tab_friends_page', 1));
        $vars = array(
            'is_owner' => $is_owner = $user->getId() == $id,
            'can_manage' => $can_manage =  $is_owner ? true : $user->hasPermission('user friend manage any'),
            'identity' => $identity,
            'tab_id' => $tabId,
            'friends' => $friends_page->getElements(),
            'friends_pages' => $friends_pages,
            'friends_page' => $friends_page,
            'friends_count_last' => $friends_count_last = $friends_page->getOffset() + $friends_page->getLimit(),
            'friends_count_first' => $friends_count_last > 0 ? $friends_page->getOffset() + 1 : 0,
        );
        if ($can_manage) {
            $requests_pending_pages = $model->Friendrequest
                ->criteria()
                ->status_is(self::FRIENDREQUEST_STATUS_PENDING)
                ->paginateByUser($id, 10, 'request_created', 'DESC');
            $requests_pending_page = $requests_pending_pages->getValidPage($request->getAsInt('tab_request_pending_page', 1));

            $requests_accepted_pages = $model->Friendrequest
                ->criteria()
                ->status_is(self::FRIENDREQUEST_STATUS_ACCEPTED)
                ->paginateByUser($id, 10, 'request_updated', 'DESC');
            $requests_accepted_page = $requests_accepted_pages->getValidPage($request->getAsInt('tab_request_accepted_page', 1));

            $requests_rejected_pages = $model->Friendrequest
                ->criteria()
                ->status_is(self::FRIENDREQUEST_STATUS_REJECTED)
                ->paginateByUser($id, 10, 'request_updated', 'DESC');
            $requests_rejected_page = $requests_rejected_pages->getValidPage($request->getAsInt('tab_request_rejected_page', 1));

            $requests_received_pages = $model->Friendrequest
                ->criteria()
                ->status_is(self::FRIENDREQUEST_STATUS_PENDING)
                ->to_is($identity->getId())
                ->paginate(10, 'request_created', 'DESC');
            $requests_received_page = $requests_received_pages->getValidPage($request->getAsInt('tab_request_received_page', 1));

            $vars = array_merge($vars, array(
                'requests_pending_pages' => $requests_pending_pages,
                'requests_pending_page' => $requests_pending_page,
                'requests_pending' => $requests_pending_page->getElements(),
                'requests_pending_count_last' => $requests_pending_count_last = $requests_pending_page->getOffset() + $requests_pending_page->getLimit(),
                'requests_pending_count_first' => $requests_pending_count_last > 0 ? $requests_pending_page->getOffset() + 1 : 0,
                'requests_accepted_pages' => $requests_accepted_pages,
                'requests_accepted_page' => $requests_accepted_page,
                'requests_accepted' => $requests_accepted_page->getElements(),
                'requests_accepted_count_last' => $requests_accepted_count_last = $requests_accepted_page->getOffset() + $requests_accepted_page->getLimit(),
                'requests_accepted_count_first' => $requests_accepted_count_last > 0 ? $requests_accepted_page->getOffset() + 1 : 0,
                'requests_rejected_pages' => $requests_rejected_pages,
                'requests_rejected_page' => $requests_rejected_page,
                'requests_rejected' => $requests_rejected_page->getElements(),
                'requests_rejected_count_last' => $requests_rejected_count_last = $requests_rejected_page->getOffset() + $requests_rejected_page->getLimit(),
                'requests_rejected_count_first' => $requests_rejected_count_last > 0 ? $requests_rejected_page->getOffset() + 1 : 0,
                'requests_received_pages' => $requests_received_pages,
                'requests_received_page' => $requests_received_page,
                'requests_received' => $requests_received_page->getElements(),
                'requests_received_count_last' => $requests_received_count_last = $requests_received_page->getOffset() + $requests_received_page->getLimit(),
                'requests_received_count_first' => $requests_received_count_last > 0 ? $requests_received_page->getOffset() + 1 : 0,
            ));
        }
        return $template->render('plugg_user_user_tab_friends.tpl', $vars);
    }

    public function onPluggCron($lastrun)
    {
        // Allow run this cron 1 time per day at most
        if (!empty($lastrun) && time() - $lastrun < 86400) return;

        $model = $this->getModel();

        // Remove expired autologin sessions
        $criteria = $model->createCriteria('Autologin')
            ->expires_isSmallerThan(time());
        $model->getGateway('Autologin')->deleteByCriteria($criteria);

        // Remove queues older than 3 days
        $criteria = $model->createCriteria('Queue')
            ->created_isSmallerThan(time() - 259200);
        $model->getGateway('Queue')->deleteByCriteria($criteria);

        // Remove confirmed friend requests
        $criteria = $model->createCriteria('Friendrequest')
            ->status_is(self::FRIENDREQUEST_STATUS_CONFIRMED);
        $model->getGateway('Friendrequest')->deleteByCriteria($criteria);

        // Remove unconfirmed but rejected/accepted friend requests that are more than 100 days old
        $criteria = $model->createCriteria('Friendrequest')
            ->status_is(self::FRIENDREQUEST_STATUS_ACCEPTED)
            ->created_isSmallerThan(time() - 8640000);
        $model->getGateway('Friendrequest')->deleteByCriteria($criteria);
        $criteria = $model->createCriteria('Friendrequest')
            ->status_is(self::FRIENDREQUEST_STATUS_REJECTED)
            ->created_isSmallerThan(time() - 8640000);
        $model->getGateway('Friendrequest')->deleteByCriteria($criteria);
    }

    private function _getMenuData()
    {
        $ret = array();
        $cache = $this->getCache();
        if (false === $data = $cache->get('menu_data')) {
            $menus = $this->getModel()->Menu->criteria()->active_is(1)->fetch(0, 0, 'menu_order', 'ASC');
            foreach ($menus as $menu) {
                $ret[] = array(
                    'plugin' => $menu->plugin,
                    'name' => $menu->name,
                    'title' => $menu->title
                );
            }
            $cache->save(serialize($ret), 'menu_data');
        } else {
            $ret = unserialize($data);
        }
        return $ret;
    }

    public function isTabActive($pluginName, $tabName)
    {
        $tab = $this->getModel()->Tab
            ->criteria()
            ->plugin_is($pluginName)
            ->active_is(1)
            ->name_is($tabName)
            ->fetch()
            ->getNext();
        return !$tab ? false : $tab->getId();
    }

    public function getRelationships($from, $to)
    {
        $friend = $this->getModel()->Friend
            ->criteria()
            ->with_is(is_object($to) ? $to->getId() : $to)
            ->fetchByUser(is_object($from) ? $from->getId() : $from, 1, 0)
            ->getNext();
        return $friend ? $friend->getRelationships() : array();
    }

    public function userWidgetGetNames()
    {
        return array(
            'friend_button' => array($this->_('Add as friend'), self::WIDGET_TYPE_MINI),
            'friends' => array($this->_('Friends')),
            'profile_button' => array($this->_('View public profile'), self::WIDGET_TYPE_PRIVATE | self::WIDGET_TYPE_MINI)
        );
    }

    public function userWidgetGetNiceName($widgetName)
    {
        switch ($widgetName) {
            case 'friend_button': return $this->_('"Add as friend" button');
            case 'friends': return $this->_('Friends list');
            case 'profile_button': return $this->_('"View public profile" button');
        }
    }

    public function userWidgetGetContent($widgetName, Sabai_User $viewer, Sabai_Template_PHP $template, Sabai_User_Identity $identity, $isMini)
    {
        switch ($widgetName) {
            case 'friend_button':
                if ($this->getParam('useFriendsFeature') &&
                    $viewer->isAuthenticated() &&
                    $viewer->getId() != $identity->getId()
                ) {
                    return sprintf(
                        '<a style="background-image:url(%s)" class="user-widget-button" href="%s">%s</a>',
                        $this->url->getImageUrl($this->_library, 'friend_add.gif'),
                        $this->url->create(array(
                            'base' => '/user',
                            'path' => '/' . $viewer->getId() . '/friend/request',
                            'params' => array('to' => $identity->getId())
                        )),
                        $this->_('Add as friend')
                    );
                }
                break;
            case 'friends':
                if ($this->getParam('useFriendsFeature')) {
                    return $this->_renderFriendsUserWidget($viewer, $template, $identity, $isMini);
                }
                break;
            case 'profile_button':
                if ($viewer->getId() == $identity->getId()) {
                    return sprintf(
                        '<a style="background-image:url(%s)" class="user-widget-button" href="%s">%s</a>',
                        $this->url->getImageUrl($this->_library, 'profile.gif'),
                        $this->url->create(array(
                            'base' => '/user',
                            'path' => '/' . $viewer->getId(),
                            'params' => array('public' => 1)
                        )),
                        $this->_('View public profile')
                    );
                }
                break;
        }
    }

    private function _renderFriendsUserWidget(&$user, &$template, &$identity, $isMini)
    {
        $id = $identity->getId();
        $model = $this->getModel();
        if (0 == $friends_count = $model->Friend->countByUser($id)) return;

        $vars = array(
            'is_owner' => $is_owner = $user->getId() == $id,
            'identity' => $identity,
            'can_manage' => $can_manage =  $is_owner ? true : $user->hasPermission('user friend manage any'),
            'friends' => $model->Friend->fetchByUser($id, 0, 10, 'friend_created', 'DESC'),
            'friends_count' => $friends_count,
        );
        if ($isMini) {
            return $template->render('plugg_user_user_widget_friends_mini.tpl', $vars);
        }
        return $template->render('plugg_user_user_widget_friends.tpl', $vars);
    }

    public function userMenuGetNames()
    {
        return array('friendrequest' => array('', Plugg_User_Plugin::MENU_TYPE_NONEDITABLE));
    }

    public function userMenuGetNicename($menuName)
    {
        switch ($menuName) {
            case 'friendrequest':
                return $this->_('New friend requests');
        }
    }

    public function userMenuGetLinkText($menuName, $menuTitle, Sabai_User $user)
    {
        switch ($menuName) {
            case 'friendrequest':
                if (!$this->getParam('useFriendsFeature')) return;

                $count = $this->getModel()->Friendrequest
                    ->criteria()
                    ->to_is($user->getId())
                    ->status_is(self::FRIENDREQUEST_STATUS_PENDING)
                    ->count();
                if ($count == 0) return;

                return sprintf($this->_('Friend requests (<strong>%d</strong>)'), $count);
        }
    }

    public function userMenuGetLinkUrl($menuName, Sabai_User $user)
    {
        switch ($menuName) {
            case 'friendrequest':
                if ($this->getParam('useFriendsFeature')) {
                    return $this->url->create(array(
                        'base' => '/user',
                        'path' => '/' . $user->getId() . '/friend')
                    );
                }
                break;
        }
    }

    public function getXFNMetaDataList($categorize = true)
    {
        $list = array(
            'Friendship' => array(
                $this->_('contact'),
                $this->_('acquaintance'),
                $this->_('friend')
            ),
            'Physical' => array($this->_('met')),
            'Professional' => array(
                $this->_('co-worker'),
                $this->_('colleague')
            ),
            'Geographical' => array(
                $this->_('co-resident'),
                $this->_('neighbor')
            ),
            'Family' => array(
                $this->_('child'),
                $this->_('parent'),
                $this->_('sibling'),
                $this->_('spouse'),
                $this->_('kin')
            ),
            'Romantic' => array(
                $this->_('muse'),
                $this->_('crush'),
                $this->_('date'),
                $this->_('sweetheart')
            ),
            'Identity' => array($this->_('me'))
        );
        if ($categorize) {
            return $list;
        }
        $ret = array();
        foreach ($list as $k => $v) {
            $ret = array_merge($ret, $v);
        }
        return $ret;
    }

    public function sendRegisterConfirmEmail($queue, $manager, $confirmByAdmin = false)
    {
        $confirm_link = $this->url->create(array(
            'base' => '/user',
            'path' => '/confirm/' . $queue->getId(),
            'params' => array('key' => $queue->get('key')),
            'separator' => '&'
        ));
        $confirm_recipient = $queue->get('notify_email');
        $data = $queue->getData();
        $replacements = array(
            '{SITE_NAME}' => $this->config->get('siteName'),
            '{SITE_URL}' => $this->config->get('siteUrl'),
            '{USER_NAME}' => $queue->get('register_username'),
            '{USER_EMAIL}' => $confirm_recipient,
            '{CONFIRM_LINK}' => $confirm_link,
            '{IP}' => getip()
        );
        $subject = sprintf($this->_('User activation key for %s'), $queue->get('register_username'));
        if ($confirmByAdmin) {
            // Send confirmation mail to admin
            $body_template = $this->getParam('registerConfirmByAdminEmail');
            $to = $this->config->get('siteEmail');
        } else {
            $body_template = $this->getParam('registerConfirmEmail');
            $to = $confirm_recipient;
        }
        $body = strtr($body_template, $replacements);

        return $this->pluginManager
            ->getPlugin('mail')
            ->getSender()
            ->mailSend($to, $subject, $body);
    }

    public function sendEditEmailConfirmEmail($queue, $manager)
    {
        $identity = $this->locator
            ->getService('UserIdentityFetcher')
            ->fetchUserIdentity($queue->get('identity_id'));
        if ($identity->isAnonymous()) {
            return false;
        }

        $confirm_link = $this->url->create(array(
            'base' => '/user',
            'path' => '/confirm/' . $queue->getId(),
            'params' => array('key' => $queue->get('key')),
            'separator' => '&'
        ));
        $confirm_email = $queue->get('notify_email');
        $replacements = array(
            '{SITE_NAME}' => $this->config->get('siteName'),
            '{SITE_URL}' => $this->config->get('siteUrl'),
            '{USER_NAME}' => $identity->getUsername(),
            '{USER_EMAIL}' => $confirm_email,
            '{CONFIRM_LINK}' => $confirm_link
        );
        $subject = sprintf($this->_('User activation key for %s'), $identity->getUsername());
        $body = strtr($this->getParam('editEmailConfirmEmail'), $replacements);

        return $this->pluginManager
            ->getPlugin('mail')
            ->getSender()
            ->mailSend($confirm_email, $subject, $body);
    }

    public function sendRequestPasswordConfirmEmail($queue, $identity, $manager)
    {
        $confirm_link = $this->url->create(array(
            'base' => '/user',
            'path' => '/confirm/' . $queue->getId(),
            'params' => array('key' => $queue->get('key')),
            'separator' => '&'
        ));
        $replacements = array(
            '{SITE_NAME}' => $site_name = $this->config->get('siteName'),
            '{SITE_URL}' => $this->config->get('siteUrl'),
            '{USER_NAME}' => $identity->getUsername(),
            '{CONFIRM_LINK}' => $confirm_link,
            '{IP}' => getip()
        );
        $subject = sprintf($this->_('New password request at %s'), $site_name);
        $body = strtr($this->getParam('newPasswordConfirmEmail'), $replacements);

        return $this->pluginManager
            ->getPlugin('mail')
            ->getSender()
            ->mailSend($identity->getEmail(), $subject, $body);
    }

    public function createAuthdata($authData, $userId)
    {
        $auth_data = $this->getModel()->create('Authdata');
        $auth_data->claimed_id = $authData['claimed_id'];
        $auth_data->display_id = $authData['display_id'];
        //$auth_data->type = $authData['type'];
        $auth_data->lastused = time();
        $auth_data->setVar('auth_id', $authData['auth_id']);
        $auth_data->setVar('userid', $userId);
        $auth_data->markNew();
        return $auth_data->commit();
    }

    public function createExtra($identity, $extraData)
    {
        $model = $this->getModel();
        $data = array();
        foreach ($model->Field->criteria()->active_is(1)->fetch() as $field) {
            if (!$field_plugin = $this->pluginManager->getPlugin($field->plugin)) {
                continue;
            }
            if ($field_data = @$extraData[$field->plugin][$field->name]) {
                $plugin_lib = $field_plugin->getLibrary();
                list($filtered_value, $filter_id) = $field_data['filter'];
                $data[$plugin_lib][$field->plugin][$field->name] = array(
                    'value' => $field_plugin->userFieldSubmit($field->name, $field_data['value'], $identity, $filtered_value, $filter_id),
                    'visibility' => $field_data['visibility']
                );
            }
        }
        $extra = $model->create('Extra');
        $extra->setData($data);
        $extra->setVar('userid', $identity->getId());
        $extra->markNew();
        return $extra->commit();
    }
}