src/maintenance/Installation.php
<?php
namespace bizley\podium\maintenance;
use bizley\podium\models\Content;
use bizley\podium\models\User;
use bizley\podium\Podium;
use bizley\podium\PodiumConfig;
use bizley\podium\rbac\Rbac;
use Exception;
use Yii;
use yii\helpers\Html;
use yii\helpers\VarDumper;
/**
* Podium Installation
*
* @author Paweł Bizley Brzozowski <pawel@positive.codes>
* @since 0.1
*
* @property array $steps
*/
class Installation extends Maintenance
{
const DEFAULT_USERNAME = 'admin';
const SESSION_KEY = 'podium-installation';
const SESSION_STEPS = 'steps';
/**
* @var array Installation steps
*/
private $_steps;
/**
* Adds Administrator account.
* @return string result message.
*/
protected function addAdmin()
{
if ($this->module->userComponent !== true) {
return $this->addInheritedAdmin();
}
$transaction = $this->db->beginTransaction();
try {
$admin = new User();
$admin->setScenario('installation');
$admin->setAttributes([
'username' => self::DEFAULT_USERNAME,
'status' => User::STATUS_ACTIVE,
'role' => User::ROLE_ADMIN,
], false);
$admin->generateAuthKey();
$admin->setPassword(self::DEFAULT_USERNAME);
if (!$admin->save()) {
throw new Exception(VarDumper::dumpAsString($admin->errors));
}
if (!$this->authManager->assign($this->authManager->getRole(Rbac::ROLE_ADMIN), $admin->id)) {
throw new Exception('Error during Administrator privileges setting!');
}
$transaction->commit();
return $this->returnSuccess(
Yii::t('podium/flash', 'Administrator account has been created.')
. ' ' . Html::tag('strong', Yii::t('podium/flash', 'Login') . ':')
. ' ' . Html::tag('kbd', self::DEFAULT_USERNAME)
. ' ' . Html::tag('strong', Yii::t('podium/flash', 'Password') . ':')
. ' ' . Html::tag('kbd', self::DEFAULT_USERNAME)
. '<br>'
. Html::tag('strong', Yii::t('podium/flash', 'Remember to change these credentials after first login!'), ['class' => 'text-danger'])
);
} catch (Exception $e) {
$transaction->rollBack();
return $this->returnError($e->getMessage(), __METHOD__,
Yii::t('podium/flash', 'Error during account creating')
);
}
}
/**
* Adds Administrator account for inherited User Identity.
* @return string result message.
* @since 0.2
*/
protected function addInheritedAdmin()
{
if (empty($this->module->adminId)) {
$this->type = self::TYPE_WARNING;
return Yii::t('podium/flash', 'No administrator privileges have been set!');
}
try {
$identity = Podium::getInstance()->user->identityClass;
$inheritedUser = $identity::findIdentity($this->module->adminId);
if (!$inheritedUser) {
throw new Exception('Can not find administrator account!');
}
$userNameField = $this->module->userNameField;
if ($userNameField !== null && empty($inheritedUser->$userNameField)) {
throw new Exception('Can not find administrator username!');
}
} catch (Exception $e) {
return $this->returnWarning(Yii::t('podium/flash', 'Cannot find inherited user of given ID. No administrator privileges have been set.'));
}
$transaction = $this->db->beginTransaction();
try {
$admin = new User();
$admin->setScenario('installation');
$admin->setAttributes([
'inherited_id' => $this->module->adminId,
'username' => $userNameField ? $inheritedUser->$userNameField : self::DEFAULT_USERNAME,
'status' => User::STATUS_ACTIVE,
'role' => User::ROLE_ADMIN,
], false);
if (!$admin->save()) {
throw new Exception(VarDumper::dumpAsString($admin->errors));
}
if (!$this->authManager->assign($this->authManager->getRole(Rbac::ROLE_ADMIN), $admin->id)) {
throw new Exception('Error during Administrator privileges setting!');
}
$transaction->commit();
return $this->returnSuccess(Yii::t('podium/flash', 'Administrator privileges have been set for the user of ID {id}.', [
'id' => $this->module->adminId
]));
} catch (Exception $e) {
$transaction->rollBack();
return $this->returnError($e->getMessage(), __METHOD__,
Yii::t('podium/flash', 'Error during account creating')
);
}
}
/**
* Adds config default settings.
* @return string result message.
*/
protected function addConfig()
{
try {
$this->db->createCommand()->batchInsert(
PodiumConfig::tableName(),
['name', 'value'],
[
['activation_token_expire', PodiumConfig::SECONDS_ACTIVATION_TOKEN_EXPIRE],
['allow_polls', PodiumConfig::FLAG_ALLOW_POLLS],
['email_token_expire', PodiumConfig::SECONDS_EMAIL_TOKEN_EXPIRE],
['from_email', PodiumConfig::DEFAULT_FROM_EMAIL],
['from_name', PodiumConfig::DEFAULT_FROM_NAME],
['hot_minimum', PodiumConfig::HOT_MINIMUM],
['maintenance_mode', PodiumConfig::MAINTENANCE_MODE],
['max_attempts', PodiumConfig::MAX_SEND_ATTEMPTS],
['members_visible', PodiumConfig::FLAG_MEMBERS_VISIBLE],
['merge_posts', PodiumConfig::FLAG_MERGE_POSTS],
['meta_description', PodiumConfig::META_DESCRIPTION],
['meta_keywords', PodiumConfig::META_KEYWORDS],
['name', PodiumConfig::PODIUM_NAME],
['password_reset_token_expire', PodiumConfig::SECONDS_PASSWORD_RESET_TOKEN_EXPIRE],
['recaptcha_secretkey', ''],
['recaptcha_sitekey', ''],
['registration_off', PodiumConfig::REGISTRATION_OFF],
['use_captcha', PodiumConfig::FLAG_USE_CAPTCHA],
['use_wysiwyg', PodiumConfig::FLAG_USE_WYSIWYG],
['version', Podium::getInstance()->version],
]
)->execute();
return $this->returnSuccess(Yii::t('podium/flash', 'Default Config settings have been added.'));
} catch (Exception $e) {
return $this->returnError($e->getMessage(), __METHOD__,
Yii::t('podium/flash', 'Error during settings adding')
);
}
}
/**
* Adds default content.
* @return string result message.
*/
protected function addContent()
{
try {
$default = Content::defaultContent();
$this->db->createCommand()->batchInsert(
Content::tableName(),
['name', 'topic', 'content'],
[
[
Content::TERMS_AND_CONDS,
$default[Content::TERMS_AND_CONDS]['topic'],
$default[Content::TERMS_AND_CONDS]['content']
],
[
Content::EMAIL_REGISTRATION,
$default[Content::EMAIL_REGISTRATION]['topic'],
$default[Content::EMAIL_REGISTRATION]['content']
],
[
Content::EMAIL_PASSWORD,
$default[Content::EMAIL_PASSWORD]['topic'],
$default[Content::EMAIL_PASSWORD]['content']
],
[
Content::EMAIL_REACTIVATION,
$default[Content::EMAIL_REACTIVATION]['topic'],
$default[Content::EMAIL_REACTIVATION]['content']
],
[
Content::EMAIL_NEW,
$default[Content::EMAIL_NEW]['topic'],
$default[Content::EMAIL_NEW]['content']
],
[
Content::EMAIL_SUBSCRIPTION,
$default[Content::EMAIL_SUBSCRIPTION]['topic'],
$default[Content::EMAIL_SUBSCRIPTION]['content']
],
]
)->execute();
return $this->returnSuccess(Yii::t('podium/flash', 'Default Content has been added.'));
} catch (Exception $e) {
return $this->returnError($e->getMessage(), __METHOD__,
Yii::t('podium/flash', 'Error during content adding')
);
}
}
/**
* Adds permission rules.
* @return string result message.
*/
protected function addRules()
{
try {
(new Rbac())->add($this->authManager);
return $this->returnSuccess(Yii::t('podium/flash', 'Access roles have been created.'));
} catch (Exception $e) {
return $this->returnError($e->getMessage(), __METHOD__,
Yii::t('podium/flash', 'Error during access roles creating')
);
}
}
/**
* Proceeds next installation step.
* @return array
* @since 0.2
*/
public function nextStep()
{
$currentStep = Yii::$app->session->get(self::SESSION_KEY, 0);
if ($currentStep === 0) {
Yii::$app->session->set(self::SESSION_STEPS, count($this->steps));
}
$maxStep = Yii::$app->session->get(self::SESSION_STEPS, 0);
$this->type = self::TYPE_ERROR;
$this->table = '...';
if ($currentStep >= $maxStep) {
return [
'drop' => false,
'type' => $this->type,
'result' => Yii::t('podium/flash', 'Weird... Installation should already complete...'),
'percent' => 100
];
}
if (!isset($this->steps[$currentStep])) {
return [
'drop' => false,
'type' => $this->type,
'result' => Yii::t('podium/flash', 'Installation aborted! Can not find the requested installation step.'),
'percent' => 100,
];
}
$this->table = $this->steps[$currentStep]['table'];
$result = call_user_func_array([$this, $this->steps[$currentStep]['call']], $this->steps[$currentStep]['data']);
Yii::$app->session->set(self::SESSION_KEY, ++$currentStep);
return [
'drop' => false,
'type' => $this->type,
'result' => $result,
'table' => $this->rawTable,
'percent' => $this->countPercent($currentStep, $maxStep),
];
}
/**
* Proceeds next drop step.
* @return array
* @since 0.2
*/
public function nextDrop()
{
$drops = $this->countDrops();
if (count($drops)) {
$currentStep = Yii::$app->session->get(self::SESSION_KEY, 0);
$maxStep = Yii::$app->session->get(self::SESSION_STEPS, 0);
if ($currentStep < $maxStep) {
$this->type = self::TYPE_ERROR;
$this->table = '...';
if (!isset($drops[$currentStep])) {
return [
'drop' => false,
'type' => $this->type,
'result' => Yii::t('podium/flash', 'Installation aborted! Can not find the requested drop step.'),
'percent' => 100,
];
}
$this->table = $drops[$currentStep]['table'];
$result = $this->dropTable();
if ($result === true) {
Yii::$app->session->set(self::SESSION_KEY, ++$currentStep);
return $this->nextDrop();
}
Yii::$app->session->set(self::SESSION_KEY, ++$currentStep);
return [
'drop' => true,
'type' => $this->type,
'result' => $result,
'table' => $this->rawTable,
'percent' => $this->countPercent($currentStep, $maxStep),
];
}
}
Yii::$app->session->set(self::SESSION_KEY, 0);
return $this->nextStep();
}
/**
* Returns list of drops.
* @return array
*/
protected function countDrops()
{
$steps = array_reverse($this->steps);
$drops = [];
foreach ($steps as $step) {
if (isset($step['call']) && $step['call'] === 'createTable') {
$drops[] = $step;
}
}
if (Yii::$app->session->get(self::SESSION_KEY, 0) === 0) {
Yii::$app->session->set(self::SESSION_STEPS, count($drops));
}
return $drops;
}
/**
* Installation steps.
* @since 0.2
*/
public function getSteps()
{
if ($this->_steps === null) {
$this->_steps = require(__DIR__ . '/steps/install.php');
}
return $this->_steps;
}
}