<?php
#BEGIN_LICENSE
#-------------------------------------------------------------------------
# Module: MAMS (c) 2020-2021 by CMS Made Simple Foundation
#  An add-on module for CMS Made Simple to provide useful functions
#  and commonly used gui capabilities to other modules.
#-------------------------------------------------------------------------
# A fork of:
#
# Module: FrontEndUsers (c) 2008-2014 by Robert Campbell
#         (calguy1000@cmsmadesimple.org)
#
#-------------------------------------------------------------------------
#
# CMSMS - CMS Made Simple is (c) 2006 - 2021 by CMS Made Simple Foundation
# CMSMS - CMS Made Simple is (c) 2005 by Ted Kulp (wishy@cmsmadesimple.org)
# Visit the CMSMS Homepage at: http://www.cmsmadesimple.org
#
#-------------------------------------------------------------------------
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# However, as a special exception to the GPL, this software is distributed
# as an addon module to CMS Made Simple.  You may not use this software
# in any Non GPL version of CMS Made simple, or in any version of CMS
# Made simple that does not indicate clearly and obviously in its admin
# section that the site was built with CMS Made simple.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
# Or read it online: http://www.gnu.org/licenses/licenses.html#GPL
#
#-------------------------------------------------------------------------
#END_LICENSE
use \MAMS\verification_assistant;
use \MAMS\email_verify_assistant;
use \CMSMS\HookManager;

/**
 * This file defines the MAMS module class.
 *
 * @package MAMS
 */

/**
 * The MAMS module, besides being a module to provide authentication services to the frontend of a CMSMS
 * website also has a complete and flexible API.
 *
 * @package MAMS
 */
final class MAMS extends \CMSMSExt\XTModule
{
  /**
   * Constant to identify a text property.
   */
  const FIELDTYPE_TEXT = 0;
  
  /**
   * Constant to identify a checkbox property.
   */
  const FIELDTYPE_CHECKBOX = 1;
  
  /**
   * Constant to identify an email property.
   */
  const FIELDTYPE_EMAIL = 2;
  
  /**
   * Constant to identify a textarea property.
   */
  const FIELDTYPE_TEXTAREA = 3;
  
  /**
   * Constant to identify a dropdown property.
   */
  const FIELDTYPE_DROPDOWN = 4;
  
  /**
   * Constant to identify a multiselect property.
   */
  const FIELDTYPE_MULTISELECT = 5;
  
  /**
   * Constant to identify an image property.
   */
  const FIELDTYPE_IMAGE = 6;
  
  /**
   * Constant to identify a radio button group property.
   */
  const FIELDTYPE_RADIOBUTNS = 7;
  
  /**
   * Constant to identify a date property.
   */
  const FIELDTYPE_DATE = 8;
  
  /**
   * Constant to identify a data property.
   */
  const FIELDTYPE_DATA = 9;
  
  /**
   * How many seconds of inactivity before a user is automatically logged out.
   *
   * @ignore
   */
  protected $expirytime;
  
  /**
   * Set this to true if user should be redirected to default page after logging in/out.
   *
   * @ignore
   */
  protected $logintarget;
  
  /**
   * @ignore
   */
  protected $otherintarget;
  
  /**
   * @ignore
   */
  protected $logouttarget;
  
  /**
   * @ignore
   */
  protected $otherouttarget;
  
  /**
   * @ignore
   */
  protected $allowuseradmin;
  
  /**
   * @ignore
   */
  protected $enableemailpw;
  
  /**
   * @ignore
   */
  protected $lang = '';
  
  /**
   * @ignore
   */
  private $usermanip = FALSE;
  
  /**
   * @ignore
   */
  private $verification_assistant = NULL;
  
  /**
   * @ignore - work around constructor called twice issue
   */
  private static $_initialized;
  
  /**
   * Constructor
   *
   * @ignore
   */
  public function __construct()
  {
    parent::__construct();

    if(self::$_initialized) { return; }
    self::$_initialized = TRUE;
  }
  
  /**
   * @ignore
   */
  private function _load()
  {
    $test = $this->usermanip;
    
    if(!$this->usermanip)
    {
      /**
       * check a preference to see
       * which user manipulator class we should use.
       */
      $this->usermanip = new MAMSManipulator($this);
      
      $test = $this->usermanip;
      
      if(!is_object($this->usermanip))
      {
        die('Error instantiating manipulator object');
      }

      $this->usermanip->SetExpireNotifier([$this, 'NotifyExpiredUser']);
    }
  }
  
  /**
   * @return string
   */
  public function GetMinimumPhpVersion() { return '7.4.0'; }
  
  final public function GetFieldTypes() : array
  {
    return [
      'text'         => self::FIELDTYPE_TEXT,
      'checkbox'     => self::FIELDTYPE_CHECKBOX,
      'email'        => self::FIELDTYPE_EMAIL,
      'textarea'     => self::FIELDTYPE_TEXTAREA,
      'dropdown'     => self::FIELDTYPE_DROPDOWN,
      'multiselect'  => self::FIELDTYPE_MULTISELECT,
      'image'        => self::FIELDTYPE_IMAGE,
      'radiobuttons' => self::FIELDTYPE_RADIOBUTNS,
      'date'         => self::FIELDTYPE_DATE,
      'data'         => self::FIELDTYPE_DATA
    ];
    
  }
  
  /**
   * Get the User Manipulator Object
   * NOT FOR EXTERNAL USE
   *
   * @return object
   * @internal
   */
  final public function GetManipulator()
  {
    $this->_load();
    
    return $this->usermanip;
  }
  
  /**
   * @ignore
   */
  function GetVersion(){ return '1.1.0'; }
  
  /**
   * @ignore
   */
  function HasContentType(){ return TRUE; }
  
  /**
   * @ignore
   */
  function IsPluginModule(){ return TRUE; }
  
  /**
   * @ignore
   */
  function AllowAutoInstall(){ return FALSE; }
  
  /**
   * @ignore
   */
  function AllowAutoUpgrade(){ return FALSE; }
  
  /**
   * @ignore
   */
  function LazyLoadAdmin(){ return FALSE; }
  
  /**
   * @ignore
   */
  function MinimumCMSVersion(){ return '2.2.9'; }
  
  /**
   * @ignore
   */
  function GetAdminDescription(){ return $this->Lang('moddescription'); }
  
  /**
   * @ignore
   */
  function GetAdminSection(){ return 'usersgroups'; }
  
  /**
   * @ignore
   */
  # todo: update the CMSMSExt version to 1.4.4 as soon as it leaves beta
  function GetDependencies(){ return ['CMSMSExt' => '1.4.3', 'SmartyExt' => '1.0']; }
  
  /**
   * @ignore
   */
  function GetEventDescription($eventname){ return $this->Lang('event_info_' . $eventname); }
  
  /**
   * @ignore
   */
  function GetEventHelp($eventname){ return $this->Lang('event_help_' . $eventname); }
  
  /**
   * @ignore
   */
  function GetFriendlyName(){ return $this->Lang('friendlyname'); }
  
  /**
   * @ignore
   */
  function HasAdmin(){ return TRUE; }
  
  /**
   * @ignore
   */
  function InstallPostMessage(){ return $this->Lang('postinstallmessage'); }
  
  /**
   * @ignore
   */
  function GetAuthor(){ return 'Jo Morg'; }
  
  /**
   * @ignore
   */
  function GetAuthorEmail(){ return 'jomorg@cmsmadesimple.org'; }
  
  function GetHelp()
  {
    $mods     = \ModuleOperations::get_instance()->GetInstalledModules();
    $have_feu = in_array('FrontEndUsers', $mods);
    \cms_utils::get_smarty()->assign('have_feu', $have_feu);
    
    return parent::GetHelp();
  }
  
  /**
   * @ignore
   * @deprecated
   */
  protected function langifyKeys($arr) : array
  {
    if( !is_array($arr) ) $arr = [$arr];
    $out = [];
    foreach($arr as $k => $v)
    {
      $k       = $this->Lang($k);
      $out[$k] = $v;
    }
    
    return $out;
  }
  
  function SetParameters()
  {
    $smarty = cmsms()->GetSmarty();
    if(!$smarty){return;}
    
    $smarty->registerClass('mams_smarty', 'mams_smarty');
    $smarty->registerPlugin('block', 'mams_protect', '\\mams_smarty_plugins::mams_protect');
    $smarty->registerPlugin('function', 'mams_user_options', '\\mams_smarty_plugins::mams_user_options');
    $this->AddImageDir('icons');
    
    $contentops = ContentOperations::get_instance();
    
    $obj               = new CmsContentTypePlaceholder();
    $obj->class        = 'MAMS\ProtectedPage';
    $obj->type         = 'mams_protected_page';  // must be lowercase, and short, this is what is stored in the database.
    $obj->filename     = __DIR__ . '/lib/class.mams_protected_page.php';
    $obj->loaded       = FALSE;
    $obj->friendlyname = $this->Lang('mams_protected_page');
    $contentops->register_content_type($obj);
    
    $obj               = new CmsContentTypePlaceholder();
    $obj->class        = 'MAMS\ProtectedSectionHead';
    $obj->type         = 'mams_protected_sec_head';
    $obj->filename     = __DIR__ . '/lib/class.mams_protected_sectionhead.php';
    $obj->loaded       = FALSE;
    $obj->friendlyname = $this->Lang('mams_protected_sectionhead');
    $contentops->register_content_type($obj);
    
    $obj               = new CmsContentTypePlaceholder();
    $obj->class        = 'mams_protected_logoutlink';
    $obj->type         = strtolower($obj->class);
    $obj->filename     = __DIR__ . '/lib/class.mams_protected_logoutlink.php';
    $obj->loaded       = FALSE;
    $obj->friendlyname = $this->Lang('mams_protected_logoutlink');
    $contentops->register_content_type($obj);
    
    HookManager::add_hook('MAMS::OnDeleteUser', 'mams_utils::hook_ondeleteuser');
    HookManager::add_hook('MAMS::AfterDeleteUser', 'mams_utils::hook_afterdeleteuser');
  }
  
  /**
   * @ignore
   */
  function InitializeFrontend()
  {
    parent::InitializeFrontend();
    $this->RestrictUnknownParams();
    $this->RegisterModulePlugin();
    $this->SetParameterType('code', CLEAN_STRING);
    $this->SetParameterType('form', CLEAN_STRING); // deprecated
    $this->SetParameterType('returnto', CLEAN_STRING);
    $this->SetParameterType('only_groups', CLEAN_STRING);
    $this->SetParameterType('nocaptcha', CLEAN_INT);
    $this->SetParameterType('input_username', CLEAN_STRING); // deprecated
    $this->SetParameterType('input_password', CLEAN_STRING); // deprecated
    $this->SetParameterType('input_repeatpassword', CLEAN_STRING); // deprecated
    $this->SetParameterType('error', CLEAN_INT); // deprecated
    $this->SetParameterType('message', CLEAN_STRING); // deprecated
    $this->SetParameterType('lostun_group', CLEAN_STRING);
    $this->SetParameterType('input_captcha', CLEAN_STRING); // deprecated
    $this->SetParameterType('submit', CLEAN_STRING); // deprecated
    $this->SetParameterType('cancel', CLEAN_STRING); // deprecated
    $this->SetParameterType('input_code', CLEAN_STRING); // deprecated
    $this->SetParameterType('uid', CLEAN_INT);
    $this->SetParameterType('checkonly', CLEAN_INT);
    $this->SetParameterType('inline', CLEAN_INT);
    $this->SetParameterType('logouttemplate', CLEAN_STRING);
    $this->SetParameterType('logintemplate', CLEAN_STRING);
    $this->SetParameterType('changesettingstemplate', CLEAN_STRING);
    $this->SetParameterType('forgotpwtemplate', CLEAN_STRING);
    $this->SetParameterType('forgotpwemailtemplate', CLEAN_STRING);
    $this->SetParameterType('lostuntemplate', CLEAN_STRING);
    $this->SetParameterType('lostunconfirmtemplate', CLEAN_STRING);
    $this->SetParameterType('forcenewpwtemplate', CLEAN_STRING);
    $this->SetParameterType('viewusertemplate', CLEAN_STRING);
    $this->SetParameterType('afterverifytemplate', CLEAN_STRING);
    $this->SetParameterType('resetsessiontemplate', CLEAN_STRING);
    $this->SetParameterType('verifyformtemplate', CLEAN_STRING);
    $this->SetParameterType(CLEAN_REGEXP . '/mams_.*/', CLEAN_STRING);
    
    $this->RegisterRoute(
      '/mams\/verifycode\/(?P<returnid>[0-9]+)\/(?P<uid>[0-9]+)\/(?P<code>.*?)$/', array('action' => 'verifycode')
    );
    $this->RegisterRoute(
      '/mams\/verify\/(?P<returnid>[0-9]+)\/(?P<uid>[0-9]+)\/(?P<code>.*?)$/', array('action' => 'verifyonly')
    );
    $this->RegisterRoute(
      '/mams\/reset\/(?P<uid>[0-9]+)$/',
      array('action' => 'reset_session', 'showtemplate' => 'false', 'checkonly' => '1')
    );
    $this->RegisterRoute('/mams\/edit\/(?P<returnid>[0-9]+)$/', array('action' => 'changesettings'));
    $this->RegisterRoute('/mams\/logout\/(?P<returnid>[0-9]+)$/', array('action' => 'logout'));
    $this->RegisterRoute('/mams\/forgot\/(?P<returnid>[0-9]+)$/', array('action' => 'forgotpw'));
    $this->RegisterRoute('/mams\/lostusername\/(?P<returnid>[0-9]+)$/', array('action' => 'lostusername'));
    $this->RegisterRoute('/mams\/ajax\/(?P<prms>.+)$/', array('action' => 'ajax'));
  }
  
  /**
   * @ignore
   */
  function get_tasks()
  {
    $out = NULL;
    
    // clear history task
    $tmp = (int)$this->GetPreference('clearhistory_age');
    
    if($tmp > 0)
    {
      $obj   = new \MAMS\ClearUserHistoryTask();
      $out[] = $obj;
    }
    
    // force logout at specific server times
    $tmp = $this->GetPreference('forcelogout_times');
    if($tmp != '')
    {
      $obj   = new MAMSForcedLogoutTask($this);
      $out[] = $obj;
    }
    
    return $out;
  }
  
  /**
   * @ignore
   */
  public function get_pretty_url($id, $action, $returnid = '', $params = [], $inline = FALSE)
  {
    if($action == 'default')
    {
      $form = \xt_param::get_string($params, 'form');
      switch($form)
      {
        case 'forgotpw':
          $action = 'forgotpw';
        break;
        
        case 'login':
          $action = 'login';
        break;
        
        case 'lostusername':
          $action = 'lostusername';
        break;
        
        case 'changesettings':
          return "mams/edit/$returnid";
      }
    }
    
    if($action == 'verifyonly')
    {
      $uid  = \xt_param::get_int($params, 'uid');
      $code = \xt_param::get_string($params, 'code');
      
      if($uid < 1 || !$code){return;}
      
      return "mams/verify/$returnid/$uid/$code";
    }
    
    if($action == 'forgotpw')
    {
      return "mams/forgot/$returnid";
    }
    else if($action == 'lostusername')
    {
      return "mams/lostusername/$returnid";
    }
    else if($action == 'logout')
    {
      $returnto = \xt_param::get_string($params, 'returnto');
      
      if($returnto){return;}
      
      return "mams/logout/$returnid";
    }
    else if($action == 'changesettings')
    {
      return "mams/edit/$returnid";
    }
    
  }
  
  /**
   * @ignore
   */
  protected function _HasSufficientPermissions($perm = '')
  {
    if($this->GetPreference('mams_specific_permissions', '0') == '1')
    {
      $pusers  = $this->CheckPermission('MAMS Modify Users');
      $pgroups = $this->CheckPermission('MAMS Modify Groups');
      $pprop   = $this->CheckPermission('MAMS Modify FrontendUserManagerProps');
    }
    else
    {
      $pusers  = $this->CheckPermission('Manage Users');
      $pgroups = $this->CheckPermission('Manage Groups');
      $pprop   = $this->CheckPermission('Modify FrontendUserManagerProps');
    }
    
    $consumer = mams_utils::get_auth_consumer();

    switch($perm)
    {
      case '':  // any permission for working with groups and users
        $ret = ($pusers || $pgroups || $pprop);
      break;
      
      case 'listusers':
      case 'users':
      case 'adduser':
      case 'editusers':
      case 'removeusers':
        $ret = $pusers;
      break;
      
      case 'properties':
        $ret = $pprop && $consumer->has_capability($consumer::CAPABILITY_LISTPROPS);
      break;
      
      case 'addprop':
      case 'editprop':
      case 'deleteprop':
        $ret = $pprop && $consumer->has_capability($consumer::CAPABILITY_EDITPROPS);
      break;
      
      case 'groups':
      case 'listgroups':
        $ret = ($pgroups &&
                $consumer->has_capability($consumer::CAPABILITY_USESTDGROUPS)) ||
               $consumer->has_capability($consumer::CAPABILITY_LISTGROUPS);
      break;
      
      case 'strict_editgroups':
      case 'removegroups':
      case 'addgroup':
        $ret = $pgroups && ($consumer->has_capability($consumer::CAPABILITY_USESTDGROUPS) ||
                            $consumer->has_capability($consumer::CAPABILITY_EDITGROUPS));
      break;
      
      case 'editgroups':
        $ret = $pgroups && ($consumer->has_capability($consumer::CAPABILITY_USESTDGROUPS) ||
                            $consumer->has_capability($consumer::CAPABILITY_EDITGROUPS) ||
                            $consumer->has_capability($consumer::CAPABILITY_EDITGROUPPROPS));
      break;
      
      case 'usersngroups':
        $ret = ($pusers || $pgroups);
      break;
      
      case 'siteprefs':
        $ret = $pprop;
      break;
      
      default:
        $ret = 0;
      break;
    }
    
    return $ret;
  }
  
  /**
   * @ignore
   */
  function VisibleToAdminUser()
  {
    return $this->_HasSufficientPermissions('');
  }
  
  public function GetAdminMenuItems()
  {
    $out = [];
    if($this->_HasSufficientPermissions(''))
    {
      $out[] = \CmsAdminMenuItem::from_module($this);
    }
    
    if($this->CheckPermission('Modify Site Preferences'))
    {
      $obj              = new CmsAdminMenuItem();
      $obj->module      = $this->GetName();
      $obj->section     = 'siteadmin';
      $obj->title       = $this->Lang('title_adminnav_settings');
      $obj->description = $this->Lang('desc_adminnav_settings');
      $obj->action      = 'admin_settings';
      $obj->url         = $this->create_url('m1_', $obj->action);
      $out[]            = $obj;
    }
    
    return $out;
  }
  
  /**
   * @ignore
   */
  protected function _DisplayErrorPage($id, &$params, $returnid, $message = '')
  {
    echo $this->DisplayErrorMessage($message);
  }
  
  /**
   * @ignore
   */
  function DoAction($action, $id, $params, $returnid = -1)
  {
    if(isset($params['mams_enc']))
    {
      // restore parameters saved in the session on redirect.
      $sig = $params['mams_enc'];
      unset($params['mams_enc']);
      if(isset($_SESSION[$sig]))
      {
        $tmp = $_SESSION[$sig];
        unset($_SESSION[$sig]);
        $params = array_merge($tmp, $params);
      }
    }
    
    if('default' === $action)
    {
      $form = 'login';
      if($this->LoggedInId())
      {
        $form = 'logout';
      }
      $form = \xt_param::get_string($params, 'form', $form);
      switch($form)
      {
        case 'login':
        case 'forgotpw':
        case 'silent':
        case 'changesettings':
        case 'lostusername':
          $action = $form;
        break;
        
        case 'logout':
          $action = 'logoutform';
        break;
        
        default:
          throw new \LogicException("$form is an invalid value for the form parameter");
      }
    }
    
    switch($action)
    {
      case 'changesettings_url':
      break;
      
      default:
        parent::DoAction($action, $id, $params, $returnid);
      break;
    }
  }
  
  /**
   * @internal
   * @deprecated
   */
  function myRedirect($id, $action, $returnid, $params = [], $ignore_returnto = FALSE)
  {
    // find any parameter values that are arrays
    // automatically store this data in the session
    unset($params['action'], $params['returnid']);
    
    $sig            = md5(__METHOD__ . time() . serialize($params));
    $_SESSION[$sig] = $params;
    $params         = ['mams_enc' => $sig];
    $this->Redirect($id, $action, $returnid, $params);
  }
  
  /**
   * @internal
   */
//  public function GetHeaderHTML()
//  {
//    $txt  = parent::GetHeaderHTML();
//    $tmpl = <<<EOT
//{JQueryTools action='require' lib='tablesorter,JQueryTools'}
//{JQueryTools action='placemarker'}
//EOT;
//
//    $obj = cms_utils::get_module('JQueryTools', '1.2');
//    if(is_object($obj))
//    {
//      $smarty = cmsms()->GetSmarty();
//      $txt    .= $smarty->fetch('string:' . $tmpl);
//    }
//
//    return $txt;
//  }
  
  /**
   * send an event that this user account has been expired
   *
   * @internal
   */
  public function NotifyExpiredUser($userid)
  {
    $user = $this->GetUserInfo($userid);
    
    if(FALSE == $user[0]) { return; } // this should be an error
    
    $parms             = array();
    $parms['id']       = $userid;
    $parms['username'] = $user[1]['username'];
    HookManager::do_hook('MAMS::OnExpireUser', $parms);
  }
  
  /**
   * @internal
   */
  public function is_allowed_upload($srcfile)
  {
    $allowed_extensions = $this->GetPreference('allowed_image_extensions', '.gif,.png,.jpg');
    $tmp                = explode(',', strtolower($allowed_extensions));
    $srcfile            = strtolower($srcfile);
    
    foreach($tmp as $ext)
    {
      if(endswith($srcfile, $ext)) { return TRUE; }
    }
    
    return FALSE;
  }
  
  /**
   * @internal
   * @todo: move me out of here
   */
  public function get_upload_dirurl()
  {
    $config = \cms_config::get_instance();
    $dn     = cms_join_path($config['uploads_url'], $this->GetPreference('image_destination_path', 'mams'));
    $dn     = str_replace('\\', '/', $dn);
    
    return $dn;
  }
  
  /**
   * @internal
   */
  public function get_upload_dirname()
  {
    $config = \cms_config::get_instance();
    $dn     = cms_join_path($config['uploads_path'], $this->GetPreference('image_destination_path', 'mams'));
    
    return $dn;
  }
  
  /**
   * @internal
   */
  public function get_upload_filename($fldname, $srcfile)
  {
    // get the filename only, not the directory
    // note: this must be reproducible given the same srcfile and filename for the same request.
    $ext  = strrchr($srcfile, '.');
    $time = \xt_param::get_int($_SERVER, 'REQUEST_TIME');
    
    return sha1(__FILE__ . session_id() . $fldname . $srcfile . $time) . $ext;
  }
  
  /**
   * @internal
   */
  public function ManageImageUpload($key, $fldname)
  {
    $res = \mams_utils::checkUpload($key);
    
    if(!$res[0]) { return $res; }
    
    $file = $_FILES[$key];
    // set the destination name
    $destDir  = $this->get_upload_dirname();
    $destname = $this->get_upload_filename($fldname, $file['name']);
    
    // Create the destination directory if necessary
    if(!mkdir($destDir, 0777, TRUE) && !is_dir($destDir))
    {
      throw new \RuntimeException(sprintf('Directory "%s" was not created', $destDir));
    }
    
    @touch($destDir . '/index.html');
    
    if(!is_writable($destDir))
    {
      return [FALSE, $this->Lang('error_destinationnotwritable')];
    }
    
    @cms_move_uploaded_file($file['tmp_name'], cms_join_path($destDir, $destname));
    
    return [TRUE, $destname];
  }
  
  /**
   * @internal
   */
  public function SetPostLoginURL($url)
  {
    if($url)
    {
      $this->session_put('postlogin_url', $url);
    }
  }
  
  /**
   * @internal
   */
  public function GetPostLoginURL()
  {
    $out = $this->session_get('postlogin_url');
    $this->session_clear('postlogin_url');
    
    return $out;
  }
  
  //////////////////////////////////////////
  //  API FUNCTIONS //
  //////////////////////////////////////////
  
  /**
   * @throws \MAMS\MethodNoFoundException
   * @deprecated use \MAMS\api instead
   * @ignore
   */
  public function __call($name, $args)
  {
    $this->_load();
    
    if( !method_exists( $this->usermanip, $name ) )
    {
      throw new \MAMS\MethodNoFoundException($name, get_class($this->usermanip));
    }

    return call_user_func_array([$this->usermanip, $name], $args);
  }
  
  /**
   * Get the filenames of all files associated with image properties for the specified user.
   * Does not check if the files exist, have thumbnails, or for permissions.
   *
   * @param int $uid The userid
   *
   * @return array
   */
  function GetUserFilesFull($userid)
  {
    $userid = (int)$userid;
    if($userid < 1) { return []; }
    
    $defns    = $this->GetPropertyDefns();
    $propvals = $this->GetUserProperties($userid);
    
    if(!$propvals) { return []; }
    
    $propvals = \xt_array::to_hash($propvals, 'title');
    $out      =  [];
    
    foreach($defns as $name => $defn)
    {
      if($defn['type'] != self::FIELDTYPE_IMAGE) { continue; }
      
      if(!isset($propvals[$name])) { continue; }
      
      if(!$propvals[$name]['data']) { continue; }
      
      $out[$name] = $propvals[$name]['data'];
    }
    
    return $out;
  }
  
  
  /**
   * @internal
   * @ignore
   */
  function HasCapability($capability, $params = [])
  {
    $capabilities = [
       'content_types',
       'content_attributes',
       'tasks',
       'clicommands'];
    
    return in_array($capability, $capabilities);
  }
  
  /**
   * @internal
   * @ignore
   */
  public function get_cli_commands($app)
  {
    $out   = NULL;
    $out[] = new MAMS\Commands\GroupEditCommand($app);
    $out[] = new MAMS\Commands\GroupListCommand($app);
    $out[] = new MAMS\Commands\GroupListPropsCommand($app);
    $out[] = new MAMS\Commands\GroupViewCommand($app);
    $out[] = new MAMS\Commands\PropEditCommand($app);
    $out[] = new MAMS\Commands\PropListCommand($app);
    $out[] = new MAMS\Commands\UserAddCommand($app);
    $out[] = new MAMS\Commands\UserDeleteCommand($app);
    $out[] = new MAMS\Commands\UserEditCommand($app);
    $out[] = new MAMS\Commands\UserPropEditCommand($app);
    $out[] = new MAMS\Commands\UserListCommand($app);
    $out[] = new MAMS\Commands\UserViewCommand($app);
    
    return $out;
  }
  
  /**
   * @internal
   * @ignore
   */
//  function get_content_attributes($content_type)
//  {
//    $tmp  = [];
//    $attr = new CmsContentTypeProfileAttribute('mams_groups', 'visitors');
//    $attr->set_helper(mams_content_attribute_helper::get_instance());
//    $tmp[] = $attr;
//
//    return $tmp;
//  }
  
  /**
   * @internal
   * @ignore
   */
  public static function page_type_lang_callback($str)
  {
    $mod = cms_utils::get_module('MAMS');
    $str = str_replace(' ', '_', $str);
    
    if('forgot_password_email' == $str) { $str = 'verify_email'; }
    
    $str = 'tpltype_' . $str;
    
    if(is_object($mod)) { return $mod->Lang($str); }
    
    return '';
  }
  
  /**
   * @internal
   * @ignore
   */
  static public function reset_page_type_defaults(CmsLayoutTemplateType $type)
  {
    if('MAMS' !== $type->get_originator())
    {
      throw new CmsLogicException('Cannot reset contents for this template type');
    }
    
    switch($type->get_name())
    {
      case 'login form':
        $fn = 'orig_loginform.tpl';
      break;
      case 'logout form':
        $fn = 'orig_logoutform.tpl';
      break;
      case 'chsettings form':
        $fn = 'orig_changesettings.tpl';
      break;
      case 'forgot password form':
        $fn = 'orig_forgotpassword1.tpl';
      break;
      case 'forgot password email':
        $fn = 'orig_forgotpassword2.tpl';
      break;
      case 'forgot password verify':
        $fn = 'orig_forgotpassword3.tpl';
      break;
      case 'force new password':
        $fn = 'orig_force_newpw_form.tpl';
      break;
      case 'lost username form':
        $fn = 'orig_lostunform_template.tpl';
      break;
      case 'lost username confirm':
        $fn = 'orig_lostunform_confirm_template.tpl';
      break;
      case 'view user':
        $fn = 'orig_viewuser_template.tpl';
      break;
      case 'reset session':
        $fn = 'orig_resetsession_template.tpl';
      break;
      case 'verify_form':
      case 'verify form':
        $fn = 'orig_verifyform.tpl';
      break;
      default:
        throw new \LogicException(
          $type->get_name() . ' is not a known type for the ' . $type->get_originator() . ' originator'
        );
    }
    
    $fn = cms_join_path(__DIR__, 'templates', $fn);
    
    if(is_file($fn)) { return @file_get_contents($fn); }
    
    return '';
  }
  
  /**
   * Exposes the MAMS API to other modules
   *
   * @return MAMS\api
   */
  public function get_api() : \MAMS\api
  {
    return \MAMS\api::getInstance();
  }
  
  public function set_verification_assistant(verification_assistant $obj)
  {
    if($this->verification_assistant)
    {
      throw new \LogicException('A verification assistant object already exists');
    }
    $this->verification_assistant = $obj;
  }
  
  protected function get_verification_assistant()
  {
    if(!$this->verification_assistant)
    {
      $this->verification_assistant = new email_verify_assistant($this);
    }
    
    return $this->verification_assistant;
  }
  
} // class

// EOF
