text:
<?php

namespace bbn;

use Exception;
use stdClass;

use bbn\File\System;
use bbn\Util\Enc;
use bbn\Db;
use bbn\User;
use bbn\User\Preferences;
use bbn\User\Permissions;
use bbn\Appui\Menu;
use bbn\Appui\Option;
use bbn\Appui\Passwords;
use bbn\Appui\Api;
use bbn\Appui\Dashboard;
use bbn\Appui\Database;

/**
 * The class which deals with App-UI configuration.
 */
class Appui
{

  /** @var array List of the variables transformed in BBN_ constants */
  protected static $vars = [
    "env",
    "is_ssl",
    "admin_email",
    "db_engine",
    "db_host",
    "database",
    "db_user",
    "db_pass",
    "server_name",
    "hostname",
    "public",
    "cur_path",
    "app_path",
    "lib_path",
    "data_path",
    "log_path",
    "user",
    "preferences",
    "permissions",
    "options",
    "history",
    "encryption_key",
    "fingerprint",
    "app_name",
    "app_prefix",
    "site_title",
    "client_name"
  ];

  /** @var bool  */
  private $_checked = null;

  /** @var bool  */
  private $_is_installer = false;

  /** @var string */
  private $_settings_file;

  /** @var string */
  private $_environment_file;

  /** @var string */
  private $_routes_file;

  /** @var array */
  private $_settings;

  /** @var array */
  private $_environment;

  /** @var array */
  private $_routes;

  /** @var array */
  private $_dbFilesContent;

  /** @var array */
  private $_optionFilesContent;

  /** @var array */
  private $_permissionFilesContent;

  /** @var array */
  private $_menuFilesContent;

  /** @var array */
  private $_dashboardFilesContent;

  /** @var array */
  private $_current = [];

  /** @var Db */
  private $_currentDb;

  /** @var Option */
  private $_currentOption;

  /** @var User */
  private $_currentUser;

  /** @var Preferences */
  private $_currentPref;

  /** @var Permissions */
  private $_currentPerm;

  /** @var System */
  private $_currentFs;

  /** @var Passwords */
  private $_currentPass;

  /** @var Menu */
  private $_currentMenu;

  /** @var array */
  private $_info;

  /**
   * Constructor
   *
   * @param null|array       $cfg An initial configuration
   * @param null|System $fs  A File System connection for the given config.
   */
  public function __construct(array $cfg = null, System $fs = null)
  {
    $this->setConfig($cfg, $fs);
  }

  /**
   * Returns all the names of the constant according to this class configuration
   *
   * @return array
   */
  public static function getConstantNames(): array
  {
    $res = [];
    foreach (self::$vars as $v) {
      $res[] = 'BBN_' . strtoupper($v);
    }

    return $res;
  }

  /**
   * Check that all the constants are defined to this class configuration
   *
   * @return bool
   */
  public static function checkConstantNamesExist(): bool
  {
    foreach (self::getConstantNames() as $v) {
      if (!defined($v)) {
        return false;
      }
    }

    return true;
  }

  /**
   * Unsets all the object created for the current environment if any
   *
   * @return void
   */
  public function unsetConfig()
  {
    $this->_current                = [];
    $this->_currentPerm            = null;
    $this->_currentPref            = null;
    $this->_currentPass            = null;
    $this->_currentMenu            = null;
    $this->_currentOption          = null;
    $this->_currentUser            = null;
    $this->_currentDb              = null;
    $this->_currentFs              = null;
    $this->_dbFilesContent         = null;
    $this->_optionFilesContent     = null;
    $this->_permissionFilesContent = null;
    $this->_menuFilesContent       = null;
    $this->_info                   = null;
  }

  public function getConfig()
  {
    return $this->_current;
  }

  /**
   * Sets the whole current config.
   *
   * @param array|null       $cfg An application configuration
   * @param System|null $fs  A filesystem object accessing the config path
   * @return void
   */
  public function setConfig(array $cfg = null, System $fs = null)
  {
    $this->unsetConfig();
    $this->_currentFs = $fs ?? new System();
    $has_cfg          = (bool)$cfg;
    foreach (self::$vars as $v) {
      if ($has_cfg) {
        $this->_current[$v] = $cfg[$v] ?? null;
      }
      elseif (defined('\\BBN_'.strtoupper($v))) {
        $this->_current[$v] = constant('\\BBN_'.strtoupper($v));
      }
    }

    $c =& $this->_current;

    $url = 'http';
    if ($c['is_ssl']) {
      $url .= 's';
    }

    $url .= '://' . $c['server_name'];
    if (isset($c['port']) && !in_array($c['port'], [80, 443])) {
      $url .= ':'.$c['port'];
    }

    if (!empty($c['cur_path'])) {
      $url .= $c['cur_path'];
    }

    if (substr($url, -1) !== '/') {
      $url .= '/';
    }

    $this->_current['url'] = $url;
  }

  /**
   * Returns the path to the main application
   *
   * @param bool $raw If false, src/ is appended to the app_path var.
   * @return string|null
   */
  public function appPath(bool $raw = false): ?string
  {
    if (isset($this->_current['app_path'])) {
      return $this->_current['app_path'].($raw ? '' : 'src/');
    }

    return null;
  }

  /**
   * Returns the path to the libraries
   *
   * @return string|null
   */
  public function libPath(): ?string
  {
    return $this->_current['lib_path'] ?? null;
  }

  /**
   * Returns the path to the data
   *
   * @return string|null
   */
  public function dataPath(): ?string
  {
    return $this->_current['data_path'] ?? null;
  }

  /**
   * Returns a Db object according to the current config, creates it if needed
   *
   * @todo Attach history if configured
   * @return Db|null
   */
  public function getDb(): ?Db
  {
    if (!$this->_currentDb) {
      if (!$this->_is_installer && X::isDefined('BBN_DB_HOST', 'BBN_DB_ENGINE', 'BBN_DB_USER')
          && ($this->_current['db_host'] == BBN_DB_HOST)
          && ($this->_current['db_engine'] == BBN_DB_ENGINE)
          && ($this->_current['db_user'] == BBN_DB_USER)
      ) {
        $db = Db::getInstance();
      }
      else {
        $db = new Db(
          [
            'engine' => $this->_current['db_engine'],
            'host' => $this->_current['db_host'] ?? '',
            'user' => $this->_current['db_user'] ?? '',
            'pass' => $this->_current['db_pass'] ?? '',
            'error_mode' => 'continue'
          ]
        );
      }

      if ($db->check()) {
        if (!empty($this->_current['database'])) {
          $db->change($this->_current['database']);
        }

        $this->_currentDb = $db;
      }
    }

    return $this->_currentDb;
  }

  /**
   * Returns a Db object according to the current config, creates it if needed
   *
   * @return Option | null
   */
  public function getOption(): ?Option
  {
    if (!$this->_currentOption) {
      $this->_currentOption = new Option($this->getDb());
    }

    return $this->_currentOption;
  }

  /**
   * Returns a special User class which connects directly as admin.
   *
   * @return User
   */
  public function getUser(): User
  {
    if ($this->_currentUser) {
      return $this->_currentUser;
    }

    $this->_currentUser = new class($this->getDb(), $this->_current) extends User {

      private $_cfg;

      /**
       * Constructor which logs in directly the admin after regular construction.
       *
       * @param Db    $db  The database connection corresponding to the current configuration
       * @param array $cfg A configuration array with admin_email and admin_password
       */
      public function __construct(Db $db, array $cfg)
      {
        parent::__construct($db, $cfg);
        $this->_cfg = $cfg;
        $this->logAdminIn();
      }

      /**
       * Logs in the admin user.
       *
       * @return void
       */
      public function logAdminIn(): void
      {
        $id_user = $this->db->selectOne('bbn_users', 'id', ['email' => $this->_cfg['admin_email']]);
        if ($id_user) {
          $this->logIn($id_user);
          return;
        }

        throw new Exception("Impossible to fiond the admin user");
      }

    };

    return $this->_currentUser;
  }

  /**
   * Returns a Db object according for the current config, creates it if needed
   *
   * @return Preferences
   */
  public function getPreferences(): ?Preferences
  {
    if (!$this->_currentPref) {
      $this->_currentPref = new Preferences($this->getDb());
    }

    return $this->_currentPref;
  }

  /**
   * Returns a Db object according for the current config, creates it if needed
   *
   * @return Permissions
   */
  public function getPermissions(): ?Permissions
  {
    if (!$this->_currentPerm) {
      $routes             = $this->getRoutes();
      $user               = $this->getUser();
      $preferences        = $this->getPreferences();
      $this->_currentPerm = new Permissions($routes);
    }

    return $this->_currentPerm;
  }

  /**
   * Returns a Password object according for the current config, creates it if needed
   *
   * @return Passwords
   */
  public function getPassword(): Passwords
  {
    if (!$this->_currentPass) {
      $this->_currentPass = new Passwords($this->getDb());
    }

    return $this->_currentPass;
  }

  /**
   * Returns a menu instance accordin to the current configuration
   *
   * @return Menu
   */
  public function getMenu(): Menu
  {
    if (!$this->_currentMenu) {
      $this->_currentMenu = new Menu();
    }

    return $this->_currentMenu;

  }

  /**
   * Checks just once whether or not all needed constant have been defined
   *
   * @param bool $throwError wil throw an error if set to true instead of returning false
   *
   * @throws Exception
   * @return bool
   */
  public function check(bool $throwError = false): bool
  {
    if ($throwError || is_null($this->_checked)) {
      $ok = true;
      foreach (self::$vars as $v) {
        if (!array_key_exists($v, $this->_current)) {
          if ($throwError) {
            throw new Exception(X::_("The parameter %s is not defined", $v));
          }

          $ok = false;
        }
      }

      $this->_checked = $ok;
    }

    return $this->_checked;
  }

  /**
   * Gets the settings of the current project.
   *
   * @return array|null
   */
  public function getSettings(): ?array
  {
    if ($this->check()) {
      if (empty($this->_settings)
          && ($file = $this->getSettingsFile())
          && ($content = $this->_currentFs->getContents($file))
      ) {
        if (substr($file, -4) === '.yml') {
          $this->_settings = yaml_parse($content);
        }
        else {
          $this->_settings = json_decode($content, true);
        }
      }

      return $this->_settings;
    }

    return null;
  }

  /**
   * Gets the settings of the current project.
   *
   * @return array|null
   */
  public function getRoutes(): ?array
  {
    if ($this->check()) {
      if (empty($this->_routes)
          && ($file = $this->getRoutesFile())
          && ($content = $this->_currentFs->getContents($file))
      ) {
        if (substr($file, -4) === '.yml') {
          $this->_routes = yaml_parse($content);
        }
        else {
          $this->_routes = json_decode($content, true);
        }
      }

      return $this->_routes;
    }

    return null;
  }

  /**
   * Gets the index for the given environment.
   *
   * @param string $hostname The hostname of the environment.
   * @param string $servname The URL of the environment.
   *
   * @return string|null
   */
  public function getEnvironmentIndex(string $hostname = null, string $servname = null): ?string
  {
    if ($this->check()) {
      if (empty($hostname) && empty($servname)) {
        if (!defined('BBN_HOSTNAME')) {
          throw new Exception(X::_("No hostname defined"));
        }

        if (!defined('BBN_SERVER_NAME')) {
          throw new Exception(X::_("No server name defined"));
        }

        $hostname = BBN_HOSTNAME;
        $servname = BBN_SERVER_NAME;
      }

      return md5($hostname.$servname);
    }

    return null;

  }

  /**
   * Returns the environment variables of an app from the current project.
   *
   * @param string $hostname The hostname of the environment.
   * @param string $servname The URL of the environment.
   *
   * @return array
   */
  public function getEnvironment($hostname = null, string $servname = null): ?array
  {
    if ($this->check()) {
      if ($hostname !== true) {
        $idx = $this->getEnvironmentIndex($hostname, $servname);
      }

      if (empty($this->_environment)
          && ($file = $this->getEnvironmentFile())
          && ($content = $this->_currentFs->getContents($file))
      ) {
        if (substr($file, -4) === '.yml') {
          $envs = yaml_parse($content);
        }
        else {
          $envs = json_decode($content, true);
        }

        $_env =& $this->_environment;
        foreach ($envs as $i => $env) {
          $md5        = $this->getEnvironmentIndex($env['hostname'], $env['server_name']);
          $_env[$md5] = [
            'index' => $i,
            'data' => $env
          ];
        }
      }

      return $hostname === true ? array_values(
        array_map(
          function ($a) {
            return $a['data'];
          },
          $this->_environment
        )
      ) : (
        isset($this->_environment[$idx]) ? $this->_environment[$idx]['data'] : null
      );
    }

    return null;
  }

  /**
   * Get the routes file location.
   *
   * @return string|null
   */
  public function getRoutesFile(): ?string
  {
    if ($this->check()) {
      if (empty($this->_routes_file)) {
        $app_path = $this->appPath();
        if (function_exists('\\yaml_parse')
            && $this->_currentFs->isFile($app_path.'cfg/routes.yml')
        ) {
          $this->_routes_file = $app_path.'cfg/routes.yml';
        }
        elseif ($this->_currentFs->isFile($app_path.'cfg/routes.json')) {
          $this->_routes_file = $app_path.'cfg/routes.json';
        }
      }

      return $this->_routes_file;
    }

    return null;
  }

  /**
   * Get the settings file location.
   *
   * @return string|null
   */
  public function getSettingsFile(): ?string
  {
    if ($this->check()) {
      if (empty($this->_settings_file)) {
        $app_path = $this->appPath();
        if (function_exists('\\yaml_parse')
            && $this->_currentFs->isFile($app_path.'cfg/settings.yml')
        ) {
          $this->_settings_file = $app_path.'cfg/settings.yml';
        }
        elseif ($this->_currentFs->isFile($app_path.'cfg/settings.json')) {
          $this->_settings_file = $app_path.'cfg/settings.json';
        }
      }

      return $this->_settings_file;
    }

    return null;
  }

  /**
   * Get the environment file location.
   *
   * @return string|null
   */
  public function getEnvironmentFile(): ?string
  {
    if ($this->check()) {
      if (empty($this->_environment_file)) {
        $app_path = $this->appPath();
        if (function_exists('\\yaml_parse')
            && $this->_currentFs->isFile($app_path.'cfg/environment.yml')
        ) {
          $this->_environment_file = $app_path.'cfg/environment.yml';
        }
        elseif ($this->_currentFs->isFile($app_path.'cfg/environment.json')) {
          $this->_environment_file = $app_path.'cfg/environment.json';
        }
      }

      return $this->_environment_file;
    }

    return null;
  }

  /**
   * Set environment vars
   *
   * @param array  $update   The new values to be added
   * @param string $hostname The environment's hostname
   * @param string $servname The environment's server name
   * @param bool   $replace  True if the whole value should be replaced by the new one
   *
   * @return bool
   */
  public function setEnvironment(
      array $update,
      string $hostname = null,
      string $servname = null,
      bool $replace = false
  ): bool
  {
    $idx = $this->getEnvironmentIndex($hostname, $servname);
    if (isset($this->_environment[$idx])) {
      if ($replace) {
        $this->_environment[$idx]['data'] = $update;
      }
      else {
        $this->_environment[$idx]['data'] = array_merge(
          $this->_environment[$idx]['data'],
          $update
        );
      }

      $file = $this->getEnvironmentFile();
      $envs = $this->getEnvironment(true);
      if (substr($file, -4) === '.yml') {
        $content = \yaml_emit($envs);
      }
      else {
        $content = json_encode($envs, JSON_PRETTY_PRINT);
      }

      return (bool)$this->_currentFs->putContents($file, $content);
    }

    return false;
  }

  /**
   * Set settings vars
   *
   * @param array $update  The new values to be added
   * @param bool  $replace True if the whole value should be replaced by the new one
   *
   * @return bool
   */
  public function setSettings(array $update, $replace = false) : bool
  {
    if ($this->getSettings()) {
      if ($replace) {
        $this->_settings = $update;
      }
      else {
        $this->_settings = array_merge(
          $this->_settings,
          $update
        );
      }

      $file = $this->getSettingsFile();
      if (substr($file, -4) === '.yml') {
        $content = \yaml_emit($this->_settings);
      }
      else {
        $content = json_encode($this->_settings, JSON_PRETTY_PRINT);
      }

      return (bool)$this->_currentFs->putContents($file, $content);
    }
    else {
      throw new Exception(X::_("Impossible to get the settings"));
    }

    return false;
  }

  /**
   * Replaces an environment with another
   *
   * @param array       $update   The new environment config
   * @param string|null $hostname A hostname, if none given default env value wwill be used
   * @param string|null $servname A server name, if none given default env value wwill be used
   * @return bool
   */
  public function replaceEnvironment(
      array $update,
      string $hostname = null,
      string $servname = null
  ): bool
  {
    return $this->setEnvironment($update, $hostname, $servname, true);
  }

  /**
   * Replaces the settings with another set of options
   *
   * @param array $update The new settings
   * @return bool
   */
  public function replaceSettings(array $update): bool
  {
    return $this->setSettings($update, true);
  }

  /**
   * Creates a plugin in the database or check its existence and returns its ID.
   *
   * @param string      $name  Created in appui if starts with `appui-`, otherwise in plugins
   * @param string|null $title The title of the plugin
   * @return null|string
   */
  public function addPlugin(string $name, string $title = null): ?string
  {
    $id_plugin = null;
    if (!$title) {
      $title = $name;
    }

    /** @var Option */
    $o = $this->getOption();
    if (!is_dir($this->libPath() . "/$name")) {
      mkdir($this->appPath() . "plugins/$name");
    }

    if (substr($name, 0, 6) === 'appui-') {
      $name   = substr($name, 6);
      $params = ['appui'];
    }
    else {
      $name   = $name;
      $params = ['plugins'];
    }

    if ($id_parent = $o->fromCode(...$params)) {
      array_unshift($params, $name);
      if ($id = $o->fromCode(...$params)) {
        $id_plugin = $id;
      }
      else {
        $pluginTemplate = $o->fromCode('plugin', 'list', 'templates', 'optiopns', 'appui');
        $id_plugin = $o->add(
          [
            'code' => $name,
            'text' => $title,
            'id_parent' => $id_parent,
            'id_alias' => $pluginTemplate
          ]
        );

        if (!$id_plugin) {
          throw new Exception(X::_("Impossible to add the plugin")." $name");
        }
      }
    }

    return $id_plugin;
  }

  /**
   * Replaces plugins names by path and *(project)* by the real project name
   *
   * @param string $st The string nwhere to replace the values
   * @return string
   */
  public function replaceMagicStrings(string $st): string
  {
    // Function for replacing *(project)* by the real project name
    $magic_strings = [
      '/\|\*(project)\*\|/' => BBN_APP_NAME,
    ];
    $routes        = $this->getRoutes();
    $plugins_urls  = $routes['root'];
    $st            = preg_replace_callback(
      '/\|\*(appui-[a-z]+)\*\|/',
      function ($a) use ($plugins_urls) {
        return X::getField($plugins_urls, ['name' => $a[1]], 'url');
      },
      $st
    );

    foreach ($magic_strings as $exp => $val) {
      $string = preg_replace($exp, $val, $st);
    }

    return $string;
  }

  /**
   * Returns the constraints on the history_uids table
   *
   * @return void
   */
  public function getHistoryConstraints(): array
  {
    $tables      = $this->getDbFilesContent();
    $constraints = [];
    $i           = 0;
    $st          = strtolower(Str::genpwd(4));
    foreach ($tables as $table => $structure) {
      foreach ($structure['keys'] as $k => $cfg) {
        if (!empty($cfg['ref_column'])) {
          ++$i;
          $tables[$table]['keys'][$k]['constraint'] = "bbn_constraint_{$st}_{$i}";
        }

        if (!empty($cfg['ref_table']) && ('bbn_history_uids' === $cfg['ref_table'])) {
          if (!isset($constraints[$table])) {
            $constraints[$table] = [];
          }

          if (!isset($constraints[$table][$k])) {
            $constraints[$table][$k] = $tables[$table]['keys'][$k];
          }
        }
      }
    }

    return $constraints;
  }

  /**
   * Returns an array of tables with their structures from the database.json files in all plugins.
   *
   * @return array
   */
  public function getDbFilesContent(): array
  {
    if (!$this->_dbFilesContent) {
      $routes   = $this->getRoutes();
      $tables   = [];
      foreach ($routes['root'] as $url => $plugin) {
        $fn = $plugin['root'] . 'Path';
        if (method_exists($this, $fn)) {
          $path = $this->$fn() . $plugin['path'] . '/src/cfg/';
          if ($this->_currentFs->exists($path.'database.json')) {
            if ($list = $this->_currentFs->decodeContents($path.'database.json', 'json', true)) {
              foreach ($list as $t => $it) {
                $tables[$t] = $it;
              }
            }
            else {
              throw new Exception(X::_("Unreadable database file in plugin %s", $plugin['name']));
            }
          }
        }
        else {
          throw new Exception(X::_("Impossible to recognize the root in plugin %s", $plugin['name']));
        }
      }

      $this->_dbFilesContent = $tables;
    }

    return $this->_dbFilesContent;
  }

  /**
   * Returns an array of tables with their structures from the database.json files in all plugins.
   *
   * @return array
   */
  public function getStructureFilesContent(): array
  {
    if (!$this->_structureFilesContent) {
      $routes   = $this->getRoutes();
      $tables   = [];
      foreach ($routes['root'] as $url => $plugin) {
        $fn = $plugin['root'] . 'Path';
        if (method_exists($this, $fn)) {
          $path = $this->$fn() . $plugin['path'] . '/src/cfg/';
          if ($this->_currentFs->exists($path.'database.json')) {
            if ($list = $this->_currentFs->decodeContents($path.'database.json', 'json', true)) {
              foreach ($list as $t => $it) {
                $tables[$t] = $it;
              }
            }
            else {
              throw new Exception(X::_("Unreadable database file in plugin %s", $plugin['name']));
            }
          }
        }
        else {
          throw new Exception(X::_("Impossible to recognize the root in plugin %s", $plugin['name']));
        }
      }

      $this->_structureFilesContent = $tables;
    }

    return $this->_structureFilesContent;
  }

  /**
   * Returns an array of tables with their structures from the database.json files in all plugins.
   *
   * @todo The options files need to be dispatched again through the plugins
   * @return array
   */
  public function getOptionFilesContent(): array
  {
    if (!$this->_optionFilesContent) {
      $routes   = $this->getRoutes();
      $options  = [];
      foreach ($routes['root'] as $url => $plugin) {
        $fn = $plugin['root'] . 'Path';
        if (method_exists($this, $fn)) {
          $path = $this->$fn() . $plugin['path'] . '/src/cfg/';
          if (('appui-core' !== $plugin['name'])
              && ('appui-options' !== $plugin['name'])
              && $this->_currentFs->exists($path.'options.json')
              /* && file_exists(BBN_LIB_PATH.'bbn/'.$p.'/src/cfg/options.json') */
          ) {
            if ($list = $this->_currentFs->decodeContents($path.'options.json', 'json', true)) {
              if (X::isAssoc($list)) {
                $options[] = $list;
              }
              else {
                $options = array_merge($options, $list);
              }
            }
            else {
              throw new Exception(X::_("The options file in %s is corrupted", $plugin['name']));
            }
          }
        }
        else {
          throw new Exception(X::_("Impossible to recognize the root in plugin %s", $plugin['name']));
        }
      }

      $this->_optionFilesContent = $options;
    }

    return $this->_optionFilesContent;
  }

  /**
   * Returns an array of tables with their structures from the database.json files in all plugins.
   *
   * @return array
   */
  public function getPermissionFilesContent(): array
  {
    if (!$this->_permissionFilesContent) {
      $routes   = $this->getRoutes();
      $perms    = [];
      foreach ($routes['root'] as $url => $plugin) {
        $fn = $plugin['root'] . 'Path';
        if (method_exists($this, $fn)) {
          $path = $this->$fn() . $plugin['path'] . '/src/cfg/';
          if ($this->_currentFs->exists($path.'permissions.json')) {
            if ($list = $this->_currentFs->decodeContents($path.'permissions.json', 'json', true)) {
              if (X::isAssoc($list)) {
                $perms[] = $list;
              }
              else {
                $perms = array_merge($perms, $list);
              }
            }
            else {
              throw new Exception(X::_("The permission file in %s is corrupted", $plugin['name']));
            }
          }
        }
        else {
          throw new Exception(X::_("Impossible to recognize the root in plugin %s", $plugin['name']));
        }
      }

      $this->_permissionFilesContent = $perms;
    }

    return $this->_permissionFilesContent;
  }

  /**
   * Returns an array of menus with their structures from the menu.json files in all plugins.
   *
   * @return array
   */
  public function getMenuFilesContent(): array
  {
    if (!$this->_menuFilesContent) {
      $routes   = $this->getRoutes();
      $menus    = [];
      foreach ($routes['root'] as $url => $plugin) {
        $fn = $plugin['root'] . 'Path';
        if (method_exists($this, $fn)) {
          $path = $this->$fn() . $plugin['path'] . '/src/cfg/';
          if ($this->_currentFs->exists($path . 'menu.json')) {
            if ($list = $this->_currentFs->decodeContents($path.'menu.json', 'json', true)) {
              if (!empty($list['items'])) {
                foreach ($list['items'] as &$it) {
                  $it['link'] = $url.'/'.$it['link'];
                }
                unset($it);
                $menus[$plugin['name']] = $list;
              }
              else {
                X::log("Problem in {$path} menu.json");
              }
            }
            else {
              throw new Exception(X::_("The menu file in %s is corrupted", $plugin['name']));
            }
          }
        }
        else {
          throw new Exception(X::_("Impossible to recognize the root in plugin %s", $plugin['name']));
        }
      }

      // Correcting the menus' sort order
      X::sortBy($menus, 'num');
      foreach ($menus as $i => &$m) {
        $m['num'] = $i + 1;
      }

      unset($m);
      $this->_menuFilesContent = $menus;
    }

    return $this->_menuFilesContent;
  }

  /**
   * Returns an array of dashboards with their structures from the dashboards.json files in all plugins.
   *
   * @return array
   */
  public function getDashboardFilesContent(): array
  {
    if (!$this->_dashboardFilesContent) {
      $routes     = $this->getRoutes();
      $dashboards = [];
      foreach ($routes['root'] as $url => $plugin) {
        $fn = $plugin['root'] . 'Path';
        if (method_exists($this, $fn)) {
          $path = $this->$fn() . $plugin['path'] . '/src/cfg/';
          if ($this->_currentFs->exists($path . 'dashboards.json')) {
            if ($list = $this->_currentFs->decodeContents($path.'dashboards.json', 'json', true)) {
              foreach ($list as $i => $item) {
                if (empty($item['items'])
                  || empty($item['code'])
                ) {
                  continue;
                }
                foreach ($item['items'] as $k => $it) {
                  if (\is_array($it['id_option'])) {
                    $list[$i]['items'][$k]['id_option'] = $this->getOption()->fromCode(...$it['id_option']);
                  }
                  if (!Str::isUid($list[$i]['items'][$k]['id_option'])) {
                    unset($list[$i]['items'][$k]);
                  }
                }
                if (!isset($dashboards[$item['code']])) {
                  $dashboards[$item['code']] = [
                    'text' => $item['text'],
                    'code' => $item['code'],
                    'id_option' => $list[$i]['id_option'],
                    'items' => []
                  ];
                }
                $dashboards[$item['code']]['items'] = X::mergeArrays($dashboards[$item['code']]['items'], $list[$i]['items']);
              }

            }
            else {
              throw new Exception(X::_("The dashboards file in %s is corrupted", $plugin['name']));
            }
          }
        }
        else {
          throw new Exception(X::_("Impossible to recognize the root in plugin %s", $plugin['name']));
        }
      }

      $this->_dashboardFilesContent = $dashboards;
    }

    return $this->_dashboardFilesContent;
  }

  /**
   * Returns an array of tables with their whole structure, keys included
   *
   * @return array
   */
  public function getDatabaseStructure(): array
  {
    $tables = $this->getDbFilesContent();
    $i      = 0;
    $st     = strtolower(Str::genpwd(4));
    foreach ($tables as $table => $structure) {
      foreach ($structure['keys'] as $k => $cfg) {
        if (!empty($cfg['ref_column'])) {
          ++$i;
          $tables[$table]['keys'][$k]['constraint'] = "bbn_constraint_{$st}_{$i}";
        }

        if (!empty($cfg['ref_table'])) {
          if (!isset($tables[$cfg['ref_table']]) || ('bbn_history_uids' === $cfg['ref_table'])) {
            $tables[$table]['keys'][$k]['ref_db']     = null;
            $tables[$table]['keys'][$k]['ref_table']  = null;
            $tables[$table]['keys'][$k]['ref_column'] = null;
          }
          else {
            $tables[$table]['keys'][$k]['ref_db'] = $this->_current['database'];
          }
        }
      }
    }

    return $tables;
  }

  /**
   * Returns an array of queries for creating the database
   *
   * @return array
   */
  public function getDatabaseCreationQueries(): array
  {
    $db      = $this->getDb();
    $tables  = $this->getDatabaseStructure();
    $queries = [
      'table' => [],
      'keys' => [],
      'constraints' => [],
    ];
    // Creating queries for tables, keys and constraint creation
    foreach (array_keys($queries) as $type) {
      $fn = 'getCreate'.ucwords($type);
      foreach ($tables as $table => $structure) {
        if ($tmp = $db->{$fn}($table, $structure)) {
          $queries[$type][$table] = $tmp;
        }
      }
    }

    return $queries;
  }

  /**
   * Returns the path of the main RSA public key of the application
   *
   * @param bool $create If true will create it if it doesn't exist
   * @return string|null
   */
  public function getPublicKey(bool $create = false): ?string
  {
    if ($this->check()) {
      $path = $this->appPath().'cfg/cert';
      if (!$this->_currentFs->isFile($path.'_rsa.pub') && $create) {
        try {
          Enc::generateCertFiles($path);
        }
        catch (Exception $e) {
          throw new Exception(X::_("Failed to create SSL certificate").': '.$e->getMessage());
        }
      }

      if ($this->_currentFs->isFile($path.'_rsa.pub')) {
        return $path.'_rsa.pub';
      }

      return null;
    }
  }

  /**
   * Creates a database with given name based on the given structure.
   *
   * @return int
   */
  public function createDatabase(): int
  {
    $db = $this->getDb();
    // creates the Database
    $db->createDatabase($this->_current['database']);
    if (!$db->change($this->_current['database'])) {
      throw new Exception(X::_("The database %s doesn't exist", $this->_current['database']));
    }

    // Getting the existing tables
    $current_tables = $db->getTables() ?: [];
    $queries        = $this->getDatabaseCreationQueries();
    $num            = 0;
    foreach ($queries as $type => $arr) {
      foreach ($arr as $table => $q) {
        if (!empty($q) && (('table' !== $type) || !in_array($table, $current_tables, true))) {
          $current_tables[] = $table;
          $db->query($q);
          $db_err = $db->getLastError();
          if ($db_err) {
            throw new Exception($db_err);
          }
          elseif ('table' === $type) {
            $num++;
          }
        }
      }
    }

    return $num;
  }

  /**
   * Returns the id_group for the given code, creating the group if needed.
   *
   * @param string $code The group's code
   * @param string $name The group's name
   * @param string $type The group's type
   * @return string|null
   */
  public function getUserGroup(string $code, string $name, $type = 'real'): ?string
  {
    $id_group = null;
    if ($this->check()
        && ($db = $this->getDb())
        && !($id_group = $db->selectOne(
          'bbn_users_groups', 'id', [
          'code' => $code,
          'type' => $type
          ]
        ))
        && $db->insert(
          'bbn_users_groups',
          [
            'group' => $name,
            'code' => $code,
            'type' => $type
          ]
        )
    ) {
      $id_group = $db->lastId();
    }

    return $id_group;
  }

  /**
   * Returns the admin user's ID and creates it if it doesn't exist.
   *
   * @param string $name     The admin's name
   * @param string $password The password if needed to create it
   * @return string|null
   */
  public function getAdminUser(string $name, string $password): ?string
  {
    $id_user = null;
    if (($db = $this->getDb())
        && !($id_user = $db->selectOne(
          'bbn_users',
          'id',
          ['login' => $this->_current['admin_email']]
        ))
        && $db->insert(
          'bbn_users',
          [
            'username' => $name,
            'email' => $this->_current['admin_email'],
            'login' => $this->_current['admin_email'],
            'id_group' => $this->getUserGroup('admin', 'Administrators'),
            'admin' => 1,
            'dev' => 1,
            'theme' => $this->_current['theme'] ?? 'default',
          ]
        )
    ) {
      $id_user = $db->lastId();
      $db->insert(
        'bbn_users_passwords',
        [
          'pass' => sha1($password),
          'id_user' => $id_user,
          'added' => date('Y-m-d H:i:s'),
        ]
      );
    }

    return $id_user;
  }

  /**
   * Returns the options' root's ID and creates it if it doesn't exist.
   *
   * @throws Exception
   * @return string|null
   */
  public function getOptionRoot(): string
  {
    $db = $this->getDb();
    $id = $db->selectOne(
      'bbn_options',
      'id',
      [
        'code' => 'root',
        'id_parent' => null
      ]
    );

    if (!$id && $db->insert(
        'bbn_options',
        [
          'id_parent' => null,
          'code' => 'root',
          'text' => 'root',
          'cfg' => '{"permissions":1}'
        ]
      )
    ) {
      $id = $db->lastId();
    }

    if (!$id) {
      throw new Exception("Impossible to create the root option");
    }

    return $id;
  }

  /**
   * Returns the ID of the main client, and creates if not exist.
   *
   * @param string $name The name of the client
   * @return string|null
   */
  public function getClient(string $name): string
  {
    $id_client = null;
    if (($db = $this->getDb())
        && !($id_client = $db->selectOne('bbn_clients', 'id', ['name' => $name]))
        && $db->insert('bbn_clients', ['name' => $name])
    ) {
      $id_client = $db->lastId();
    }

    if (!$id_client) {
      throw new Exception("Impossible to create the client");
    }

    return $id_client;
  }

  /**
   * Returns the ID of the main app project, and creates if not exist.
   *
   * @todo Check what is the right id_project: from table options or projects?
   * @return string|null
   */
  public function getProject(): string
  {
    $opt = $this->getOption();
    $id_project_list = $opt->fromCode('list', 'project', 'appui');
    if (!($id_project = $opt->fromCode($this->_current['app_name'], $id_project_list))) {
      $id_project = $opt->add(
        [
          'id_parent' => $id_project_list,
          'id_alias' => ['project', 'templates', 'appui-option', 'plugins', 'project', 'appui'],
          'text' => $this->_current['app_name'],
          'code' => $this->_current['app_name'],
        ]
      );
      $opt->add(
        [
          'id_parent' => $id_project,
          'text' => X::_("Environment"),
          'code' => 'env'
        ]
      );
    }

    $id_client = $this->getClient($this->_current['client_name']);

    $db = $this->getDb();
    // Create project
    $id_project = $db->selectOne(
      'bbn_projects',
      'id',
      [
        'id_client' => $id_client,
        'name' => $this->_current['app_name']
      ]
    );
    if (!$id_project
        && $db->insert(
          'bbn_projects',
          [
            'id_client' => $id_client,
            'db' => BBN_DATABASE,
            'name' => BBN_APP_NAME,
            'lang' => 'en',
          ]
        )
    ) {
      $id_project = $db->lastId();
    }

    if (!$id_project) {
      throw new Exception("Impossible to create the project");
    }

    return $id_project;
  }

  /**
   * Returns the ID and creates if needed the app entry in the options
   *
   * @return string|null
   */
  public function getAppId(): ?string
  {
    $id_app = null;
    if (($db = $this->getDb())
        && ($opt = $this->getOption())
    ) {
      $id_env = $opt->fromCode('env', $this->_current['app_name'], 'list', 'project', 'appui');
      if (!$id_env) {
        $this->getProject();
        $id_env = $opt->fromCode('env', $this->_current['app_name'], 'list', 'project', 'appui');
      }

      if (!$id_env) {
        throw new Exception("Impossible to retrieve the environment ID");
      }

      if ($id_env
          && !($id_app = $db->selectOne(
            'bbn_options',
            'id',
            [
              'id_parent' => $id_env,
              'text' => $this->_current['app_path'],
              'code' => $this->_current['server_name'].
                  ($this->_current['cur_path'] === '/' ? '' : $this->_current['cur_path'])
            ]
          ))
      ) {
        $id_app = $opt->add(
          [
            'id_parent' => $id_env,
            'text' => $this->_current['app_path'],
            'code' => $this->_current['server_name'].
                ($this->_current['cur_path'] === '/' ? '' : $this->_current['cur_path']),
            'env' => $this->_current['env']
          ]
        );
      }
    }

    if (!$id_app) {
      throw new Exception("Impossible to create the application row");
    }

    return $id_app;
  }

  /**
   * Gets and creates if needs be the main internal user's ID
   *
   * @return string
   */
  public function getInternalUser(): string
  {
    $id_appui_user = null;
    $db            = $this->getDb();
    // Create "APPUI" user
    $id_internal_group = $this->getUserGroup('internal', 'Internal users', 'internal');
    if (!($id_appui_user = $db->selectOne(
      'bbn_users',
      'id',
      [
        'username' => 'APPUI',
        'email' => null,
        'login' => null,
        'id_group' => $id_internal_group,
      ]
    )) && $db->insert(
      'bbn_users',
      [
        'username' => 'APPUI',
        'email' => null,
        'login' => null,
        'id_group' => $id_internal_group,
        'admin' => 1,
        'dev' => 1,
        'theme' => 'default',
      ]
    )
    ) {
      $id_appui_user = $db->lastId();
    }

    if (!$id_appui_user) {
      throw new Exception("Impossible to create the client");
    }

    return $id_appui_user;
  }

  /**
   * Import the options from the default bbn file in the current environment.
   *
   * @return int
   */
  public function importOptions(): int
  {
    $res = 0;
    if ($opt = $this->getOption()) {
      $root   = $this->getOptionRoot();
      if ($tmp = $this->getRoutes()) {
        $routes = array_values($tmp['root']);
      }

      $idx = X::find($routes, ['name' => 'appui-core']);
      if ($routes[$idx]) {
        $appui_options = [];
        if ($idx !== 0) {
          X::move($routes, $idx, 0);
        }

        foreach (array_values($routes) as $i => $r) {
          $file = $this->libPath() . $r['path'] . '/src/cfg/options.json';
          if ($this->_currentFs->exists($file)) {
            $tmp = $this->_currentFs->decodeContents($file, 'json', true);
            if (!$tmp) {
              throw new Exception(X::_("Illegal JSON in %s", $file));
            }

            if ($i === 0) {
              /** @var array */
              $appui_options = $tmp;
            }
            else {
              if (X::isAssoc($tmp)) {
                $appui_options[0]['items'][] = $tmp;
              }
              else {
                array_push($appui_options[0]['items'], ...$tmp);
              }
            }
          }
        }

        $res += (int)$opt->import($appui_options, $root);
        if (!defined('BBN_APPUI')) {
          define('BBN_APPUI', $opt->fromCode('appui'));
        }

        $opt->deleteCache(null);
      }
    }

    return $res;
  }

  /**
   * Update the plugins in the options table for the current environment
   *
   * @return int
   */
  public function updatePlugins(): int
  {
    $res = 0;
    if ($opt = $this->getOption()) {
      $res = (int)$opt->updatePlugins();
      $opt->deleteCache(null);
    }

    return $res;
  }

  /**
   * Update all the templates in the options table for the current environment
   *
   * @return int
   */
  public function updateTemplates(): int
  {
    $res = 0;
    if ($opt = $this->getOption()) {
      $res = (int)$opt->updateAllTemplates();
      $opt->deleteCache(null);
    }

    return $res;
  }

  /**
   * Updates all the permissions in the current environment.
   *
   * @return int
   */
  public function updatePermissions(): int
  {
    $res = 0;
    if ($perm = $this->getPermissions()) {
      $perm_routes = [];
      $routes      = $this->getRoutes();
      foreach ($routes['root'] as $u => $r) {
        $r['path']       = BBN_LIB_PATH . $r['path'].'/';
        $r['url']        = $u;
        $perm_routes[$u] = $r;
      }

      $perms = $perm->updateAll($perm_routes);
      $res   = $perms['total'] ?? 0;
      $this->getOption()->deleteCache(null);
    }

    return $res;
  }

  /**
   * Registers the application to the central server.
   *
   * @return bool
   */
  public function register(): bool
  {
    $user       = $this->getUser();
    $db         = $this->getDb();
    $api        = new Api($user, $db);
    $pass       = $this->getPassword();
    $rsa        = $this->getPublicKey();
    $id_project = $this->getProject();
    $id_app     = $this->getAppId();
    try {
      $reg = $api->registerProject(
        [
          'key' => file_get_contents($rsa),
          'id_project' => $id_project,
          'id_app' => $id_app,
          'site_title' => $this->_current['site_title'],
          'user' => $this->_current['admin_email'],
          'id_user' => $user->getId(),
          'app_name' => $this->_current['app_name'],
          'url' => $this->_current['url'],
          'hostname' => $this->_current['hostname']
        ]
      );
    }
    catch (Exception $e) {
      throw new Exception(X::_("The application didn't register!").PHP_EOL.$e->getMessage());
    }

    if (!empty($reg) && !empty($reg['id_app'])) {
      $this->setEnvironment(['id_app' => $reg['id_app']]);
      $this->setSettings(['id_project' => $reg['id_project']]);
      $pass->store($reg['key'], $id_app);
      return true;
    }

    return false;
  }

  /**
   * Update the menus in the database
   *
   * @return int The number of changes made
   */
  public function updateMenus(): int
  {
    $db         = $this->getDb();
    $opt_class  = $this->getOption();
    $menu_class = $this->getMenu();
    $pref_class = $this->getPreferences();
    $perm_class = $this->getPermissions();
    $numChanges = 0;
    if (!($id_main_menu = $menu_class->getByCode('main'))
      && ($id_main_menu = $menu_class->add(
        [
          'text' => 'Main menu',
          'code' => 'main',
          'num' => 1,
        ]
      ))
    ) {
      $numChanges++;
      $pref_class->makePublic($id_main_menu);
      // Set default menu
      if ($pref_class->add(
        $opt_class->fromCode('default', 'menu', 'appui'),
        [
          'text' => 'Default menu',
          'id_alias' => $id_main_menu,
        ]
      )) {
        $pref_class->makePublic($id_main_menu);
        $numChanges++;
      }
    }

    $menus = $this->getMenuFilesContent();
    if (!$menus) {
      X::log("No menu!");
    }

    // Check if Plugins menu exists
    if ($idPluginMenu = $menu_class->getByCode('plugins')) {
      $items = $menu_class->get($idPluginMenu);
      foreach ($menus as $m) {
        $menu = X::getRow($items, ['text' => $m['text']]);
        $idMenu = !empty($menu['id']) ? $menu['id'] :null;
        if (empty($menu)) {
          if (!$idMenu = $menu_class->add($idPluginMenu, $m)) {
            throw new Exception(X::_("Impossible to add the menu element %s!", $m['text']));
          }
          $numChanges++;
        }
        else if (($m['icon'] !== $menu['icon'])
          || ($m['num'] !== $menu['num'])
        ) {
          if (!$menu_class->set($idMenu, [
            'icon' => $m['icon'],
            'num' => $m['num']
          ])) {
            throw new Exception(X::_("Impossible to update the menu element %s!", $idMenu));
          }
          $numChanges++;
        }
        if (!empty($idMenu)) {
          if (!empty($m['items'])) {
            $subItems = $menu_class->get($idPluginMenu, $idMenu);
            foreach ($m['items'] as $mit) {
              $item = X::getRow($subItems, ['text' => $mit['text']]);
              $idItem = !empty($item['id']) ? $item['id'] : null;
              if (empty($item)) {
                $mit = array_merge(
                  $mit,
                  [
                    'id_parent' => $idMenu,
                    'id_option' => $perm_class->fromPath($mit['link']),
                  ]
                );
                unset($mit['link']);
                if (!$menu_class->add($idPluginMenu, $mit)) {
                  throw new Exception(X::_("Impossible to add the menu element %s!", $mit['text']));
                }
                $numChanges++;
              }
              else {
                $idOption = $perm_class->fromPath($mit['link']);
                if (\array_key_exists('icon', $mit)
                  && \array_key_exists('icon', $item)
                  && ($mit['icon'] !== $item['icon'])
                ) {
                  if (!$menu_class->set($idItem, [
                    'icon' => $mit['icon']
                  ])) {
                    throw new Exception(X::_("Impossible to update the menu element %s!", $idItem));
                  }
                  $numChanges++;
                }
                if (\array_key_exists('num', $mit)
                  && \array_key_exists('num', $item)
                  && ($mit['num'] !== $item['num'])
                ) {
                  if (!$menu_class->set($idItem, [
                    'num' => $mit['num']
                  ])) {
                    throw new Exception(X::_("Impossible to update the menu element %s!", $idItem));
                  }
                  $numChanges++;
                }
                if ($idOption !== $item['id_option']) {
                  if (!$menu_class->set($idItem, [
                    'id_option' => $idOption
                  ])) {
                    throw new Exception(X::_("Impossible to update the menu element %s!", $idItem));
                  }
                  $numChanges++;
                }
              }
            }
          }
        }
      }
    }
    // Add Plugins menu
    elseif ($id_plugin_menu = $menu_class->add(
      [
        'text' => 'Plugins',
        'code' => 'plugins',
        'num' => 2,
      ]
    )) {
      $numChanges++;
      $pref_class->makePublic($id_plugin_menu);
      foreach ($menus as $m) {
        if ($id_parent_menu = $menu_class->add($id_plugin_menu, $m)) {
          foreach ($m['items'] as $mit) {
            $mit = array_merge(
              $mit,
              [
                'id_parent' => $id_parent_menu,
                'id_option' => $perm_class->fromPath($mit['link']),
              ]
            );
            unset($mit['link']);
            $menu_class->add($id_plugin_menu, $mit);
            $numChanges++;
          }
        }
        else {
          throw new Exception(X::_("Impossible to add the menu element %s!", $m['text']));
        }
      }

      $id_group = $this->getUserGroup('admin', 'Administrators');
      $db->update(
        'bbn_users_options',
        [
          'id_user' => null,
          'id_group' => $id_group,
        ],
        [
          'id' => $id_plugin_menu,
        ]
      );
    }

    return $numChanges;

  }

  /**
   * Updates the dashboard on the database
   *
   * @return int The number of rows inserted
   */
  public function updateDashboard(): int
  {
    $db          = $this->getDb();
    $optClass   = $this->getOption();
    $prefClass  = $this->getPreferences();
    $prefCfg = $prefClass->getClassCfg();
    $dashClass   = new Dashboard();
    $adminGroup = $this->getUserGroup('admin', 'Administrators');
    $devGroup   = $this->getUserGroup('dev', 'Developers');
    $numChanges = 0;
    $defaultCreated = false;
    if (!($idDashboard = $optClass->fromCode('dashboard', 'appui'))) {
      throw new Exception(X::_('Dashboard option not found'));
    }
    if (!($idList = $optClass->fromCode('list', $idDashboard))) {
      throw new Exception(X::_('Dashboard list option not found'));
    }
    if (!($defDash = $optClass->fromCode('default', $idDashboard))) {
      throw new Exception(X::_('Default dashboard option not found'));
    }

    if (!($idDefaultDashboard = $dashClass->getId('default'))
      && ($idDefaultDashboard = $dashClass->insert([
        $prefCfg['arch']['user_options']['text'] => 'Default dashboard',
        'code' => 'default',
        $prefCfg['arch']['user_options']['public'] => 1
      ]))
    ) {
      $defaultCreated = true;
      $numChanges++;
    }

    if (empty($idDefaultDashboard)) {
      throw new Exception(X::_('Default dashboard not found'));
    }

    if (!($idPluginsDashboard = $dashClass->getId('plugins'))
      && ($idPluginsDashboard = $dashClass->insert([
        $prefCfg['arch']['user_options']['text'] => 'Plugins dashboard',
        'code' => 'plugins',
        $prefCfg['arch']['user_options']['public'] => 1
      ]))
    ) {
      $numChanges++;
      if (!$db->selectOne($prefCfg['table'], $prefCfg['arch']['user_options']['id_alias'], [
        $prefCfg['arch']['user_options']['id_option'] => $defDash,
        $prefCfg['arch']['user_options']['id_group'] => $adminGroup,
      ])) {
        $db->insert(
          $prefCfg['table'], [
          $prefCfg['arch']['user_options']['id_option'] => $defDash,
          $prefCfg['arch']['user_options']['id_group'] => $adminGroup,
          $prefCfg['arch']['user_options']['id_alias'] => $idPluginsDashboard,
          ]
        );
      }
      if (!$db->selectOne($prefCfg['table'], $prefCfg['arch']['user_options']['id_alias'], [
        $prefCfg['arch']['user_options']['id_option'] => $defDash,
        $prefCfg['arch']['user_options']['id_group'] => $devGroup,
      ])) {
        $db->insert(
          $prefCfg['table'], [
          $prefCfg['arch']['user_options']['id_option'] => $defDash,
          $prefCfg['arch']['user_options']['id_group'] => $devGroup,
          $prefCfg['arch']['user_options']['id_alias'] => $idPluginsDashboard,
          ]
        );
      }
    }

    if (empty($idPluginsDashboard)) {
      throw new Exception(X::_('Plugins dashboard not found'));
    }

    if ($dashboards = $this->getDashboardFilesContent()) {
      foreach ($dashboards as $dash) {
        if (empty($dash['code'])) {
          throw new Exception(X::_('Dashboard code not found %s', \json_encode($dash)));
        }
        if (!($idDash = $dashClass->getId($dash['code']))) {
          if (!($idDash = $dashClass->insert([
            $prefCfg['arch']['user_options']['text'] => $dash['text'],
            'code' => $dash['code'],
            $prefCfg['arch']['user_options']['public'] => 1
          ]))) {
            throw new Exception(X::_('Dashboard not created %s', \json_encode($dash)));
          }
          $dashClass->setCurrent($idDash);
          $numChanges++;
        }
        else {
          $dashClass->setCurrent($idDash);
          if (($d = $dashClass->get($idDash))
            && ($dash['text'] !== $d[$prefCfg['arch']['user_options']['text']])
          ) {
            $d[$prefCfg['arch']['user_options']['text']] = $dash['text'];
            if (!$dashClass->update($d)) {
              throw new Exception(X::_('Dashboard %s not updated', $idDash));
            }
            $numChanges++;
          }
        }
        if (empty($idDash)) {
          throw new Exception(X::_('Dashboard %s not found', $dash['code']));
        }
        if (!empty($dash['items'])) {
          $dashWidgets = $dashClass->getWidgets();
          foreach ($dash['items'] as $item) {
            $dashClass->setCurrent($idDash);
            $widgetOpt = $optClass->option($item['id_option']);
            if (\is_null(X::find($dashWidgets, [
              $prefCfg['arch']['user_options_bits']['id_option'] => $item['id_option']
            ]))) {
              $dashClass->addWidget($item['id_option']);
              $numChanges++;
              if (($dash['code'] === 'plugins')
                && $defaultCreated
                && !empty($widgetOpt['public'])
              ) {
                $dashClass->setCurrent($idDefaultDashboard);
                $dashClass->addWidget($item['id_option']);
                $numChanges++;
              }
            }
          }
        }
      }
    }
    return $numChanges;
  }

  /**
   * Updates the history tables and add the history constraints.
   *
   * @return int
   */
  public function updateHistory(): int
  {
    $tot_insert     = 0;
    $inserted       = 0;
    $db             = $this->getDb();
    $opt_class      = $this->getOption();
    $pass           = $this->getPassword();
    $id_appui_user  = $this->getInternalUser();
    $dbc            = new Database($db);
    $id_connections = $opt_class->fromCode(
      'connections',
      $this->_current['db_engine'],
      'database',
      'appui'
    );
    if ($id_connections
        && ($id_connection = $opt_class->add(
          [
            'id_parent' => $id_connections,
            'text' => $this->_current['db_host'],
            'code' => $this->_current['db_user'].'@'.$this->_current['db_host'],
          ]
        ))
        && $pass->store($this->_current['db_pass'], $id_connection)
        && ($id_option_database = $dbc->importDb($this->_current['database'], $id_connection, true))
    ) {
      $id_option_table  = $dbc->tableId('bbn_options', $id_option_database);
      $id_option_column = $dbc->columnId('id', 'bbn_options', $id_option_database);
      $tst              = microtime(true);
      // Insert all options into bbn_history_uids and bbn_history tables
      $history_rows     = $db->getColumnValues('bbn_options', 'id');
      $num_history_rows = count($history_rows);
      foreach ($history_rows as $o) {
        ++$tot_insert;
        if (0 === $tot_insert % 100) {
          echo "{$tot_insert} / {$num_history_rows} rows written".PHP_EOL;
        }

        if ($db->insert(
          'bbn_history_uids',
          [
            'bbn_uid' => $o,
            'bbn_table' => $id_option_table,
            'bbn_active' => 1,
          ]
        )
            && $db->insert(
              'bbn_history',
              [
                'opr' => 'INSERT',
                'uid' => $o,
                'col' => $id_option_column,
                'tst' => $tst,
                'usr' => $id_appui_user,
              ]
            )
        ) {
          ++$inserted;
        }
      }

      // Create constraints
      $constraints = $this->getHistoryConstraints();
      foreach ($constraints as $ctable => $ckeys) {
        $db->query($db->getCreateConstraints($ctable, ['keys' => $ckeys]));
      }
    }

    return $inserted;
  }

  /**
   * Installs an app-ui instance after the installation of composer and directories structure.
   *
   * @param stdClass $installer An installer object coming from the previously executed script.
   * @param array|null $cfg       The configuration comuing from the post.
   * @return bool
   */
  public function install(stdClass $installer, array $cfg, int $step = null): bool
  {
    if (!method_exists($installer, 'report')) {
      throw new Exception(X::_("The installer is invalid"));
    }

    $this->_is_installer = true;

    $installer->report(' ');
    $installer->report('Starting the initialization file');
    $installer->report(' ');

    // Initial settings
    /*
    date_default_timezone_set('UTC');
    ignore_user_abort(true);
    ini_set('output_buffering', 'Off');
    if (function_exists('apache_setenv')) {
      apache_setenv('no-gzip', '1');
      apache_setenv('dont-vary', '1');
    }
    */

    // Cache, deleting all before starting
    $installer->report('Deleting cache if any');
    $cache    = Cache::getEngine();
    $cache->deleteAll('');

    $routes   = $this->getRoutes();
    $settings = $this->getSettings();

    // Making a simple plugins array with only the names
    $plugins = array_map(
      function ($r) {
        return $r['name'];
      },
      array_values($routes['root'])
    );
    $installer->report(count($plugins).' plugins found');

    $plugins_urls = [];
    foreach ($routes['root'] as $url => $route) {
      $plugins_urls[$route['name']] = $url;
    }

    // We need a key, if we don't find it we create one..
    $installer->report('Generating the certificate');
    try {
      $this->getPublicKey(true);
    }
    catch (Exception $e) {
      $installer->report('Failed to create SSL certificate: '.$e->getMessage(), false, true);
    }

    // All the update info
    $installer->report('Gathering all the update information');

    // Database creation phase
    $installer->report('Creating the database');
    try {
      $this->createDatabase();
    }
    catch (Exception $e) {
      $installer->report($e->getMessage(), false, true);
    }

    $installer->report('Database created successfully');

    // Records generation
    if ($installer->has_appui()) {
      $installer->report('Records generation', true);

      if ($id_appui_user = $this->getInternalUser()) {
        $installer->report('Internal user and group created successfully');
      }
      else {
        $installer->report('Error during internal user or group creation', false, true);
      }

      $settings = $this->getSettings();

      if (!empty($settings)) {
        $settings['external_user_id'] = $id_appui_user;
        if (!defined('BBN_EXTERNAL_USER_ID')) {
          define('BBN_EXTERNAL_USER_ID', $id_appui_user);
        }

        $this->setSettings(['external_user_id' => $id_appui_user]);
      }
      else {
        $installer->report('Impossible to retrieve the settings file!', false, true);
      }

      if ($this->getUserGroup('admin', 'Administrators')
          && $this->getUserGroup('dev', 'Developers')
      ) {
        $installer->report('Admin and dev group created/retrieved successfully');
      }
      else {
        $installer->report('Error during admin group creation', false, true);
      }

      if ($this->getAdminUser($cfg['admin_name'], $cfg['admin_password'])) {
        $installer->report('Admin user created successfully');
      }
      else {
        $installer->report('Error during admin user creation', false, true);
      }

      if ($res = $this->importOptions()) {
        $installer->report("{$res} options imported");
      }
      else {
        $installer->report('No new option created');
      }

      if ($res = $this->updatePlugins()) {
        $installer->report("{$res} options for plugins imported");
      }
      else {
        $installer->report('No new option imported');
      }

      if ($res = $this->updateTemplates()) {
        $installer->report("{$res} options from templates");
      }
      else {
        $installer->report("No new options from templates");
      }

      $cache->deleteAll('');

      if ($res = $this->updatePermissions()) {
        $installer->report("{$res} Permissions created");
      }
      else {
        $installer->report('No new permissions created');
      }

      if ($this->getProject()) {
        $installer->report("Main project OK");
      }
      else {
        $installer->report('Impossible retrieving the main project', false, true);
      }

      if ($id_app = $this->getAppId()) {
        $installer->report("App ID OK");
      }
      else {
        $installer->report('Impossible retrieving the app ID', false, true);
      }

      // Add Main menu
      $installer->report('Creating menus...');
      $this->updateMenus();

      $installer->report('Menus created');

      if (in_array('appui-dashboard', $plugins)) {

        $this->updateDashboard();
        $installer->report('Dashboard created');
      }

      // If history is active
      if (!empty($settings['history'])) {
        $installer->report(X::_("History update starting, it might take a while..."));
        if ($this->updateHistory()) {
          $installer->report(X::_("History update successful"));
        }
        else {
          $installer->report('Error during history update', false, true);
        }
      }

      $cache->deleteAll('');
      $installer->report('Default DB records created.', true);

      // Contacting the server to give the id_project, id_client, id_user and the public key
      // Using JWT and JWE
      if ($this->register()) {
        $installer->report("Registering the app with ID {$id_app}...");
      }
      else {
        $installer->report('Impossible retrieving the app ID', false, true);
      }

    }

    return true;
  }

}

redpajama_set_name: RedPajamaGithub


: As the discipline of nursing works collaboratively to strategically address global health needs, understanding the processes that assist in building and sustaining effective partnerships will continue to become increasingly important in the future. The purpose of the study was to understand how the contextual features of history, culture, setting, decision-making, and intercultural communication contribute to building and sustaining a successful international development partnership in higher education in nursing. A qualitative methodological framework guided the research process using case study research and participatory action research (PAR). The case study analyzed was of an established educational partnership between a Faculty of Nursing in Canada, and a School of Nursing in Ghana in the development of a Master of Philosophy. Participants included nursing/medical educators, leaders, and administrators in Canada and Ghana, and Ghanaian nursing students who described how they perceived their experiences with this international partnership. As part of the PAR process there was ongoing consultation with Management Committee members in Canada and Ghana, at various stages, during the research process. The findings revealed three phases of the partnering relationship among the Canadian and Ghanaian partners which were; Getting Started, Keeping it Going, and Following the Project. Further analysis of the data provided an in-depth understanding of three integral components; ease of communication, sharing of power, and focus on capacity building and seven key elements: shared and individual institutional goals; shared expertise; shared workload; shared leadership; adequate resources; mobilization of external resources ; and commitment to sustainability which provided the framework for understanding what contributed to a mutually satisfying partnering relationship and a successful outcome. Social and critical perspectives were used to offer a theoretical understanding of the complexities of the partnering relationship. The findings in this particular case study highlight the importance of the partnering relationship for a successful outcome and likelihood for sustainability in an international partnership in higher education in nursing. Nurse scholars engaged in similar international development projects may find the insights from this study, based upon collaborative reflections between the researcher and the Management Committee members, useful as a guide to developing a mutual and satisfying partnering relationship..


Beer, high tides, public holidays and blood thirsty mozzies. The perfect mix to set the media into a spin. How can you get the best public health messages out about mosquito bite protection?
It was almost as tricky managing the media this summer as it was the mozzies. Since late October, I've been interviewed on almost 50 occasions. A mix of pre-dawn calls from radio stations to live crossed to nationally broadcast breakfast televsion to taking talkback and dealing with mobile phone dropouts. It was a sweaty and stressful couple of months….and 'mozzie season' still isn't over just yet.
The last few years have followed a pretty similar pattern. I get my first few calls around August/September. This is usually when we get our first blast of unseasonal heat and there are typically a few stories about people noticing bugs about their home and are worried about an early start to the mozzie season.
This year we genuinely did have an early start to the mozzie season. The warmest spring on record kicked off the mozzie season early and one of the first big stories I did for Channel 9 News was almost derailed by swarms of mozzies! A few of the crew needed to retreat to the safety of their car. You can listen to me speaking with ABC New Radio here.
Following plenty of rain in early December, mosquito populations starting jumping up along the coast. Just in time for the Christmas holidays. During this period it is pretty common to respond to requests from media to talk about mozzies, particularly if there have been some public health alters from local health authorities.
Getting ready for a live cross to Weekend Today (Channel 9) but what you cannot see in this shot is the hundreds of mosquitoes that were swarming around me, standing in the middle of the mangroves for 20min getting ready for the segment attracted plenty of mozzie attention!
A few things helped keep mozzies in the news. There were health warnings from local authorities after the detection of Ross River virus in southern Sydney. There were warnings about a new outbreak of dengue in Far North QLD. Flooding in central Australia also prompted warnings of outbreaks of mosquito-borne disease.
Then there was this piece I wrote on why mosquitoes bite some people more than others that attracted plenty of attention too….over 1.2 million readers in fact (thanks to republication by IFLS, SBS and Mamamia)!
As we headed towards our national holiday weekend and Australia Day celebrations, there were warnings that a big boost in mosquito populations were on their way. Yes, "mozziegeddon" was coming and the pesky little biters were set to turn our long weekend into a "moztralia day bloodbath". Worse still, those taking part in the traditional Australia Day past time of beer drinking were being scared off the booze by fears of becoming "mozzie magnets".
There is little surprise that stories about beer drinking and mozzies attract plenty of attention. It does almost every year. However, this year was a little different because health authorities were concerned about potential increases in mosquito populations and given recent detections of mosquito-borne pathogens such as Ross River virus, there was concern about public health risks. Those risks range from both the north coast of NSW to north-west of WA!
There was plenty of mosquito media coverage from SE QLD too. Local authorities were battling big mosquito populations and trying to control "3000 known mosquito breeding sites" the next generation of mosquitoes hatching following heavy rain and tidal flooding of local wetlands.
Local insect repellent manufacturers were also taking advantage of the boost in mosquito numbers. I've noticed an increase in tv and radio ads spruiking mosquito repellents and Aerogard also sent out "swat" teams to local parklands around Sydney on Australia Day promoting their "Mozzie Index"* website!
How could this media interest help spread the word on effectively stopping mosquito bites?
It can be tricky getting good public health messages out during these very brief interviews, particularly for television. Radio can be pretty good as there is often plenty of time to get the message out (sometimes even time for talk back callers and questions) but for some of the commercial stations, time can be brief. Television is particularly challenging, I usually spend more time in the make up chair than being interviewed!
This summer I've been determined to ensure some key messages get out, particularly about choosing and using insect repellents most effectively. This is an issue I feel strongly about and I have an article coming out shortly in the Medical Journal of Australia on how local health authorities can do this a little better.
The two key messages were "if you're using botanical based topical repellents, they need to be reapplied more frequently than the recommended DEET and picaridin based repellents" and "when using repellents, they must be applied as a thin coat over all exposed skin to get the best protection, not a dab here and there".
Overall, I think I managed to get these two points into most radio and television interviews and I was happy to see that the general message got through in a lot of the print/online media too.
Below are some of my tips on getting a specific message out while dealing with the media.
1. Prepare. You would practice giving a conference presentation ahead of time so why not prepare for media? Think about the messages and how you can deliver them. What questions might you get asked? What will be the style of the presenter? Are there any questions you may be asked that you may want to avoid answering (e.g. questions of a political nature or something that could embarrass your employer)? How can you do that?
2. Learn from the professionals. If know you're going to do some media in the coming weeks or months. Spend some time listening to talk back radio and reading newspapers. Take note of the number and length of quotes journalists use in articles. Make their job a little easier by providing concise quotes where possible. How do radio broadcasters conduct interviews? Listen to politicians and journalists being interviewed. How do they get their message across (or don't in some circumstances). What makes you "switch off" from an interview – is it the topic or interviewee?
3. Create bridges between questions and your message. This is the thing I've found quick tricky but once you've got the hang of it, you can more effectively get the message out. There may not be a question asked that specifically relates to the message you need to get out. Learn how to transition from a brief response to the question asked onto the key messages you want to get out there. Don't just launch off into your spiel at first chance, it is important to engage with presenter too, its a subtle art but like all things, it is only hard before it becomes easy.
4. Post-interview review. I'll often take notes after an interview that help prepare for the next one. Things like the type of questions asked or how I answered questions, particularly if I feel my responses were clunky or I rambled a little! I've always found it interesting that slight differences in the way that questions are asked can often throw you off balance in an interview. If there are talkback callers, what questions were asked, especially if there was something out of left field! Making a note of these can help when preparing for the next batch of media.
5. Keep track of media activity. You never know when it may come in handy when applying for a promotion, grant or new job. I try to keep track of all media activities by recording the date, journalist, media outlet and brief description of topic. You can also speak to your local media and communications unit to see if they gather statistics on these things too. The team at the University of Sydney are great and it is fascinating to compare the analysis of different media activities, their reach and estimated value.
[update 21 March 2015] Following the detection of Ross River virus amongst mosquitoes collected in NSW combined with a dramatic increase in human notifications of Ross River virus disease, there was another wave of interest by local media. You read a piece at the Sydney Morning Herald and watch a segment with me from Nine News.
Why not join the conversation on Twitter?
*A disclaimer: I provided some assistance to a local PR company back in 2012 that developed the "Mozzie Index" for Aerogard, particularly some info on the associations between mosquitoes and local environmental conditions.


text:
You are requesting information from Brown Harris Stevens on Yacht Club
Home > Condos > Yacht Club
Yacht Club - Units For Sale
90 Alton Rd, Miami Beach, FL 33139
Quick Facts About Yacht Club
Developer: The Related Group
Number of Units For Sale: 23
Description of Yacht Club
The Yacht Club at Portofino is one of South Beach's best priced luxury buildings. It sits on Biscayne Bay at South Beach's famous South Pointe, and is a full-service lxury condo that makes an ideal place to live. It also has a somewhat-modest rental policy, so many buyers choose to rent out their units seasonally, when not in use.
The Yacht Club is a Related Group of Florida project, and one of the first of its South Beach condo projects. The views are stunning from just about any unit, and the condo is within walking distance of such famous restaurants as Prime 112, Joe's Stone Crab and Smith and Wollensky's.
You'll love the pool and resort area, which offers breathtaking views of the Miami Beach Marina.
Thinking About Selling Your Condo at Yacht Club ?
Yacht Club Map and Directions
Yacht Club Floorplans
PORTOFINO 01 2/2/0 1212 (112.6)
TROPEZ 03 2/2/0 1077 (100.1)
CARLO 05 1/1/0 783 (72.7)
MAJORCA 06 1/1/0 798 (74.1)
Our Exclusive Listings at Yacht Club
1207 $464,900 $576.08 1/1/0 807 s.f. (75.0 m²) Sebastian Vallejo
You are requesting information from Sebastian Vallejo on Yacht Club At Portof #1207
[ Contact ]
[ Condo Tour ]
2603 $799,000 $739.81 2/2/0 1080 s.f. (100.3 m²) Sonia Toth
You are requesting information from Sonia Toth on Yacht Club #2603
23 Units for Sale at Yacht Club
1 Bedroom Units for Sale at Yacht Club
New 1005 $458,000 $619 1/1 740 s.f. 7/16/2019
A,BHS,Exclusive,Listing 1207 $464,900 $576 1/1 807 s.f. 5/28/2019
6.7% 806 $499,000 $640 1/1 780 s.f. 1/7/2019
907 $520,000 $703 1/1 740 s.f. 10/30/2018
15.6% 2107 $539,000 $728 1/1 740 s.f. 5/9/2019
FL-8 $775,000 $881 1/1 880 s.f. 2/19/2019
25.9% n/a $500,000 $442 2/2 1130 s.f. 1/14/2019
16.7% 612 $750,000 $664 2/2 1130 s.f. 5/12/2017
A,BHS,Exclusive,Listing 2603 $799,000 $740 2/2 1080 s.f. 6/12/2017
5.6% 1011 $845,000 $748 2/2 1130 s.f. 11/30/2018
5.5% 1203 $860,000 $796 2/2 1080 s.f. 5/8/2017
1411 $1,020,000 $903 2/2 1130 s.f. 6/28/2019
2501 $1,175,000 $1,040 2/2 1130 s.f. 3/3/2019
Pending Sales at Yacht Club
2010 $1,075,000 Pending Pending 2/2 1090 sqft
2208 $555,000 Pending Pending 1/1 780 sqft
2012 $899,000 Pending Pending 2/2 1130 sqft
Condo Sales History at Yacht Club
1 Bedroom Units Sold at Yacht Club
4.2% 2806 $599,000 $589,000 $755 1/1 780 s.f. 5/31/2019
5.5% 505 $430,000 $400,000 $541 1/1 740 s.f. 3/29/2019
PH3308 $650,000 $612,000 $805 1/1 760 s.f. 2/1/2019
1205 $499,000 $485,000 $619 1/1 783 s.f. 7/2/2018
Sold,by,BHS 2707 $539,000 $505,000 $682 1/1 740 s.f. 3/2/2018
PH3308 $619,000 $619,000 $814 1/1 760 s.f. 11/22/2017
5.4% 2807 $530,000 $515,000 $696 1/1 740 s.f. 4/7/2017
1008 $549,000 $525,000 $673 1/1 780 s.f. 12/22/2016
16.3% 807 $434,900 $423,500 $572 1/1 740 s.f. 6/18/2015
Sold,by,BHS PH3305 $555,000 $500,000 $676 1/1 740 s.f. 3/13/2015
7.6% 1706 $545,000 $515,000 $660 1/1 780 s.f. 12/2/2014
4.1% PH3307 $590,000 $560,000 $757 1/1 740 s.f. 5/28/2014
3.9% PH3308 $615,000 $605,000 $796 1/1 760 s.f. 5/7/2014
2305 $454,900 $426,000 $576 1/1 740 s.f. 10/1/2013
806 $548,000 $525,000 $673 1/1 780 s.f. 8/2/2013
Sold,by,BHS 2606 $595,000 $570,000 $731 1/1 780 s.f. 6/24/2013
Sold,by,BHS FL-2 $560,000 $555,000 $631 1/1 880 s.f. 4/15/2013
708 $449,000 $410,000 $526 1/1 780 s.f. 12/27/2012
3.7% 507 $399,500 $375,000 $507 1/1 740 s.f. 10/5/2012
FL-8 $550,000 $510,000 $580 1/1 880 s.f. 5/1/2012
505 $325,000 $320,000 $432 1/1 740 s.f. 3/30/2012
PH3207 $440,000 $420,000 $568 1/1 740 s.f. 9/22/2011
5.0% 707 $303,900 $290,000 $392 1/1 740 s.f. 5/6/2011
8.0% 1605 $348,400 $332,000 $449 1/1 740 s.f. 11/15/2010
26.1% 2008 $435,000 $380,000 $487 1/1 780 s.f. 5/28/2010
17.4% 1107 $380,000 $365,000 $468 1/1 780 s.f. 3/4/2010
Sold,by,BHS,Agent 2905 $449,000 $421,000 $569 1/1 740 s.f. 11/23/2009
Sold,by,BHS 808 $450,000 $417,500 $535 1/1 780 s.f. 5/15/2009
1508 $458,000 $427,000 n/a 1/1 n/a 9/23/2008
Sold,by,BHS PH3207 $445,000 $380,000 $514 1/1 740 s.f. 5/8/2008
21.5% PH3307 $420,000 $395,000 $534 1/1 740 s.f. 4/14/2008
24.3% 3006 $499,000 $455,000 $583 1/1 780 s.f. 12/14/2007
806 $455,000 $428,000 n/a 1/1 n/a 10/25/2007
Sold,by,BHS 301 $529,000 $510,000 $432 2/2 1180 s.f. 4/24/2019
Sold,by,BHS 2709 $799,000 $785,000 $727 2/2 1080 s.f. 4/18/2019
2802 $999,000 $950,000 $841 2/2 1130 s.f. 4/8/2019
21.0% FL-6 $830,000 $750,000 $701 2/2 1070 s.f. 1/15/2019
18.6% 2001 $650,000 $640,000 $566 2/2 1130 s.f. 12/28/2018
703 $649,000 $630,000 $583 2/2 1080 s.f. 11/15/2018
8.7% 604 $799,000 $730,000 $670 2/2 1090 s.f. 10/15/2018
FL-4 $850,000 $803,500 $751 2/2 1070 s.f. 9/11/2018
5.0% 1211 $779,000 $735,000 $650 2/2 1130 s.f. 8/6/2018
16.3% 1901 $669,900 $588,000 $520 2/2 1130 s.f. 5/24/2018
2401 $775,000 $720,000 $637 2/2 1130 s.f. 3/21/2018
1.8% 2312 $1,075,000 $925,000 $819 2/2 1130 s.f. 11/30/2017
22.8% 2210 $849,000 $810,000 $743 2/2 1090 s.f. 10/5/2017
Sold,by,BHS 2604 $869,999 $850,000 $780 2/2 1090 s.f. 7/6/2017
8.3% 209 $549,000 $512,500 $454 2/2 1130 s.f. 4/12/2017
2.6% 1012 $1,099,000 $1,050,000 $929 2/2 1130 s.f. 8/26/2016
19.2% 1812 $1,050,000 $1,020,000 $903 2/2 1130 s.f. 8/5/2016
3.1% 1911 $774,900 $708,750 $627 2/2 1130 s.f. 5/25/2016
9.0% FL-5 $810,000 $762,500 $713 2/2 1070 s.f. 5/13/2016
1902 $1,200,000 $1,150,000 $1,018 2/2 1130 s.f. 3/8/2016
601 $541,000 $739,114 $654 2/2 1130 s.f. 1/28/2016
Sold,by,BHS 1702 $999,000 $995,000 $881 2/2 1130 s.f. 12/31/2015
2404 $999,000 $955,000 $876 2/2 1090 s.f. 11/30/2015
4.2% 2010 $859,000 $824,000 $756 2/2 1090 s.f. 10/13/2015
3010 $1,100,000 $980,000 $899 2/2 1090 s.f. 2/23/2015
Sold,by,BHS,Agent 1003 $685,000 $665,000 $616 2/2 1080 s.f. 1/9/2015
711 $799,000 $750,000 $664 2/2 1130 s.f. 12/3/2014
Sold,by,BHS,Agent 2009 $799,000 $750,000 $694 2/2 1080 s.f. 5/30/2014
811 $610,000 $550,000 $487 2/2 1130 s.f. 4/4/2013
TH-1 $575,000 $559,000 $396 2/2½ 1410 s.f. 11/16/2012
12.5% 612 $699,000 $670,000 $593 2/2 1130 s.f. 4/13/2012
3.1% FL-5 $627,500 $562,500 $526 2/2 1070 s.f. 4/8/2011
Sold,by,BHS,Agent 612 $499,000 $400,000 $354 2/2 1130 s.f. 3/17/2010
TH-9 $999,999 $862,500 $493 2/3 1750 s.f. 2/22/2010
Sold,by,BHS,Agent 1912 $699,500 $605,000 $535 2/2 1130 s.f. 12/30/2009
20.2% 2204 $650,000 $475,000 $436 2/2 1090 s.f. 7/7/2008
8.4% 604 $640,000 $600,000 $550 2/2 1090 s.f. 5/9/2008
TH-3 $750,000 $750,000 $487 2/2½ 1540 s.f. 6/5/2006
15.4% TH-12 $2,199,999 $1,800,000 $1,029 3/2½ 1750 s.f. 8/22/2018
PH3408 $1,930,000 $1,900,000 $960 3/3 1980 s.f. 12/6/2011
TH-6 $895,000 $840,000 $480 3/2½ 1750 s.f. 11/1/2010
PH3309 $999,000 $930,000 $624 3/2 1490 s.f. 9/3/2010
TH-10 $895,000 $790,000 $451 3/2½ 1750 s.f. 7/15/2010
23.3% TH8 $880,000 $840,000 $480 3/2½ 1750 s.f. 12/15/2008
TH-5 $1,295,000 $1,205,000 $675 3/2½ 1785 s.f. 5/22/2007
Brown Harris Stevens notes on Yacht Club

redpajama_set_name: RedPajamaCommonCrawl


text:
Peter McIntyre (* 24. srpna 1927 Melbourne) je australský architekt a pedagog.

Životopis
Vystudoval gymnázium Trinity, Royal Melbourne Institute of Technology a Melbourne University, v roce 1950 založil praxi, která kombinovala moderní materiály špičkové technologie se zájmem o "emocionální funkcionalismus" nebo dopad zastavěného prostředí na jeho obyvatele. Jeho návrh ekologicky přizpůsobené nemocnice Mallee byl chválen kritikem Robinem Boydem jako začátek nové australské architektury. V roce 1953 založil McIntyre Partnership Pty Ltd., kde působil jako praktický ředitel, hlavní a senior partner.

McIntyre uzavřela partnerství s architekty Johnem a Phyllis Murphyovými a Kevinem Borlandem a ve spolupráci s technickým konzultantem Billem Irwinem navrhli v roce 1952 olympijský bazén v Melbourne.

McIntyre napsal Strategický plán pro město Melbourne z roku 1973, který omezil výškový rozvoj na jeho východní a západní ramena. Mezi jeho hlavní projekty patří Melbourne's Parliament Station, The Jam Factory Complex v South Yarra, Westfield Knox ve Wantirna South a vytvoření alpské vesnice Dinner Plain poblíž Mount Hotham ve Victorii.

Byl profesorem architektury na Melbourne University v letech 1988 až 1992 a získal řadu ocenění. Jeho manželka Dione je také architektkou.

Film
 1960 Your House and Mine, režie Peter McIntyre, distribuováno Státním filmovým centrem.

Reference

Externí odkazy

Australští architekti
Narození v roce 1927
Narození 24. srpna
Narození v Melbourne
Žijící lidé
Muži

redpajama_set_name: RedPajamaWikipedia


'Man errs. Man does not merely stray into errancy. . . . The errancy through which man strays is not something which, as it were, extends alongside man like a ditch into which he occasionally stumbles; rather, errancy belongs to the inner constitution of the Da-sein into which historical man is admitted. . . . Errancy is the open site for and ground of error. Error is not merely an isolated mistake but the realm (the domain) of the history of those entanglements in which all kinds of erring get interwoven.' - Heidegger, "On the Essence of Truth," in Basic Writings from "Being and Time" (1927) to The Task of Thinking" (1964), trans. and ed. David Farrell Krell (Routledge: London, 1993), 133.


What are the different functions of kidneys?
Home > kidney > What are the Different Functions of Kidneys?
Healthy kidneys make significant substances in the body called 'hormones'. A hormone is a chemical produced by the body which is released into the blood. These hormones travel in the bloodstream like 'messengers' and help different organs by giving signals to regulate blood pressure, red blood cell production and the calcium balance in the body. Kidneys produce many different hormones and regulate various body functions.
When the blood runs through arteries in the body which is called circulatory system, it generates the force on the arterial walls. This process is known as blood pressure which relies on the heartbeat rate and pressure of the blood.
It is important to have blood pressure within normal range. This means blood pressure should neither be higher or less than the required healthy parameters. Regulating normal blood pressure is one of the prime jobs done by kidneys.
In a normal healthy condition, kidneys produce a hormone called renin. This hormone is generated by the kidneys to control blood pressure. Whenever a person's blood pressure goes low, kidney releases renin hormone to alert the body blood vessels squeeze and become smaller and firm resulting in less blood flow with adequate pressure to reach everywhere in the body. Similarly high pressure is controlled by blood vessels expansion to give more space and slow down of blood flow speed.
Our body requires sufficient oxygen level to survive. Red blood cells (RBCs) are the carrier of oxygen in the body. Red blood cells transports oxygen from the lungs to supply it throughout the body. Red blood cells provide us the energy required for our day to-day's activities. Inadequate oxygen supply in the body is a condition known as hypoxia in medical terms.
Kidneys balancing act plays a vital role as production of any hormone should be within significant range to maintain good health. Overproduction of RBC can result in hemochromatosis disease in which too much iron is generated in the body which can further damage many body organs like heart, liver and pancreas; endocrine glands and joints etc.
Erythropoietin (EPO) is a hormone produced by peritubular capillary lining cells of the kidney. These kidney cells are able to sense oxygen levels in the body. When kidney receive less oxygen due to anaemia, peritubular capillary lining cells release erythropoietin hormone which sends the message immediately to the body mechanism (bone marrow) to increase the production red blood cells.
Potential Hydrogen balance, commonly known as the pH balance, is the measurement of acidity and alkalinity. We know our body has high percentage of water and pH is a measure of how acidic a water-based solution is. Therefore pH level is used to know how much acid is mixed up in the fluids in a person's body. The kidneys prevent blood plasma from becoming too acidic or basic by regulating ions.
Both acid and alkaline are checked on the pH scale ranging from 0 (completely acidic) to 14 (completely alkaline). A pH of 7.0 is considered neutral. Normally the body maintains the pH of blood close to 7.40. If this balance changes, and fluid in the body gets on either extreme side on the scale, the condition can create many health problems.
Healthy kidneys regulate this pH balance by filtering the acids and bases in the blood regularly by removing extra acid or base in the form of urine.
Like other nutrients, vitamin D is equally essential. Vitamin D is necessary to utilise calcium we receive from dairy products and some other foods also. Calcium is needed to make strong bones and teeth.
We get required amount of vitamin D from food, sun's ultraviolet rays and vitamin pills. Kidneys convert vitamin D from supplements or the sun to the active form of vitamin D that is required by the body.
Apart from food and supplement, this particular vitamin D is also produced by special skin cells when they are exposed to sunlight. If the skin cells are unable to absorb vitamin D, then the liver do the needful. Incase for any medical reason if the liver fails, the same task is done by the kidneys.
High sugar level destroys small blood vessels throughout in the body. Since kidneys become unable to filter blood completely, more water and salt is retained in the body. Protein existence in the urine of a diabetic patient can specify the level of damage done to the nephrons. If blood sugar levels are dangerously high, the blood flow towards kidneys may be blocked which can lead to renal failure.
What is Chronic Kidney Disease or Renal Failure?
As an organ, kidney has many important functions but when due to an injury or disease there is loss in its performance, it is referred as kidney disease or kidney failure.
What are the Tasks Performed by Kidneys?
Why Kidney is an Important Organ?
The kidneys are pair of vital organs, located just below the rib cage (that is lower back in the body) one on each side of the spine. They have an unusual shape similar to beans. Human body has normally two kidneys that are the primary organs as no person can survive without them. Each kidney is approximately 4.5 inch in length and weighs around 4-6 ounces. Like any other organ in the body, kidneys are equally necessary to help the body system function well. Kidney has many important roles to play.


text:
Portugal's D2 Visa for Foreign Entrepreneurs: Everything You Need to Know
By JeremiahDecember 18, 2020November 19th, 2021No Comments
In this article, we'll detail the requirements and application process for the D2 Portugal Visa – the so-called "immigrant entrepreneur" visa for non-EU citizens wishing to commence investment activity in the country. The D2 is a much sought-after European visa, but what's the attraction of this sunny land on the Iberian Peninsula for foreign entrepreneurs?
What's the D2 Visa Portugal?
Back in 2009, Portugal introduced a wide range of tax benefits for EU and non-EU citizens alike. A decade on, it's looking like that might just have been one of the wisest decisions the Portuguese government has ever made. Long valued by holidaymakers and tourists for its amenable climate, vibrant culture, and the beauty of its ancient cities, it has for some time now also been a top spot for expatriate investment.
At a top-tiered rate of 42% and a whole range of other incentives, Portuguese taxes tend to be lower – and the economic climate generally more friendly – than the vast majority of European nations. It's little wonder then that PWC saw fit to commission a report on the country, dubbing it "Europe's best kept secret". Forbes quickly followed suit with a timely article on the skyrocketing popularity of the country for foreign investment.
This article makes for tantalising reading, reporting on the massive influx of both EU and non-EU investment, with British, French, Swedish (all EU), American, Brazilian, and Middle Eastern (all non-EU) being particularly significant. The benevolent tax climate has also attracted much Chinese interest. It would appear that all of the major players are looking towards Portugal.
The country itself has also felt much of the benefit of this interest. Property markets from Lisbon to Porto to the Algarve are showing healthy, steady growth – and Lisbon is gaining a real international reputation as a tech start-up hub.
Relatively friendly visa requirements are certainly part of this picture. For most foreign investors of moderate size, the D2 visa is the way to go (there is also the much sought after Golden Visa for larger investments which offer more residency freedom). Although no walk in the park, this visa poses less obstacles and difficulties than many others and will in turn allow you to get a residence permit – at which point the investor is all set to do business in Portugal.
What are the main requirements to get the D2 Visa Portugal
To get the D2 Visa, you will need to show the Portuguese government that you have intentions of hosting your business in Portugal long term – and that the enterprise has value, and you have the means to carry it out. The D2 Visa is for small- to medium-sized enterprises roughly related to social, scientific, technological, cultural, or economic areas. This has, however, been codified into a set of essential requirements. These are:
Proof of Business Viability
The main things you'll want to demonstrate here are that your business has a clear purpose (clearly associating it with one of the aforementioned enterprise areas will help) and also that it is fairly unique. Simply copying countless other Portuguese businesses is not the way to go.
A Business Plan
This must be clear, concise yet sufficiently detailed. Outlining the main steps in establishing your business is a must.
The government has not set any official requirements on social capital for immigrant entrepreneurs, but in the competitive environment of seeking approval for your investment, this cannot be ignored. You should aim to demonstrate how your business will be socially cohesive – both internally among employees and within the community that you wish to establish it in.
Reasons You Have Chosen Portugal
Tax incentives aside. For this requirement, you will need to demonstrate what your business has to do with the country you wish to establish it in. The previous requirement for social capital is closely related to this, but you should also outline what it is about the Portugal that is particularly suited to your business – and how your business is suited to Portugal.
Steps to Getting Your D2 Visa and Residence Permit
Having established how you will convince the Portuguese government to welcome your business, it's time to begin the official progress. The application process begins in your country of residence, so you will have to make your case at a Portuguese consulate or embassy. Further details on this can be found on the Portuguese Foreign Affairs website. Once you have a watertight pitch and business plan, you will have to collect the following official documentation ahead of your appointment:
The official form (the embassy/consulate will provide this)
Passport/other valid travel document (and two up-to-date passport photos)
Valid travel insurance
Proof of being in a regular situation in your current country of residence
A request for a criminal record enquiry by Portugal's Immigration and Border Services (the SEF)
A Criminal record certificate from your country of residence
Proof of means of subsistence (this varies depending on where you live, but you will also need to provide a statement of responsibility and have it signed by a Portuguese national or someone resident in Portugal).
The Residence Permit and Beyond
After you get your D2 visa, you will have four months to move to Portugal and get your residence permit. Becoming a legal citizen can take a while in Portugal – sometimes more than four months. However, all you need to do within that time window is to get an appointment. Should your appointment be months down the line and your visa expires, mere proof of the appointment is enough for you to be considered a legal resident – and able to do business.
If you are a non-EU citizen seeking to relocate to Portugal with your own funding and you are not looking to set up business here, the D7 visa — also known as the Portugal Passive Income Visa — is a perfect option for you.
Since December 2020 there have been many changes to the residency application for non-EU citizens.
It is now a legal requirement that all non-residents, wishing to acquire the D2 Visa designed for investors in Portugal, appoint a Fiscal Representative in Portugal.
The Portuguese Financas office now request the following:
For residents of the European Union who do not come in person to their fiscal representative's office:
Certified copy of passport
Power of Attorney with notarised signature
Proof of address can be sent by email
For residents outside the European Union who do not come in person to their fiscal representative's office:
Certified and Apostille copy of the passport
Power of attorney with recognised signature and handout.
Our legal team at RHJ assists non-EEA nationals to obtain residency in Portugal. Our English-speaking accountants specialise in helping entrepreneurs to set up, manage and grow their businesses in Europe.
Ensure that your transition to Portuguese residency is hassle-free.
Contact us to get your D2 application started today.
Previous PostD7 Visa Portugal: A simple guide for Portuguese residency in 2021.
Next PostA serial entrepreneurs guide to starting a business in Portugal
BlogExpatsFreelancerMaltaPortugalResidencyUnited Arab EmiratesUnited KingdomUnited States of AmericaVisa
Healthcare options for UK citizens abroad
JeremiahJanuary 5, 2023
AccountancyBlogExpat businessExpatsFreelancerMaltaPortugalTaxUnited Arab EmiratesUnited KingdomUnited States of America
International tax services: a simple guide
BlogExpatsMaltaPortugalResidencyUnited Arab EmiratesUnited KingdomUnited States of America
Relocating abroad with your pooch


We thank you for the opportunity to present the services offered by Mind Lease Inc.
Please use the below contact form if you have any questions or requests, concerning our services.
Mind Lease Inc's Team will respond to your message as earliest as possible. Below you can find a roadmap to reach our office. Contact us today to feel the experience of Mind Lease Inc's innovation.