Commit c3ce4290e6b76d273a437f76d2cf19bc5a71885b

Authored by Gustavo
1 parent 674108cb

Started bulletin app

bulletin/__init__.py 0 → 100644
bulletin/admin.py 0 → 100644
@@ -0,0 +1,3 @@ @@ -0,0 +1,3 @@
  1 +from django.contrib import admin
  2 +
  3 +# Register your models here.
bulletin/apps.py 0 → 100644
@@ -0,0 +1,5 @@ @@ -0,0 +1,5 @@
  1 +from django.apps import AppConfig
  2 +
  3 +
  4 +class BulletinConfig(AppConfig):
  5 + name = 'bulletin'
bulletin/forms.py 0 → 100644
@@ -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
bulletin/migrations/__init__.py 0 → 100644
bulletin/models.py 0 → 100644
@@ -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')
bulletin/templates/bulletin/_form.html 0 → 100644
@@ -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">&times;</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">&times;</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">&times;</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">&times;</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">&times;</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">&times;</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">&times;</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">&times;</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">&times;</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">&times;</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">&times;</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">&times;</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>
bulletin/templates/bulletin/create.html 0 → 100644
@@ -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 %}
bulletin/templates/bulletin/relatorios.html 0 → 100644
@@ -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">&times;</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">&times;</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">&times;</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 %}
bulletin/templates/bulletin/send_message.html 0 → 100644
@@ -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">&times;</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">&times;</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
bulletin/templates/bulletin/update.html 0 → 100644
@@ -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 %}
bulletin/templates/bulletin/view.html 0 → 100644
@@ -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">&times;</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 %}
bulletin/templates/bulletin/window_view.html 0 → 100644
@@ -0,0 +1 @@ @@ -0,0 +1 @@
  1 +{{ bulletin.content|safe }}
bulletin/tests.py 0 → 100644
@@ -0,0 +1,3 @@ @@ -0,0 +1,3 @@
  1 +from django.test import TestCase
  2 +
  3 +# Create your tests here.
bulletin/urls.py 0 → 100644
@@ -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 +]
bulletin/views.py 0 → 100644
@@ -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