chore: sync module from working instance and add install guide
This commit is contained in:
55
INSTALL.md
Normal file
55
INSTALL.md
Normal file
@@ -0,0 +1,55 @@
|
||||
# Space Profiles Installation Guide
|
||||
|
||||
This guide installs the `space_profiles` module in a reusable way for any HumHub instance.
|
||||
|
||||
## 1. Requirements
|
||||
|
||||
- HumHub `1.14+`
|
||||
- Module directory access on the target instance
|
||||
- Optional but recommended: `rescue_foundation` module
|
||||
|
||||
## 2. Clone into HumHub Modules Directory
|
||||
|
||||
The folder name must be exactly `space_profiles`.
|
||||
|
||||
```bash
|
||||
git clone https://gitea.kelinreij.duckdns.org/humhub-modules/space-profiles.git \
|
||||
/var/www/localhost/htdocs/protected/modules/space_profiles
|
||||
```
|
||||
|
||||
If the folder already exists:
|
||||
|
||||
```bash
|
||||
cd /var/www/localhost/htdocs/protected/modules/space_profiles
|
||||
git pull
|
||||
```
|
||||
|
||||
## 3. Enable the Module
|
||||
|
||||
In HumHub UI:
|
||||
|
||||
1. Go to `Administration` -> `Modules`.
|
||||
2. Enable `Space Profiles`.
|
||||
3. Enable it per space where needed.
|
||||
|
||||
## 4. Run Migrations
|
||||
|
||||
From the HumHub app host/container:
|
||||
|
||||
```bash
|
||||
php /var/www/localhost/htdocs/protected/yii migrate/up \
|
||||
--include-module-migrations=1 --interactive=0
|
||||
```
|
||||
|
||||
## 5. Verify
|
||||
|
||||
1. Open a space with module enabled.
|
||||
2. Confirm profile page renders the template sections.
|
||||
3. Confirm space settings page saves expected profile fields.
|
||||
|
||||
## Docker Example
|
||||
|
||||
```bash
|
||||
docker exec humhub php /var/www/localhost/htdocs/protected/yii migrate/up \
|
||||
--include-module-migrations=1 --interactive=0
|
||||
```
|
||||
@@ -7,8 +7,10 @@ use humhub\modules\content\components\ContentContainerControllerAccess;
|
||||
use humhub\modules\rescue_foundation\widgets\RescueSettingsMenu;
|
||||
use humhub\modules\space\models\Space;
|
||||
use humhub\modules\space_profiles\models\forms\SpaceProfileForm;
|
||||
use humhub\modules\space_profiles\services\ModuleSetupService;
|
||||
use Yii;
|
||||
use yii\web\UploadedFile;
|
||||
use yii\web\BadRequestHttpException;
|
||||
|
||||
class SettingsController extends ContentContainerController
|
||||
{
|
||||
@@ -41,4 +43,34 @@ class SettingsController extends ContentContainerController
|
||||
'subNav' => $subNav,
|
||||
]);
|
||||
}
|
||||
|
||||
public function actionSetup()
|
||||
{
|
||||
if (!Yii::$app->request->isPost) {
|
||||
throw new BadRequestHttpException('Invalid request method.');
|
||||
}
|
||||
|
||||
if (!$this->contentContainer instanceof Space) {
|
||||
$this->view->error(Yii::t('SpaceProfilesModule.base', 'Setup can only be run inside a space.'));
|
||||
return $this->redirect($this->contentContainer->createUrl('/space_profiles/settings'));
|
||||
}
|
||||
|
||||
try {
|
||||
$result = ModuleSetupService::runForSpace($this->contentContainer);
|
||||
$appliedCount = count($result['applied'] ?? []);
|
||||
|
||||
if ($appliedCount > 0) {
|
||||
$this->view->success(Yii::t('SpaceProfilesModule.base', 'Setup completed. Applied {count} migration(s).', [
|
||||
'count' => $appliedCount,
|
||||
]));
|
||||
} else {
|
||||
$this->view->success(Yii::t('SpaceProfilesModule.base', 'Setup completed. No pending migrations were found.'));
|
||||
}
|
||||
} catch (\Throwable $e) {
|
||||
Yii::error($e, 'space_profiles.setup');
|
||||
$this->view->error(Yii::t('SpaceProfilesModule.base', 'Setup failed. Please check logs and try again.'));
|
||||
}
|
||||
|
||||
return $this->redirect($this->contentContainer->createUrl('/space_profiles/settings'));
|
||||
}
|
||||
}
|
||||
|
||||
70
services/ModuleSetupService.php
Normal file
70
services/ModuleSetupService.php
Normal file
@@ -0,0 +1,70 @@
|
||||
<?php
|
||||
|
||||
namespace humhub\modules\space_profiles\services;
|
||||
|
||||
use humhub\modules\space\models\Space;
|
||||
use Yii;
|
||||
|
||||
class ModuleSetupService
|
||||
{
|
||||
public static function runForSpace(Space $space): array
|
||||
{
|
||||
$result = static::applyModuleMigrations();
|
||||
|
||||
$defaultUrl = $space->createUrl('/space_profiles/profile/view');
|
||||
$settings = $space->getSettings();
|
||||
$settings->set('indexUrl', $defaultUrl);
|
||||
$settings->set('indexGuestUrl', $defaultUrl);
|
||||
|
||||
$result['defaultHomeUrl'] = $defaultUrl;
|
||||
return $result;
|
||||
}
|
||||
|
||||
private static function applyModuleMigrations(): array
|
||||
{
|
||||
$migrationDir = dirname(__DIR__) . '/migrations';
|
||||
$files = glob($migrationDir . '/m*.php') ?: [];
|
||||
sort($files, SORT_NATURAL);
|
||||
|
||||
$existingVersions = Yii::$app->db->createCommand('SELECT version FROM migration')->queryColumn();
|
||||
$history = array_fill_keys($existingVersions, true);
|
||||
|
||||
$applied = [];
|
||||
$skipped = [];
|
||||
|
||||
foreach ($files as $file) {
|
||||
$version = pathinfo($file, PATHINFO_FILENAME);
|
||||
if (isset($history[$version])) {
|
||||
$skipped[] = $version;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!class_exists($version, false)) {
|
||||
require_once $file;
|
||||
}
|
||||
|
||||
if (!class_exists($version, false)) {
|
||||
throw new \RuntimeException('Migration class not found: ' . $version);
|
||||
}
|
||||
|
||||
$migration = new $version();
|
||||
$ok = method_exists($migration, 'safeUp') ? $migration->safeUp() : $migration->up();
|
||||
if ($ok === false) {
|
||||
throw new \RuntimeException('Migration failed: ' . $version);
|
||||
}
|
||||
|
||||
Yii::$app->db->createCommand()->insert('migration', [
|
||||
'version' => $version,
|
||||
'apply_time' => time(),
|
||||
])->execute();
|
||||
|
||||
$applied[] = $version;
|
||||
$history[$version] = true;
|
||||
}
|
||||
|
||||
return [
|
||||
'applied' => $applied,
|
||||
'skipped' => $skipped,
|
||||
];
|
||||
}
|
||||
}
|
||||
52
views/profile/_rescue-info.php
Normal file
52
views/profile/_rescue-info.php
Normal file
@@ -0,0 +1,52 @@
|
||||
<?php
|
||||
|
||||
use humhub\modules\space\models\Space;
|
||||
use humhub\modules\space_profiles\models\SpaceProfile;
|
||||
use yii\helpers\Html;
|
||||
|
||||
/* @var Space $space */
|
||||
/* @var SpaceProfile|null $profile */
|
||||
/* @var string $spaceDescription */
|
||||
?>
|
||||
|
||||
<div class="panel panel-default" style="margin-bottom:16px;">
|
||||
<div class="panel-body">
|
||||
<?php if (!$profile): ?>
|
||||
<div class="text-muted">
|
||||
<?= Yii::t('SpaceProfilesModule.base', 'Contact details will appear after the profile is published.') ?>
|
||||
</div>
|
||||
<?php else: ?>
|
||||
<div style="font-weight:700;font-size:18px;text-align:center;margin-bottom:12px;">
|
||||
<?= Html::encode($space->name) ?>
|
||||
</div>
|
||||
<div><?= Html::encode($profile->address) ?></div>
|
||||
<div style="margin-bottom:10px;"><?= Html::encode($profile->city) ?>, <?= Html::encode($profile->state) ?> <?= Html::encode($profile->zip) ?></div>
|
||||
<div style="margin-bottom:6px;">
|
||||
<strong><?= Yii::t('SpaceProfilesModule.base', 'Email') ?>:</strong>
|
||||
<a href="mailto:<?= Html::encode($profile->email) ?>"><?= Html::encode($profile->email) ?></a>
|
||||
</div>
|
||||
<div>
|
||||
<strong><?= Yii::t('SpaceProfilesModule.base', 'Phone') ?>:</strong>
|
||||
<a href="tel:<?= Html::encode(preg_replace('/[^0-9+]/', '', (string)$profile->phone) ?? '') ?>"><?= Html::encode($profile->phone) ?></a>
|
||||
</div>
|
||||
|
||||
<?php if ($spaceDescription !== '' || trim((string)$profile->mission_statement) !== '' || trim((string)$profile->animals_we_accept) !== ''): ?>
|
||||
<hr style="margin:12px 0;">
|
||||
<?php endif; ?>
|
||||
|
||||
<?php if ($spaceDescription !== ''): ?>
|
||||
<div style="margin-bottom:10px;"><?= nl2br(Html::encode($spaceDescription)) ?></div>
|
||||
<?php endif; ?>
|
||||
|
||||
<?php if (trim((string)$profile->mission_statement) !== ''): ?>
|
||||
<div style="margin-bottom:4px;"><strong><?= Yii::t('SpaceProfilesModule.base', 'Mission Statement') ?>:</strong></div>
|
||||
<div style="margin-bottom:10px;"><?= nl2br(Html::encode($profile->mission_statement)) ?></div>
|
||||
<?php endif; ?>
|
||||
|
||||
<?php if (trim((string)$profile->animals_we_accept) !== ''): ?>
|
||||
<div style="margin-bottom:4px;"><strong><?= Yii::t('SpaceProfilesModule.base', 'Animals We Accept') ?>:</strong></div>
|
||||
<div><?= nl2br(Html::encode($profile->animals_we_accept)) ?></div>
|
||||
<?php endif; ?>
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
</div>
|
||||
@@ -4,7 +4,6 @@ use yii\helpers\Html;
|
||||
|
||||
/* @var \humhub\modules\space\models\Space $space */
|
||||
/* @var \humhub\modules\space_profiles\models\SpaceProfile $profile */
|
||||
/* @var string $searchBlockWidget */
|
||||
?>
|
||||
|
||||
<div class="media" style="margin-bottom:20px;">
|
||||
@@ -30,16 +29,6 @@ use yii\helpers\Html;
|
||||
<section style="margin-bottom:20px;"><?= $profile->body_html ?></section>
|
||||
<?php endif; ?>
|
||||
|
||||
<section style="margin-bottom:20px;">
|
||||
<?php if (class_exists($searchBlockWidget)): ?>
|
||||
<?= $searchBlockWidget::widget(['contentContainer' => $space]) ?>
|
||||
<?php else: ?>
|
||||
<div class="well well-sm" style="margin-bottom:0;">
|
||||
<?= Yii::t('SpaceProfilesModule.base', 'Animal Management plugin integration point is ready. The block will appear here when that plugin is installed.') ?>
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
</section>
|
||||
|
||||
<?php if (!empty($profile->footer_html)): ?>
|
||||
<section><?= $profile->footer_html ?></section>
|
||||
<?php endif; ?>
|
||||
|
||||
@@ -1,8 +1,11 @@
|
||||
<?php
|
||||
|
||||
use humhub\modules\space\models\Space;
|
||||
use humhub\modules\space\widgets\Sidebar as SpaceSidebar;
|
||||
use humhub\modules\space_profiles\components\TemplateRegistry;
|
||||
use humhub\modules\space_profiles\models\SpaceProfile;
|
||||
use humhub\modules\space_profiles\widgets\RescueInfoSidebar;
|
||||
use yii\web\View;
|
||||
use yii\helpers\Html;
|
||||
|
||||
/* @var Space $space */
|
||||
@@ -16,14 +19,6 @@ if ($profile && !empty($profile->background_image_path)) {
|
||||
|
||||
$searchBlockWidget = '\\humhub\\modules\\animal_management\\widgets\\SearchAnimalProfilesBlock';
|
||||
$templateView = TemplateRegistry::resolveView($profile ? $profile->template_key : null);
|
||||
$spaceDescription = trim((string)$space->about);
|
||||
if ($spaceDescription === '') {
|
||||
$spaceDescription = trim((string)$space->description);
|
||||
}
|
||||
|
||||
if ($spaceDescription === '') {
|
||||
$spaceDescription = trim((string)($profile?->description ?? ''));
|
||||
}
|
||||
?>
|
||||
|
||||
<div class="panel panel-default rescue-profile-scope" style="<?= $backgroundStyle ?>">
|
||||
@@ -42,52 +37,58 @@ if ($spaceDescription === '') {
|
||||
<?= $this->render($templateView, [
|
||||
'space' => $space,
|
||||
'profile' => $profile,
|
||||
'searchBlockWidget' => $searchBlockWidget,
|
||||
]) ?>
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<?php $this->beginBlock('sidebar'); ?>
|
||||
<div class="panel panel-default">
|
||||
<div class="panel-body">
|
||||
<?php if (!$profile): ?>
|
||||
<div class="text-muted">
|
||||
<?= Yii::t('SpaceProfilesModule.base', 'Contact details will appear after the profile is published.') ?>
|
||||
</div>
|
||||
<?php else: ?>
|
||||
<div style="font-weight:700;font-size:18px;text-align:center;margin-bottom:12px;">
|
||||
<?= Html::encode($space->name) ?>
|
||||
</div>
|
||||
<div><?= Html::encode($profile->address) ?></div>
|
||||
<div style="margin-bottom:10px;"><?= Html::encode($profile->city) ?>, <?= Html::encode($profile->state) ?> <?= Html::encode($profile->zip) ?></div>
|
||||
<div style="margin-bottom:6px;">
|
||||
<strong><?= Yii::t('SpaceProfilesModule.base', 'Email') ?>:</strong>
|
||||
<a href="mailto:<?= Html::encode($profile->email) ?>"><?= Html::encode($profile->email) ?></a>
|
||||
</div>
|
||||
<div>
|
||||
<strong><?= Yii::t('SpaceProfilesModule.base', 'Phone') ?>:</strong>
|
||||
<a href="tel:<?= Html::encode(preg_replace('/[^0-9+]/', '', (string)$profile->phone) ?? '') ?>"><?= Html::encode($profile->phone) ?></a>
|
||||
</div>
|
||||
<?php
|
||||
$sidebarWidgets = [];
|
||||
|
||||
<?php if ($spaceDescription !== '' || trim((string)$profile->mission_statement) !== '' || trim((string)$profile->animals_we_accept) !== ''): ?>
|
||||
<hr style="margin:12px 0;">
|
||||
<?php endif; ?>
|
||||
if ($profile && class_exists($searchBlockWidget)) {
|
||||
$sidebarWidgets[] = [$searchBlockWidget, ['contentContainer' => $space], ['sortOrder' => 20]];
|
||||
}
|
||||
|
||||
<?php if ($spaceDescription !== ''): ?>
|
||||
<div style="margin-bottom:10px;"><?= nl2br(Html::encode($spaceDescription)) ?></div>
|
||||
<?php endif; ?>
|
||||
|
||||
<?php if (trim((string)$profile->mission_statement) !== ''): ?>
|
||||
<div style="margin-bottom:4px;"><strong><?= Yii::t('SpaceProfilesModule.base', 'Mission Statement') ?>:</strong></div>
|
||||
<div style="margin-bottom:10px;"><?= nl2br(Html::encode($profile->mission_statement)) ?></div>
|
||||
<?php endif; ?>
|
||||
|
||||
<?php if (trim((string)$profile->animals_we_accept) !== ''): ?>
|
||||
<div style="margin-bottom:4px;"><strong><?= Yii::t('SpaceProfilesModule.base', 'Animals We Accept') ?>:</strong></div>
|
||||
<div><?= nl2br(Html::encode($profile->animals_we_accept)) ?></div>
|
||||
<?php endif; ?>
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
</div>
|
||||
echo SpaceSidebar::widget([
|
||||
'space' => $space,
|
||||
'widgets' => $sidebarWidgets,
|
||||
]);
|
||||
?>
|
||||
<?php $this->endBlock(); ?>
|
||||
|
||||
<?php if ($profile): ?>
|
||||
<div id="space-profile-left-rescue-info" style="display:none;">
|
||||
<?= RescueInfoSidebar::widget(['space' => $space]) ?>
|
||||
</div>
|
||||
<?php
|
||||
$this->registerJs(<<<JS
|
||||
(function() {
|
||||
var panel = document.getElementById('space-profile-left-rescue-info');
|
||||
if (!panel) {
|
||||
return;
|
||||
}
|
||||
|
||||
var navContainer = document.querySelector('.layout-nav-container');
|
||||
if (!navContainer) {
|
||||
return;
|
||||
}
|
||||
|
||||
var menu = navContainer.querySelector('#space-main-menu');
|
||||
panel.style.display = 'block';
|
||||
panel.style.marginTop = '12px';
|
||||
|
||||
if (menu && menu.parentNode === navContainer) {
|
||||
if (menu.nextSibling) {
|
||||
navContainer.insertBefore(panel, menu.nextSibling);
|
||||
} else {
|
||||
navContainer.appendChild(panel);
|
||||
}
|
||||
} else {
|
||||
navContainer.appendChild(panel);
|
||||
}
|
||||
})();
|
||||
JS
|
||||
, View::POS_END);
|
||||
?>
|
||||
<?php endif; ?>
|
||||
|
||||
@@ -20,6 +20,25 @@ $profile = $model->getProfile();
|
||||
<?php endif; ?>
|
||||
|
||||
<div class="panel-body">
|
||||
<div class="well well-sm" style="margin-bottom:14px;">
|
||||
<div style="font-weight:700;margin-bottom:6px;"><?= Yii::t('SpaceProfilesModule.base', 'Module Setup') ?></div>
|
||||
<div style="margin-bottom:8px;">
|
||||
<?= Yii::t('SpaceProfilesModule.base', 'Space Profiles publishes a branded rescue profile page for this space and can set it as the default space home page.') ?>
|
||||
</div>
|
||||
<div style="margin-bottom:10px;">
|
||||
<?= Yii::t('SpaceProfilesModule.base', 'Run setup to apply any pending Space Profiles migrations and configure this space home route automatically.') ?>
|
||||
</div>
|
||||
<?= Html::a(
|
||||
Yii::t('SpaceProfilesModule.base', 'Run Space Profiles Setup'),
|
||||
$model->contentContainer->createUrl('/space_profiles/settings/setup'),
|
||||
[
|
||||
'class' => 'btn btn-primary btn-sm',
|
||||
'data-method' => 'post',
|
||||
'data-confirm' => Yii::t('SpaceProfilesModule.base', 'Run Space Profiles setup now for this space?'),
|
||||
]
|
||||
) ?>
|
||||
</div>
|
||||
|
||||
<div class="help-block">
|
||||
<?= Yii::t('SpaceProfilesModule.base', 'Configure your rescue profile content, optional HTML regions, and branding assets.') ?>
|
||||
</div>
|
||||
|
||||
31
widgets/RescueInfoSidebar.php
Normal file
31
widgets/RescueInfoSidebar.php
Normal file
@@ -0,0 +1,31 @@
|
||||
<?php
|
||||
|
||||
namespace humhub\modules\space_profiles\widgets;
|
||||
|
||||
use humhub\components\Widget;
|
||||
use humhub\modules\space\models\Space;
|
||||
use humhub\modules\space_profiles\models\SpaceProfile;
|
||||
|
||||
class RescueInfoSidebar extends Widget
|
||||
{
|
||||
public Space $space;
|
||||
|
||||
public function run()
|
||||
{
|
||||
$profile = SpaceProfile::findOne(['contentcontainer_id' => $this->space->contentcontainer_id]);
|
||||
|
||||
$spaceDescription = trim((string)$this->space->about);
|
||||
if ($spaceDescription === '') {
|
||||
$spaceDescription = trim((string)$this->space->description);
|
||||
}
|
||||
if ($spaceDescription === '') {
|
||||
$spaceDescription = trim((string)($profile?->description ?? ''));
|
||||
}
|
||||
|
||||
return $this->render('rescueInfoSidebar', [
|
||||
'space' => $this->space,
|
||||
'profile' => $profile,
|
||||
'spaceDescription' => $spaceDescription,
|
||||
]);
|
||||
}
|
||||
}
|
||||
52
widgets/views/rescueInfoSidebar.php
Normal file
52
widgets/views/rescueInfoSidebar.php
Normal file
@@ -0,0 +1,52 @@
|
||||
<?php
|
||||
|
||||
use humhub\modules\space\models\Space;
|
||||
use humhub\modules\space_profiles\models\SpaceProfile;
|
||||
use yii\helpers\Html;
|
||||
|
||||
/* @var Space $space */
|
||||
/* @var SpaceProfile|null $profile */
|
||||
/* @var string $spaceDescription */
|
||||
?>
|
||||
|
||||
<div class="panel panel-default" style="margin-bottom:16px;">
|
||||
<div class="panel-body">
|
||||
<?php if (!$profile): ?>
|
||||
<div class="text-muted">
|
||||
<?= Yii::t('SpaceProfilesModule.base', 'Contact details will appear after the profile is published.') ?>
|
||||
</div>
|
||||
<?php else: ?>
|
||||
<div style="font-weight:700;font-size:18px;text-align:center;margin-bottom:12px;">
|
||||
<?= Html::encode($space->name) ?>
|
||||
</div>
|
||||
<div><?= Html::encode($profile->address) ?></div>
|
||||
<div style="margin-bottom:10px;"><?= Html::encode($profile->city) ?>, <?= Html::encode($profile->state) ?> <?= Html::encode($profile->zip) ?></div>
|
||||
<div style="margin-bottom:6px;">
|
||||
<strong><?= Yii::t('SpaceProfilesModule.base', 'Email') ?>:</strong>
|
||||
<a href="mailto:<?= Html::encode($profile->email) ?>"><?= Html::encode($profile->email) ?></a>
|
||||
</div>
|
||||
<div>
|
||||
<strong><?= Yii::t('SpaceProfilesModule.base', 'Phone') ?>:</strong>
|
||||
<a href="tel:<?= Html::encode(preg_replace('/[^0-9+]/', '', (string)$profile->phone) ?? '') ?>"><?= Html::encode($profile->phone) ?></a>
|
||||
</div>
|
||||
|
||||
<?php if ($spaceDescription !== '' || trim((string)$profile->mission_statement) !== '' || trim((string)$profile->animals_we_accept) !== ''): ?>
|
||||
<hr style="margin:12px 0;">
|
||||
<?php endif; ?>
|
||||
|
||||
<?php if ($spaceDescription !== ''): ?>
|
||||
<div style="margin-bottom:10px;"><?= nl2br(Html::encode($spaceDescription)) ?></div>
|
||||
<?php endif; ?>
|
||||
|
||||
<?php if (trim((string)$profile->mission_statement) !== ''): ?>
|
||||
<div style="margin-bottom:4px;"><strong><?= Yii::t('SpaceProfilesModule.base', 'Mission Statement') ?>:</strong></div>
|
||||
<div style="margin-bottom:10px;"><?= nl2br(Html::encode($profile->mission_statement)) ?></div>
|
||||
<?php endif; ?>
|
||||
|
||||
<?php if (trim((string)$profile->animals_we_accept) !== ''): ?>
|
||||
<div style="margin-bottom:4px;"><strong><?= Yii::t('SpaceProfilesModule.base', 'Animals We Accept') ?>:</strong></div>
|
||||
<div><?= nl2br(Html::encode($profile->animals_we_accept)) ?></div>
|
||||
<?php endif; ?>
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
</div>
|
||||
Reference in New Issue
Block a user