Commit c3ce4290e6b76d273a437f76d2cf19bc5a71885b
1 parent
674108cb
Exists in
master
and in
2 other branches
Started bulletin app
Showing
16 changed files
with
1815 additions
and
0 deletions
Show diff stats
@@ -0,0 +1,128 @@ | @@ -0,0 +1,128 @@ | ||
1 | +# coding=utf-8 | ||
2 | +from django import forms | ||
3 | +from django.utils.translation import ugettext_lazy as _ | ||
4 | +from django.utils.html import strip_tags | ||
5 | + | ||
6 | +from subjects.models import Tag | ||
7 | + | ||
8 | +from .models import Bulletin | ||
9 | + | ||
10 | +from resubmit.widgets import ResubmitFileWidget | ||
11 | + | ||
12 | +class BulletinForm(forms.ModelForm): | ||
13 | + subject = None | ||
14 | + | ||
15 | + def __init__(self, *args, **kwargs): | ||
16 | + super(BulletinForm, self).__init__(*args, **kwargs) | ||
17 | + | ||
18 | + self.subject = kwargs['initial'].get('subject', None) | ||
19 | + | ||
20 | + if self.instance.id: | ||
21 | + self.subject = self.instance.topic.subject | ||
22 | + self.initial['tags'] = ", ".join(self.instance.tags.all().values_list("name", flat = True)) | ||
23 | + | ||
24 | + self.fields['students'].queryset = self.subject.students.all() | ||
25 | + self.fields['groups'].queryset = self.subject.group_subject.all() | ||
26 | + | ||
27 | + tags = forms.CharField(label = _('Tags'), required = False) | ||
28 | + | ||
29 | + class Meta: | ||
30 | + model = Bulletin | ||
31 | + fields = ['name', 'content', 'brief_description', 'all_students', 'students', 'groups', 'show_window', 'visible'] | ||
32 | + labels = { | ||
33 | + 'name': _('Bulletin name'), | ||
34 | + 'content': _('Bulletin content'), | ||
35 | + } | ||
36 | + widgets = { | ||
37 | + 'content': forms.Textarea, | ||
38 | + 'brief_description': forms.Textarea, | ||
39 | + 'students': forms.SelectMultiple, | ||
40 | + 'groups': forms.SelectMultiple, | ||
41 | + } | ||
42 | + | ||
43 | + def clean_name(self): | ||
44 | + name = self.cleaned_data.get('name', '') | ||
45 | + | ||
46 | + topics = self.subject.topic_subject.all() | ||
47 | + | ||
48 | + for topic in topics: | ||
49 | + if self.instance.id: | ||
50 | + same_name = topic.resource_topic.filter(name__unaccent__iexact = name).exclude(id = self.instance.id).count() | ||
51 | + else: | ||
52 | + same_name = topic.resource_topic.filter(name__unaccent__iexact = name).count() | ||
53 | + | ||
54 | + if same_name > 0: | ||
55 | + self._errors['name'] = [_('This subject already has a bulletin with this name')] | ||
56 | + | ||
57 | + return ValueError | ||
58 | + | ||
59 | + return name | ||
60 | + | ||
61 | + def clean_content(self): | ||
62 | + content = self.cleaned_data.get('content', '') | ||
63 | + cleaned_content = strip_tags(content) | ||
64 | + | ||
65 | + if cleaned_content == '': | ||
66 | + self._errors['content'] = [_('This field is required.')] | ||
67 | + | ||
68 | + return ValueError | ||
69 | + | ||
70 | + return content | ||
71 | + | ||
72 | + def save(self, commit = True): | ||
73 | + super(BulletinForm, self).save(commit = True) | ||
74 | + | ||
75 | + self.instance.save() | ||
76 | + | ||
77 | + previous_tags = self.instance.tags.all() | ||
78 | + | ||
79 | + tags = self.cleaned_data['tags'].split(",") | ||
80 | + | ||
81 | + #Excluding unwanted tags | ||
82 | + for prev in previous_tags: | ||
83 | + if not prev.name in tags: | ||
84 | + self.instance.tags.remove(prev) | ||
85 | + | ||
86 | + for tag in tags: | ||
87 | + tag = tag.strip() | ||
88 | + | ||
89 | + exist = Tag.objects.filter(name = tag).exists() | ||
90 | + | ||
91 | + if exist: | ||
92 | + new_tag = Tag.objects.get(name = tag) | ||
93 | + else: | ||
94 | + new_tag = Tag.objects.create(name = tag) | ||
95 | + | ||
96 | + if not new_tag in self.instance.tags.all(): | ||
97 | + self.instance.tags.add(new_tag) | ||
98 | + | ||
99 | + return self.instance | ||
100 | + | ||
101 | +class FormModalMessage(forms.Form): | ||
102 | + MAX_UPLOAD_SIZE = 5*1024*1024 | ||
103 | + | ||
104 | + comment = forms.CharField(widget=forms.Textarea,label=_("Message")) | ||
105 | + image = forms.FileField(widget=ResubmitFileWidget(attrs={'accept':'image/*'}),required=False) | ||
106 | + | ||
107 | + def clean_comment(self): | ||
108 | + comment = self.cleaned_data.get('comment', '') | ||
109 | + cleaned_comment = strip_tags(comment) | ||
110 | + | ||
111 | + if cleaned_comment == '': | ||
112 | + self._errors['comment'] = [_('This field is required.')] | ||
113 | + | ||
114 | + return ValueError | ||
115 | + | ||
116 | + return comment | ||
117 | + | ||
118 | + def clean_image(self): | ||
119 | + image = self.cleaned_data.get('image', False) | ||
120 | + | ||
121 | + if image: | ||
122 | + if hasattr(image, '_size'): | ||
123 | + if image._size > self.MAX_UPLOAD_SIZE: | ||
124 | + self._errors['image'] = [_("The image is too large. It should have less than 5MB.")] | ||
125 | + | ||
126 | + return ValueError | ||
127 | + | ||
128 | + return image |
@@ -0,0 +1,49 @@ | @@ -0,0 +1,49 @@ | ||
1 | +import os | ||
2 | +from django.db import models | ||
3 | +from django.core.exceptions import ValidationError | ||
4 | +from django.utils.translation import ugettext_lazy as _ | ||
5 | +from django.core.urlresolvers import reverse_lazy | ||
6 | + | ||
7 | +from topics.models import Resource | ||
8 | + | ||
9 | +def validate_file_extension(value): | ||
10 | + valid_formats = [ | ||
11 | + 'image/jpeg','image/x-citrix-jpeg','image/png','image/x-citrix-png','image/x-png', | ||
12 | + 'application/vnd.openxmlformats-officedocument.wordprocessingml.document', | ||
13 | + 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet', | ||
14 | + 'application/vnd.openxmlformats-officedocument.presentationml.slideshow', | ||
15 | + 'application/vnd.openxmlformats-officedocument.presentationml.presentation', | ||
16 | + 'application/vnd.ms-excel','text/html','application/msword','application/vnd.oasis.opendocument.presentation', | ||
17 | + 'application/vnd.oasis.opendocument.spreadsheet','application/vnd.oasis.opendocument.text', | ||
18 | + 'application/pdf' | ||
19 | + ] | ||
20 | + | ||
21 | + if hasattr(value.file, 'content_type'): | ||
22 | + if not value.file.content_type in valid_formats: | ||
23 | + raise ValidationError(_('Please select a valid file. The uploaded file must have one of the following extensions: .doc, .docx, .html, .jpg, .odp, .ods, .odt, .pdf, .png, .ppt, .pptx, .xlx e .xlsx')) | ||
24 | + | ||
25 | +class Bulletin(Resource): | ||
26 | + content = models.TextField(_('Bulletin Content'), blank = True) | ||
27 | + file_content = models.FileField(_('File'), blank = True, upload_to = 'files/', validators = [validate_file_extension]) | ||
28 | + | ||
29 | + class Meta: | ||
30 | + verbose_name = _('Bulletin') | ||
31 | + verbose_name_plural = _('Bulletins') | ||
32 | + | ||
33 | + def __str__(self): | ||
34 | + return self.name | ||
35 | + | ||
36 | + def access_link(self): | ||
37 | + if self.show_window: | ||
38 | + return reverse_lazy('bulletin:window_view', args = (), kwargs = {'slug': self.slug}) | ||
39 | + | ||
40 | + return reverse_lazy('bulletin:view', args = (), kwargs = {'slug': self.slug}) | ||
41 | + | ||
42 | + def update_link(self): | ||
43 | + return 'bulletin:update' | ||
44 | + | ||
45 | + def delete_link(self): | ||
46 | + return 'bulletin:delete' | ||
47 | + | ||
48 | + def delete_message(self): | ||
49 | + return _('Are you sure you want delete the bulletin') |
@@ -0,0 +1,347 @@ | @@ -0,0 +1,347 @@ | ||
1 | +{% load static i18n %} | ||
2 | +{% load widget_tweaks %} | ||
3 | + | ||
4 | +<form method="post" action="" enctype="multipart/form-data"> | ||
5 | + {% csrf_token %} | ||
6 | + | ||
7 | + {% render_field form.control_subject %} | ||
8 | + | ||
9 | + <div class="form-group{% if form.has_error %} has-error {% endif %} is-fileinput"> | ||
10 | + <label for="{{ form.name.auto_id }}">{{ form.name.label }} <span>*</span></label> | ||
11 | + {% render_field form.name class='form-control' %} | ||
12 | + | ||
13 | + <span id="helpBlock" class="help-block">{{ form.name.help_text }}</span> | ||
14 | + | ||
15 | + {% if form.name.errors %} | ||
16 | + <div class="alert alert-danger alert-dismissible" role="alert"> | ||
17 | + <button type="button" class="close" data-dismiss="alert" aria-label="Close"> | ||
18 | + <span aria-hidden="true">×</span> | ||
19 | + </button> | ||
20 | + <ul> | ||
21 | + {% for error in form.name.errors %} | ||
22 | + <li>{{ error }}</li> | ||
23 | + {% endfor %} | ||
24 | + </ul> | ||
25 | + </div> | ||
26 | + {% endif %} | ||
27 | + </div> | ||
28 | + | ||
29 | + <div class="form-group{% if form.has_error %} has-error {% endif %} is-fileinput"> | ||
30 | + <label for="{{ form.content.auto_id }}">{{ form.content.label }} <span>*</span></label> | ||
31 | + {% render_field form.content class='form-control text_wysiwyg' %} | ||
32 | + | ||
33 | + <span id="helpBlock" class="help-block">{{ form.content.help_text }}</span> | ||
34 | + | ||
35 | + {% if form.content.errors %} | ||
36 | + <div class="alert alert-danger alert-dismissible" role="alert"> | ||
37 | + <button type="button" class="close" data-dismiss="alert" aria-label="Close"> | ||
38 | + <span aria-hidden="true">×</span> | ||
39 | + </button> | ||
40 | + <ul> | ||
41 | + {% for error in form.content.errors %} | ||
42 | + <li>{{ error }}</li> | ||
43 | + {% endfor %} | ||
44 | + </ul> | ||
45 | + </div> | ||
46 | + {% endif %} | ||
47 | + </div> | ||
48 | + | ||
49 | + <legend>{% trans 'Common resources settings' %}</legend> | ||
50 | + | ||
51 | + <div class="form-group{% if form.has_error %} has-error {% endif %} is-fileinput"> | ||
52 | + <label for="{{ form.brief_description.auto_id }}">{{ form.brief_description.label }}</label> | ||
53 | + {% render_field form.brief_description class='form-control text_wysiwyg' %} | ||
54 | + | ||
55 | + <span id="helpBlock" class="help-block">{{ form.brief_description.help_text }}</span> | ||
56 | + | ||
57 | + {% if form.brief_description.errors %} | ||
58 | + <div class="alert alert-danger alert-dismissible" role="alert"> | ||
59 | + <button type="button" class="close" data-dismiss="alert" aria-label="Close"> | ||
60 | + <span aria-hidden="true">×</span> | ||
61 | + </button> | ||
62 | + <ul> | ||
63 | + {% for error in form.brief_description.errors %} | ||
64 | + <li>{{ error }}</li> | ||
65 | + {% endfor %} | ||
66 | + </ul> | ||
67 | + </div> | ||
68 | + {% endif %} | ||
69 | + </div> | ||
70 | + | ||
71 | + <div class="form-group{% if form.has_error %} has-error {% endif %} is-fileinput"> | ||
72 | + <label for="{{ form.tags.auto_id }}">{{ form.tags.label }}</label> | ||
73 | + {% render_field form.tags class='form-control' data-role="tagsinput" %} | ||
74 | + | ||
75 | + <span id="helpBlock" class="help-block">{{ form.tags.help_text }}</span> | ||
76 | + | ||
77 | + {% if form.tags.errors %} | ||
78 | + <div class="alert alert-danger alert-dismissible" role="alert"> | ||
79 | + <button type="button" class="close" data-dismiss="alert" aria-label="Close"> | ||
80 | + <span aria-hidden="true">×</span> | ||
81 | + </button> | ||
82 | + <ul> | ||
83 | + {% for error in form.tags.errors %} | ||
84 | + <li>{{ error }}</li> | ||
85 | + {% endfor %} | ||
86 | + </ul> | ||
87 | + </div> | ||
88 | + {% endif %} | ||
89 | + </div> | ||
90 | + | ||
91 | + <div class="panel-group" id="professors_accordion" role="tablist" aria-multiselectable="true"> | ||
92 | + <div class="panel panel-info"> | ||
93 | + <div class="panel-heading"> | ||
94 | + <div class="row"> | ||
95 | + <div class="col-md-12"> | ||
96 | + <a data-parent="#professors_accordion" data-toggle="collapse" href="#notifications"> | ||
97 | + <h4 class="panel-title"> | ||
98 | + <button class="btn btn-default btn-xs text-center cat-selector"><i class="fa fa-angle-right fa-2x" aria-hidden="true"></i></button><label>{% trans 'Pendencies Notifications' %}</label> | ||
99 | + </h4> | ||
100 | + </a> | ||
101 | + </div> | ||
102 | + </div> | ||
103 | + </div> | ||
104 | + <div id="notifications" class="panel-collapse collapse"> | ||
105 | + | ||
106 | + <div class="notifies"> | ||
107 | + <div style="text-align:left"> | ||
108 | + {% render_field pendencies_form.id %} | ||
109 | + {% render_field pendencies_form.resource %} | ||
110 | + {% render_field pendencies_form.subject class='pend_subj' %} | ||
111 | + | ||
112 | + <div class="form-group{% if pendencies_form.has_error %} has-error {% endif %} row"> | ||
113 | + <label for="{{ pendencies_form.action.auto_id }}" class="pull-left action_label contol-label"> | ||
114 | + {% trans 'Action not performed by the user' %}: | ||
115 | + </label> | ||
116 | + <div class="col-md-3"> | ||
117 | + {% render_field pendencies_form.action class='form-control' %} | ||
118 | + </div> | ||
119 | + | ||
120 | + <br clear="all" /> | ||
121 | + | ||
122 | + <span id="helpBlock" class="help-block">{{ pendencies_form.action.help_text }}</span> | ||
123 | + | ||
124 | + {% if pendencies_form.action.errors %} | ||
125 | + <div class="alert alert-danger alert-dismissible" role="alert"> | ||
126 | + <button type="button" class="close" data-dismiss="alert" aria-label="Close"> | ||
127 | + <span aria-hidden="true">×</span> | ||
128 | + </button> | ||
129 | + <ul> | ||
130 | + {% for error in pendencies_form.action.errors %} | ||
131 | + <li>{{ error }}</li> | ||
132 | + {% endfor %} | ||
133 | + </ul> | ||
134 | + </div> | ||
135 | + {% endif %} | ||
136 | + </div> | ||
137 | + <br clear="all" /> | ||
138 | + <div class="row"> | ||
139 | + <div class="col-md-12"> | ||
140 | + <p>{% trans 'Wished period' %}: </p> | ||
141 | + </div> | ||
142 | + </div> | ||
143 | + <div class="form-group{% if pendencies_form.has_error %} has-error {% endif %} row"> | ||
144 | + <div class="col-lg-2 col-md-2 col-sm-2 col-xs-3 checkbox"> | ||
145 | + <label> | ||
146 | + {% render_field pendencies_form.begin_date_check class="begin_date" %} {{ pendencies_form.begin_date.label }} | ||
147 | + </label> | ||
148 | + </div> | ||
149 | + <div class="col-lg-4 col-md-4 col-sm-4 col-xs-4"> | ||
150 | + {% render_field pendencies_form.begin_date class='form-control datetime-picker begin_date_input' %} | ||
151 | + </div> | ||
152 | + </div> | ||
153 | + <div class="row"> | ||
154 | + <span id="helpBlock" class="help-block">{{ pendencies_form.begin_date.help_text }}</span> | ||
155 | + | ||
156 | + {% if pendencies_form.begin_date.errors %} | ||
157 | + <div class="alert alert-danger alert-dismissible" role="alert"> | ||
158 | + <button type="button" class="close" data-dismiss="alert" aria-label="Close"> | ||
159 | + <span aria-hidden="true">×</span> | ||
160 | + </button> | ||
161 | + <ul> | ||
162 | + {% for error in pendencies_form.begin_date.errors %} | ||
163 | + <li>{{ error }}</li> | ||
164 | + {% endfor %} | ||
165 | + </ul> | ||
166 | + </div> | ||
167 | + {% endif %} | ||
168 | + </div> | ||
169 | + <div class="form-group{% if pendencies_form.has_error %} has-error {% endif %} row"> | ||
170 | + <div class="col-lg-2 col-md-2 col-sm-2 col-xs-3 checkbox"> | ||
171 | + <label> | ||
172 | + {% render_field pendencies_form.end_date_check class="end_date" %} {{ pendencies_form.end_date.label }} | ||
173 | + </label> | ||
174 | + </div> | ||
175 | + <div class="col-lg-4 col-md-4 col-sm-4 col-xs-4"> | ||
176 | + {% render_field pendencies_form.end_date class='form-control datetime-picker end_date_input' %} | ||
177 | + </div> | ||
178 | + </div> | ||
179 | + <div class="row"> | ||
180 | + <span id="helpBlock" class="help-block">{{ pendencies_form.end_date.help_text }}</span> | ||
181 | + | ||
182 | + {% if pendencies_form.end_date.errors %} | ||
183 | + <div class="alert alert-danger alert-dismissible" role="alert"> | ||
184 | + <button type="button" class="close" data-dismiss="alert" aria-label="Close"> | ||
185 | + <span aria-hidden="true">×</span> | ||
186 | + </button> | ||
187 | + <ul> | ||
188 | + {% for error in pendencies_form.end_date.errors %} | ||
189 | + <li>{{ error }}</li> | ||
190 | + {% endfor %} | ||
191 | + </ul> | ||
192 | + </div> | ||
193 | + {% endif %} | ||
194 | + </div> | ||
195 | + </div> | ||
196 | + </div> | ||
197 | + </div> | ||
198 | + </div> | ||
199 | + | ||
200 | + <div class="panel panel-info"> | ||
201 | + <div class="panel-heading"> | ||
202 | + <div class="row"> | ||
203 | + <div class="col-md-12"> | ||
204 | + <a data-parent="#professors_accordion" data-toggle="collapse" href="#students"> | ||
205 | + <h4 class="panel-title"> | ||
206 | + <button class="btn btn-default btn-xs text-center cat-selector"><i class="fa fa-angle-right fa-2x" aria-hidden="true"></i></button><label for="{{ form.students.auto_id }}">{{ form.students.label }}</label> | ||
207 | + </h4> | ||
208 | + </a> | ||
209 | + </div> | ||
210 | + </div> | ||
211 | + </div> | ||
212 | + <div id="students" class="panel-collapse collapse"> | ||
213 | + <div class="form-group{% if form.has_error %} has-error {% endif %}"> | ||
214 | + <div class=" checkbox"> | ||
215 | + <label for="{{ form.all_students.auto_id }}"> | ||
216 | + {% render_field form.all_students %} {{ form.all_students.label }} | ||
217 | + </label> | ||
218 | + </div> | ||
219 | + | ||
220 | + <span id="helpBlock" class="help-block">{{ form.all_students.help_text }}</span> | ||
221 | + | ||
222 | + {% if form.all_students.errors %} | ||
223 | + <div class="alert alert-danger alert-dismissible" role="alert"> | ||
224 | + <button type="button" class="close" data-dismiss="alert" aria-label="Close"> | ||
225 | + <span aria-hidden="true">×</span> | ||
226 | + </button> | ||
227 | + <ul> | ||
228 | + {% for error in form.all_students.errors %} | ||
229 | + <li>{{ error }}</li> | ||
230 | + {% endfor %} | ||
231 | + </ul> | ||
232 | + </div> | ||
233 | + {% endif %} | ||
234 | + </div> | ||
235 | + | ||
236 | + <p><em>{% trans 'Attribute students to bulletin' %}:</em></p> | ||
237 | + {% render_field form.students class='form-control' %} | ||
238 | + | ||
239 | + <span id="helpBlock" class="help-block">{{ form.students.help_text }}</span> | ||
240 | + | ||
241 | + {% if form.students.errors %} | ||
242 | + <div class="alert alert-danger alert-dismissible" role="alert"> | ||
243 | + <button type="button" class="close" data-dismiss="alert" aria-label="Close"> | ||
244 | + <span aria-hidden="true">×</span> | ||
245 | + </button> | ||
246 | + <ul> | ||
247 | + {% for error in form.students.errors %} | ||
248 | + <li>{{ error }}</li> | ||
249 | + {% endfor %} | ||
250 | + </ul> | ||
251 | + </div> | ||
252 | + {% endif %} | ||
253 | + | ||
254 | + <br clear="all" /> | ||
255 | + | ||
256 | + <p><em>{% trans 'Attribute groups to bulletin' %}:</em></p> | ||
257 | + {% render_field form.groups class='form-control' %} | ||
258 | + | ||
259 | + <span id="helpBlock" class="help-block">{{ form.groups.help_text }}</span> | ||
260 | + | ||
261 | + {% if form.groups.errors %} | ||
262 | + <div class="alert alert-danger alert-dismissible" role="alert"> | ||
263 | + <button type="button" class="close" data-dismiss="alert" aria-label="Close"> | ||
264 | + <span aria-hidden="true">×</span> | ||
265 | + </button> | ||
266 | + <ul> | ||
267 | + {% for error in form.groups.errors %} | ||
268 | + <li>{{ error }}</li> | ||
269 | + {% endfor %} | ||
270 | + </ul> | ||
271 | + </div> | ||
272 | + {% endif %} | ||
273 | + </div> | ||
274 | + </div> | ||
275 | + </div> | ||
276 | + | ||
277 | + <div class="form-group{% if form.has_error %} has-error {% endif %}"> | ||
278 | + <div class=" checkbox"> | ||
279 | + <label for="{{ form.show_window.auto_id }}"> | ||
280 | + {% render_field form.show_window %} {{ form.show_window.label }} | ||
281 | + </label> | ||
282 | + </div> | ||
283 | + | ||
284 | + <span id="helpBlock" class="help-block">{{ form.show_window.help_text }}</span> | ||
285 | + | ||
286 | + {% if form.show_window.errors %} | ||
287 | + <div class="alert alert-danger alert-dismissible" role="alert"> | ||
288 | + <button type="button" class="close" data-dismiss="alert" aria-label="Close"> | ||
289 | + <span aria-hidden="true">×</span> | ||
290 | + </button> | ||
291 | + <ul> | ||
292 | + {% for error in form.show_window.errors %} | ||
293 | + <li>{{ error }}</li> | ||
294 | + {% endfor %} | ||
295 | + </ul> | ||
296 | + </div> | ||
297 | + {% endif %} | ||
298 | + </div> | ||
299 | + | ||
300 | + <div class="form-group{% if form.has_error %} has-error {% endif %}"> | ||
301 | + <div class=" checkbox"> | ||
302 | + <label for="{{ form.visible.auto_id }}"> | ||
303 | + {% render_field form.visible %} {{ form.visible.label }} | ||
304 | + </label> | ||
305 | + </div> | ||
306 | + | ||
307 | + <span id="helpBlock" class="help-block">{{ form.visible.help_text }}</span> | ||
308 | + | ||
309 | + {% if form.visible.errors %} | ||
310 | + <div class="alert alert-danger alert-dismissible" role="alert"> | ||
311 | + <button type="button" class="close" data-dismiss="alert" aria-label="Close"> | ||
312 | + <span aria-hidden="true">×</span> | ||
313 | + </button> | ||
314 | + <ul> | ||
315 | + {% for error in form.visible.errors %} | ||
316 | + <li>{{ error }}</li> | ||
317 | + {% endfor %} | ||
318 | + </ul> | ||
319 | + </div> | ||
320 | + {% endif %} | ||
321 | + </div> | ||
322 | + | ||
323 | + <div class="col-md-12 col-lg-12 col-sm-12 col-xs-12"> | ||
324 | + <div class="text-center"> | ||
325 | + <input type="submit" value="{% trans 'Save' %}" class="btn btn-raised btn-success" /> | ||
326 | + </div> | ||
327 | + </div> | ||
328 | +</form> | ||
329 | +<script type="text/javascript"> | ||
330 | + $(function() { | ||
331 | + var begin_val = $('.begin_date_input').val(), | ||
332 | + end_val = $('.end_date_input').val(); | ||
333 | + | ||
334 | + if (begin_val != '') { | ||
335 | + $(".begin_date").prop('checked', true); | ||
336 | + } | ||
337 | + | ||
338 | + if (end_val != '') { | ||
339 | + $(".end_date").prop('checked', true); | ||
340 | + } | ||
341 | + | ||
342 | + {% if not pendencies_form.is_valid and pendencies_form.is_bound %} | ||
343 | + $("#notifications").collapse('toggle'); | ||
344 | + {% endif %} | ||
345 | + }); | ||
346 | +</script> | ||
347 | +<script type="text/javascript" src="{% static 'js/resources.js' %}"></script> |
@@ -0,0 +1,34 @@ | @@ -0,0 +1,34 @@ | ||
1 | +{% extends 'subjects/view.html' %} | ||
2 | + | ||
3 | +{% load static i18n django_bootstrap_breadcrumbs %} | ||
4 | + | ||
5 | +{% block style %} | ||
6 | + {{block.super}} | ||
7 | + <link rel="stylesheet" type="text/css" href="{% static "css/bootstrap-tagsinput.css" %}"> | ||
8 | +{% endblock %} | ||
9 | + | ||
10 | +{% block javascript %} | ||
11 | + {{block.super}} | ||
12 | + <script type="text/javascript" src="{% static "js/bootstrap-tagsinput.js" %} "></script> | ||
13 | +{% endblock %} | ||
14 | + | ||
15 | +{% block breadcrumbs %} | ||
16 | + {{ block.super }} | ||
17 | + | ||
18 | + {% breadcrumb topic 'subjects:topic_view' topic.subject.slug topic.slug %} | ||
19 | + | ||
20 | + {% trans 'Create Bulletin' as bread %} | ||
21 | + {% breadcrumb bread 'bulletin:create' topic.slug %} | ||
22 | +{% endblock %} | ||
23 | + | ||
24 | +{% block content %} | ||
25 | + <div class="card"> | ||
26 | + <div class="card-content"> | ||
27 | + <div class="card-body"> | ||
28 | + {% include 'bulletin/_form.html' %} | ||
29 | + </div> | ||
30 | + </div> | ||
31 | + </div> | ||
32 | + <br clear="all" /> | ||
33 | + <br clear="all" /> | ||
34 | +{% endblock %} |
@@ -0,0 +1,435 @@ | @@ -0,0 +1,435 @@ | ||
1 | +{% extends "bulletin/view.html" %} | ||
2 | + | ||
3 | +{% load static i18n pagination permissions_tags subject_counter %} | ||
4 | +{% load django_bootstrap_breadcrumbs %} | ||
5 | + | ||
6 | +{% block javascript%} | ||
7 | + {{ block.super }} | ||
8 | + <script type="text/javascript" src="https://www.gstatic.com/charts/loader.js"></script> | ||
9 | + <script type="text/javascript"> | ||
10 | + var tabela_atual = true; | ||
11 | + | ||
12 | + var array_history = []; | ||
13 | + {%for data_json in json_history.data %} | ||
14 | + array_history.push(["{{data_json.0}}","{{data_json.1}}","{{view}}",{% if data_json.3 is not None %}new Date('{{data_json.3.isoformat}}'){% else%}null{% endif %}]); | ||
15 | + {% endfor%} | ||
16 | + var json_history = {"data":array_history}; | ||
17 | + var column_history = [{"string":'{% trans "User" %}'},{"string":'{% trans "Group" %}'},{"string":'{% trans "Action" %}'},{"date":'{% trans "Date of Action" %}'}]; | ||
18 | + | ||
19 | + var search = []; | ||
20 | + for (var i in json_history["data"]){ | ||
21 | + search.push([json_history["data"][i][0],json_history["data"][i][1], | ||
22 | + json_history["data"][i][2],json_history["data"][i][3]]); | ||
23 | + } | ||
24 | + | ||
25 | + var array_n_did = []; | ||
26 | + var checkbox = {}; | ||
27 | + {%for data_json in json_n_did.data%} | ||
28 | + var input = '<div class="checkbox">\ | ||
29 | + <label for="{{data_json.0}}_google_table">\ | ||
30 | + <input id="{{data_json.0}}_google_table" name="{{data_json.0}}_google_table" type="checkbox"><span class="checkbox-material"><span class="check"></span></span>\ | ||
31 | + </label>\ | ||
32 | + </div>' | ||
33 | + checkbox["{{data_json.0}}_google_table"] = "{{data_json.4}}"; | ||
34 | + array_n_did.push([input,"{{data_json.1}}","{{data_json.2}}","{{data_json.3}}"]); | ||
35 | + {% endfor%} | ||
36 | + var json_n_did = {"data":array_n_did}; | ||
37 | + var column_n_did = [{"string":'<a href="javascript:void(0);" onclick="return openmodal();"> {% trans "Send message" %}</a>'},{"string":'{% trans "User" %}'},{"string":'{% trans "Group" %}'},{"string":"{% trans "Action don't realized" %}"}]; | ||
38 | + </script> | ||
39 | + | ||
40 | + | ||
41 | + <script type="text/javascript"> | ||
42 | + google.charts.load('current', {'packages':['corechart',"table"]}); | ||
43 | + google.charts.setOnLoadCallback(drawChart); | ||
44 | + google.charts.setOnLoadCallback(drawTable); | ||
45 | + | ||
46 | + function drawChart() { | ||
47 | + var data = google.visualization.arrayToDataTable({{db_data|safe}}); | ||
48 | + var options = { | ||
49 | + title: '{{title_chart}}', | ||
50 | + // legend: {position: 'right', maxLines: 1}, | ||
51 | + bar: { groupWidth: '30%' }, | ||
52 | + chartArea:{width:"50%"}, | ||
53 | + titlePosition: 'out', | ||
54 | + vAxis: { | ||
55 | + title: '{{title_vAxis}}', | ||
56 | + ticks: [0, .20, .40, .60, .80, 1], | ||
57 | + viewWindow: { | ||
58 | + min: 0, | ||
59 | + max: 1 | ||
60 | + } | ||
61 | + }, | ||
62 | + isStacked: "percent", | ||
63 | + }; | ||
64 | + | ||
65 | + function selectHandler() { | ||
66 | + var selectedItem = chart.getSelection()[0]; | ||
67 | + if (selectedItem) { | ||
68 | + var col = data.getColumnLabel(selectedItem.column); | ||
69 | + if (col == "{{n_did_table}}"){ | ||
70 | + tabela_atual = false; | ||
71 | + search = []; | ||
72 | + for (var i in json_n_did["data"]){ | ||
73 | + search.push([json_n_did["data"][i][0],json_n_did["data"][i][1], | ||
74 | + json_n_did["data"][i][2],json_n_did["data"][i][3]]); | ||
75 | + } | ||
76 | + searcher(col, tabela_atual,true); | ||
77 | + | ||
78 | + } else if (col == "{{did_table}}"){ | ||
79 | + tabela_atual = true; | ||
80 | + search = []; | ||
81 | + for (var i in json_history["data"]){ | ||
82 | + search.push([json_history["data"][i][0],json_history["data"][i][1], | ||
83 | + json_history["data"][i][2],json_history["data"][i][3]]); | ||
84 | + } | ||
85 | + searcher(col, tabela_atual,true); | ||
86 | + } | ||
87 | + scroll("#title-table"); | ||
88 | + } | ||
89 | + chart.setSelection([]) | ||
90 | + } | ||
91 | + | ||
92 | + var chart = new google.visualization.ColumnChart(document.getElementById('chart_div')); | ||
93 | + google.visualization.events.addListener(chart, 'select', selectHandler); | ||
94 | + chart.draw(data, options); | ||
95 | + | ||
96 | + } | ||
97 | + | ||
98 | + var sortAscending = {0:false,1:false,2:false,3:false}; | ||
99 | + function drawTable(columns = column_history,rows = pagination(json_history["data"],1),isdate = true,columndate = 3) { | ||
100 | + var data_table = new google.visualization.DataTable(); | ||
101 | + for (var i in columns){ | ||
102 | + for (var item in columns[i]){ | ||
103 | + data_table.addColumn(item,columns[i][item]); | ||
104 | + } | ||
105 | + } | ||
106 | + | ||
107 | + data_table.addRows(rows); | ||
108 | + var formate_date = new google.visualization.DateFormat({pattern: 'dd/MM/yyyy HH:mm'}); | ||
109 | + if (isdate) formate_date.format(data_table, columndate); | ||
110 | + | ||
111 | + // var methods = []; | ||
112 | + // for (var m in data_table) { | ||
113 | + // if (typeof data_table[m] == "function") { | ||
114 | + // methods.push(m); | ||
115 | + // } | ||
116 | + // } | ||
117 | + // console.log(methods.join(",")); | ||
118 | + var options = { | ||
119 | + sort: "event", | ||
120 | + allowHtml: true, | ||
121 | + cssClassNames : { | ||
122 | + tableRow: 'text-center', | ||
123 | + tableCell: 'text-center', | ||
124 | + headerCell: 'text-center' | ||
125 | + }, | ||
126 | + showRowNumber: true, | ||
127 | + width: '100%', | ||
128 | + height: '100%', | ||
129 | + } | ||
130 | + function ordenar(properties){ | ||
131 | + var columnIndex = properties['column']; | ||
132 | + if (columnIndex > 0) { | ||
133 | + options["sortColumn"] = columnIndex; | ||
134 | + options["sortAscending"] = sortAscending[columnIndex]; | ||
135 | + data_table.sort({column:columnIndex,desc:sortAscending[columnIndex]}); | ||
136 | + sortAscending = {0:false,1:false,2:false,3:false}; | ||
137 | + sortAscending[columnIndex] = !sortAscending[columnIndex]; | ||
138 | + // console.log(sortAscending); | ||
139 | + table.draw(data_table, options); | ||
140 | + } | ||
141 | + } | ||
142 | + | ||
143 | + var table = new google.visualization.Table(document.getElementById('table_div')); | ||
144 | + google.visualization.events.addListener(table, 'sort', function(e) {ordenar(e)}); | ||
145 | + table.draw(data_table, options); | ||
146 | + } | ||
147 | + </script> | ||
148 | +{% endblock%} | ||
149 | + | ||
150 | +{% block breadcrumbs %} | ||
151 | + {{ block.super }} | ||
152 | + {% trans 'Reports' as bread %} | ||
153 | + {% breadcrumb bread bulletin%} | ||
154 | +{% endblock %} | ||
155 | + | ||
156 | +{% block content %} | ||
157 | + <div id="message-top"> | ||
158 | + {% if messages %} | ||
159 | + {% for message in messages %} | ||
160 | + <div class="alert alert-{{ message.tags }} alert-dismissible" role="alert"> | ||
161 | + <button type="button" class="close" data-dismiss="alert" aria-label="Close"> | ||
162 | + <span aria-hidden="true">×</span> | ||
163 | + </button> | ||
164 | + <p>{{ message }}</p> | ||
165 | + </div> | ||
166 | + {% endfor %} | ||
167 | + {% endif %} | ||
168 | + </div> | ||
169 | + <div class="panel panel-info topic-panel"> | ||
170 | + <div class="panel-heading"> | ||
171 | + <div class="row"> | ||
172 | + <div class="col-md-12 category-header"> | ||
173 | + <h4 class="panel-title" style="margin-top: 10px; margin-bottom: 8px"> | ||
174 | + <span>{{bulletin}} / {% trans "Reports" %}</span> | ||
175 | + </h4> | ||
176 | + </div> | ||
177 | + </div> | ||
178 | + </div> | ||
179 | + <div class="row"> | ||
180 | + <div class="col-md-12 text-center"> | ||
181 | + <h4 style="margin-top: 15px; margin-bottom: 10px" ><strong>{% trans "Report of the resource " %}{{bulletin}}</strong></h4> | ||
182 | + </div> | ||
183 | + </div> | ||
184 | + <div class="row"> | ||
185 | + <div class="col-md-12"> | ||
186 | + | ||
187 | + <ul class="list-inline nav-justified"> | ||
188 | + <div id="general-parameters-div"> | ||
189 | + <div class="general-parameters-field"> | ||
190 | + <li class="text-right"><h4>{% trans "Select the period: " %}</h4></li> | ||
191 | + </div> | ||
192 | + <form id="period-form" action="" method="get"> | ||
193 | + <div class="general-parameters-field"> | ||
194 | + <li> <input class="form-control datetime-picker" name="init_date" type="text" required="" value="{% if LANGUAGE_CODE == 'pt-br' %}{{init_date|date:'d/m/Y H:i'}} {% else %} {{init_date|date:'m/d/Y H:i P'}} {% endif %}"></li> | ||
195 | + </div> | ||
196 | + <div class="general-parameters-field"> | ||
197 | + <li><input id="inputdate" class="form-control datetime-picker" name="end_date" type="text" required="" value="{% if LANGUAGE_CODE == 'pt-br' %}{{end_date|date:'d/m/Y H:i'}} {% else %} {{end_date|date:'m/d/Y H:i P'}} {% endif %}"></li> | ||
198 | + </div> | ||
199 | + <li><input type="submit" value="{% trans 'Search' %}" style="margin-left: 15px;" class="btn btn-success btn-raised"></li> | ||
200 | + </form> | ||
201 | + </div> | ||
202 | + <ul> | ||
203 | + </div> | ||
204 | + </div> | ||
205 | + | ||
206 | + <div class="row"> | ||
207 | + <div class="col-md-10 col-md-offset-1"> | ||
208 | + <div id="chart_div" style="height: 500px; margin-top: -50px;"></div> | ||
209 | + </div> | ||
210 | + </div> | ||
211 | + | ||
212 | + <div class="row"> | ||
213 | + <div class="col-md-10 col-md-offset-1"> | ||
214 | + <div class="text-center"> | ||
215 | + <ul class="list-inline nav-justified"> | ||
216 | + <li> | ||
217 | + <ul id="view-table" class="list-inline text-right"> | ||
218 | + <li><h3 id="title-table"></h3></li> | ||
219 | + </ul> | ||
220 | + </li> | ||
221 | + <li> | ||
222 | + <ul class="list-inline text-right"> | ||
223 | + <li><p>{% trans "Filter: " %}</p></li> | ||
224 | + <li><input id="search-input" class="form-control" type="text" name="search" value=""></li> | ||
225 | + </ul> | ||
226 | + </li> | ||
227 | + </ul> | ||
228 | + </div> | ||
229 | + <form id="google-chart-checkbox" action="" method="get"> | ||
230 | + <div id="table_div"></div> | ||
231 | + </form> | ||
232 | + <div class="col-md-12 col-lg-12 col-sm-12 col-xs-12 text-center"> | ||
233 | + <ul class="pagination"> | ||
234 | + | ||
235 | + </ul> | ||
236 | + </div> | ||
237 | + </div> | ||
238 | + </div> | ||
239 | + <div id="modal-message"></div> | ||
240 | + <div class="row"> | ||
241 | + <br><br> | ||
242 | + </div> | ||
243 | + </div> | ||
244 | + | ||
245 | + <script type="text/javascript"> | ||
246 | + | ||
247 | + $("#title-table").text(search.length + " {% trans 'record(s)' %}"); | ||
248 | + function putpagination(data = json_history["data"], load_histoty = true){ | ||
249 | + var len = Math.ceil(data.length / 20); | ||
250 | + $(".pagination").empty(); | ||
251 | + $(".pagination").append('<li class="disabled"><span>«</span></li>'); | ||
252 | + $(".pagination").append('<li id="1" class="active">\ | ||
253 | + <a href="javascript:void(0);" onclick="return clickPagination(1, '+ load_histoty +');">1</a>\ | ||
254 | + </li>'); | ||
255 | + for (var i = 2; i <= len;i++){ | ||
256 | + $(".pagination").append('<li id="' + i + '">\ | ||
257 | + <a href="javascript:void(0);" onclick="return clickPagination(' + i +', ' + load_histoty + ');">' + i + '</a>\ | ||
258 | + </li>'); | ||
259 | + } | ||
260 | + if (len > 1) $(".pagination").append('<li><a href="javascript:void(0);" onclick="return clickPagination(2, '+ load_histoty +');"><span>»</span></a></li>'); | ||
261 | + else $(".pagination").append('<li class="disabled"><span>»</span></li>'); | ||
262 | + }; | ||
263 | + putpagination(); | ||
264 | + | ||
265 | + $('#period-form').submit(function(event) { | ||
266 | + $('<input />').attr('type', 'hidden') | ||
267 | + .attr('name', "language") | ||
268 | + .attr('value', '{{ LANGUAGE_CODE }}') | ||
269 | + .appendTo('#period-form'); | ||
270 | + }); | ||
271 | + function add(element,local, first = false){ | ||
272 | + if (first) $(local).prepend(element); | ||
273 | + else $(local).append(element); | ||
274 | + } | ||
275 | + function text(element){ | ||
276 | + return $(element).text(); | ||
277 | + } | ||
278 | + function length(element) { | ||
279 | + return $(element).length; | ||
280 | + } | ||
281 | + | ||
282 | + $("#search-input").on("keyup",function(){ | ||
283 | + search = []; | ||
284 | + var text = $("#search-input").val(); | ||
285 | + searcher(text,tabela_atual); | ||
286 | + }); | ||
287 | + | ||
288 | + function searcher(text, load_histoty = false,apaga=false){ | ||
289 | + if(apaga){ | ||
290 | + $("#search-input").val(""); | ||
291 | + } | ||
292 | + var data = []; | ||
293 | + if (!load_histoty){ | ||
294 | + data = $.map(json_n_did["data"], function (obj) { | ||
295 | + return $.extend(true, {}, obj); | ||
296 | + }); | ||
297 | + } else { | ||
298 | + data = $.map(json_history["data"], function (obj) { | ||
299 | + return $.extend(true, {}, obj); | ||
300 | + }); | ||
301 | + } | ||
302 | + if (load_histoty){ | ||
303 | + for (var i in data){ | ||
304 | + data[i][3] = moment(data[i][3]).format("DD/MM/YYYY HH:mm"); | ||
305 | + } | ||
306 | + } | ||
307 | + if (load_histoty){ | ||
308 | + for (var i in data){ | ||
309 | + if (data[i][0].toLowerCase().includes(text.toLowerCase()) | ||
310 | + || data[i][1].toLowerCase().includes(text.toLowerCase()) | ||
311 | + || data[i][2].toLowerCase().includes(text.toLowerCase()) | ||
312 | + || data[i][3].toLowerCase().includes(text.toLowerCase())){ | ||
313 | + search.push(json_history["data"][i]); | ||
314 | + } | ||
315 | + } | ||
316 | + } | ||
317 | + else { | ||
318 | + for (var i in data){ | ||
319 | + if (data[i][1].toLowerCase().includes(text.toLowerCase()) | ||
320 | + || data[i][2].toLowerCase().includes(text.toLowerCase()) | ||
321 | + || data[i][3].toLowerCase().includes(text.toLowerCase())){ | ||
322 | + search.push(json_n_did["data"][i]); | ||
323 | + } | ||
324 | + } | ||
325 | + } | ||
326 | + console.log(search); | ||
327 | + if (!load_histoty){ | ||
328 | + drawTable(column_n_did,pagination(search,1),false); | ||
329 | + } else { | ||
330 | + drawTable(column_history,pagination(search,1),true,3); | ||
331 | + } | ||
332 | + $("#title-table").text(search.length + " {% trans 'record(s)' %}"); | ||
333 | + putpagination(search,load_histoty); | ||
334 | + } | ||
335 | + | ||
336 | + function pagination(data,pag){ | ||
337 | + var len = data.length; | ||
338 | + var first = (pag * 20 - 20 < len) ? pag * 20 - 20:len; | ||
339 | + var end = (pag * 20 < len) ? pag * 20:len; | ||
340 | + var search = data.slice(first,end); | ||
341 | + return search; | ||
342 | + } | ||
343 | + | ||
344 | + function clickPagination(pag, load_histoty = false){ | ||
345 | + $(".pagination > li").last().remove(); | ||
346 | + $(".pagination > li").first().remove(); | ||
347 | + | ||
348 | + if (!load_histoty){ | ||
349 | + drawTable(column_n_did,pagination(search,pag),false); | ||
350 | + } else { | ||
351 | + drawTable(column_history,pagination(search,pag),true,3); | ||
352 | + } | ||
353 | + | ||
354 | + if (pag < Math.ceil(search.length / 20)) | ||
355 | + $(".pagination").append('<li><a href="javascript:void(0);" onclick="return clickPagination(' + (pag + 1) + ', '+ load_histoty +');"><span>»</span></a></li>'); | ||
356 | + else $(".pagination").append('<li class="disabled"><span>»</span></li>'); | ||
357 | + if (pag > 1) | ||
358 | + $(".pagination").prepend('<li><a href="javascript:void(0);" onclick="return clickPagination(' + (pag - 1) + ', '+ load_histoty +');"><span>«</span></a></li>'); | ||
359 | + else $(".pagination").prepend('<li class="disabled"><span>«</span></li>'); | ||
360 | + $(".active").removeClass("active"); | ||
361 | + $("#" + pag).addClass("active"); | ||
362 | + } | ||
363 | + | ||
364 | + function openmodal(){ | ||
365 | + $( "#modal-message" ).empty(); | ||
366 | + $.get( "{% url 'bulletin:send_message' bulletin.slug %}", function( data ) { | ||
367 | + $( "#modal-message" ).append( data ); | ||
368 | + $("#send-message-modal").modal("show"); | ||
369 | + }); | ||
370 | + } | ||
371 | + | ||
372 | + function sendMessage(){ | ||
373 | + $("#send-message-modal").modal("hide"); | ||
374 | + var checked = $("#google-chart-checkbox").serializeArray(); | ||
375 | + var email = []; | ||
376 | + for (var i in checked){ | ||
377 | + email.push(checkbox[checked[i]["name"]]); | ||
378 | + } | ||
379 | + $('<input />').attr('type', 'hidden') | ||
380 | + .attr('name', "users[]") | ||
381 | + .attr('value', email) | ||
382 | + .appendTo('#text_chat_form'); | ||
383 | + | ||
384 | + var formData = new FormData($('#text_chat_form').get(0)); | ||
385 | + $.ajax({ | ||
386 | + url: "{% url 'bulletin:send_message' bulletin.slug %}", | ||
387 | + type: "POST", | ||
388 | + data: formData, | ||
389 | + cache: false, | ||
390 | + processData: false, | ||
391 | + contentType: false, | ||
392 | + success: function(data) { | ||
393 | + if (data["message"]){ | ||
394 | + console.log("success"); | ||
395 | + $("body").removeClass("modal-open"); | ||
396 | + $( "#modal-message" ).empty(); | ||
397 | + $(".modal-backdrop.fade.in").remove(); | ||
398 | + $("#message-top").empty(); | ||
399 | + $("#message-top").append('\ | ||
400 | + <div class="alert alert-success alert-dismissible" role="alert">\ | ||
401 | + <button type="button" class="close" data-dismiss="alert" aria-label="Close">\ | ||
402 | + <span aria-hidden="true">×</span>\ | ||
403 | + </button>\ | ||
404 | + <p>' + data["message"] + '</p>\ | ||
405 | + </div>\ | ||
406 | + '); | ||
407 | + $("html, body").animate({ scrollTop: 0 }, "slow"); | ||
408 | + $('#google-chart-checkbox')[0].reset(); | ||
409 | + } else { | ||
410 | + $( "#modal-message" ).empty(); | ||
411 | + $(".modal-backdrop.fade.in").remove(); | ||
412 | + $( "#modal-message" ).append( data ); | ||
413 | + $("#send-message-modal").modal("show"); | ||
414 | + $("html, body").animate({ scrollTop: 0 }, "slow"); | ||
415 | + } | ||
416 | + }, | ||
417 | + error: function(data){ | ||
418 | + $("#message-top").empty(); | ||
419 | + $("#message-top").append('\ | ||
420 | + <div class="alert alert-danger alert-dismissible" role="alert">\ | ||
421 | + <button type="button" class="close" data-dismiss="alert" aria-label="Close">\ | ||
422 | + <span aria-hidden="true">×</span>\ | ||
423 | + </button>\ | ||
424 | + <p>' + data.responseText + '</p>\ | ||
425 | + </div>\ | ||
426 | + '); | ||
427 | + $("html, body").animate({ scrollTop: 0 }, "slow"); | ||
428 | + } | ||
429 | + }); | ||
430 | + } | ||
431 | + function scroll(to){ | ||
432 | + $("html, body").animate({ scrollTop: $(to).offset().top }, "slow"); | ||
433 | + } | ||
434 | + </script> | ||
435 | +{% endblock %} |
@@ -0,0 +1,112 @@ | @@ -0,0 +1,112 @@ | ||
1 | + | ||
2 | + {% load widget_tweaks i18n %} | ||
3 | + <!-- Modal (remember to change the ids!!!) --> | ||
4 | +<div class="modal fade" id="send-message-modal" tabindex="-1" role="dialog" aria-labelledby="myModalLabel"> | ||
5 | + <div class="modal-dialog" role="document"> | ||
6 | + <div class="modal-content"> | ||
7 | + <!-- Modal Body --> | ||
8 | + <div class="modal-body"> | ||
9 | + <form id="text_chat_form" action="" method="POST" enctype="multipart/form-data"> | ||
10 | + {% csrf_token %} | ||
11 | + {% comment %}Area para o Texto{% endcomment %} | ||
12 | + <div class="form-group{% if form.has_error %} has-error {% endif %}"> | ||
13 | + <label for="{{ form.comment.auto_id }}">{{ form.comment.label }}: <span>*</span></label> | ||
14 | + {% render_field form.comment class='form-control text_simple_wysiwyg' %} | ||
15 | + | ||
16 | + <span id="helpBlock" class="help-block">{{ form.comment.help_text }}</span> | ||
17 | + | ||
18 | + {% if form.comment.errors %} | ||
19 | + <div class="alert alert-danger alert-dismissible" role="alert"> | ||
20 | + <button type="button" class="close" data-dismiss="alert" aria-label="Close"> | ||
21 | + <span aria-hidden="true">×</span> | ||
22 | + </button> | ||
23 | + <ul> | ||
24 | + {% for error in form.comment.errors %} | ||
25 | + <li>{{ error }}</li> | ||
26 | + {% endfor %} | ||
27 | + </ul> | ||
28 | + </div> | ||
29 | + {% endif %} | ||
30 | + </div> | ||
31 | + {% comment %}Area para anexar a imagem {% endcomment %} | ||
32 | + <div class="form-group{% if form.has_error %} has-error {% endif %} is-fileinput"> | ||
33 | + {% render_field form.image %} | ||
34 | + | ||
35 | + <div class="filedrag"> | ||
36 | + {% trans 'Click or drop the picture here' %}<br /> | ||
37 | + | ||
38 | + <small>{% trans 'The picture could not exceed 5MB.' %}</small> | ||
39 | + </div> | ||
40 | + | ||
41 | + {% if form.image.errors %} | ||
42 | + <div class="alert alert-danger alert-dismissible" role="alert"> | ||
43 | + <button type="button" class="close" data-dismiss="alert" aria-label="Close"> | ||
44 | + <span aria-hidden="true">×</span> | ||
45 | + </button> | ||
46 | + <ul> | ||
47 | + {% for error in form.image.errors %} | ||
48 | + <li>{{ error }}</li> | ||
49 | + {% endfor %} | ||
50 | + </ul> | ||
51 | + </div> | ||
52 | + {% endif %} | ||
53 | + | ||
54 | + </div> | ||
55 | + </form> | ||
56 | + </div> | ||
57 | + <!-- Modal Footer --> | ||
58 | + <div id="delete-category-footer"class="modal-footer"> | ||
59 | + <!-- Don't remove that!!! --> | ||
60 | + <button type="button" class="btn btn-default btn-raised" data-dismiss="modal">{% trans "Close" %}</button> | ||
61 | + <a href="javascript:void(0)" onclick="return sendMessage()" form="text_chat_form" class="btn btn-success btn-raised erase-button">{% trans "Send" %}</a> | ||
62 | + </div> | ||
63 | + </div> | ||
64 | + </div> | ||
65 | +</div> | ||
66 | + | ||
67 | +<script type="text/javascript"> | ||
68 | + | ||
69 | + $('.text_simple_wysiwyg').summernote({ | ||
70 | + dialogsInBody: true, | ||
71 | + disableDragAndDrop: true, | ||
72 | + height: 150, | ||
73 | + toolbar: [ | ||
74 | + // [groupName, [list of button]] | ||
75 | + ['style', ['bold', 'italic']], | ||
76 | + ['insert', ['link']] | ||
77 | + ] | ||
78 | + }); | ||
79 | + | ||
80 | + if (window.File && window.FileList && window.FileReader) { | ||
81 | + Init(); | ||
82 | + } | ||
83 | + | ||
84 | + function Init() { | ||
85 | + var small = $("#id_image"), | ||
86 | + filedrag = $(".filedrag"), | ||
87 | + common = $(".common-file-input"); | ||
88 | + | ||
89 | + // file select | ||
90 | + small.on("change", FileSelectHandler); | ||
91 | + | ||
92 | + // is XHR2 available? | ||
93 | + var xhr = new XMLHttpRequest(); | ||
94 | + if (xhr.upload) { | ||
95 | + // file drop | ||
96 | + filedrag.on("drop", FileSelectHandler); | ||
97 | + filedrag.attr('style', 'display:block'); | ||
98 | + common.attr('style', 'display:none'); | ||
99 | + } | ||
100 | + } | ||
101 | + | ||
102 | + // file selection | ||
103 | + function FileSelectHandler(e) { | ||
104 | + var files = e.target.files || e.dataTransfer.files, | ||
105 | + parent = $(e.target.offsetParent); | ||
106 | + | ||
107 | + // process all File objects | ||
108 | + for (var i = 0, f; f = files[i]; i++) { | ||
109 | + parent.find('.filedrag').html(f.name); | ||
110 | + } | ||
111 | + } | ||
112 | +</script> | ||
0 | \ No newline at end of file | 113 | \ No newline at end of file |
@@ -0,0 +1,36 @@ | @@ -0,0 +1,36 @@ | ||
1 | +{% extends 'subjects/view.html' %} | ||
2 | + | ||
3 | +{% load static i18n django_bootstrap_breadcrumbs %} | ||
4 | + | ||
5 | +{% block style %} | ||
6 | + {{block.super}} | ||
7 | + <link rel="stylesheet" type="text/css" href="{% static "css/bootstrap-tagsinput.css" %}"> | ||
8 | +{% endblock %} | ||
9 | + | ||
10 | +{% block javascript %} | ||
11 | + {{block.super}} | ||
12 | + <script type="text/javascript" src="{% static "js/bootstrap-tagsinput.js" %} "></script> | ||
13 | +{% endblock %} | ||
14 | + | ||
15 | +{% block breadcrumbs %} | ||
16 | + {{ block.super }} | ||
17 | + | ||
18 | + {% breadcrumb topic 'subjects:topic_view' topic.subject.slug topic.slug %} | ||
19 | + | ||
20 | + {% trans 'Edit: ' as bread %} | ||
21 | + {% with bread|add:bulletin.name as bread_slug %} | ||
22 | + {% breadcrumb bread_slug 'bulletin:update' topic.slug bulletin.slug %} | ||
23 | + {% endwith %} | ||
24 | +{% endblock %} | ||
25 | + | ||
26 | +{% block content %} | ||
27 | + <div class="card"> | ||
28 | + <div class="card-content"> | ||
29 | + <div class="card-body"> | ||
30 | + {% include 'bulletin/_form.html' %} | ||
31 | + </div> | ||
32 | + </div> | ||
33 | + </div> | ||
34 | + <br clear="all" /> | ||
35 | + <br clear="all" /> | ||
36 | +{% endblock %} |
@@ -0,0 +1,58 @@ | @@ -0,0 +1,58 @@ | ||
1 | +{% extends 'subjects/view.html' %} | ||
2 | + | ||
3 | +{% load static i18n pagination permissions_tags subject_counter %} | ||
4 | +{% load django_bootstrap_breadcrumbs %} | ||
5 | + | ||
6 | +{% block javascript%} | ||
7 | + {{ block.super }} | ||
8 | +{% endblock%} | ||
9 | + | ||
10 | +{% block breadcrumbs %} | ||
11 | + {{ block.super }} | ||
12 | + {% breadcrumb topic 'subjects:topic_view' subject.slug topic.slug %} | ||
13 | + {% breadcrumb bulletin 'bulletin:view' bulletin.slug %} | ||
14 | +{% endblock %} | ||
15 | + | ||
16 | +{% block content %} | ||
17 | + {% if messages %} | ||
18 | + {% for message in messages %} | ||
19 | + <div class="alert alert-{{ message.tags }} alert-dismissible" role="alert"> | ||
20 | + <button type="button" class="close" data-dismiss="alert" aria-label="Close"> | ||
21 | + <span aria-hidden="true">×</span> | ||
22 | + </button> | ||
23 | + <p>{{ message }}</p> | ||
24 | + </div> | ||
25 | + {% endfor %} | ||
26 | + {% endif %} | ||
27 | + | ||
28 | + {% resource_permissions request.user bulletin as has_resource_permissions %} | ||
29 | + | ||
30 | + {% if bulletin.visible %} | ||
31 | + <div class="panel panel-info topic-panel"> | ||
32 | + <div class="panel-heading"> | ||
33 | + {% elif has_resource_permissions %} | ||
34 | + <div class="panel panel-info topic-panel-invisible"> | ||
35 | + <div class="panel-heading panel-invisible"> | ||
36 | + {% endif %} | ||
37 | + <div class="row"> | ||
38 | + <div class="col-md-12 category-header"> | ||
39 | + <h4 class="panel-title" style="margin-top: 10px; margin-bottom: 8px"> | ||
40 | + <span>{{ bulletin }}</span> | ||
41 | + </h4> | ||
42 | + | ||
43 | + <div class="col-md-5 pull-right category-card-items"> | ||
44 | + <a href="{% url 'mural:resource_view' bulletin.slug %}" class="pull-right action_icon"> | ||
45 | + <i class="fa fa-list" aria-hidden="true"></i> | ||
46 | + {% resource_mural_number bulletin request.user %} | ||
47 | + </a> | ||
48 | + </div> | ||
49 | + </div> | ||
50 | + </div> | ||
51 | + </div> | ||
52 | + <div id="{{subject.slug}}" class="panel-collapse in collapse category-panel-content"> | ||
53 | + {% autoescape off %} | ||
54 | + {{ bulletin.content }} | ||
55 | + {% endautoescape %} | ||
56 | + </div> | ||
57 | + </div> | ||
58 | +{% endblock %} |
@@ -0,0 +1 @@ | @@ -0,0 +1 @@ | ||
1 | +{{ bulletin.content|safe }} |
@@ -0,0 +1,14 @@ | @@ -0,0 +1,14 @@ | ||
1 | +from django.conf.urls import url | ||
2 | +from django.contrib.auth import views as auth_views | ||
3 | + | ||
4 | +from . import views | ||
5 | + | ||
6 | +urlpatterns = [ | ||
7 | + url(r'^create/(?P<slug>[\w_-]+)/$', views.CreateView.as_view(), name = 'create'), | ||
8 | + url(r'^update/(?P<topic_slug>[\w_-]+)/(?P<slug>[\w_-]+)/$', views.UpdateView.as_view(), name = 'update'), | ||
9 | + url(r'^delete/(?P<slug>[\w_-]+)/$', views.DeleteView.as_view(), name = 'delete'), | ||
10 | + url(r'^window_view/(?P<slug>[\w_-]+)/$', views.NewWindowView.as_view(), name = 'window_view'), | ||
11 | + url(r'^view/(?P<slug>[\w_-]+)/$', views.InsideView.as_view(), name = 'view'), | ||
12 | + url(r'^chart/(?P<slug>[\w_-]+)/$', views.StatisticsView.as_view(), name = 'get_chart'), | ||
13 | + url(r'^send-message/(?P<slug>[\w_-]+)/$', views.SendMessage.as_view(), name = 'send_message'), | ||
14 | +] |
@@ -0,0 +1,590 @@ | @@ -0,0 +1,590 @@ | ||
1 | +from django.shortcuts import get_object_or_404, redirect, render | ||
2 | +from django.views import generic | ||
3 | +from django.contrib import messages | ||
4 | +from django.core.urlresolvers import reverse, reverse_lazy | ||
5 | +from django.utils.translation import ugettext_lazy as _ | ||
6 | +from django.contrib.auth.mixins import LoginRequiredMixin | ||
7 | +from django.http import JsonResponse | ||
8 | + | ||
9 | +from amadeus.permissions import has_subject_permissions, has_resource_permissions | ||
10 | + | ||
11 | +import time | ||
12 | +import datetime | ||
13 | +from log.mixins import LogMixin | ||
14 | + | ||
15 | +from topics.models import Topic | ||
16 | + | ||
17 | +from pendencies.forms import PendenciesForm | ||
18 | + | ||
19 | +from .forms import BulletinForm | ||
20 | +from .models import Bulletin | ||
21 | + | ||
22 | +from log.models import Log | ||
23 | +from chat.models import Conversation, TalkMessages, ChatVisualizations | ||
24 | +from users.models import User | ||
25 | +from subjects.models import Subject | ||
26 | + | ||
27 | +from .forms import FormModalMessage | ||
28 | + | ||
29 | +from django.template.loader import render_to_string | ||
30 | +from django.utils import formats | ||
31 | +import textwrap | ||
32 | +from django.utils.html import strip_tags | ||
33 | +import json | ||
34 | +from channels import Group | ||
35 | + | ||
36 | +class NewWindowView(LoginRequiredMixin, LogMixin, generic.DetailView): | ||
37 | + log_component = 'resources' | ||
38 | + log_action = 'view' | ||
39 | + log_resource = 'bulletin' | ||
40 | + log_context = {} | ||
41 | + | ||
42 | + login_url = reverse_lazy("users:login") | ||
43 | + redirect_field_name = 'next' | ||
44 | + | ||
45 | + template_name = 'bulletin/window_view.html' | ||
46 | + model = Bulletin | ||
47 | + context_object_name = 'bulletin' | ||
48 | + | ||
49 | + def dispatch(self, request, *args, **kwargs): | ||
50 | + slug = self.kwargs.get('slug', '') | ||
51 | + bulletin = get_object_or_404(Bulletin, slug=slug) | ||
52 | + | ||
53 | + if not has_resource_permissions(request.user, bulletin): | ||
54 | + return redirect(reverse_lazy('subjects:home')) | ||
55 | + | ||
56 | + return super(NewWindowView, self).dispatch(request, *args, **kwargs) | ||
57 | + | ||
58 | + def get_context_data(self, **kwargs): | ||
59 | + context = super(NewWindowView, self).get_context_data(**kwargs) | ||
60 | + | ||
61 | + self.log_context['category_id'] = self.object.topic.subject.category.id | ||
62 | + self.log_context['category_name'] = self.object.topic.subject.category.name | ||
63 | + self.log_context['category_slug'] = self.object.topic.subject.category.slug | ||
64 | + self.log_context['subject_id'] = self.object.topic.subject.id | ||
65 | + self.log_context['subject_name'] = self.object.topic.subject.name | ||
66 | + self.log_context['subject_slug'] = self.object.topic.subject.slug | ||
67 | + self.log_context['topic_id'] = self.object.topic.id | ||
68 | + self.log_context['topic_name'] = self.object.topic.name | ||
69 | + self.log_context['topic_slug'] = self.object.topic.slug | ||
70 | + self.log_context['bulletin_id'] = self.object.id | ||
71 | + self.log_context['bulletin_name'] = self.object.name | ||
72 | + self.log_context['bulletin_slug'] = self.object.slug | ||
73 | + self.log_context['timestamp_start'] = str(int(time.time())) | ||
74 | + | ||
75 | + super(NewWindowView, self).createLog(self.request.user, self.log_component, self.log_action, | ||
76 | + self.log_resource, self.log_context) | ||
77 | + | ||
78 | + self.request.session['log_id'] = Log.objects.latest('id').id | ||
79 | + | ||
80 | + return context | ||
81 | + | ||
82 | +class InsideView(LoginRequiredMixin, LogMixin, generic.DetailView): | ||
83 | + log_component = 'resources' | ||
84 | + log_action = 'view' | ||
85 | + log_resource = 'bulletin' | ||
86 | + log_context = {} | ||
87 | + | ||
88 | + login_url = reverse_lazy("users:login") | ||
89 | + redirect_field_name = 'next' | ||
90 | + | ||
91 | + template_name = 'bulletins/view.html' | ||
92 | + model = Bulletin | ||
93 | + context_object_name = 'bulletin' | ||
94 | + | ||
95 | + def dispatch(self, request, *args, **kwargs): | ||
96 | + slug = self.kwargs.get('slug', '') | ||
97 | + bulletin = get_object_or_404(Bulletin, slug=slug) | ||
98 | + | ||
99 | + if not has_resource_permissions(request.user, bulletin): | ||
100 | + return redirect(reverse_lazy('subjects:home')) | ||
101 | + | ||
102 | + return super(InsideView, self).dispatch(request, *args, **kwargs) | ||
103 | + | ||
104 | + def get_context_data(self, **kwargs): | ||
105 | + context = super(InsideView, self).get_context_data(**kwargs) | ||
106 | + | ||
107 | + context['title'] = self.object.name | ||
108 | + | ||
109 | + context['topic'] = self.object.topic | ||
110 | + context['subject'] = self.object.topic.subject | ||
111 | + | ||
112 | + self.log_context['category_id'] = self.object.topic.subject.category.id | ||
113 | + self.log_context['category_name'] = self.object.topic.subject.category.name | ||
114 | + self.log_context['category_slug'] = self.object.topic.subject.category.slug | ||
115 | + self.log_context['subject_id'] = self.object.topic.subject.id | ||
116 | + self.log_context['subject_name'] = self.object.topic.subject.name | ||
117 | + self.log_context['subject_slug'] = self.object.topic.subject.slug | ||
118 | + self.log_context['topic_id'] = self.object.topic.id | ||
119 | + self.log_context['topic_name'] = self.object.topic.name | ||
120 | + self.log_context['topic_slug'] = self.object.topic.slug | ||
121 | + self.log_context['bulletin_id'] = self.object.id | ||
122 | + self.log_context['bulletin_name'] = self.object.name | ||
123 | + self.log_context['bulletin_slug'] = self.object.slug | ||
124 | + self.log_context['timestamp_start'] = str(int(time.time())) | ||
125 | + | ||
126 | + super(InsideView, self).createLog(self.request.user, self.log_component, self.log_action, self.log_resource, self.log_context) | ||
127 | + | ||
128 | + self.request.session['log_id'] = Log.objects.latest('id').id | ||
129 | + | ||
130 | + return context | ||
131 | + | ||
132 | +class CreateView(LoginRequiredMixin, LogMixin, generic.edit.CreateView): | ||
133 | + log_component = 'resources' | ||
134 | + log_action = 'create' | ||
135 | + log_resource = 'bulletin' | ||
136 | + log_context = {} | ||
137 | + | ||
138 | + login_url = reverse_lazy("users:login") | ||
139 | + redirect_field_name = 'next' | ||
140 | + | ||
141 | + template_name = 'bulletin/create.html' | ||
142 | + form_class = BulletinForm | ||
143 | + | ||
144 | + def dispatch(self, request, *args, **kwargs): | ||
145 | + slug = self.kwargs.get('slug', '') | ||
146 | + topic = get_object_or_404(Topic, slug = slug) | ||
147 | + | ||
148 | + if not has_subject_permissions(request.user, topic.subject): | ||
149 | + return redirect(reverse_lazy('subjects:home')) | ||
150 | + | ||
151 | + return super(CreateView, self).dispatch(request, *args, **kwargs) | ||
152 | + | ||
153 | + def get(self, request, *args, **kwargs): | ||
154 | + self.object = None | ||
155 | + | ||
156 | + form_class = self.get_form_class() | ||
157 | + form = self.get_form(form_class) | ||
158 | + | ||
159 | + slug = self.kwargs.get('slug', '') | ||
160 | + topic = get_object_or_404(Topic, slug = slug) | ||
161 | + | ||
162 | + pendencies_form = PendenciesForm(initial = {'subject': topic.subject.id, 'actions': [("", "-------"),("view", _("Visualize"))]}) | ||
163 | + | ||
164 | + return self.render_to_response(self.get_context_data(form = form, pendencies_form = pendencies_form)) | ||
165 | + | ||
166 | + def post(self, request, *args, **kwargs): | ||
167 | + self.object = None | ||
168 | + | ||
169 | + form_class = self.get_form_class() | ||
170 | + form = self.get_form(form_class) | ||
171 | + | ||
172 | + slug = self.kwargs.get('slug', '') | ||
173 | + topic = get_object_or_404(Topic, slug = slug) | ||
174 | + | ||
175 | + pendencies_form = PendenciesForm(self.request.POST, initial = {'subject': topic.subject.id, 'actions': [("", "-------"),("view", _("Visualize"))]}) | ||
176 | + | ||
177 | + if (form.is_valid() and pendencies_form.is_valid()): | ||
178 | + return self.form_valid(form, pendencies_form) | ||
179 | + else: | ||
180 | + return self.form_invalid(form, pendencies_form) | ||
181 | + | ||
182 | + def get_initial(self): | ||
183 | + initial = super(CreateView, self).get_initial() | ||
184 | + | ||
185 | + slug = self.kwargs.get('slug', '') | ||
186 | + | ||
187 | + topic = get_object_or_404(Topic, slug = slug) | ||
188 | + initial['subject'] = topic.subject | ||
189 | + | ||
190 | + return initial | ||
191 | + | ||
192 | + def form_invalid(self, form, pendencies_form): | ||
193 | + return self.render_to_response(self.get_context_data(form = form, pendencies_form = pendencies_form)) | ||
194 | + | ||
195 | + def form_valid(self, form, pendencies_form): | ||
196 | + self.object = form.save(commit = False) | ||
197 | + slug = self.kwargs.get('slug', '') | ||
198 | + topic = get_object_or_404(Topic, slug = slug) | ||
199 | + | ||
200 | + self.object.topic = topic | ||
201 | + self.object.order = topic.resource_topic.count() + 1 | ||
202 | + | ||
203 | + if not self.object.topic.visible and not self.object.topic.repository: | ||
204 | + self.object.visible = False | ||
205 | + | ||
206 | + self.object.save() | ||
207 | + | ||
208 | + pend_form = pendencies_form.save(commit = False) | ||
209 | + pend_form.resource = self.object | ||
210 | + | ||
211 | + if not pend_form.action == "": | ||
212 | + pend_form.save() | ||
213 | + | ||
214 | + self.log_context['category_id'] = self.object.topic.subject.category.id | ||
215 | + self.log_context['category_name'] = self.object.topic.subject.category.name | ||
216 | + self.log_context['category_slug'] = self.object.topic.subject.category.slug | ||
217 | + self.log_context['subject_id'] = self.object.topic.subject.id | ||
218 | + self.log_context['subject_name'] = self.object.topic.subject.name | ||
219 | + self.log_context['subject_slug'] = self.object.topic.subject.slug | ||
220 | + self.log_context['topic_id'] = self.object.topic.id | ||
221 | + self.log_context['topic_name'] = self.object.topic.name | ||
222 | + self.log_context['topic_slug'] = self.object.topic.slug | ||
223 | + self.log_context['bulletin_id'] = self.object.id | ||
224 | + self.log_context['bulletin_name'] = self.object.name | ||
225 | + self.log_context['bulletin_slug'] = self.object.slug | ||
226 | + | ||
227 | + super(CreateView, self).createLog(self.request.user, self.log_component, self.log_action, self.log_resource, self.log_context) | ||
228 | + | ||
229 | + return redirect(self.get_success_url()) | ||
230 | + | ||
231 | + def get_context_data(self, **kwargs): | ||
232 | + context = super(CreateView, self).get_context_data(**kwargs) | ||
233 | + | ||
234 | + context['title'] = _('Create Bulletin') | ||
235 | + | ||
236 | + slug = self.kwargs.get('slug', '') | ||
237 | + topic = get_object_or_404(Topic, slug = slug) | ||
238 | + | ||
239 | + context['topic'] = topic | ||
240 | + context['subject'] = topic.subject | ||
241 | + | ||
242 | + return context | ||
243 | + | ||
244 | + def get_success_url(self): | ||
245 | + messages.success(self.request, _('The Bulletin "%s" was added to the Topic "%s" of the virtual environment "%s" successfully!')%(self.object.name, self.object.topic.name, self.object.topic.subject.name)) | ||
246 | + | ||
247 | + success_url = reverse_lazy('bulletin:view', kwargs = {'slug': self.object.slug}) | ||
248 | + | ||
249 | + if self.object.show_window: | ||
250 | + self.request.session['resources'] = {} | ||
251 | + self.request.session['resources']['new_page'] = True | ||
252 | + self.request.session['resources']['new_page_url'] = reverse('bulletin:window_view', kwargs = {'slug': self.object.slug}) | ||
253 | + | ||
254 | + success_url = reverse_lazy('subjects:view', kwargs = {'slug': self.object.topic.subject.slug}) | ||
255 | + | ||
256 | + return success_url | ||
257 | + | ||
258 | +class UpdateView(LoginRequiredMixin, LogMixin, generic.UpdateView): | ||
259 | + log_component = 'resources' | ||
260 | + log_action = 'update' | ||
261 | + log_resource = 'bulletin' | ||
262 | + log_context = {} | ||
263 | + | ||
264 | + login_url = reverse_lazy("users:login") | ||
265 | + redirect_field_name = 'next' | ||
266 | + | ||
267 | + template_name = 'bulletin/update.html' | ||
268 | + model = Bulletin | ||
269 | + form_class = BulletinForm | ||
270 | + | ||
271 | + def dispatch(self, request, *args, **kwargs): | ||
272 | + slug = self.kwargs.get('topic_slug', '') | ||
273 | + topic = get_object_or_404(Topic, slug = slug) | ||
274 | + | ||
275 | + if not has_subject_permissions(request.user, topic.subject): | ||
276 | + return redirect(reverse_lazy('subjects:home')) | ||
277 | + | ||
278 | + return super(UpdateView, self).dispatch(request, *args, **kwargs) | ||
279 | + | ||
280 | + def get(self, request, *args, **kwargs): | ||
281 | + self.object = self.get_object() | ||
282 | + | ||
283 | + form_class = self.get_form_class() | ||
284 | + form = self.get_form(form_class) | ||
285 | + | ||
286 | + slug = self.kwargs.get('topic_slug', '') | ||
287 | + topic = get_object_or_404(Topic, slug = slug) | ||
288 | + | ||
289 | + pend_form = self.object.pendencies_resource.all() | ||
290 | + | ||
291 | + if len(pend_form) > 0: | ||
292 | + pendencies_form = PendenciesForm(instance = pend_form[0], initial = {'subject': topic.subject.id, 'actions': [("", "-------"),("view", _("Visualize"))]}) | ||
293 | + else: | ||
294 | + pendencies_form = PendenciesForm(initial = {'subject': topic.subject.id, 'actions': [("", "-------"),("view", _("Visualize"))]}) | ||
295 | + | ||
296 | + return self.render_to_response(self.get_context_data(form = form, pendencies_form = pendencies_form)) | ||
297 | + | ||
298 | + def post(self, request, *args, **kwargs): | ||
299 | + self.object = self.get_object() | ||
300 | + | ||
301 | + form_class = self.get_form_class() | ||
302 | + form = self.get_form(form_class) | ||
303 | + | ||
304 | + slug = self.kwargs.get('topic_slug', '') | ||
305 | + topic = get_object_or_404(Topic, slug = slug) | ||
306 | + | ||
307 | + pend_form = self.object.pendencies_resource.all() | ||
308 | + | ||
309 | + if len(pend_form) > 0: | ||
310 | + pendencies_form = PendenciesForm(self.request.POST, instance = pend_form[0], initial = {'subject': topic.subject.id, 'actions': [("", "-------"),("view", _("Visualize"))]}) | ||
311 | + else: | ||
312 | + pendencies_form = PendenciesForm(self.request.POST, initial = {'subject': topic.subject.id, 'actions': [("", "-------"),("view", _("Visualize"))]}) | ||
313 | + | ||
314 | + if (form.is_valid() and pendencies_form.is_valid()): | ||
315 | + return self.form_valid(form, pendencies_form) | ||
316 | + else: | ||
317 | + return self.form_invalid(form, pendencies_form) | ||
318 | + | ||
319 | + def form_invalid(self, form, pendencies_form): | ||
320 | + return self.render_to_response(self.get_context_data(form = form, pendencies_form = pendencies_form)) | ||
321 | + | ||
322 | + def form_valid(self, form, pendencies_form): | ||
323 | + self.object = form.save(commit = False) | ||
324 | + | ||
325 | + if not self.object.topic.visible and not self.object.topic.repository: | ||
326 | + self.object.visible = False | ||
327 | + | ||
328 | + self.object.save() | ||
329 | + | ||
330 | + pend_form = pendencies_form.save(commit = False) | ||
331 | + pend_form.resource = self.object | ||
332 | + | ||
333 | + if not pend_form.action == "": | ||
334 | + pend_form.save() | ||
335 | + | ||
336 | + self.log_context['category_id'] = self.object.topic.subject.category.id | ||
337 | + self.log_context['category_name'] = self.object.topic.subject.category.name | ||
338 | + self.log_context['category_slug'] = self.object.topic.subject.category.slug | ||
339 | + self.log_context['subject_id'] = self.object.topic.subject.id | ||
340 | + self.log_context['subject_name'] = self.object.topic.subject.name | ||
341 | + self.log_context['subject_slug'] = self.object.topic.subject.slug | ||
342 | + self.log_context['topic_id'] = self.object.topic.id | ||
343 | + self.log_context['topic_name'] = self.object.topic.name | ||
344 | + self.log_context['topic_slug'] = self.object.topic.slug | ||
345 | + self.log_context['bulletin_id'] = self.object.id | ||
346 | + self.log_context['bulletin_name'] = self.object.name | ||
347 | + self.log_context['bulletin_slug'] = self.object.slug | ||
348 | + | ||
349 | + super(UpdateView, self).createLog(self.request.user, self.log_component, self.log_action, self.log_resource, self.log_context) | ||
350 | + | ||
351 | + return redirect(self.get_success_url()) | ||
352 | + | ||
353 | + def get_context_data(self, **kwargs): | ||
354 | + context = super(UpdateView, self).get_context_data(**kwargs) | ||
355 | + | ||
356 | + context['title'] = _('Update Bulletin') | ||
357 | + | ||
358 | + slug = self.kwargs.get('topic_slug', '') | ||
359 | + topic = get_object_or_404(Topic, slug = slug) | ||
360 | + | ||
361 | + context['topic'] = topic | ||
362 | + context['subject'] = topic.subject | ||
363 | + | ||
364 | + return context | ||
365 | + | ||
366 | + def get_success_url(self): | ||
367 | + messages.success(self.request, _('The Bulletin "%s" was updated successfully!')%(self.object.name)) | ||
368 | + | ||
369 | + success_url = reverse_lazy('bulletin:view', kwargs = {'slug': self.object.slug}) | ||
370 | + | ||
371 | + if self.object.show_window: | ||
372 | + self.request.session['resources'] = {} | ||
373 | + self.request.session['resources']['new_page'] = True | ||
374 | + self.request.session['resources']['new_page_url'] = reverse('bulletin:window_view', kwargs = {'slug': self.object.slug}) | ||
375 | + | ||
376 | + success_url = reverse_lazy('subjects:view', kwargs = {'slug': self.object.topic.subject.slug}) | ||
377 | + | ||
378 | + return success_url | ||
379 | + | ||
380 | +class DeleteView(LoginRequiredMixin, LogMixin, generic.DeleteView): | ||
381 | + log_component = 'resources' | ||
382 | + log_action = 'delete' | ||
383 | + log_resource = 'bulletin' | ||
384 | + log_context = {} | ||
385 | + | ||
386 | + login_url = reverse_lazy("users:login") | ||
387 | + redirect_field_name = 'next' | ||
388 | + | ||
389 | + template_name = 'resources/delete.html' | ||
390 | + model = Bulletin | ||
391 | + context_object_name = 'resource' | ||
392 | + | ||
393 | + def dispatch(self, request, *args, **kwargs): | ||
394 | + slug = self.kwargs.get('slug', '') | ||
395 | + bulletin = get_object_or_404(Bulletin, slug = slug) | ||
396 | + | ||
397 | + if not has_subject_permissions(request.user, bulletin.topic.subject): | ||
398 | + return redirect(reverse_lazy('subjects:home')) | ||
399 | + | ||
400 | + return super(DeleteView, self).dispatch(request, *args, **kwargs) | ||
401 | + | ||
402 | + def get_success_url(self): | ||
403 | + messages.success(self.request, _('The bulletin "%s" was removed successfully from virtual environment "%s"!')%(self.object.name, self.object.topic.subject.name)) | ||
404 | + | ||
405 | + self.log_context['category_id'] = self.object.topic.subject.category.id | ||
406 | + self.log_context['category_name'] = self.object.topic.subject.category.name | ||
407 | + self.log_context['category_slug'] = self.object.topic.subject.category.slug | ||
408 | + self.log_context['subject_id'] = self.object.topic.subject.id | ||
409 | + self.log_context['subject_name'] = self.object.topic.subject.name | ||
410 | + self.log_context['subject_slug'] = self.object.topic.subject.slug | ||
411 | + self.log_context['topic_id'] = self.object.topic.id | ||
412 | + self.log_context['topic_name'] = self.object.topic.name | ||
413 | + self.log_context['topic_slug'] = self.object.topic.slug | ||
414 | + self.log_context['bulletin_id'] = self.object.id | ||
415 | + self.log_context['bulletin_name'] = self.object.name | ||
416 | + self.log_context['bulletin_slug'] = self.object.slug | ||
417 | + | ||
418 | + super(DeleteView, self).createLog(self.request.user, self.log_component, self.log_action, self.log_resource, self.log_context) | ||
419 | + | ||
420 | + return reverse_lazy('subjects:view', kwargs = {'slug': self.object.topic.subject.slug}) | ||
421 | + | ||
422 | + | ||
423 | +class StatisticsView(LoginRequiredMixin, LogMixin, generic.DetailView): | ||
424 | + log_component = 'resources' | ||
425 | + log_action = 'view_statistics' | ||
426 | + log_resource = 'bulletin' | ||
427 | + log_context = {} | ||
428 | + | ||
429 | + login_url = reverse_lazy("users:login") | ||
430 | + redirect_field_name = 'next' | ||
431 | + model = Bulletin | ||
432 | + template_name = 'bulletin/relatorios.html' | ||
433 | + | ||
434 | + def dispatch(self, request, *args, **kwargs): | ||
435 | + slug = self.kwargs.get('slug', '') | ||
436 | + bulletin = get_object_or_404(Bulletin, slug = slug) | ||
437 | + | ||
438 | + if not has_subject_permissions(request.user, bulletin.topic.subject): | ||
439 | + return redirect(reverse_lazy('subjects:home')) | ||
440 | + | ||
441 | + return super(StatisticsView, self).dispatch(request, *args, **kwargs) | ||
442 | + | ||
443 | + def get_context_data(self, **kwargs): | ||
444 | + context = super(StatisticsView, self).get_context_data(**kwargs) | ||
445 | + | ||
446 | + self.log_context['category_id'] = self.object.topic.subject.category.id | ||
447 | + self.log_context['category_name'] = self.object.topic.subject.category.name | ||
448 | + self.log_context['category_slug'] = self.object.topic.subject.category.slug | ||
449 | + self.log_context['subject_id'] = self.object.topic.subject.id | ||
450 | + self.log_context['subject_name'] = self.object.topic.subject.name | ||
451 | + self.log_context['subject_slug'] = self.object.topic.subject.slug | ||
452 | + self.log_context['topic_id'] = self.object.topic.id | ||
453 | + self.log_context['topic_name'] = self.object.topic.name | ||
454 | + self.log_context['topic_slug'] = self.object.topic.slug | ||
455 | + self.log_context['bulletin_id'] = self.object.id | ||
456 | + self.log_context['bulletin_name'] = self.object.name | ||
457 | + self.log_context['bulletin_slug'] = self.object.slug | ||
458 | + | ||
459 | + super(StatisticsView, self).createLog(self.request.user, self.log_component, self.log_action, self.log_resource, self.log_context) | ||
460 | + | ||
461 | + | ||
462 | + context['title'] = _('Bulletin Reports') | ||
463 | + | ||
464 | + slug = self.kwargs.get('slug') | ||
465 | + bulletin = get_object_or_404(Bulletin, slug = slug) | ||
466 | + | ||
467 | + date_format = "%d/%m/%Y %H:%M" if self.request.GET.get('language','') == 'pt-br' else "%m/%d/%Y %I:%M %p" | ||
468 | + if self.request.GET.get('language','') == "": | ||
469 | + start_date = datetime.datetime.now() - datetime.timedelta(30) | ||
470 | + end_date = datetime.datetime.now() | ||
471 | + else : | ||
472 | + start_date = datetime.datetime.strptime(self.request.GET.get('init_date',''),date_format) | ||
473 | + end_date = datetime.datetime.strptime(self.request.GET.get('end_date',''),date_format) | ||
474 | + context["init_date"] = start_date | ||
475 | + context["end_date"] = end_date | ||
476 | + alunos = bulletin.students.all() | ||
477 | + if bulletin.all_students : | ||
478 | + alunos = bulletin.topic.subject.students.all() | ||
479 | + | ||
480 | + vis_ou = Log.objects.filter(context__contains={'bulletin_id':bulletin.id},resource="bulletin",action="view",user_email__in=(aluno.email for aluno in alunos), datetime__range=(start_date,end_date + datetime.timedelta(minutes = 1))) | ||
481 | + did,n_did,history = str(_("Realized")),str(_("Unrealized")),str(_("Historic")) | ||
482 | + re = [] | ||
483 | + data_n_did,data_history = [],[] | ||
484 | + json_n_did, json_history = {},{} | ||
485 | + | ||
486 | + for log_al in vis_ou.order_by("datetime"): | ||
487 | + data_history.append([str(alunos.get(email=log_al.user_email)), | ||
488 | + ", ".join([str(x) for x in bulletin.topic.subject.group_subject.filter(participants__email=log_al.user_email)]), | ||
489 | + log_al.action,log_al.datetime]) | ||
490 | + | ||
491 | + json_history["data"] = data_history | ||
492 | + | ||
493 | + not_view = alunos.exclude(email__in=[log.user_email for log in vis_ou.distinct("user_email")]) | ||
494 | + index = 0 | ||
495 | + for alun in not_view: | ||
496 | + data_n_did.append([index,str(alun),", ".join([str(x) for x in bulletin.topic.subject.group_subject.filter(participants__email=alun.email)]),str(_('View')), str(alun.email)]) | ||
497 | + index += 1 | ||
498 | + json_n_did["data"] = data_n_did | ||
499 | + | ||
500 | + | ||
501 | + context["json_n_did"] = json_n_did | ||
502 | + context["json_history"] = json_history | ||
503 | + c_visualizou = vis_ou.distinct("user_email").count() | ||
504 | + column_view = str(_('View')) | ||
505 | + re.append([str(_('Bulletin')),did,n_did]) | ||
506 | + re.append([column_view,c_visualizou, alunos.count() - c_visualizou]) | ||
507 | + context['topic'] = bulletin.topic | ||
508 | + context['subject'] = bulletin.topic.subject | ||
509 | + context['db_data'] = re | ||
510 | + context['title_chart'] = _('Actions about resource') | ||
511 | + context['title_vAxis'] = _('Quantity') | ||
512 | + context['view'] = column_view | ||
513 | + context["n_did_table"] = n_did | ||
514 | + context["did_table"] = did | ||
515 | + context["history_table"] = history | ||
516 | + return context | ||
517 | + | ||
518 | + | ||
519 | + | ||
520 | +from django.http import HttpResponse #used to send HTTP 404 error to ajax | ||
521 | + | ||
522 | +class SendMessage(LoginRequiredMixin, LogMixin, generic.edit.FormView): | ||
523 | + log_component = 'resources' | ||
524 | + log_action = 'send' | ||
525 | + log_resource = 'bulletin' | ||
526 | + log_context = {} | ||
527 | + | ||
528 | + login_url = reverse_lazy("users:login") | ||
529 | + redirect_field_name = 'next' | ||
530 | + | ||
531 | + template_name = 'bulletin/send_message.html' | ||
532 | + form_class = FormModalMessage | ||
533 | + | ||
534 | + def dispatch(self, request, *args, **kwargs): | ||
535 | + slug = self.kwargs.get('slug', '') | ||
536 | + bulletin = get_object_or_404(Bulletin, slug = slug) | ||
537 | + self.bulletin = bulletin | ||
538 | + | ||
539 | + if not has_subject_permissions(request.user, bulletin.topic.subject): | ||
540 | + return redirect(reverse_lazy('subjects:home')) | ||
541 | + | ||
542 | + return super(SendMessage, self).dispatch(request, *args, **kwargs) | ||
543 | + | ||
544 | + def form_valid(self, form): | ||
545 | + message = form.cleaned_data.get('comment') | ||
546 | + image = form.cleaned_data.get("image") | ||
547 | + users = (self.request.POST.get('users[]','')).split(",") | ||
548 | + user = self.request.user | ||
549 | + subject = self.bulletin.topic.subject | ||
550 | + | ||
551 | + if (users[0] is not ''): | ||
552 | + for u in users: | ||
553 | + to_user = User.objects.get(email=u) | ||
554 | + talk, create = Conversation.objects.get_or_create(user_one=user,user_two=to_user) | ||
555 | + created = TalkMessages.objects.create(text=message,talk=talk,user=user,subject=subject,image=image) | ||
556 | + | ||
557 | + simple_notify = textwrap.shorten(strip_tags(message), width = 30, placeholder = "...") | ||
558 | + | ||
559 | + if image is not '': | ||
560 | + simple_notify += " ".join(_("[Photo]")) | ||
561 | + | ||
562 | + notification = { | ||
563 | + "type": "chat", | ||
564 | + "subtype": "subject", | ||
565 | + "space": subject.slug, | ||
566 | + "user_icon": created.user.image_url, | ||
567 | + "notify_title": str(created.user), | ||
568 | + "simple_notify": simple_notify, | ||
569 | + "view_url": reverse("chat:view_message", args = (created.id, ), kwargs = {}), | ||
570 | + "complete": render_to_string("chat/_message.html", {"talk_msg": created}, self.request), | ||
571 | + "container": "chat-" + str(created.user.id), | ||
572 | + "last_date": _("Last message in %s")%(formats.date_format(created.create_date, "SHORT_DATETIME_FORMAT")) | ||
573 | + } | ||
574 | + | ||
575 | + notification = json.dumps(notification) | ||
576 | + | ||
577 | + Group("user-%s" % to_user.id).send({'text': notification}) | ||
578 | + | ||
579 | + ChatVisualizations.objects.create(viewed = False, message = created, user = to_user) | ||
580 | + | ||
581 | + success = str(_('The message was successfull sent!')) | ||
582 | + return JsonResponse({"message":success}) | ||
583 | + erro = HttpResponse(str(_("No user selected!"))) | ||
584 | + erro.status_code = 404 | ||
585 | + return erro | ||
586 | + | ||
587 | + def get_context_data(self, **kwargs): | ||
588 | + context = super(SendMessage,self).get_context_data() | ||
589 | + context["bulletin"] = get_object_or_404(Bulletin, slug=self.kwargs.get('slug', '')) | ||
590 | + return context |