- = $form->field($model, 'recap_id')->dropDownList(
- RecapQuery::allFromCurrentEpicForSelector(),
- ['prompt' => ' --- ' . Yii::t('app', 'RECAP_PROMPT') . ' --- ']
- ) ?>
+
diff --git a/backend/views/game/view.php b/backend/views/game/view.php
index 7d9da88e..472725fa 100644
--- a/backend/views/game/view.php
+++ b/backend/views/game/view.php
@@ -41,6 +41,8 @@
'format' => 'raw',
'value' => Html::a($model->epic->name, ['epic/view', 'key' => $model->epic->key], []),
],
+ 'planned_date',
+ 'planned_location',
[
'attribute' => 'recap_id',
'format' => 'raw',
diff --git a/backend/views/group/_view_basic.php b/backend/views/group/_view_basic.php
index 2c2505d9..9ca78943 100644
--- a/backend/views/group/_view_basic.php
+++ b/backend/views/group/_view_basic.php
@@ -37,11 +37,19 @@
'label' => Yii::t('app', 'GROUP_IMPORTANCE'),
'value' => $model->getImportanceCategory(),
],
+ [
+ 'label' => Yii::t('app', 'DESCRIPTION_COUNT_UNIQUE'),
+ 'value' => $model->descriptionPack->getUniqueDescriptionTypesCount(),
+ ],
[
'label' => Yii::t('app', 'DESCRIPTION_COUNT_EXPECTED'),
'format' => 'raw',
'value' => $model->getImportanceCategoryObject()->minimum() . ' — ' . $model->getImportanceCategoryObject()->maximum(),
],
+ [
+ 'attribute' => 'updated_at',
+ 'format' => 'datetime',
+ ],
[
'attribute' => 'master_group_id',
'format' => 'raw',
diff --git a/backend/web/js/game.js b/backend/web/js/game.js
index a61dfb31..8c6e57db 100644
--- a/backend/web/js/game.js
+++ b/backend/web/js/game.js
@@ -1,6 +1,19 @@
-$('#game-planned_date').on('change', function () {
- $('#game-basics-constructed').val($(this).val());
-});
+$('#game-basics-constructed').val($('#game-basics').val());
+
+function setBasicsConstructed() {
+ var datetime = $('#game-planned_date').val();
+ var location = $('#game-planned_location').val();
+ var separator = '';
+
+ if (datetime && location) {
+ separator = ', ';
+ }
+
+ $('#game-basics-constructed').val(location + separator + datetime);
+}
+
+$('#game-planned_date').on('change', setBasicsConstructed);
+$('#game-planned_location').on('keyup', setBasicsConstructed);
$('#game-basics-transfer').on('click', function () {
$('#game-basics').val($('#game-basics-constructed').val());
diff --git a/common/messages/en/app.php b/common/messages/en/app.php
index 05ab2771..d7d74ca6 100644
--- a/common/messages/en/app.php
+++ b/common/messages/en/app.php
@@ -318,6 +318,7 @@
'GAME_ID' => 'ID',
'GAME_NOTES' => 'Notes',
'GAME_PLANNED_DATE' => 'Planned date',
+ 'GAME_PLANNED_LOCATION' => 'Planned location',
'GAME_POSITION' => 'Position',
'GAME_SESSION_NOT_AVAILABLE' => 'Session not available',
'GAME_STATUS' => 'Status',
@@ -359,6 +360,7 @@
'GROUP_NOT_AVAILABLE' => 'Group not available',
'GROUP_STATISTICS' => 'Technical data',
'GROUP_SUBGROUPS' => 'Subgroups',
+ 'GROUP_UPDATED_AT' => 'Changed',
'GROUP_VISIBILITY' => 'Visibility',
'GROUP_WITHOUT_MASTER' => 'no master group',
'GROUP_WITHOUT_SUBGROUPS' => 'no subgroups',
@@ -600,6 +602,19 @@
'SCENARIO_TECHNICAL_DETAILS' => 'Technical details',
'SCENARIO_TITLE_CREATE' => 'Create scenario',
'SCENARIO_TITLE_UPDATE' => 'Modify scenario',
+ 'SCRIBBLES_BUTTON_NO' => 'Click to mark as favorite',
+ 'SCRIBBLES_BUTTON_WORKING' => 'Status change in progress...',
+ 'SCRIBBLES_BUTTON_YES' => 'Marked as favorite; click to unmark',
+ 'SCRIBBLES_FAVORITE_ERROR_GENERIC' => 'Status change error',
+ 'SCRIBBLES_TITLE_NO' => 'Click for player\'s scribbles',
+ 'SCRIBBLES_TITLE_YES' => 'Click for player\'s scribbles',
+ 'SCRIBBLE_DENIED_ACCESS' => 'You have no rights to access this',
+ 'SCRIBBLE_ID' => 'ID',
+ 'SCRIBBLE_IS_FAVORITE' => 'Favorited?',
+ 'SCRIBBLE_PACK' => 'Pack of scribbles',
+ 'SCRIBBLE_PACK_CLASS' => 'Class',
+ 'SCRIBBLE_PACK_ID' => 'ID',
+ 'SCRIBBLE_TITLE' => 'Player\'s scribbles',
'SEEN_ALERT' => 'Alert threshold',
'SEEN_BEFORE_UPDATE' => 'Modified for:',
'SEEN_ID' => 'ID',
diff --git a/common/messages/pl/app.php b/common/messages/pl/app.php
index 9b08b983..83ffaaf9 100644
--- a/common/messages/pl/app.php
+++ b/common/messages/pl/app.php
@@ -318,6 +318,7 @@
'GAME_ID' => 'ID',
'GAME_NOTES' => 'Notatki',
'GAME_PLANNED_DATE' => 'Planowana data',
+ 'GAME_PLANNED_LOCATION' => 'Planowane miejsce',
'GAME_POSITION' => 'Pozycja',
'GAME_SESSION_NOT_AVAILABLE' => 'Sesja niedostępna',
'GAME_STATUS' => 'Status',
@@ -359,6 +360,7 @@
'GROUP_NOT_AVAILABLE' => 'Grupa niedostępna',
'GROUP_STATISTICS' => 'Dane techniczne',
'GROUP_SUBGROUPS' => 'Grupy podrzędne',
+ 'GROUP_UPDATED_AT' => 'Zmieniano',
'GROUP_VISIBILITY' => 'Widoczność',
'GROUP_WITHOUT_MASTER' => 'Brak grupy nadrzędnej',
'GROUP_WITHOUT_SUBGROUPS' => 'Brak grup podrzędnych',
@@ -600,6 +602,19 @@
'SCENARIO_TECHNICAL_DETAILS' => 'Szczegóły techniczne',
'SCENARIO_TITLE_CREATE' => 'Dodaj scenariusz',
'SCENARIO_TITLE_UPDATE' => 'Zmień scenariusz',
+ 'SCRIBBLES_BUTTON_NO' => 'Klinij, by dodać do ulubionych',
+ 'SCRIBBLES_BUTTON_WORKING' => 'Zmiana statusu w toku...',
+ 'SCRIBBLES_BUTTON_YES' => 'W ulubionych; kliknij, by usunąć z ulubionych',
+ 'SCRIBBLES_FAVORITE_ERROR_GENERIC' => 'Błąd zapisu statusu',
+ 'SCRIBBLES_TITLE_NO' => 'Zapiski',
+ 'SCRIBBLES_TITLE_YES' => 'Zapiski',
+ 'SCRIBBLE_DENIED_ACCESS' => 'Brak dostępu',
+ 'SCRIBBLE_ID' => 'ID',
+ 'SCRIBBLE_IS_FAVORITE' => 'Ulubione?',
+ 'SCRIBBLE_PACK' => 'Paczka zapisków',
+ 'SCRIBBLE_PACK_CLASS' => 'Klasa',
+ 'SCRIBBLE_PACK_ID' => 'ID',
+ 'SCRIBBLE_TITLE' => 'Zapiski',
'SEEN_ALERT' => 'Próg sygnalizacji',
'SEEN_BEFORE_UPDATE' => 'Zmienione dla:',
'SEEN_ID' => 'ID',
diff --git a/common/models/Character.php b/common/models/Character.php
index 34d0b6c8..82e8d06e 100644
--- a/common/models/Character.php
+++ b/common/models/Character.php
@@ -7,6 +7,7 @@
use common\models\core\HasEpicControl;
use common\models\core\HasImportance;
use common\models\core\HasImportanceCategory;
+use common\models\core\HasScribbles;
use common\models\core\HasSightings;
use common\models\core\HasVisibility;
use common\models\core\ImportanceCategory;
@@ -36,6 +37,7 @@
* @property string $external_data_pack_id
* @property string $seen_pack_id
* @property string $importance_pack_id
+ * @property int|null $scribble_pack_id
* @property string $utility_bag_id
*
* @property Epic $epic
@@ -44,12 +46,13 @@
* @property ExternalDataPack $externalDataPack
* @property ImportancePack $importancePack
* @property SeenPack $seenPack
+ * @property ScribblePack $scribblePack
* @property UtilityBag $utilityBag
* @property CharacterSheet[] $characterSheets
* @property GroupMembership[] $groupMemberships
* @property GroupMembership[] $groupMembershipsVisibleToUser
*/
-class Character extends ActiveRecord implements Displayable, HasDescriptions, HasEpicControl, HasImportance, HasImportanceCategory, HasReputations, HasVisibility, HasSightings
+class Character extends ActiveRecord implements Displayable, HasDescriptions, HasEpicControl, HasImportance, HasImportanceCategory, HasReputations, HasVisibility, HasScribbles, HasSightings
{
use ToolsForEntity;
use ToolsForHasDescriptions;
@@ -63,7 +66,7 @@ public function rules()
{
return [
[['epic_id', 'name', 'tagline', 'visibility', 'importance_category'], 'required'],
- [['epic_id', 'character_sheet_id', 'description_pack_id'], 'integer'],
+ [['epic_id', 'character_sheet_id', 'description_pack_id', 'scribble_pack_id'], 'integer'],
[['data', 'visibility', 'importance_category'], 'string'],
[['key'], 'string', 'max' => 80],
[['name', 'tagline'], 'string', 'max' => 120],
@@ -95,6 +98,13 @@ public function rules()
'targetClass' => ExternalDataPack::class,
'targetAttribute' => ['external_data_pack_id' => 'external_data_pack_id']
],
+ [
+ ['scribble_pack_id'],
+ 'exist',
+ 'skipOnError' => true,
+ 'targetClass' => ScribblePack::class,
+ 'targetAttribute' => ['scribble_pack_id' => 'scribble_pack_id']
+ ],
[
['visibility'],
'in',
@@ -122,6 +132,7 @@ public function attributeLabels()
'external_data_pack_id' => Yii::t('app', 'EXTERNAL_DATA_PACK'),
'seen_pack_id' => Yii::t('app', 'SEEN_PACK_ID'),
'importance_pack_id' => Yii::t('app', 'IMPORTANCE_PACK'),
+ 'scribble_pack_id' => Yii::t('app', 'SCRIBBLE_PACK'),
'utility_bag_id' => Yii::t('app', 'UTILITY_BAG'),
];
}
@@ -167,6 +178,11 @@ public function beforeSave($insert)
$this->importance_pack_id = $pack->importance_pack_id;
}
+ if (empty($this->scribble_pack_id)) {
+ $pack = ScribblePack::create('Character');
+ $this->scribble_pack_id = $pack->scribble_pack_id;
+ }
+
return parent::beforeSave($insert);
}
@@ -204,7 +220,7 @@ static public function allowedVisibilities(): array
/**
* @return ActiveQuery
*/
- public function getEpic(): ActiveQuery
+ public function getEpic(): ActiveQuery
{
return $this->hasOne(Epic::class, ['epic_id' => 'epic_id']);
}
@@ -238,6 +254,16 @@ public function getImportancePack()
return $this->hasOne(ImportancePack::class, ['importance_pack_id' => 'importance_pack_id']);
}
+ /**
+ * Gets query for [[ScribblePack]].
+ *
+ * @return \yii\db\ActiveQuery|ScribblePackQuery
+ */
+ public function getScribblePack(): ActiveQuery|ScribblePackQuery
+ {
+ return $this->hasOne(ScribblePack::class, ['scribble_pack_id' => 'scribble_pack_id']);
+ }
+
/**
* @return ActiveQuery
*/
diff --git a/common/models/DescriptionPack.php b/common/models/DescriptionPack.php
index f1ad800b..9c759275 100644
--- a/common/models/DescriptionPack.php
+++ b/common/models/DescriptionPack.php
@@ -3,7 +3,7 @@
namespace common\models;
use common\models\core\HasEpicControl;
-use common\models\core\IsPack;
+use common\models\core\IsEditablePack;
use common\models\core\Language;
use Yii;
use yii\behaviors\TimestampBehavior;
@@ -21,7 +21,7 @@
* @property Character[] $people
* @property Epic $epic
*/
-final class DescriptionPack extends ActiveRecord implements Displayable, IsPack
+final class DescriptionPack extends ActiveRecord implements Displayable, IsEditablePack
{
public static function tableName()
{
diff --git a/common/models/ExternalDataPack.php b/common/models/ExternalDataPack.php
index 12c1667e..f381ee51 100644
--- a/common/models/ExternalDataPack.php
+++ b/common/models/ExternalDataPack.php
@@ -3,7 +3,7 @@
namespace common\models;
use common\models\core\HasEpicControl;
-use common\models\core\IsPack;
+use common\models\core\IsEditablePack;
use common\models\core\Visibility;
use Yii;
use yii\behaviors\TimestampBehavior;
@@ -20,7 +20,7 @@
*
* @property Epic $epic
*/
-class ExternalDataPack extends ActiveRecord implements IsPack
+class ExternalDataPack extends ActiveRecord implements IsEditablePack
{
public static function tableName()
{
diff --git a/common/models/Game.php b/common/models/Game.php
index 3d81c992..e0ac7298 100644
--- a/common/models/Game.php
+++ b/common/models/Game.php
@@ -20,6 +20,7 @@
* @property string $epic_id
* @property string $basics
* @property string $planned_date
+ * @property string $planned_location
* @property string $status
* @property int $position
* @property string $notes
@@ -45,20 +46,21 @@ class Game extends ActiveRecord implements HasEpicControl, HasStatus
const STATUS_COMPLETED = 'completed'; // game was completed; next: CLOSED
const STATUS_CLOSED = 'closed'; // game was described; next: none
- public static function tableName()
+ public static function tableName(): string
{
return 'game';
}
- public function rules()
+ public function rules(): array
{
return [
[['epic_id'], 'required'],
[['epic_id', 'position', 'recap_id', 'utility_bag_id'], 'integer'],
[['planned_date'], 'safe'],
- [['planned_date'], 'default', 'value' => null],
+ [['planned_date', 'planned_location'], 'default', 'value' => null],
[['notes'], 'string'],
[['basics'], 'string', 'max' => 255],
+ [['planned_location'], 'string', 'max' => 80],
[['status'], 'string', 'max' => 20],
[
['status'],
@@ -95,13 +97,17 @@ public function rules()
];
}
- public function attributeLabels()
+ /**
+ * @return string[]
+ */
+ public function attributeLabels(): array
{
return [
'game_id' => Yii::t('app', 'GAME_ID'),
'epic_id' => Yii::t('app', 'LABEL_EPIC'),
'basics' => Yii::t('app', 'GAME_BASICS'),
'planned_date' => Yii::t('app', 'GAME_PLANNED_DATE'),
+ 'planned_location' => Yii::t('app', 'GAME_PLANNED_LOCATION'),
'status' => Yii::t('app', 'GAME_STATUS'),
'position' => Yii::t('app', 'GAME_POSITION'),
'notes' => Yii::t('app', 'GAME_NOTES'),
@@ -110,7 +116,7 @@ public function attributeLabels()
];
}
- public function beforeSave($insert)
+ public function beforeSave($insert): bool
{
if (empty($this->utility_bag_id)) {
$pack = UtilityBag::create('Game');
@@ -120,13 +126,13 @@ public function beforeSave($insert)
return parent::beforeSave($insert);
}
- public function afterSave($insert, $changedAttributes)
+ public function afterSave($insert, $changedAttributes): void
{
$this->utilityBag->flagAsChanged();
parent::afterSave($insert, $changedAttributes);
}
- public function behaviors()
+ public function behaviors(): array
{
return [
'positionBehavior' => [
@@ -142,10 +148,7 @@ public function behaviors()
];
}
- /**
- * @return ActiveQuery
- */
- public function getEpic()
+ public function getEpic(): ActiveQuery
{
return $this->hasOne(Epic::class, ['epic_id' => 'epic_id']);
}
@@ -198,13 +201,13 @@ public function statusAllowedChanges(): array
public function getStatus(): string
{
$names = self::statusNames();
- return isset($names[$this->status]) ? $names[$this->status] : '?';
+ return $names[$this->status] ?? '?';
}
public function getStatusClass(): string
{
$names = self::statusClasses();
- return isset($names[$this->status]) ? $names[$this->status] : '';
+ return $names[$this->status] ?? '';
}
public function getAllowedChange(): array
@@ -224,10 +227,7 @@ public function getRecap(): ActiveQuery
return $this->hasOne(Recap::class, ['recap_id' => 'recap_id']);
}
- /**
- * @return ActiveQuery
- */
- public function getUtilityBag()
+ public function getUtilityBag(): ActiveQuery
{
return $this->hasOne(UtilityBag::class, ['utility_bag_id' => 'utility_bag_id']);
}
@@ -272,10 +272,7 @@ static function throwExceptionAboutView()
self::thrownExceptionAbout(Yii::t('app', 'NO_RIGHT_TO_VIEW_SESSION'));
}
- /**
- * @return string|null
- */
- public function getNotesFormatted()
+ public function getNotesFormatted(): ?string
{
return Markdown::process(Html::encode($this->notes), 'gfm');
}
diff --git a/common/models/Group.php b/common/models/Group.php
index b9d88eb5..2074b27d 100644
--- a/common/models/Group.php
+++ b/common/models/Group.php
@@ -7,6 +7,7 @@
use common\models\core\HasEpicControl;
use common\models\core\HasImportance;
use common\models\core\HasImportanceCategory;
+use common\models\core\HasScribbles;
use common\models\core\HasSightings;
use common\models\core\HasVisibility;
use common\models\core\ImportanceCategory;
@@ -14,7 +15,10 @@
use common\models\external\HasReputations;
use common\models\tools\ToolsForEntity;
use common\models\tools\ToolsForHasDescriptions;
+use DateTimeImmutable;
+use ReflectionClass;
use Yii;
+use yii\behaviors\TimestampBehavior;
use yii\db\ActiveQuery;
use yii\db\ActiveRecord;
use yii\helpers\Html;
@@ -30,11 +34,13 @@
* @property string $seen_pack_id
* @property string $visibility
* @property string $importance_category
- * @property string $description_pack_id
- * @property string $external_data_pack_id
- * @property string $importance_pack_id
- * @property string $master_group_id
- * @property string $utility_bag_id
+ * @property string $updated_at
+ * @property int|null $description_pack_id
+ * @property int|null $external_data_pack_id
+ * @property int|null $importance_pack_id
+ * @property int|null $master_group_id
+ * @property int|null $scribble_pack_id
+ * @property int|null $utility_bag_id
*
* @property DescriptionPack $descriptionPack
* @property ExternalDataPack $externalDataPack
@@ -50,21 +56,34 @@
* @property GroupMembership[] $groupCharacterMembershipsPassive
* @property GroupMembership[] $groupCharacterMembershipsPast
*/
-class Group extends ActiveRecord implements Displayable, HasDescriptions, HasEpicControl, HasImportance, HasImportanceCategory, HasReputations, HasSightings, HasVisibility
+class Group extends ActiveRecord implements Displayable, HasDescriptions, HasEpicControl, HasImportance, HasImportanceCategory, HasReputations, HasScribbles, HasSightings, HasVisibility
{
use ToolsForEntity;
use ToolsForHasDescriptions;
- public static function tableName()
+ public static function tableName(): string
{
return 'group';
}
- public function rules()
+ public function rules(): array
{
return [
[['epic_id', 'name'], 'required'],
- [['epic_id', 'master_group_id'], 'integer'],
+ [
+ [
+ 'epic_id',
+ 'seen_pack_id',
+ 'updated_at',
+ 'description_pack_id',
+ 'external_data_pack_id',
+ 'importance_pack_id',
+ 'scribble_pack_id',
+ 'utility_bag_id',
+ 'master_group_id'
+ ],
+ 'integer'
+ ],
[['name'], 'string', 'max' => 120],
[['visibility', 'importance_category'], 'string', 'max' => 20],
[
@@ -81,6 +100,41 @@ public function rules()
return $this->allowedVisibilities();
}
],
+ [
+ ['importance_pack_id'],
+ 'exist',
+ 'skipOnError' => true,
+ 'targetClass' => ImportancePack::class,
+ 'targetAttribute' => ['importance_pack_id' => 'importance_pack_id']
+ ],
+ [
+ ['master_group_id'],
+ 'exist',
+ 'skipOnError' => true,
+ 'targetClass' => Group::class,
+ 'targetAttribute' => ['master_group_id' => 'group_id']
+ ],
+ [
+ ['scribble_pack_id'],
+ 'exist',
+ 'skipOnError' => true,
+ 'targetClass' => ScribblePack::class,
+ 'targetAttribute' => ['scribble_pack_id' => 'scribble_pack_id']
+ ],
+ [
+ ['seen_pack_id'],
+ 'exist',
+ 'skipOnError' => true,
+ 'targetClass' => SeenPack::class,
+ 'targetAttribute' => ['seen_pack_id' => 'seen_pack_id']
+ ],
+ [
+ ['utility_bag_id'],
+ 'exist',
+ 'skipOnError' => true,
+ 'targetClass' => UtilityBag::class,
+ 'targetAttribute' => ['utility_bag_id' => 'utility_bag_id']
+ ],
];
}
@@ -92,7 +146,7 @@ public function afterFind()
parent::afterFind();
}
- public function attributeLabels()
+ public function attributeLabels(): array
{
return [
'group_id' => Yii::t('app', 'GROUP_ID'),
@@ -102,10 +156,12 @@ public function attributeLabels()
'data' => Yii::t('app', 'GROUP_DATA'),
'visibility' => Yii::t('app', 'GROUP_VISIBILITY'),
'importance_category' => Yii::t('app', 'GROUP_IMPORTANCE'),
+ 'updated_at' => Yii::t('app', 'GROUP_UPDATED_AT'),
'description_pack_id' => Yii::t('app', 'DESCRIPTION_PACK'),
'external_data_pack_id' => Yii::t('app', 'EXTERNAL_DATA_PACK'),
'importance_pack_id' => Yii::t('app', 'IMPORTANCE_PACK'),
'master_group_id' => Yii::t('app', 'GROUP_MASTER_GROUP'),
+ 'scribble_pack_id' => Yii::t('app', 'SCRIBBLE_PACK'),
'utility_bag_id' => Yii::t('app', 'UTILITY_BAG'),
];
}
@@ -118,10 +174,10 @@ public function afterSave($insert, $changedAttributes)
parent::afterSave($insert, $changedAttributes);
}
- public function beforeSave($insert)
+ public function beforeSave($insert): bool
{
if ($insert) {
- $this->key = $this->generateKey(strtolower((new \ReflectionClass($this))->getShortName()));
+ $this->key = $this->generateKey(strtolower((new ReflectionClass($this))->getShortName()));
$this->data = json_encode([]);
}
@@ -150,6 +206,11 @@ public function beforeSave($insert)
$this->importance_pack_id = $pack->importance_pack_id;
}
+ if (empty($this->scribble_pack_id)) {
+ $pack = ScribblePack::create('Group');
+ $this->scribble_pack_id = $pack->scribble_pack_id;
+ }
+
return parent::beforeSave($insert);
}
@@ -175,14 +236,18 @@ static public function allowedDescriptionTypes(): array
];
}
- public function behaviors()
+ public function behaviors(): array
{
return [
'performedActionBehavior' => [
'class' => PerformedActionBehavior::class,
'idName' => 'group_id',
'className' => 'Group',
- ]
+ ],
+ 'timestampBehavior' => [
+ 'class' => TimestampBehavior::class,
+ 'createdAtAttribute' => null,
+ ],
];
}
@@ -191,82 +256,60 @@ public function getDescriptionPack(): ActiveQuery
return $this->hasOne(DescriptionPack::class, ['description_pack_id' => 'description_pack_id']);
}
- /**
- * @return ActiveQuery
- */
- public function getExternalDataPack()
+ public function getExternalDataPack(): ActiveQuery
{
return $this->hasOne(ExternalDataPack::class, ['external_data_pack_id' => 'external_data_pack_id']);
}
- /**
- * @return ActiveQuery
- */
- public function getEpic()
+ public function getEpic(): ActiveQuery
{
return $this->hasOne(Epic::class, ['epic_id' => 'epic_id']);
}
- /**
- * @return ActiveQuery
- */
- public function getImportancePack()
+ public function getImportancePack(): ActiveQuery
{
return $this->hasOne(ImportancePack::class, ['importance_pack_id' => 'importance_pack_id']);
}
/**
- * @return \yii\db\ActiveQuery
+ * Gets query for [[ScribblePack]]
*/
- public function getMasterGroup()
+ public function getScribblePack(): ActiveQuery|ScribblePackQuery
+ {
+ return $this->hasOne(ScribblePack::class, ['scribble_pack_id' => 'scribble_pack_id']);
+ }
+
+ public function getMasterGroup(): ActiveQuery
{
return $this->hasOne(Group::class, ['group_id' => 'master_group_id']);
}
- /**
- * @return \yii\db\ActiveQuery
- */
- public function getSubGroups()
+ public function getSubGroups(): ActiveQuery
{
return $this->hasMany(Group::class, ['master_group_id' => 'group_id']);
}
- /**
- * @return ActiveQuery
- */
- public function getSeenPack()
+ public function getSeenPack(): ActiveQuery
{
return $this->hasOne(SeenPack::class, ['seen_pack_id' => 'seen_pack_id']);
}
- /**
- * @return ActiveQuery
- */
- public function getUtilityBag()
+ public function getUtilityBag(): ActiveQuery
{
return $this->hasOne(UtilityBag::class, ['utility_bag_id' => 'utility_bag_id']);
}
- /**
- * @return ActiveQuery
- */
- public function getGroupCharacterMemberships()
+ public function getGroupCharacterMemberships(): ActiveQuery
{
return $this->hasMany(GroupMembership::class, ['group_id' => 'group_id']);
}
- /**
- * @return ActiveQuery
- */
- public function getGroupCharacterMembershipsOrderedByPosition()
+ public function getGroupCharacterMembershipsOrderedByPosition(): ActiveQuery
{
return $this->hasMany(GroupMembership::class, ['group_id' => 'group_id'])->orderBy('position ASC');
}
- /**
- * @return ActiveQuery
- */
- public function getGroupCharacterMembershipsActive()
+ public function getGroupCharacterMembershipsActive(): ActiveQuery
{
return $this->hasMany(GroupMembership::class, ['group_id' => 'group_id'])->where([
'status' => GroupMembership::STATUS_ACTIVE,
@@ -274,10 +317,7 @@ public function getGroupCharacterMembershipsActive()
])->orderBy('position ASC');
}
- /**
- * @return ActiveQuery
- */
- public function getGroupCharacterMembershipsPast()
+ public function getGroupCharacterMembershipsPast(): ActiveQuery
{
return $this->hasMany(GroupMembership::class, ['group_id' => 'group_id'])->where([
'status' => GroupMembership::STATUS_PAST,
@@ -285,10 +325,7 @@ public function getGroupCharacterMembershipsPast()
])->orderBy('position ASC');
}
- /**
- * @return ActiveQuery
- */
- public function getGroupCharacterMembershipsPassive()
+ public function getGroupCharacterMembershipsPassive(): ActiveQuery
{
return $this->hasMany(GroupMembership::class, ['group_id' => 'group_id'])->where([
'status' => GroupMembership::STATUS_PASSIVE,
@@ -296,7 +333,7 @@ public function getGroupCharacterMembershipsPassive()
])->orderBy('position ASC');
}
- public function getSimpleDataForApi()
+ public function getSimpleDataForApi(): array
{
return [
'name' => $this->name,
@@ -314,7 +351,7 @@ public function getCompleteDataForApi()
return $decodedData;
}
- public function isVisibleInApi()
+ public function isVisibleInApi(): bool
{
return true;
}
@@ -420,10 +457,9 @@ public function getVisibilityLowercase(): string
return $visibility->getNameLowercase();
}
- public function getLastModified(): \DateTimeImmutable
+ public function getLastModified(): DateTimeImmutable
{
- /* @todo Implement update date on object */
- return new \DateTimeImmutable('now');
+ return new DateTimeImmutable(date("Y-m-d H:i:s", $this->updated_at));
}
public function getSeenStatusForUser(int $userId): string
diff --git a/common/models/Importance.php b/common/models/Importance.php
index 1f4f6846..e298379a 100644
--- a/common/models/Importance.php
+++ b/common/models/Importance.php
@@ -73,6 +73,16 @@ public function getUser(): ActiveQuery
return $this->hasOne(User::class, ['id' => 'user_id']);
}
+ public static function createEmptyForPack(int $userId, ImportancePack $pack): self
+ {
+ $object = new Importance();
+ $object->user_id = $userId;
+ $object->importance_pack_id = $pack->importance_pack_id;
+ $object->importance = 0;
+
+ return $object;
+ }
+
/**
* Calculates the importance value
*
diff --git a/common/models/ImportancePack.php b/common/models/ImportancePack.php
index 17dcaa16..3e368880 100644
--- a/common/models/ImportancePack.php
+++ b/common/models/ImportancePack.php
@@ -3,6 +3,8 @@
namespace common\models;
use common\models\core\HasImportance;
+use common\models\core\IsSelfFillingPack;
+use common\models\tools\ToolsForSelfFillingPacks;
use Yii;
use yii\db\ActiveQuery;
use yii\db\ActiveRecord;
@@ -17,8 +19,10 @@
* @property Group[] $groups
* @property Importance[] $importances
*/
-class ImportancePack extends ActiveRecord
+class ImportancePack extends ActiveRecord implements IsSelfFillingPack
{
+ use ToolsForSelfFillingPacks;
+
/**
* @var HasImportance
*/
@@ -105,13 +109,22 @@ public function getEpic(): Epic
return $this->getControllingObject()->getEpic()->one();
}
+ public function createEmptyContent(int $userId): Importance
+ {
+ return Importance::createEmptyForPack($userId, $this);
+ }
+
/**
* Recalculates pack importance objects
* @return bool
*/
public function recalculatePack(): bool
{
- $result = $this->createAbsentImportanceObjects();
+ $result = $this->createAbsentRecords(
+ $this->getEpic(),
+ $this,
+ Importance::findAll(['importance_pack_id' => $this->importance_pack_id])
+ );
foreach ($this->importances as $importance) {
$result = $result && $importance->calculateAndSave();
@@ -119,34 +132,4 @@ public function recalculatePack(): bool
return $result;
}
-
- /**
- * Creates new Importance objects for users that do not have them
- * @return bool
- */
- private function createAbsentImportanceObjects(): bool
- {
- $users = $this->getEpic()->participants;
- $importanceObjectsRaw = Importance::findAll(['importance_pack_id' => $this->importance_pack_id]);
- $importanceObjectsOrdered = [];
-
- foreach ($importanceObjectsRaw as $importanceObject) {
- $importanceObjectsOrdered[$importanceObject->user_id] = $importanceObject;
- }
-
- $result = true;
-
- foreach ($users as $user) {
- if (!isset($importanceObjectsOrdered[$user->user_id])) {
- $importanceObject = new Importance();
- $importanceObject->user_id = $user->user_id;
- $importanceObject->importance_pack_id = $this->importance_pack_id;
- $importanceObject->importance = 0;
-
- $saveResult = $importanceObject->save();
- $result = $result && $saveResult;
- }
- }
- return $result;
- }
}
diff --git a/common/models/ParameterPack.php b/common/models/ParameterPack.php
index 4bd46c80..866fee29 100644
--- a/common/models/ParameterPack.php
+++ b/common/models/ParameterPack.php
@@ -3,7 +3,7 @@
namespace common\models;
use common\models\core\HasEpicControl;
-use common\models\core\IsPack;
+use common\models\core\IsEditablePack;
use Yii;
use yii\behaviors\TimestampBehavior;
use yii\db\ActiveQuery;
@@ -21,7 +21,7 @@
* @property Story[] $stories
* @property Epic $epic
*/
-class ParameterPack extends ActiveRecord implements IsPack
+class ParameterPack extends ActiveRecord implements IsEditablePack
{
/**
* @inheritdoc
diff --git a/common/models/Scribble.php b/common/models/Scribble.php
new file mode 100644
index 00000000..4db5746f
--- /dev/null
+++ b/common/models/Scribble.php
@@ -0,0 +1,84 @@
+ true, 'targetClass' => ScribblePack::class, 'targetAttribute' => ['scribble_pack_id' => 'scribble_pack_id']],
+ [['user_id'], 'exist', 'skipOnError' => true, 'targetClass' => User::class, 'targetAttribute' => ['user_id' => 'id']],
+ ];
+ }
+
+ public function attributeLabels()
+ {
+ return [
+ 'scribble_id' => Yii::t('app', 'SCRIBBLE_ID'),
+ 'scribble_pack_id' => Yii::t('app', 'SCRIBBLE_PACK'),
+ 'user_id' => Yii::t('app', 'USER_LABEL'),
+ 'favorite' => Yii::t('app', 'SCRIBBLE_IS_FAVORITE'),
+ ];
+ }
+
+ /**
+ * Gets query for [[ScribblePack]].
+ *
+ * @return ActiveQuery|ScribblePackQuery
+ */
+ public function getScribblePack(): ActiveQuery|ScribblePackQuery
+ {
+ return $this->hasOne(ScribblePack::class, ['scribble_pack_id' => 'scribble_pack_id']);
+ }
+
+ /**
+ * Gets query for [[User]].
+ *
+ * @return ActiveQuery
+ */
+ public function getUser(): ActiveQuery
+ {
+ return $this->hasOne(User::class, ['id' => 'user_id']);
+ }
+
+ /**
+ * @return ScribbleQuery the active query used by this AR class.
+ */
+ public static function find(): ScribbleQuery
+ {
+ return new ScribbleQuery(get_called_class());
+ }
+
+ public static function createEmptyForPack(int $userId, ScribblePack $pack): Scribble
+ {
+ $object = new Scribble();
+ $object->user_id = $userId;
+ $object->scribble_pack_id = $pack->scribble_pack_id;
+ $object->favorite = false;
+
+ return $object;
+ }
+}
diff --git a/common/models/ScribblePack.php b/common/models/ScribblePack.php
new file mode 100644
index 00000000..5675e597
--- /dev/null
+++ b/common/models/ScribblePack.php
@@ -0,0 +1,123 @@
+ 20],
+ ];
+ }
+
+ public function attributeLabels(): array
+ {
+ return [
+ 'scribble_pack_id' => Yii::t('app', 'SCRIBBLE_PACK_ID'),
+ 'class' => Yii::t('app', 'SCRIBBLE_PACK_CLASS'),
+ ];
+ }
+
+ /**
+ * Gets query for [[Characters]].
+ *
+ * @return ActiveQuery
+ */
+ public function getCharacters(): ActiveQuery
+ {
+ return $this->hasMany(Character::class, ['scribble_pack_id' => 'scribble_pack_id']);
+ }
+
+ /**
+ * Gets query for [[Groups]].
+ *
+ * @return ActiveQuery
+ */
+ public function getGroups(): ActiveQuery
+ {
+ return $this->hasMany(Group::class, ['scribble_pack_id' => 'scribble_pack_id']);
+ }
+
+ /**
+ * Gets query for [[Scribbles]].
+ *
+ * @return ActiveQuery|ScribbleQuery
+ */
+ public function getScribbles()
+ {
+ return $this->hasMany(Scribble::class, ['scribble_pack_id' => 'scribble_pack_id']);
+ }
+
+ public static function create(string $class): ScribblePack
+ {
+ $pack = new ScribblePack(['class' => $class]);
+
+ $pack->save();
+ $pack->refresh();
+
+ return $pack;
+ }
+
+ public static function find(): ScribblePackQuery
+ {
+ return new ScribblePackQuery(get_called_class());
+ }
+
+ public function canUserReadYou(): bool
+ {
+ $className = 'common\models\\' . $this->class;
+ /** @var HasEpicControl $object */
+ $object = ($className)::findOne(['scribble_pack_id' => $this->scribble_pack_id]);
+ return $object->canUserViewYou();
+ }
+
+ public function canUserControlYou(): bool
+ {
+ $className = 'common\models\\' . $this->class;
+ /** @var HasEpicControl $object */
+ $object = ($className)::findOne(['scribble_pack_id' => $this->scribble_pack_id]);
+ return $object->canUserControlYou();
+ }
+
+ public function getScribbleByUserId(int $userId): Scribble
+ {
+ $scribble = Scribble::findOne([
+ 'scribble_pack_id' => $this->scribble_pack_id,
+ 'user_id' => $userId,
+ ]);
+
+ if (empty($scribble)) {
+ $scribble = Scribble::createEmptyForPack($userId, $this);
+ $scribble->save();
+ $scribble->refresh();
+ }
+
+ return $scribble;
+ }
+}
diff --git a/common/models/ScribblePackQuery.php b/common/models/ScribblePackQuery.php
new file mode 100644
index 00000000..7560d8e7
--- /dev/null
+++ b/common/models/ScribblePackQuery.php
@@ -0,0 +1,35 @@
+andWhere('[[status]]=1');
+ }*/
+
+ /**
+ * @return ScribblePack[]|array
+ */
+ public function all($db = null): array
+ {
+ return parent::all($db);
+ }
+
+ /**
+ * @param null $db
+ * @return array|ActiveRecord|null
+ */
+ public function one($db = null): array|ActiveRecord|null
+ {
+ return parent::one($db);
+ }
+}
diff --git a/common/models/ScribbleQuery.php b/common/models/ScribbleQuery.php
new file mode 100644
index 00000000..66c61960
--- /dev/null
+++ b/common/models/ScribbleQuery.php
@@ -0,0 +1,32 @@
+andWhere('[[status]]=1');
+ }*/
+
+ /**
+ * @return Scribble[]|array
+ */
+ public function all($db = null): array
+ {
+ return parent::all($db);
+ }
+
+ /**
+ * @return Scribble|array|null
+ */
+ public function one($db = null): Scribble|array|null
+ {
+ return parent::one($db);
+ }
+}
diff --git a/common/models/core/HasEpic.php b/common/models/core/HasEpic.php
new file mode 100644
index 00000000..b9f8e1be
--- /dev/null
+++ b/common/models/core/HasEpic.php
@@ -0,0 +1,10 @@
+participants;
+ $objectsOrdered = [];
+
+ foreach ($objectsRaw as $object) {
+ $objectsOrdered[$object->user_id] = $object;
+ }
+
+ $result = true;
+
+ foreach ($users as $user) {
+ if (!isset($objectsOrdered[$user->user_id])) {
+ $scribbleObject = $pack->createEmptyContent($user->user_id);
+ $saveResult = $scribbleObject->save();
+ $result = $result && $saveResult;
+ }
+ }
+ return $result;
+ }
+}
diff --git a/console/migrations/m230910_123838_v1_1_0.php b/console/migrations/m230910_123838_v1_1_0.php
new file mode 100644
index 00000000..4f7fc1e1
--- /dev/null
+++ b/console/migrations/m230910_123838_v1_1_0.php
@@ -0,0 +1,117 @@
+addColumn(
+ '{{%group}}',
+ 'updated_at',
+ $this->integer()->notNull()->after('importance_category')
+ );
+ $this->update(
+ '{{%group}}',
+ ['updated_at' => time()]
+ );
+
+ /* Session location added */
+ $this->addColumn('{{%game}}', 'planned_location', $this->string(80)->after('planned_date'));
+
+ /* Scribbles created */
+ $this->createTable(
+ '{{%scribble_pack}}',
+ [
+ 'scribble_pack_id' => $this->primaryKey()->unsigned(),
+ 'class' => $this->string(20)->notNull()->comment("Name of class this pack belongs to; necessary for proper type assignment"),
+ ],
+ $tableOptions
+ );
+
+ $this->createTable(
+ '{{%scribble}}',
+ [
+ 'scribble_id' => $this->primaryKey()->unsigned(),
+ 'scribble_pack_id' => $this->integer(11)->unsigned(),
+ 'user_id' => $this->integer(10)->unsigned(),
+ 'favorite' => $this->boolean(),
+ ],
+ $tableOptions
+ );
+
+ $this->addForeignKey(
+ 'scribble_pack',
+ '{{%scribble}}',
+ 'scribble_pack_id',
+ '{{%scribble_pack}}',
+ 'scribble_pack_id',
+ 'RESTRICT',
+ 'CASCADE'
+ );
+ $this->addForeignKey(
+ 'scribble_user',
+ '{{%scribble}}',
+ 'user_id',
+ '{{%user}}',
+ 'id',
+ 'RESTRICT',
+ 'CASCADE'
+ );
+
+ $this->addColumn(
+ '{{%character}}',
+ 'scribble_pack_id',
+ $this->integer(11)->unsigned()->after('importance_pack_id')
+ );
+ $this->addColumn(
+ '{{%group}}',
+ 'scribble_pack_id',
+ $this->integer(11)->unsigned()->after('importance_pack_id')
+ );
+
+ $this->addForeignKey(
+ 'character_scribble_pack',
+ '{{%character}}',
+ 'scribble_pack_id',
+ '{{%scribble_pack}}',
+ 'scribble_pack_id',
+ 'RESTRICT',
+ 'CASCADE'
+ );
+ $this->addForeignKey(
+ 'group_scribble_pack',
+ '{{%group}}',
+ 'scribble_pack_id',
+ '{{%scribble_pack}}',
+ 'scribble_pack_id',
+ 'RESTRICT',
+ 'CASCADE'
+ );
+ }
+
+ public function safeDown()
+ {
+ /* Scribbles removed */
+ $this->dropForeignKey('group_scribble_pack', '{{%group}}');
+ $this->dropForeignKey('character_scribble_pack', '{{%character}}');
+
+ $this->dropColumn('{{%group}}', 'scribble_pack_id');
+ $this->dropColumn('{{%character}}', 'scribble_pack_id');
+
+ $this->dropTable('{{%scribble}}');
+ $this->dropTable('{{%scribble_pack}}');
+
+ /* Group updated_at removed */
+ $this->dropColumn('{{%group}}', 'updated_at');
+
+ /* Game location removed */
+ $this->dropColumn('{{%game}}', 'planned_location');
+ }
+}
diff --git a/frontend/controllers/CharacterController.php b/frontend/controllers/CharacterController.php
index 64aa95b3..eb03797e 100644
--- a/frontend/controllers/CharacterController.php
+++ b/frontend/controllers/CharacterController.php
@@ -33,7 +33,13 @@ public function behaviors()
'class' => AccessControl::class,
'rules' => [
[
- 'actions' => ['index', 'view', 'external-reputation', 'external-reputation-event'],
+ 'actions' => [
+ 'index',
+ 'view',
+ 'external-reputation',
+ 'external-reputation-event',
+ 'open-scribble-modal'
+ ],
'allow' => true,
'roles' => ['@'],
],
@@ -190,6 +196,23 @@ public function actionExternalReputationEvent(string $key): string
throw new HttpException(204, Yii::t('external', 'NO_DATA'));
}
+ public function actionOpenScribbleModal(string $key): string
+ {
+ $model = $this->findModelByKey($key);
+
+ if (!$model->canUserViewYou()) {
+ Character::throwExceptionAboutView();
+ }
+
+ $scribbleModel = $model->scribblePack->getScribbleByUserId(Yii::$app->user->getId());
+
+ if (Yii::$app->request->isAjax) {
+ return $this->renderAjax('../scribble/_modal_box', ['model' => $scribbleModel]);
+ } else {
+ return $this->render('../scribble/_modal_box', ['model' => $scribbleModel]);
+ }
+ }
+
/**
* Finds the Character model based on its primary key value.
* If the model is not found, a 404 HTTP exception will be thrown.
diff --git a/frontend/controllers/ScribbleController.php b/frontend/controllers/ScribbleController.php
new file mode 100644
index 00000000..a36cf22d
--- /dev/null
+++ b/frontend/controllers/ScribbleController.php
@@ -0,0 +1,114 @@
+ [
+ 'class' => AccessControl::class,
+ 'rules' => [
+ [
+ 'actions' => [
+ 'reverse-favorite'
+ ],
+ 'allow' => true,
+ 'roles' => ['@'],
+ ],
+ ],
+ ],
+ 'verbs' => [
+ 'class' => VerbFilter::class,
+ 'actions' => [
+ 'delete' => ['POST'],
+ ],
+ ],
+ ]
+ );
+ }
+
+ public function actionReverseFavorite(int $id)
+ {
+ $scribble = $this->getModelWithValidation($id);
+ $scribble->favorite = !$scribble->favorite;
+ if (!$scribble->save()) {
+ throw new ServerErrorHttpException();
+ }
+ }
+
+ public function actionSetAsFavorite(int $id)
+ {
+ $scribble = $this->getModelWithValidation($id);
+ $scribble->favorite = true;
+ if (!$scribble->save()) {
+ throw new ServerErrorHttpException();
+ }
+ }
+
+ public function actionUnsetAsFavorite(int $id)
+ {
+ $scribble = $this->getModelWithValidation($id);
+ $scribble->favorite = false;
+ if (!$scribble->save()) {
+ throw new ServerErrorHttpException();
+ }
+ }
+
+ /**
+ * @param int $scribbleId
+ *
+ * @return Scribble
+ *
+ * @throws ForbiddenHttpException
+ * @throws MethodNotAllowedHttpException
+ * @throws NotFoundHttpException
+ */
+ private function getModelWithValidation(int $scribbleId): Scribble
+ {
+ if (!Yii::$app->request->isAjax) {
+ throw new MethodNotAllowedHttpException(Yii::t('app', 'ERROR_AJAX_REQUESTS_ONLY'));
+ }
+
+ $scribble = $this->findModel($scribbleId);
+
+ if (!$scribble->scribblePack->canUserReadYou()) {
+ throw new ForbiddenHttpException(Yii::t('app', 'SCRIBBLE_DENIED_ACCESS'));
+ }
+
+ return $scribble;
+ }
+
+ /**
+ * Finds the Scribble model based on its primary key value.
+ * If the model is not found, a 404 HTTP exception will be thrown.
+ * @param int $scribble_id Scribble ID
+ * @return Scribble the loaded model
+ * @throws NotFoundHttpException if the model cannot be found
+ */
+ protected function findModel($scribble_id)
+ {
+ if (($model = Scribble::findOne(['scribble_id' => $scribble_id])) !== null) {
+ return $model;
+ }
+
+ throw new NotFoundHttpException('The requested page does not exist.');
+ }
+}
diff --git a/frontend/views/character/_index_box.php b/frontend/views/character/_index_box.php
index 3637a813..2c427f1f 100644
--- a/frontend/views/character/_index_box.php
+++ b/frontend/views/character/_index_box.php
@@ -29,20 +29,29 @@
break;
}
+$favorite = false; // to be replaced by an actual value based on the database record
+
$classesForBox = 'index-box' . ($additionalBoxClasses ? ' ' . $additionalBoxClasses : '');
+$favoriteClass = $favorite ? 'glyphicon-tags' : 'glyphicon-tag';
+$favoriteTitle = $favorite ? Yii::t('app', 'SCRIBBLES_TITLE_YES') : Yii::t('app', 'SCRIBBLES_TITLE_NO');
$titleText = $model->tagline . ($additionalTitleText ? ' ' . $additionalTitleText : '');
?>
-
+
+
+
= StringHelper::truncateWords($model->tagline, 16, ' (...)', false) ?>
diff --git a/frontend/views/character/index.php b/frontend/views/character/index.php
index a543c272..6023768e 100644
--- a/frontend/views/character/index.php
+++ b/frontend/views/character/index.php
@@ -1,6 +1,7 @@
+
+ 'scribble-modal',
+ 'header' => '
' . Yii::t('app', 'SCRIBBLE_TITLE') . '
',
+ 'clientOptions' => ['backdrop' => 'static'],
+ 'size' => Modal::SIZE_LARGE,
+]); ?>
+
diff --git a/frontend/views/scribble/_modal_box.php b/frontend/views/scribble/_modal_box.php
new file mode 100644
index 00000000..d7a0ecde
--- /dev/null
+++ b/frontend/views/scribble/_modal_box.php
@@ -0,0 +1,53 @@
+ Yii::t('app', 'SCRIBBLES_BUTTON_NO'),
+ true => Yii::t('app', 'SCRIBBLES_BUTTON_YES'),
+];
+
+?>
+
+
+ = Html::button($favoriteButtonTexts[$model->favorite], [
+ 'id' => 'favorite-button',
+ 'class' => 'btn btn-primary btn-block',
+ 'data-scribble-id' => $model->scribble_id,
+ ]) ?>
+
+
+
diff --git a/frontend/web/css/site.css b/frontend/web/css/site.css
index 215209b0..711cb063 100644
--- a/frontend/web/css/site.css
+++ b/frontend/web/css/site.css
@@ -346,6 +346,20 @@ a:focus {
border: 3px solid yellowgreen;
}
+.index-box-header-icon {
+ color: green;
+ cursor: pointer;
+ font-size: 2.5rem;
+ padding: 5px;
+ position: absolute;
+ right: 2%;
+ top: 2%;
+}
+
+.index-box-header-narrow {
+ width: 88%;
+}
+
.tab-pane {
margin-top: 20px;
}
diff --git a/frontend/web/js/character.js b/frontend/web/js/character.js
index c239423a..c65d2359 100644
--- a/frontend/web/js/character.js
+++ b/frontend/web/js/character.js
@@ -22,3 +22,14 @@ $.get(
}
}
);
+
+$(".scribble-button").on('click', function () {
+ $.get(
+ '../character/open-scribble-modal',
+ {key: $(this).data('character-key')},
+ function (data) {
+ $('.modal-body').html(data);
+ $('#scribble-modal').modal();
+ }
+ );
+});