Skip to content

Commit f1d5fd9

Browse files
committed
Fix failing email notifications while case contains Teams.
Better notifications.
1 parent 4f7872f commit f1d5fd9

6 files changed

Lines changed: 72 additions & 7 deletions

File tree

colander/core/api/views.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
from django.http import Http404, HttpResponse, JsonResponse
2+
from django.urls import reverse
23
from rest_framework import mixins, status
34
from rest_framework.authentication import SessionAuthentication, TokenAuthentication
45
from rest_framework.decorators import action
@@ -293,7 +294,8 @@ def perform_create(self, serializer):
293294
CaseContextConsumer.send_message_to_user_consumers(
294295
self.request.user, {
295296
'msg': 'A new drop is available',
296-
'detail': inst.filename
297+
'detail': inst.filename,
298+
'url': reverse('dropped_files_triage_base_view'),
297299
}
298300
)
299301
return inst

colander/core/archives/exporters/__init__.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@ def _archive_export_end(task):
4343
CaseContextConsumer.send_message_to_user_consumers(archive_export.case.owner, {
4444
'msg': 'A new archive is available',
4545
'detail': archive_export.filename,
46+
'url': archive_export.case.get_absolute_url(),
4647
})
4748
notify_case_archive_done(archive_export)
4849

colander/core/notifications.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -41,10 +41,10 @@ def notify_case_archive_done(archive_export: ArchiveExport):
4141
# json.dumps with DjangoJSONEncoder: serialize all django 'objects' as str
4242
# json.loads: get back json object to by stored in database
4343

44-
ctx['case'] = json.loads(json.dumps(model_to_dict(archive_export.case), cls=DjangoJSONEncoder))
44+
ctx['case'] = json.loads(json.dumps(model_to_dict(archive_export.case, exclude=['teams']), cls=DjangoJSONEncoder))
4545
ctx['user'] = json.loads(json.dumps(model_to_dict(archive_export.case.owner, exclude=['password']), cls=DjangoJSONEncoder))
4646
ctx['subject'] = "A new archive is available"
47-
ctx['url'] = f"{base_url}{reverse('case_details_view', kwargs={'pk': str(archive_export.case.id)})}"
47+
ctx['url'] = f"{base_url}{archive_export.case.get_absolute_url()}"
4848

4949
nm = NotificationMessage.objects.create(
5050
template_path='notification/case-archive-done',

colander/frontend/colander-vue-components/NotificationCenter.vue

Lines changed: 37 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -9,25 +9,48 @@ export default {
99
},
1010
data() {
1111
return {
12+
loaded: false,
1213
opened: false,
1314
notifications: [],
1415
lastNotification: null,
1516
};
1617
},
17-
ready() {
18+
created() {
1819
this.$logger(this, 'NotificationCenter');
1920
},
21+
watch: {
22+
'notifications.length'(new_value, old_value) {
23+
if (!this.loaded) return;
24+
this.save();
25+
}
26+
},
2027
mounted() {
2128
this.$bus.on('notification', this.onNotification);
2229
document.addEventListener('click', (evt) => {
2330
if (!evt.target.closest('#notification-center')) {
2431
this.close();
2532
}
2633
});
34+
this.load();
2735
},
2836
methods: {
37+
load() {
38+
let existingNotifications = this.$localStorage.get('notifications', []);
39+
for(let n of existingNotifications) {
40+
this.notifications.push(n);
41+
}
42+
this.loaded = true;
43+
this.$debug('loaded');
44+
},
45+
save() {
46+
this.$localStorage.set('notifications', this.notifications);
47+
this.$debug('saved');
48+
},
2949
onNotification(notification) {
30-
let notif = Object.assign({id: new Date().getTime()}, notification);
50+
let now = new Date().getTime();
51+
let idSuffix = (Math.random() + 1).toString(36).substring(7);
52+
let id = `${now}-${idSuffix}`;
53+
let notif = Object.assign({id: id, timestamp: now}, notification);
3154
this.notifications.splice(0, 0, notif);
3255
this.lastNotification = Object.assign({}, notif);
3356
},
@@ -78,7 +101,7 @@ export default {
78101
<Badge v-if="hasNotifications" :value="notificationCount" :severity="maxSeverity" size="small" class="local-badge" />
79102
</a>
80103
<div class="notification-container">
81-
<Message v-if="lastNotification" severity="info" life="10000" @lifeEnd="dismissLastNotification()">
104+
<Message v-if="lastNotification" severity="info" :life="3000" @lifeEnd="dismissLastNotification()" closable>
82105
<strong class="notification-msg">{{lastNotification.msg}}</strong>
83106
<div v-if="lastNotification.detail" class="notification-detail">{{lastNotification.detail}}</div>
84107
</Message>
@@ -90,6 +113,10 @@ export default {
90113
closable @close="ack(index)">
91114
<strong class="notification-msg">{{notif.msg}}</strong>
92115
<div v-if="notif.detail" class="notification-detail">{{notif.detail}}</div>
116+
<a v-if="notif.url" class="notification-url link-secondary" :href="notif.url">
117+
<i class="fa fa-eye"></i>
118+
Open
119+
</a>
93120
</Message>
94121
</div>
95122
<div v-else>
@@ -122,10 +149,17 @@ a.activable.active
122149
z-index: 10;
123150
124151
.p-message {
152+
position: relative;
125153
margin: 1rem;
126154
box-shadow: 0 0 1rem white;
127155
}
128156
157+
.p-message-close-button {
158+
position: absolute;
159+
right: 0.25rem;
160+
top: 0.25rem;
161+
}
162+
129163
.notification-msg {}
130164
.notification-detail {}
131165
}

colander/frontend/colander/index.js

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,9 +6,10 @@ import ToastService from 'primevue/toastservice';
66
import ColanderTheme from './theme-preset';
77

88
import CachePlugin from './plugins/Cache';
9+
import EventBusPlugin from './plugins/EventBus';
910
import i18nPlugin from './plugins/i18n';
11+
import LocalStorage from "./plugins/LocalStorage";
1012
import LoggerPlugin, {LogLevel} from './plugins/Logger';
11-
import EventBusPlugin from './plugins/EventBus';
1213
import ThemeUtilsPlugin from './plugins/ThemeUtils';
1314

1415
import HarAnalyzerPlugin from 'har-analyzer-vue';
@@ -168,6 +169,7 @@ export default () => {
168169
} catch(err) {}
169170

170171
// -- Plugin registration
172+
colander_application.use(LocalStorage);
171173
colander_application.use(LoggerPlugin, {
172174
logLevel: localLogLevel,
173175
});
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
class LocalStorage {
2+
static DEFAULT_OPTIONS = Object.freeze({ prefix: 'colander' });
3+
4+
options
5+
constructor(options) {
6+
this.options = Object.assign({}, LocalStorage.DEFAULT_OPTIONS, options);
7+
}
8+
_key(subKey) {
9+
return `${this.options.prefix}.${subKey}`;
10+
}
11+
get(key, _default) {
12+
_default ??= "";
13+
let _stored = localStorage.getItem(this._key(key));
14+
if (_stored) return JSON.parse(_stored);
15+
return _default;
16+
}
17+
set(key, value) {
18+
localStorage.setItem(this._key(key), JSON.stringify(value));
19+
}
20+
}
21+
22+
export default {
23+
install(app, options) {
24+
app.config.globalProperties.$localStorage = new LocalStorage(options);
25+
}
26+
};

0 commit comments

Comments
 (0)