nodebb-theme-persona 15.0.0-beta.1 → 15.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (43) hide show
  1. package/lib/controllers.js +2 -2
  2. package/library.js +2 -2
  3. package/package.json +3 -4
  4. package/public/admin.js +1 -1
  5. package/public/persona.js +18 -20
  6. package/scss/account.scss +0 -139
  7. package/scss/persona.scss +0 -1
  8. package/templates/account/categories.tpl +1 -1
  9. package/templates/account/consent.tpl +2 -0
  10. package/templates/account/info.tpl +2 -2
  11. package/templates/account/posts.tpl +1 -1
  12. package/templates/account/profile.tpl +69 -23
  13. package/templates/account/settings.tpl +3 -3
  14. package/templates/account/tags.tpl +1 -1
  15. package/templates/account/topics.tpl +1 -1
  16. package/templates/account/uploads.tpl +1 -1
  17. package/templates/partials/account/header.tpl +10 -10
  18. package/templates/partials/categories/lastpost.tpl +1 -1
  19. package/templates/partials/categories/link.tpl +5 -5
  20. package/templates/partials/category/tags.tpl +1 -1
  21. package/templates/partials/cookie-consent.tpl +2 -2
  22. package/templates/partials/menu.tpl +4 -4
  23. package/templates/partials/notifications_list.tpl +1 -1
  24. package/templates/partials/posts_list_item.tpl +2 -2
  25. package/templates/partials/search-filters.tpl +9 -9
  26. package/templates/partials/search-results.tpl +2 -2
  27. package/templates/partials/tags_list.tpl +4 -1
  28. package/templates/partials/topic/icon.tpl +4 -0
  29. package/templates/partials/topic/post.tpl +2 -2
  30. package/templates/partials/topic/tag.tpl +1 -1
  31. package/templates/partials/topics_list.tpl +3 -3
  32. package/templates/topic.tpl +1 -1
  33. package/scss/groups.scss +0 -111
  34. package/templates/groups/details.tpl +0 -114
  35. package/templates/groups/list.tpl +0 -46
  36. package/templates/groups/members.tpl +0 -6
  37. package/templates/partials/groups/admin.tpl +0 -96
  38. package/templates/partials/groups/badge.tpl +0 -1
  39. package/templates/partials/groups/invited.tpl +0 -33
  40. package/templates/partials/groups/list.tpl +0 -21
  41. package/templates/partials/groups/memberlist.tpl +0 -45
  42. package/templates/partials/groups/pending.tpl +0 -28
  43. package/templates/partials/toast.tpl +0 -19
@@ -1,7 +1,7 @@
1
1
  'use strict';
2
2
 
3
- const accountHelpers = require.main.require('./src/controllers/accounts/helpers');
4
- const helpers = require.main.require('./src/controllers/helpers');
3
+ const accountHelpers = nodebb.require('./src/controllers/accounts/helpers');
4
+ const helpers = nodebb.require('./src/controllers/helpers');
5
5
 
6
6
  const Controllers = module.exports;
7
7
 
package/library.js CHANGED
@@ -1,6 +1,6 @@
1
1
  'use strict';
2
2
 
3
- const meta = require.main.require('./src/meta');
3
+ const meta = nodebb.require('./src/meta');
4
4
 
5
5
  const controllers = require('./lib/controllers');
6
6
 
@@ -8,7 +8,7 @@ const library = module.exports;
8
8
 
9
9
  library.init = async function (params) {
10
10
  const { router, middleware } = params;
11
- const routeHelpers = require.main.require('./src/routes/helpers');
11
+ const routeHelpers = nodebb.require('./src/routes/helpers');
12
12
  routeHelpers.setupAdminPageRoute(router, '/admin/plugins/persona', [], controllers.renderAdminPage);
13
13
 
14
14
  routeHelpers.setupPageRoute(router, '/user/:userslug/theme', [
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "nodebb-theme-persona",
3
- "version": "15.0.0-beta.1",
3
+ "version": "15.0.0",
4
4
  "nbbpm": {
5
5
  "compatibility": "^4.14.0"
6
6
  },
@@ -45,8 +45,7 @@
45
45
  "pulling": "^2.0.0"
46
46
  },
47
47
  "devDependencies": {
48
- "eslint": "9.25.1",
49
- "eslint-config-nodebb": "^1.1.4",
50
- "eslint-plugin-import": "^2.31.0"
48
+ "eslint": "10.5.0",
49
+ "eslint-config-nodebb": "^2.0.3"
51
50
  }
52
51
  }
package/public/admin.js CHANGED
@@ -1,7 +1,7 @@
1
1
  'use strict';
2
2
 
3
3
  define('admin/plugins/persona', ['settings'], function (Settings) {
4
- var ACP = {};
4
+ const ACP = {};
5
5
 
6
6
  ACP.init = function () {
7
7
  Settings.load('persona', $('.persona-settings'));
package/public/persona.js CHANGED
@@ -23,7 +23,7 @@ $(document).ready(function () {
23
23
  document.documentElement.style.setProperty('--panel-offset', `${offset}px`);
24
24
  }
25
25
 
26
- var lastBSEnv = '';
26
+ let lastBSEnv = '';
27
27
  function configureNavbarHiding() {
28
28
  if (!$.fn.autoHidingNavbar) {
29
29
  return;
@@ -37,13 +37,13 @@ $(document).ready(function () {
37
37
  } catch (e) {
38
38
  console.warn('[persona/settings] Unable to parse value for navbar autohiding');
39
39
  }
40
- var env = utils.findBootstrapEnvironment();
40
+ const env = utils.findBootstrapEnvironment();
41
41
  // if env didn't change don't destroy and recreate
42
42
  if (env === lastBSEnv) {
43
43
  return;
44
44
  }
45
45
  lastBSEnv = env;
46
- var navbarEl = $('[component="navbar"]');
46
+ const navbarEl = $('[component="navbar"]');
47
47
  navbarEl.autoHidingNavbar('destroy').removeData('plugin_autoHidingNavbar');
48
48
  navbarEl.css('top', '');
49
49
 
@@ -69,7 +69,7 @@ $(document).ready(function () {
69
69
  if (ajaxify.data.template.topic) {
70
70
  $('.topic .topic-header').css({ top: topValue });
71
71
  } else {
72
- var topicListHeader = $('.topic-list-header');
72
+ const topicListHeader = $('.topic-list-header');
73
73
  if (topicListHeader.length) {
74
74
  topicListHeader.css({ top: topValue });
75
75
  }
@@ -119,24 +119,22 @@ $(document).ready(function () {
119
119
  function setupEditedByIcon() {
120
120
  function activateEditedTooltips() {
121
121
  $('[data-pid] [component="post/editor"]').each(function () {
122
- var el = $(this);
123
- var icon;
124
-
122
+ const el = $(this);
125
123
  if (!el.attr('data-editor')) {
126
124
  return;
127
125
  }
128
126
 
129
- icon = el.closest('[data-pid]').find('.edit-icon').first();
127
+ const icon = el.closest('[data-pid]').find('.edit-icon').first();
130
128
  icon.prop('title', el.text()).tooltip().removeClass('hidden');
131
129
  });
132
130
  }
133
131
 
134
132
  $(window).on('action:posts.edited', function (ev, data) {
135
- var parent = $('[data-pid="' + data.post.pid + '"]');
136
- var icon = parent.find('.edit-icon').filter(function (index, el) {
133
+ const parent = $('[data-pid="' + data.post.pid + '"]');
134
+ const icon = parent.find('.edit-icon').filter(function (index, el) {
137
135
  return parseInt($(el).closest('[data-pid]').attr('data-pid'), 10) === parseInt(data.post.pid, 10);
138
136
  });
139
- var el = parent.find('[component="post/editor"]').first();
137
+ const el = parent.find('[component="post/editor"]').first();
140
138
  icon.prop('title', el.text()).tooltip().removeClass('hidden');
141
139
  });
142
140
 
@@ -157,15 +155,15 @@ $(document).ready(function () {
157
155
  });
158
156
 
159
157
  $(window).on('action:posts.loading', function (ev, data) {
160
- for (var i = 0, ii = data.posts.length; i < ii; i++) {
158
+ for (let i = 0, ii = data.posts.length; i < ii; i++) {
161
159
  (ajaxify.data.topics || ajaxify.data.posts)[data.posts[i].index] = data.posts[i];
162
160
  }
163
161
  });
164
162
  }
165
163
 
166
164
  function generateUserCard(ev) {
167
- var avatar = $(this);
168
- var uid = avatar.parents('[data-uid]').attr('data-uid');
165
+ const avatar = $(this);
166
+ const uid = avatar.parents('[data-uid]').attr('data-uid');
169
167
  const topicOrPost = (ajaxify.data.topics || ajaxify.data.posts || []).find(d => String(d.uid) === String(uid));
170
168
  if (!topicOrPost) return;
171
169
  const user = topicOrPost.user;
@@ -182,7 +180,7 @@ $(document).ready(function () {
182
180
  }
183
181
 
184
182
  app.parseAndTranslate('modules/usercard', user, function (html) {
185
- var card = $(html);
183
+ const card = $(html);
186
184
  avatar.parents('a').after(card.hide());
187
185
 
188
186
  if (String(app.user.uid) === String(user.uid) || !app.user.uid) {
@@ -229,8 +227,8 @@ $(document).ready(function () {
229
227
  function setupFavouriteMorph(parent, uid, username) {
230
228
  require(['api', 'alerts'], function (api, alerts) {
231
229
  parent.find('.btn-morph').click(function (ev) {
232
- var type = $(this).hasClass('plus') ? 'follow' : 'unfollow';
233
- var method = $(this).hasClass('plus') ? 'put' : 'del';
230
+ const type = $(this).hasClass('plus') ? 'follow' : 'unfollow';
231
+ const method = $(this).hasClass('plus') ? 'put' : 'del';
234
232
 
235
233
  api[method]('/users/' + uid + '/follow').then(() => {
236
234
  alerts.success('[[global:alert.' + type + ', ' + username + ']]');
@@ -243,9 +241,9 @@ $(document).ready(function () {
243
241
  $(this).prepend('<b class="drop"></b>');
244
242
  }
245
243
 
246
- var drop = $(this).find('b.drop').removeClass('animate');
247
- var x = ev.pageX - (drop.width() / 2) - $(this).offset().left;
248
- var y = ev.pageY - (drop.height() / 2) - $(this).offset().top;
244
+ const drop = $(this).find('b.drop').removeClass('animate');
245
+ const x = ev.pageX - (drop.width() / 2) - $(this).offset().left;
246
+ const y = ev.pageY - (drop.height() / 2) - $(this).offset().top;
249
247
 
250
248
  drop.css({ top: y + 'px', left: x + 'px' }).addClass('animate');
251
249
  });
package/scss/account.scss CHANGED
@@ -15,136 +15,6 @@
15
15
  margin-right: auto;
16
16
  }
17
17
 
18
- .cover {
19
- background-size: cover;
20
- background-repeat: no-repeat;
21
- height: 200px;
22
- position: relative;
23
- margin-bottom: 1em;
24
- background-origin: content-box;
25
- width: 100%;
26
- top: var(--panel-offset);
27
- position: absolute;
28
- left: auto;
29
- right: 0px;
30
-
31
- .avatar-wrapper {
32
- position: absolute;
33
- left: 50%;
34
- margin-left: -64px;
35
- top: 128px;
36
- border: 4px solid var(--bs-body-bg);
37
- background-color: var(--bs-body-bg);
38
- border-radius: 50%;
39
-
40
- .persona-fab.btn-morph {
41
- top: 93px;
42
- right: 4px;
43
- position: absolute;
44
- }
45
-
46
- .status {
47
- position: absolute;
48
- font-size: 23px;
49
- top: 17px;
50
- right: -3px;
51
- padding: 10px;
52
- }
53
-
54
- .chat, .follow {
55
- position: absolute;
56
- width: 30px;
57
- height: 30px;
58
- }
59
-
60
- .chat {
61
- left: -1px;
62
- bottom: 1px;
63
- }
64
-
65
- .follow {
66
- right: 1px;
67
- bottom: 1px;
68
- }
69
- }
70
-
71
- &:hover .controls, .controls:focus-within {
72
- opacity: 0.8;
73
- }
74
-
75
- .controls {
76
- text-align: center;
77
- height: 200px;
78
- line-height: 200px;
79
- opacity: 0;
80
- @include transition(opacity .15s linear);
81
- cursor: pointer;
82
- pointer-events: none;
83
-
84
- > * {
85
- pointer-events: all;
86
- }
87
-
88
- .fa {
89
- color: white;
90
- background-color: #333;
91
- opacity: 1;
92
- margin: 15px;
93
- padding: 5px;
94
- }
95
- }
96
-
97
- &.active {
98
- &:hover {
99
- cursor: move;
100
- }
101
-
102
- .controls {
103
- > * {
104
- display: none;
105
- }
106
- }
107
-
108
- .save {
109
- display: inline-block;
110
- }
111
- }
112
-
113
- &.saving {
114
- .save {
115
- display: none;
116
- }
117
-
118
- .indicator {
119
- display: inline-block;
120
- }
121
- }
122
-
123
- .save, .indicator {
124
- display: inline-block;
125
- position: absolute;
126
- top: 1em;
127
- right: 2em;
128
- opacity: 1;
129
- background-color: $primary;
130
- color: $gray-200;
131
- padding: 0.5em;
132
- font-weight: bold;
133
-
134
- &:hover {
135
- cursor: pointer;
136
- }
137
- }
138
-
139
- .save {
140
- display: none;
141
- }
142
-
143
- .indicator {
144
- display: none;
145
- }
146
- }
147
-
148
18
  margin-top: 200px;
149
19
 
150
20
  .container {
@@ -272,12 +142,3 @@
272
142
  }
273
143
  }
274
144
 
275
- .user-picture-label {
276
- font-size:20px;
277
- }
278
-
279
-
280
- .account-username{
281
- font-size:20px;
282
- font-weight:bold;
283
- }
package/scss/persona.scss CHANGED
@@ -9,7 +9,6 @@
9
9
  @import "header";
10
10
  @import "status";
11
11
  @import "account";
12
- @import "groups";
13
12
  @import "chats";
14
13
  @import "search";
15
14
  @import "topics_list";
@@ -2,7 +2,7 @@
2
2
 
3
3
  <div>
4
4
  <div class="d-flex justify-content-between align-items-center mb-3">
5
- <h1 class="fs-4">{title}</h1>
5
+ <h1 class="fs-4">{tx(title)}</h1>
6
6
  <div class="mb-2">
7
7
  <div class="dropdown bottom-sheet" component="category/watch/all">
8
8
  <button class="btn btn-outline-secondary dropdown-toggle" data-bs-toggle="dropdown" type="button" aria-haspopup="true" aria-expanded="false">
@@ -51,6 +51,7 @@
51
51
  <p><strong>[[user:consent.right-to-data-portability]]</strong></p>
52
52
  <p>[[user:consent.right-to-data-portability-description]]</p>
53
53
 
54
+ {{{ if canExport }}}
54
55
  <div class="btn-group-vertical d-grid">
55
56
  <a data-action="export-profile" class="btn btn-outline-secondary">
56
57
  <i class="fa fa-download"></i> [[user:consent.export-profile]]
@@ -62,6 +63,7 @@
62
63
  <i class="fa fa-download"></i> [[user:consent.export-uploads]]
63
64
  </a>
64
65
  </div>
66
+ {{{ end }}}
65
67
  </div>
66
68
  </div>
67
69
  </div>
@@ -151,7 +151,7 @@
151
151
  {{{ end }}}
152
152
  </div>
153
153
  <p class="mb-1">
154
- <span class="reason">[[user:info.banned-reason-label]]: <strong>{./reason}</strong></span>
154
+ <span class="reason">[[user:info.banned-reason-label]]: <strong>{tx(./reason)}</strong></span>
155
155
  </p>
156
156
  <p>
157
157
  {{{ if ./until }}}
@@ -205,7 +205,7 @@
205
205
  {{{ end }}}
206
206
  </div>
207
207
  <p class="mb-1">
208
- <span class="reason">[[user:info.banned-reason-label]]: <strong>{./reason}</strong></span>
208
+ <span class="reason">[[user:info.banned-reason-label]]: <strong>{tx(./reason)}</strong></span>
209
209
  </p>
210
210
  <p>
211
211
  {{{ if ./until }}}
@@ -21,7 +21,7 @@
21
21
  </div>
22
22
 
23
23
  <!-- IF !posts.length -->
24
- <div class="alert alert-warning text-center">{noItemsFoundKey}</div>
24
+ <div class="alert alert-warning text-center">{tx(noItemsFoundKey)}</div>
25
25
  <!-- ENDIF !posts.length -->
26
26
 
27
27
  <div class="col-12">
@@ -36,7 +36,7 @@
36
36
  {{{ end }}}
37
37
 
38
38
  <!-- IF aboutme -->
39
- <div component="aboutme" class="text-center aboutme text-secondary w-75 mx-auto text-center">{{aboutmeParsed}}</div>
39
+ <div component="aboutme" class="text-center aboutme text-secondary w-75 mx-auto text-center">{{txEscape(aboutmeParsed)}}</div>
40
40
  <!-- ENDIF aboutme -->
41
41
 
42
42
  <div class="account-stats fs-1 text-center mb-3">
@@ -156,28 +156,74 @@
156
156
  <!-- ENDIF ips.length -->
157
157
 
158
158
  <div class="row">
159
- {{{ if bestPosts.length }}}
160
- <div class="col-lg-12 col-12">
161
- <h1 class="fs-3">[[pages:account/best, {username}]]</h1>
162
-
163
- <div class="col-12">
164
- <ul component="posts" class="posts-list list-unstyled">
165
- {{{each bestPosts}}}
166
- <!-- IMPORT partials/posts_list_item.tpl -->
167
- {{{end}}}
168
- </ul>
169
- </div>
170
- </div>
171
- {{{ end }}}
172
- {{{ if latestPosts.length}}}
173
- <div class="col-lg-12 col-12">
174
- <h1 class="fs-3">[[pages:account/latest-posts, {username}]]</h1>
175
- <div class="col-12">
176
- <ul component="posts" class="posts-list list-unstyled">
177
- {{{each latestPosts}}}
178
- <!-- IMPORT partials/posts_list_item.tpl -->
179
- {{{end}}}
180
- </ul>
159
+ {{{ if (bestPosts.length || latestPosts.length) }}}
160
+ <div class="col-12">
161
+ <ul class="nav nav-tabs mb-3" role="tablist">
162
+ {{{ if bestPosts.length }}}
163
+ <li class="nav-item" role="presentation">
164
+ <button
165
+ class="nav-link active"
166
+ id="best-posts-tab"
167
+ data-bs-toggle="tab"
168
+ data-bs-target="#best-posts-pane"
169
+ type="button"
170
+ role="tab"
171
+ aria-controls="best-posts-pane"
172
+ aria-selected="true"
173
+ >
174
+ [[pages:account/best, {username}]]
175
+ </button>
176
+ </li>
177
+ {{{ end }}}
178
+ {{{ if latestPosts.length }}}
179
+ <li class="nav-item" role="presentation">
180
+ <button
181
+ class="nav-link {{{ if !bestPosts.length }}}active{{{ end }}}"
182
+ id="latest-posts-tab"
183
+ data-bs-toggle="tab"
184
+ data-bs-target="#latest-posts-pane"
185
+ type="button"
186
+ role="tab"
187
+ aria-controls="latest-posts-pane"
188
+ aria-selected="{{{ if !bestPosts.length }}}true{{{ else }}}false{{{ end }}}"
189
+ >
190
+ [[pages:account/latest-posts, {username}]]
191
+ </button>
192
+ </li>
193
+ {{{ end }}}
194
+ </ul>
195
+
196
+ <div class="tab-content">
197
+ {{{ if bestPosts.length }}}
198
+ <div
199
+ class="tab-pane fade show active"
200
+ id="best-posts-pane"
201
+ role="tabpanel"
202
+ aria-labelledby="best-posts-tab"
203
+ tabindex="0"
204
+ >
205
+ <ul component="posts" class="posts-list list-unstyled">
206
+ {{{each bestPosts}}}
207
+ <!-- IMPORT partials/posts_list_item.tpl -->
208
+ {{{end}}}
209
+ </ul>
210
+ </div>
211
+ {{{ end }}}
212
+ {{{ if latestPosts.length }}}
213
+ <div
214
+ class="tab-pane fade {{{ if !bestPosts.length }}}show active{{{ end }}}"
215
+ id="latest-posts-pane"
216
+ role="tabpanel"
217
+ aria-labelledby="latest-posts-tab"
218
+ tabindex="0"
219
+ >
220
+ <ul component="posts" class="posts-list list-unstyled">
221
+ {{{each latestPosts}}}
222
+ <!-- IMPORT partials/posts_list_item.tpl -->
223
+ {{{end}}}
224
+ </ul>
225
+ </div>
226
+ {{{ end }}}
181
227
  </div>
182
228
  </div>
183
229
  {{{ end }}}
@@ -8,7 +8,7 @@
8
8
  <div class="card card-body mb-3">
9
9
  <select class="form-select" id="bootswatchSkin" data-property="bootswatchSkin">
10
10
  {{{each bootswatchSkinOptions}}}
11
- <option value="{bootswatchSkinOptions.value}" <!-- IF bootswatchSkinOptions.selected -->selected<!-- ENDIF bootswatchSkinOptions.selected -->>{bootswatchSkinOptions.name}</option>
11
+ <option value="{bootswatchSkinOptions.value}" {{{ if bootswatchSkinOptions.selected }}}selected{{{ end }}}>{tx(bootswatchSkinOptions.name)}</option>
12
12
  {{{end}}}
13
13
  </select>
14
14
  </div>
@@ -165,7 +165,7 @@
165
165
  <label class="form-label" for="dailyDigestFreq">[[user:digest-label]]</label>
166
166
  <select class="form-select" id="dailyDigestFreq" data-property="dailyDigestFreq" autocomplete="off">
167
167
  {{{each dailyDigestFreqOptions}}}
168
- <option value="{dailyDigestFreqOptions.value}" <!-- IF dailyDigestFreqOptions.selected -->selected="1"<!-- ENDIF dailyDigestFreqOptions.selected -->>{dailyDigestFreqOptions.name}</option>
168
+ <option value="{./value}" {{{ if ./selected }}}selected="1"{{{ end }}}>{tx(./name)}</option>
169
169
  {{{end}}}
170
170
  </select>
171
171
  <p class="form-text">[[user:digest-description]]</p>
@@ -255,7 +255,7 @@
255
255
  <tr component="notification/setting" class="align-middle">
256
256
  <td style="width:100%;">
257
257
  <div class="align-items-center">
258
- <label class="text-sm tracking-tight" for="{./name}">{./label}</label>
258
+ <label class="text-sm tracking-tight" for="{./name}">{tx(./label)}</label>
259
259
  <input type="hidden" data-property="{./name}" value="{./value}">
260
260
  </div>
261
261
  </td>
@@ -1,6 +1,6 @@
1
1
  <!-- IMPORT partials/account/header.tpl -->
2
2
 
3
- <h1 class="fs-4">{title}</h1>
3
+ <h1 class="fs-4">{tx(title)}</h1>
4
4
  <input component="tags/watch" type="text" class="form-control">
5
5
 
6
6
  <!-- IMPORT partials/account/footer.tpl -->
@@ -32,7 +32,7 @@
32
32
 
33
33
 
34
34
  {{{ if !topics.length }}}
35
- <div class="alert alert-warning text-center">{noItemsFoundKey}</div>
35
+ <div class="alert alert-warning text-center">{tx(noItemsFoundKey)}</div>
36
36
  {{{ end }}}
37
37
 
38
38
  <div class="category">
@@ -1,5 +1,5 @@
1
1
  <!-- IMPORT partials/account/header.tpl -->
2
- <h1 class="fs-4">{title}</h1>
2
+ <h1 class="fs-4">{tx(title)}</h1>
3
3
  <!-- IF privateUploads -->
4
4
  <div class="alert alert-info text-center">[[uploads:private-uploads-info]]</div>
5
5
  <!-- ELSE -->
@@ -7,13 +7,13 @@
7
7
  {{{end}}}
8
8
  </div>
9
9
 
10
- <div class="cover" component="account/cover" style="background-image: url({cover:url}); background-position: {cover:position};">
11
- <div class="avatar-wrapper" data-uid="{uid}">
10
+ <div class="cover position-absolute start-0 top-0" component="account/cover" style="background-image: url({cover:url}); background-position: {cover:position};">
11
+ <div class="avatar-wrapper bg-body rounded-circle position-absolute start-50 top-100 translate-middle" data-uid="{uid}" style="padding: 4px;">
12
12
  {{buildAvatar(@value, "128px", true)}}
13
- <span component="user/status" class="position-absolute border border-white border-2 rounded-circle status {status}"><span class="visually-hidden">[[global:{status}]]</span></span>
13
+ <span component="user/status" class="fs-3 position-absolute border border-white border-2 rounded-circle status {status}" style="top: 17px; right: 3px; padding: 10px;"><span class="visually-hidden">[[global:{status}]]</span></span>
14
14
 
15
15
  {{{ if !isSelf }}}
16
- <button class="btn-morph persona-fab {{{ if isFollowing }}}heart{{{ else }}}plus{{{ end }}}" title="{{{ if isFollowing }}}[[global:unfollow]]{{{ else }}}[[global:follow]]{{{ end }}}">
16
+ <button style="top: 93px; right:4px;" class="btn-morph persona-fab position-absolute {{{ if isFollowing }}}heart{{{ else }}}plus{{{ end }}}" title="{{{ if isFollowing }}}[[global:unfollow]]{{{ else }}}[[global:follow]]{{{ end }}}">
17
17
  <span>
18
18
  <span class="s1"></span>
19
19
  <span class="s2"></span>
@@ -25,13 +25,13 @@
25
25
 
26
26
  <div class="container">
27
27
  {{{ if (allowCoverPicture && canEdit) }}}
28
- <div class="controls">
29
- <a href="#" class="upload"><i class="fa fa-fw fa-4x fa-upload"></i></a>
30
- <a href="#" class="resize"><i class="fa fa-fw fa-4x fa-arrows"></i></a>
31
- <a href="#" class="remove"><i class="fa fa-fw fa-4x fa-times"></i></a>
28
+ <div class="controls text-center">
29
+ <span class="upload p-2 m-2 rounded-1 text-bg-light opacity-75"><i class="fa fa-fw fa-upload"></i></span>
30
+ <span class="resize p-2 m-2 rounded-1 text-bg-light opacity-75"><i class="fa fa-fw fa-arrows"></i></span>
31
+ <span class="remove p-2 m-2 rounded-1 text-bg-light opacity-75"><i class="fa fa-fw fa-times"></i></span>
32
32
  </div>
33
- <a href="#" class="save">[[groups:cover-save]] <i class="fa fa-fw fa-floppy-o"></i></a>
34
- <div class="indicator">[[groups:cover-saving]] <i class="fa fa-fw fa-refresh fa-spin"></i></div>
33
+ <a href="#" class="save btn btn-primary">[[groups:cover-save]] <i class="fa fa-fw fa-floppy-o"></i></a>
34
+ <div class="indicator text-bg-primary">[[groups:cover-saving]] <i class="fa fa-fw fa-refresh fa-spin"></i></div>
35
35
  {{{ end }}}
36
36
 
37
37
  <!-- IMPORT partials/account/menu.tpl -->
@@ -8,7 +8,7 @@
8
8
  </div>
9
9
  <div class="post-content text-xs text-break line-clamp-sm-2 lh-sm position-relative flex-fill">
10
10
  <a class="stretched-link" tabindex="-1" href="{config.relative_path}/topic/{./topic.slug}{{{ if ./index }}}/{./index}{{{ end }}}" aria-label="[[global:lastpost]]"></a>
11
- {{./content}}
11
+ {{txEscape(./content)}}
12
12
  </div>
13
13
  </div>
14
14
  {{{ end }}}
@@ -1,5 +1,5 @@
1
- {{{ if ./isSection }}}
2
- {./name}
3
- {{{ else }}}
4
- <a class="text-reset" href="{{{ if ./link }}}{./link}{{{ else }}}{config.relative_path}/category/{./slug}{{{ end }}}" itemprop="url">{./name}</a>
5
- {{{ end }}}
1
+ {{{ if ./isSection }}}
2
+ {tx(./name)}
3
+ {{{ else }}}
4
+ <a class="text-reset" href="{{{ if ./link }}}{./link}{{{ else }}}{config.relative_path}/category/{./slug}{{{ end }}}" itemprop="url">{tx(./name)}</a>
5
+ {{{ end }}}
@@ -1,3 +1,3 @@
1
1
  {{{ each tags }}}
2
- <a href="{config.relative_path}/tags/{./valueEncoded}"><span class="tag tag-item fw-bold tag-class-{./class}" data-tag="{./value}">{./valueEscaped}</span></a>
2
+ <a href="{config.relative_path}/tags/{./valueEncoded}"><span class="tag tag-item fw-bold tag-class-{./class}" data-tag="{./value}">{./value}</span></a>
3
3
  {{{ end }}}
@@ -1,4 +1,4 @@
1
1
  <div class="cookie-consent">
2
- <button class="float-end btn btn-primary">{dismiss}</button>
3
- {message} <a target="_blank" rel="noopener" href="{link_url}">{link}</a>
2
+ <button class="float-end btn btn-primary">{tx(dismiss)}</button>
3
+ {tx(message)} <a target="_blank" rel="noopener" href="{link_url}">{tx(link)}</a>
4
4
  </div>
@@ -47,14 +47,14 @@
47
47
  <ul id="main-nav" class="navbar-nav me-auto mb-2 mb-lg-0">
48
48
  {{{each navigation}}}
49
49
  {{{ if displayMenuItem(@root, @index) }}}
50
- <li class="nav-item {navigation.class}{{{ if navigation.dropdown }}} dropdown{{{ end }}}" title="{navigation.title}">
50
+ <li class="nav-item {navigation.class}{{{ if navigation.dropdown }}} dropdown{{{ end }}}" title="{tx(./title)}">
51
51
  <a class="nav-link navigation-link {{{ if navigation.dropdown }}}dropdown-toggle{{{ end }}}"
52
52
  {{{ if navigation.dropdown }}} href="#" role="button" data-bs-toggle="dropdown" aria-haspopup="true" aria-expanded="false" {{{ else }}} href="{navigation.route}"{{{ end }}} {{{ if navigation.id }}}id="{navigation.id}"{{{ end }}}{{{ if navigation.targetBlank }}} target="_blank"{{{ end }}}>
53
53
  {{{ if navigation.iconClass }}}
54
- <i class="fa fa-fw {navigation.iconClass}" data-content="{navigation.content}"></i>
54
+ <i class="fa fa-fw {navigation.iconClass}" data-content="{./content}"></i>
55
55
  {{{ end }}}
56
- {{{ if navigation.text }}}
57
- <span class="{navigation.textClass}">{navigation.text}</span>
56
+ {{{ if ./text }}}
57
+ <span class="{./textClass}">{tx(./text)}</span>
58
58
  {{{ end }}}
59
59
  {{{ if navigation.dropdown}}}
60
60
  <i class="fa fa-caret-down"></i>
@@ -25,7 +25,7 @@
25
25
 
26
26
  <div class="d-flex flex-grow-1 flex-column gap-1 align-items-start position-relative">
27
27
  <a href="{./path}" class="text-decoration-none d-inline-block text-reset text-break text-sm ff-sans stretched-link" component="notifications/item/link">
28
- {./bodyShort}
28
+ {{./bodyShort}}
29
29
  </a>
30
30
  {{{ if ./bodyLong}}}
31
31
  <div class="text-secondary text-sm line-clamp-2 text-contain hidden-blockquote hidden-pre hidden-first-child-br">