diff --git a/backend/config/main.php b/backend/config/main.php index 9b04722..d0fda70 100755 --- a/backend/config/main.php +++ b/backend/config/main.php @@ -1,4 +1,6 @@ false, 'rules' => [], ], + 'sitemap' => [ + 'class' => Sitemap::className(), + 'entities' => [ + [ + 'class' => Page::className(), + 'conditions' => [ + [ 'in_menu' => 1 ], + ], + 'url' => 'site/page', + ], + ], + ], ], 'params' => $params, ]; diff --git a/backend/controllers/SitemapController.php b/backend/controllers/SitemapController.php new file mode 100755 index 0000000..0ad1fa2 --- /dev/null +++ b/backend/controllers/SitemapController.php @@ -0,0 +1,247 @@ + [ + 'class' => AccessControl::className(), + 'rules' => [ + [ + 'actions' => [ + 'login', + 'error', + ], + 'allow' => true, + ], + [ + 'allow' => true, + 'roles' => [ '@' ], + ], + ], + ], + ]; + } + + /** + * Action to configure sitemap of the website + * + * @return string + */ + public function actionIndex() + { + return $this->render('index'); + } + + /** + * Action to configure sitemap of the website + * + * @return string + */ + public function actionUpdate() + { + /** + * @var Sitemap $sitemap + */ + $request = \Yii::$app->request; + // ***** Generate SitemapDynamic models for every entity in Sitemap component + $sitemap = \Yii::$app->get('sitemap'); + $entities = $sitemap->entities; + /** + * @var SitemapDynamic[] $entity_models + */ + $entity_models = []; + foreach ($entities as $entity) { + $entity_model = new SitemapDynamic(); + $entity_model->entity = $entity[ 'class' ]; + $entity_model->status = SitemapDynamic::STATUS_DISABLED; + $entity_models[] = $entity_model; + } + // ***** <<< End + if ($request->isPost) { + $success = false; + // ***** Create SitemapStatic models from POST and delete existing + $models = []; + $index = 1; + foreach ($request->post('SitemapStatic') as $item) { + $model = new SitemapStatic(); + if ($model->load($item, '') && $model->validate()) { + $model->id = $index++; + $models[] = $model; + } + } + if (!empty( $models )) { + $old = SitemapStatic::find() + ->all(); + foreach ($old as $item) { + $item->delete(); + } + foreach ($models as $model) { + $model->save(false); + $success = true; + } + } + // ***** <<< End + // ***** Create SitemapDynamic models from POST and delete existing + + /** + * @var SitemapDynamic[] $old_entity_models + */ + $old_entity_models = SitemapDynamic::find() + ->all(); + foreach ($old_entity_models as $old_entity_model) { + $old_entity_model->delete(); + } + $index = 1; + $entity_models = []; + foreach ($request->post('SitemapDynamic') as $item) { + $entity = new SitemapDynamic(); + if ($entity->load($item, '') && $entity->validate()) { + $entity->id = $index++; + $entity->save(false); + $entity_models[] = $entity; + $success = true; + } + } + if ($success) { + if ($request->post('action', '') == 'generate') { + if ($sitemap->generateXML()) { + \Yii::$app->session->setFlash( + 'success', + \Yii::t( + 'core', + 'Sitemap generated to ' . \Yii::getAlias( + $sitemap->path . '.' + ) + ) + ); + } + } + return $this->redirect([ 'index' ]); + } + // ***** <<< End + } else { + // ***** Find existing SitemapStatic models + $models = SitemapStatic::find() + ->all(); + if (empty( $models )) { + $models = [ new SitemapStatic() ]; + } + // ***** <<< End + // ***** Fill SitemapDynamic models from Sitemap component with existing models + /** + * @var SitemapDynamic[] $old_entity_models + */ + $old_entity_models = SitemapDynamic::find() + ->indexBy('entity') + ->all(); + foreach ($entity_models as $index => $entity_model) { + if (isset( $old_entity_models[ $entity_model->entity ] )) { + $entity_model->status = $old_entity_models[ $entity_model->entity ]->status; + $entity_model->priority = $old_entity_models[ $entity_model->entity ]->priority; + } + } + // ***** <<< End + } + return $this->render( + 'update', + [ + 'models' => $models, + 'entity_models' => $entity_models, + ] + ); + } + + /** + * Create activeField for static sitemap + * + * @return string + */ + public function actionCreateStatic() + { + $content = ''; + $request = \Yii::$app->request; + $formId = $request->get('formId'); + $count = $request->get('count'); + if (empty( $formId ) || empty( $count )) { + return $this->renderContent($content); + } + $model = new SitemapStatic(); + $form = new ActiveForm(); + $content .= $form->field( + $model, + "[$count]url", + [ + 'options' => [ + 'class' => 'form-group col-xs-8 col-sm-9', + ], + ] + ) + ->textInput() + ->render(); + $content .= $form->field( + $model, + "[$count]priority", + [ + 'options' => [ + 'class' => 'form-group col-xs-3 col-sm-2', + ], + ] + ) + ->textInput() + ->render(); + $content .= Html::icon( + 'minus', + [ + 'class' => 'col-xs-1 field-group-remove', + 'onclick' => 'sitemap_remove(this)', + ] + ); + foreach ($form->attributes as $index => $attribute) { + $content .= Html::script("$('#w0').yiiActiveForm('add', " . Json::htmlEncode($attribute) . ");"); + } + $content = Html::tag( + 'div', + $content, + [ + 'class' => 'row field-group', + ] + ); + $this->layout = false; + return $this->renderContent($content); + } + + /** + * Generate sitemap XML to Sitemap::$path + * + * @return bool + */ + public function actionGenerate() + { + $response = \Yii::$app->response; + $response->format = $response::FORMAT_JSON; + /** + * @var Sitemap $sitemap + */ + $sitemap = \Yii::$app->get('sitemap'); + return $sitemap->generateXML(); + } + } + \ No newline at end of file diff --git a/backend/views/layouts/main.php b/backend/views/layouts/main.php index 23ac713..f22cbb6 100755 --- a/backend/views/layouts/main.php +++ b/backend/views/layouts/main.php @@ -172,7 +172,7 @@ ], [ 'label' => \Yii::t('core', 'Sitemap'), - 'url' => [ 'seo/sitemap' ], + 'url' => [ '/sitemap/index' ], 'icon' => 'map-signs', ], ], diff --git a/backend/views/sitemap/index.php b/backend/views/sitemap/index.php new file mode 100644 index 0000000..4ec692a --- /dev/null +++ b/backend/views/sitemap/index.php @@ -0,0 +1,74 @@ +title = \Yii::t('core', 'Sitemap'); + $this->params[ 'breadcrumbs' ][] = $this->title; +?> +
+ false, + 'title' => $this->title, + 'options' => [ + 'class' => 'sitemap-buttons', + ], + ] + ); + ?> +
+
+ 'fa fa-', + ] + ) . \Yii::t('core', 'Edit'), + [ 'update' ], + [ + 'class' => 'btn btn-app', + ] + ); + ?> +
+
+ 'fa fa-', + 'class' => 'indexed', + ] + ) . Html::tag( + 'span', + \Yii::t('core', 'Generate'), + [ + 'class' => 'indexed', + ] + ) . Html::tag( + 'div', + ' ', + [ + 'class' => 'spinner', + ] + ), + [ 'generate' ], + [ + 'class' => 'btn btn-app ajax', + ] + ); + ?> +
+
+ +
diff --git a/backend/views/sitemap/update.php b/backend/views/sitemap/update.php new file mode 100644 index 0000000..9a386bf --- /dev/null +++ b/backend/views/sitemap/update.php @@ -0,0 +1,179 @@ +params[ 'breadcrumbs' ][] = [ + 'label' => \Yii::t('core', 'Sitemap'), + 'url' => [ 'index' ], + ]; + $this->title = \Yii::t('core', 'Update sitemap'); + $this->params[ 'breadcrumbs' ][] = $this->title; + $form = ActiveForm::begin(); + $xPanel = XPanel::begin( + [ + 'title' => \Yii::t('core', 'Static pages'), + 'toolbarLayout' => '{collapse}', + 'options' => [ + 'class' => 'dynamic_fields', + ], + ] + ); + foreach ($models as $index => $model) { + echo Html::tag( + 'div', + $form->field( + $model, + "[$index]url", + [ + 'options' => [ + 'class' => 'form-group col-xs-8 col-sm-9', + ], + ] + ) + ->textInput() . $form->field( + $model, + "[$index]priority", + [ + 'options' => [ + 'class' => 'form-group col-xs-3 col-sm-2', + ], + ] + ) + ->textInput() . Html::icon( + 'minus', + [ + 'class' => 'col-xs-1 field-group-remove', + 'onclick' => 'sitemap_remove(this)', + ] + ), + [ + 'class' => 'row field-group', + ] + ); + } + echo Html::button( + \Yii::t('core', 'Add field'), + [ + 'class' => 'btn btn-default', + 'onclick' => 'sitemap_add(this)', + 'data' => [ + 'url' => Url::to([ 'create-static' ]), + ], + ] + ); + $xPanel::end(); + $xPanel2 = XPanel::begin( + [ + 'title' => \Yii::t('core', 'Dynamic pages'), + 'toolbarLayout' => '{collapse}', + ] + ); +?> +
+
getAttributeLabel('priority'); ?>
+
+ $entity_model) { + ?> +
+ 'form-control', + 'readonly' => 1, + ] + ) . Html::tag( + 'span', + Html::activeCheckbox( + $entity_model, + "[$index]status", + [ + 'label' => false, + ] + ), + [ + 'class' => 'input-group-addon', + ] + ), + [ + 'class' => 'input-group', + ] + ); + ?> +
+
+ field($entity_model, "[$index]priority") + ->label(false) + ->input( + 'number', + [ + 'step' => 0.1, + 'min' => 0, + 'max' => 1, + ] + ); + ?> +
+ 'btn btn-success', + ] + ); + echo Html::submitButton( + \Yii::t('core', 'Save and generate'), + [ + 'class' => 'btn btn-primary', + 'name' => 'action', + 'value' => 'generate', + ] + ); + $form::end(); + $js = << 1) { + $(e) + .parents('.field-group') + .remove(); + } + } +JS; + $this->registerJs($js, $this::POS_END); \ No newline at end of file diff --git a/common/components/Sitemap.php b/common/components/Sitemap.php new file mode 100644 index 0000000..c4bfd25 --- /dev/null +++ b/common/components/Sitemap.php @@ -0,0 +1,122 @@ +saveXML($this->generateOneShot()); + } + + /** + * Save generated xml to $path file + * + * @param string $xml + * + * @return bool + */ + protected function saveXML(string $xml): bool + { + $realpath = \Yii::getAlias($this->path); + if (file_put_contents($realpath, $xml)) { + return true; + } else { + return false; + } + } + + /** + * Generate xml from configs + * + * @return string + */ + public function generateOneShot(): string + { + $content = ''; + $content .= ''; + /** + * @var SitemapStatic[] $static + */ + // ***** Begin generating static pages + $static = SitemapStatic::find() + ->all(); + foreach ($static as $item) { + $content .= Html::tag( + 'url', + Html::tag('loc', $item->url) . Html::tag('lastmod', date('Y-m-d')) . Html::tag( + 'changefreq', + 'monthly' + ) . Html::tag('priority', $item->priority) + ); + } + // ***** <<< End + /** + * @var SitemapDynamic $dynamic + */ + $dynamic = SitemapDynamic::find() + ->indexBy('entity') + ->where([ 'status' => 1 ]) + ->all(); + $entities = $this->entities; + foreach ($entities as $entity) { + /** + * @var string $class + */ + $class = $entity[ 'class' ]; + /** + * @var ActiveRecord $classInstance + */ + $classInstance = new $class(); + if (is_subclass_of($classInstance, ActiveRecord::className())) { + if (!empty( $dynamic[ $class ] )) { + /** + * @var SitemapDynamic $model + */ + $model = $dynamic[ $class ]; + $query = $classInstance::find(); + if (isset( $entity[ 'conditions' ] )) { + foreach ($entity[ 'conditions' ] as $condition) { + $query->where($condition); + } + } + $result = $query->all(); + foreach ($result as $record) { + $content .= Html::tag( + 'url', + Html::tag( + 'loc', + Url::to( + [ + $entity[ 'url' ], + 'id' => $record->getAttribute('id'), + ], + true + ) + ) . Html::tag('lastmod', date('Y-m-d')) . Html::tag( + 'changefreq', + 'monthly' + ) . Html::tag('priority', $model->priority) + ); + } + } + } + } + $content .= ''; + return $content; + } + } \ No newline at end of file diff --git a/common/config/SitemapDynamic.php b/common/config/SitemapDynamic.php new file mode 100644 index 0000000..b90b5a0 --- /dev/null +++ b/common/config/SitemapDynamic.php @@ -0,0 +1,10 @@ + [ + 'entity' => 'artbox\\core\\models\\Page', + 'status' => '1', + 'priority' => '0.6', + 'id' => 1, + ], + ]; \ No newline at end of file diff --git a/common/config/SitemapStatic.php b/common/config/SitemapStatic.php new file mode 100644 index 0000000..17cea76 --- /dev/null +++ b/common/config/SitemapStatic.php @@ -0,0 +1,9 @@ + [ + 'url' => 'http://www.artbox.dev/', + 'priority' => '1', + 'id' => 1, + ], + ]; \ No newline at end of file diff --git a/common/config/main.php b/common/config/main.php index ad48c82..c27e905 100644 --- a/common/config/main.php +++ b/common/config/main.php @@ -4,10 +4,10 @@ return [ 'vendorPath' => dirname(dirname(__DIR__)) . '/vendor', 'components' => [ - 'cache' => [ + 'cache' => [ 'class' => 'yii\caching\FileCache', ], - 'i18n' => [ + 'i18n' => [ 'translations' => [ 'core' => [ 'class' => 'yii\i18n\PhpMessageSource', @@ -15,11 +15,16 @@ ], ], ], - 'filedb' => [ + 'filedb' => [ 'class' => 'yii2tech\filedb\Connection', 'path' => '@common/config', ], - 'seo' => [ + 'sitemapdb' => [ + 'class' => 'yii2tech\filedb\Connection', + 'path' => '@common/config', + 'primaryKeyName' => 'id', + ], + 'seo' => [ 'class' => SeoComponent::className(), ], ], diff --git a/common/models/SitemapDynamic.php b/common/models/SitemapDynamic.php new file mode 100644 index 0000000..2ecb05b --- /dev/null +++ b/common/models/SitemapDynamic.php @@ -0,0 +1,115 @@ +get('sitemapdb'); + } + + /** + * @inheritdoc + */ + public function attributes() + { + return [ + 'id', + 'entity', + 'status', + 'priority', + ]; + } + + /** + * @inheritdoc + */ + public static function primaryKey() + { + return [ 'id' ]; + } + + /** + * @inheritdoc + */ + public function rules() + { + return [ + [ + [ + 'entity', + 'status', + 'priority', + ], + 'required', + ], + [ + [ + 'status', + ], + 'boolean', + ], + [ + [ + 'entity', + ], + 'string', + ], + [ + [ + 'priority', + ], + 'double', + 'min' => 0, + 'max' => 1, + ], + ]; + } + + /** + * @inheritdoc + */ + public function attributeLabels() + { + return [ + 'id' => Yii::t('core', 'ID'), + 'entity' => Yii::t('core', 'Model'), + 'status' => Yii::t('core', 'Status'), + 'priority' => Yii::t('core', 'Priority'), + ]; + } + + /** + * Find maximum ID value from SitemapStatic models + */ + public static function max(): int + { + $models = self::find() + ->all(); + $array = ArrayHelper::getColumn($models, self::primaryKey()[ 0 ], false); + if (empty( $array )) { + return 0; + } else { + return max($array); + } + } + } \ No newline at end of file diff --git a/common/models/SitemapStatic.php b/common/models/SitemapStatic.php new file mode 100644 index 0000000..16af9a3 --- /dev/null +++ b/common/models/SitemapStatic.php @@ -0,0 +1,101 @@ +get('sitemapdb'); + } + + /** + * @inheritdoc + */ + public function attributes() + { + return [ + 'id', + 'url', + 'priority', + ]; + } + + /** + * @inheritdoc + */ + public static function primaryKey() + { + return [ 'id' ]; + } + + /** + * @inheritdoc + */ + public function rules() + { + return [ + [ + [ + 'url', + 'priority', + ], + 'required', + ], + [ + [ + 'priority', + ], + 'double', + 'min' => 0, + 'max' => 1, + ], + [ + [ + 'url', + ], + 'string', + ], + ]; + } + + /** + * @inheritdoc + */ + public function attributeLabels() + { + return [ + 'id' => Yii::t('core', 'ID'), + 'url' => Yii::t('core', 'Url'), + 'priority' => Yii::t('core', 'Priority'), + ]; + } + + /** + * Find maximum ID value from SitemapStatic models + */ + public static function max(): int + { + $models = self::find() + ->all(); + $array = ArrayHelper::getColumn($models, self::primaryKey()[ 0 ], false); + if (empty( $array )) { + return 0; + } else { + return max($array); + } + } + } \ No newline at end of file -- libgit2 0.21.4