Commit 5c581c0badb24b6062be2a4ad419247c17be3d39

Authored by Zambom
1 parent 7cfca3d7

Adding file link creation

amadeus/settings.py
@@ -61,6 +61,7 @@ INSTALLED_APPS = [ @@ -61,6 +61,7 @@ INSTALLED_APPS = [
61 'students_group', 61 'students_group',
62 'topics', 62 'topics',
63 'pendencies', 63 'pendencies',
  64 + 'file_link',
64 'webpage', 65 'webpage',
65 'mailsender', 66 'mailsender',
66 'security', 67 'security',
amadeus/static/js/resources.js 0 → 100644
@@ -0,0 +1,135 @@ @@ -0,0 +1,135 @@
  1 +$('#id_groups').multiSelect({
  2 + selectableHeader: "<input type='text' class='search-input category-search-users' autocomplete='off' placeholder=' '>",
  3 + selectionHeader: "<input type='text' class='search-input category-search-users' autocomplete='off' placeholder=''>",
  4 + afterInit: function(ms){
  5 + var that = this,
  6 + $selectableSearch = that.$selectableUl.prev(),
  7 + $selectionSearch = that.$selectionUl.prev(),
  8 + selectableSearchString = '#'+that.$container.attr('id')+' .ms-elem-selectable:not(.ms-selected)',
  9 + selectionSearchString = '#'+that.$container.attr('id')+' .ms-elem-selection.ms-selected';
  10 +
  11 + that.qs1 = $selectableSearch.quicksearch(selectableSearchString)
  12 + .on('keydown', function(e){
  13 + if (e.which === 40){
  14 + that.$selectableUl.focus();
  15 + return false;
  16 + }
  17 + });
  18 +
  19 + that.qs2 = $selectionSearch.quicksearch(selectionSearchString)
  20 + .on('keydown', function(e){
  21 + if (e.which == 40){
  22 + that.$selectionUl.focus();
  23 + return false;
  24 + }
  25 + });
  26 + },
  27 + afterSelect: function(){
  28 + this.qs1.cache();
  29 + this.qs2.cache();
  30 + },
  31 + afterDeselect: function(){
  32 + this.qs1.cache();
  33 + this.qs2.cache();
  34 + }
  35 +});// Used to create multi-select css style
  36 +
  37 +$('#id_students').multiSelect({
  38 + selectableHeader: "<input type='text' class='search-input category-search-users' autocomplete='off' placeholder=' '>",
  39 + selectionHeader: "<input type='text' class='search-input category-search-users' autocomplete='off' placeholder=''>",
  40 + afterInit: function(ms){
  41 + var that = this,
  42 + $selectableSearch = that.$selectableUl.prev(),
  43 + $selectionSearch = that.$selectionUl.prev(),
  44 + selectableSearchString = '#'+that.$container.attr('id')+' .ms-elem-selectable:not(.ms-selected)',
  45 + selectionSearchString = '#'+that.$container.attr('id')+' .ms-elem-selection.ms-selected';
  46 +
  47 + that.qs1 = $selectableSearch.quicksearch(selectableSearchString)
  48 + .on('keydown', function(e){
  49 + if (e.which === 40){
  50 + that.$selectableUl.focus();
  51 + return false;
  52 + }
  53 + });
  54 +
  55 + that.qs2 = $selectionSearch.quicksearch(selectionSearchString)
  56 + .on('keydown', function(e){
  57 + if (e.which == 40){
  58 + that.$selectionUl.focus();
  59 + return false;
  60 + }
  61 + });
  62 + },
  63 + afterSelect: function(){
  64 + this.qs1.cache();
  65 + this.qs2.cache();
  66 + },
  67 + afterDeselect: function(){
  68 + this.qs1.cache();
  69 + this.qs2.cache();
  70 + }
  71 +});// Used to create multi-select css style
  72 +
  73 +$('.collapse').on('show.bs.collapse', function (e) {
  74 + if($(this).is(e.target)){
  75 + var btn = $(this).parent().find('.fa-angle-right');
  76 +
  77 + btn.switchClass("fa-angle-right", "fa-angle-down", 250, "easeInOutQuad");
  78 + }
  79 +});
  80 +
  81 +$('.collapse').on('hide.bs.collapse', function (e) {
  82 + if($(this).is(e.target)){
  83 + var btn = $(this).parent().find('.fa-angle-down');
  84 +
  85 + btn.switchClass("fa-angle-down", "fa-angle-right", 250, "easeInOutQuad");
  86 + }
  87 +});
  88 +
  89 +$('.begin_date_input').on('click', function () {
  90 + var checkbox = $(this).parent().parent().find('.begin_date');
  91 +
  92 + $(checkbox).prop('checked', true);
  93 +});
  94 +
  95 +$('.end_date_input').on('click', function () {
  96 + var checkbox = $(this).parent().parent().find('.end_date');
  97 +
  98 + $(checkbox).prop('checked', true);
  99 +});
  100 +
  101 +// check if browser supports drag n drop
  102 +// call initialization file
  103 +if (window.File && window.FileList && window.FileReader) {
  104 + Init();
  105 +}
  106 +
  107 +// initialize
  108 +function Init() {
  109 + var small = $("#id_file_content"),
  110 + filedrag = $(".filedrag"),
  111 + common = $(".common-file-input");
  112 +
  113 + // file select
  114 + small.on("change", FileSelectHandler);
  115 +
  116 + // is XHR2 available?
  117 + var xhr = new XMLHttpRequest();
  118 + if (xhr.upload) {
  119 + // file drop
  120 + filedrag.on("drop", FileSelectHandler);
  121 + filedrag.attr('style', 'display:block');
  122 + common.attr('style', 'display:none');
  123 + }
  124 +}
  125 +
  126 +// file selection
  127 +function FileSelectHandler(e) {
  128 + var files = e.target.files || e.dataTransfer.files,
  129 + parent = $(e.target.offsetParent);
  130 +
  131 + // process all File objects
  132 + for (var i = 0, f; f = files[i]; i++) {
  133 + parent.find('.filedrag').html(f.name);
  134 + }
  135 +}
0 \ No newline at end of file 136 \ No newline at end of file
amadeus/urls.py
@@ -30,6 +30,7 @@ urlpatterns = [ @@ -30,6 +30,7 @@ urlpatterns = [
30 url(r'^groups/', include('students_group.urls', namespace = 'groups')), 30 url(r'^groups/', include('students_group.urls', namespace = 'groups')),
31 url(r'^topics/', include('topics.urls', namespace = 'topics')), 31 url(r'^topics/', include('topics.urls', namespace = 'topics')),
32 url(r'^webpages/', include('webpage.urls', namespace = 'webpages')), 32 url(r'^webpages/', include('webpage.urls', namespace = 'webpages')),
  33 + url(r'^file_links/', include('file_link.urls', namespace = 'file_links')),
33 url(r'^mailsender/', include('mailsender.urls', namespace = 'mailsender')), 34 url(r'^mailsender/', include('mailsender.urls', namespace = 'mailsender')),
34 url(r'^security/', include('security.urls', namespace = 'security')), 35 url(r'^security/', include('security.urls', namespace = 'security')),
35 url(r'^themes/', include('themes.urls', namespace = 'themes')), 36 url(r'^themes/', include('themes.urls', namespace = 'themes')),
file_link/__init__.py 0 → 100644
file_link/admin.py 0 → 100644
@@ -0,0 +1,3 @@ @@ -0,0 +1,3 @@
  1 +from django.contrib import admin
  2 +
  3 +# Register your models here.
file_link/apps.py 0 → 100644
@@ -0,0 +1,5 @@ @@ -0,0 +1,5 @@
  1 +from django.apps import AppConfig
  2 +
  3 +
  4 +class FileLinkConfig(AppConfig):
  5 + name = 'file_link'
file_link/forms.py 0 → 100644
@@ -0,0 +1,97 @@ @@ -0,0 +1,97 @@
  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 FileLink
  9 +
  10 +class FileLinkForm(forms.ModelForm):
  11 + subject = None
  12 + MAX_UPLOAD_SIZE = 10*1024*1024
  13 +
  14 + def __init__(self, *args, **kwargs):
  15 + super(FileLinkForm, self).__init__(*args, **kwargs)
  16 +
  17 + self.subject = kwargs['initial'].get('subject', None)
  18 +
  19 + if self.instance.id:
  20 + self.subject = self.instance.topic.subject
  21 + self.initial['tags'] = ", ".join(self.instance.tags.all().values_list("name", flat = True))
  22 +
  23 + self.fields['students'].queryset = self.subject.students.all()
  24 + self.fields['groups'].queryset = self.subject.group_subject.all()
  25 +
  26 + tags = forms.CharField(label = _('Tags'), required = False)
  27 +
  28 + class Meta:
  29 + model = FileLink
  30 + fields = ['name', 'file_content', 'brief_description', 'all_students', 'students', 'groups', 'visible']
  31 + labels = {
  32 + 'name': _('File name'),
  33 + }
  34 + widgets = {
  35 + 'brief_description': forms.Textarea,
  36 + 'students': forms.SelectMultiple,
  37 + 'groups': forms.SelectMultiple,
  38 + }
  39 +
  40 + def clean_name(self):
  41 + name = self.cleaned_data.get('name', '')
  42 +
  43 + topics = self.subject.topic_subject.all()
  44 +
  45 + for topic in topics:
  46 + if self.instance.id:
  47 + same_name = topic.resource_topic.filter(name__unaccent__iexact = name).exclude(id = self.instance.id).count()
  48 + else:
  49 + same_name = topic.resource_topic.filter(name__unaccent__iexact = name).count()
  50 +
  51 + if same_name > 0:
  52 + self._errors['name'] = [_('This subject already has a file link with this name')]
  53 +
  54 + return ValueError
  55 +
  56 + return name
  57 +
  58 + def clean_file_content(self):
  59 + file_content = self.cleaned_data.get('file_content', False)
  60 +
  61 + if file_content:
  62 + if hasattr(file_content, '_size'):
  63 + if file_content._size > self.MAX_UPLOAD_SIZE:
  64 + self._errors['file_content'] = [_("The file is too large. It should have less than 10MB.")]
  65 +
  66 + return ValueError
  67 +
  68 + return file_content
  69 +
  70 + def save(self, commit = True):
  71 + super(FileLinkForm, self).save(commit = True)
  72 +
  73 + self.instance.save()
  74 +
  75 + previous_tags = self.instance.tags.all()
  76 +
  77 + tags = self.cleaned_data['tags'].split(",")
  78 +
  79 + #Excluding unwanted tags
  80 + for prev in previous_tags:
  81 + if not prev.name in tags:
  82 + self.instance.tags.remove(prev)
  83 +
  84 + for tag in tags:
  85 + tag = tag.strip()
  86 +
  87 + exist = Tag.objects.filter(name = tag).exists()
  88 +
  89 + if exist:
  90 + new_tag = Tag.objects.get(name = tag)
  91 + else:
  92 + new_tag = Tag.objects.create(name = tag)
  93 +
  94 + if not new_tag in self.instance.tags.all():
  95 + self.instance.tags.add(new_tag)
  96 +
  97 + return self.instance
0 \ No newline at end of file 98 \ No newline at end of file
file_link/migrations/0001_initial.py 0 → 100644
@@ -0,0 +1,31 @@ @@ -0,0 +1,31 @@
  1 +# -*- coding: utf-8 -*-
  2 +# Generated by Django 1.10 on 2017-01-24 21:48
  3 +from __future__ import unicode_literals
  4 +
  5 +from django.db import migrations, models
  6 +import django.db.models.deletion
  7 +import file_link.models
  8 +
  9 +
  10 +class Migration(migrations.Migration):
  11 +
  12 + initial = True
  13 +
  14 + dependencies = [
  15 + ('topics', '0007_auto_20170123_1911'),
  16 + ]
  17 +
  18 + operations = [
  19 + migrations.CreateModel(
  20 + name='FileLink',
  21 + fields=[
  22 + ('resource_ptr', models.OneToOneField(auto_created=True, on_delete=django.db.models.deletion.CASCADE, parent_link=True, primary_key=True, serialize=False, to='topics.Resource')),
  23 + ('file_content', models.FileField(upload_to='files/', validators=[file_link.models.validate_file_extension], verbose_name='File')),
  24 + ],
  25 + options={
  26 + 'verbose_name': 'File Link',
  27 + 'verbose_name_plural': 'File Links',
  28 + },
  29 + bases=('topics.resource',),
  30 + ),
  31 + ]
file_link/migrations/__init__.py 0 → 100644
file_link/models.py 0 → 100644
@@ -0,0 +1,40 @@ @@ -0,0 +1,40 @@
  1 +from django.db import models
  2 +from django.core.exceptions import ValidationError
  3 +from django.utils.translation import ugettext_lazy as _
  4 +
  5 +from topics.models import Resource
  6 +
  7 +def validate_file_extension(value):
  8 + valid_formats = [
  9 + 'image/jpeg','image/x-citrix-jpeg','image/png','image/x-citrix-png','image/x-png',
  10 + 'application/vnd.openxmlformats-officedocument.wordprocessingml.document',
  11 + 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
  12 + 'application/vnd.openxmlformats-officedocument.presentationml.slideshow',
  13 + 'application/vnd.openxmlformats-officedocument.presentationml.presentation',
  14 + 'application/vnd.ms-excel','text/html','application/msword','application/vnd.oasis.opendocument.presentation',
  15 + 'application/vnd.oasis.opendocument.spreadsheet','application/vnd.oasis.opendocument.text',
  16 + 'application/pdf'
  17 + ]
  18 +
  19 + if hasattr(value.file, 'content_type'):
  20 + if not value.file.content_type in valid_formats:
  21 + raise ValidationError(_('File not supported.'))
  22 +
  23 +class FileLink(Resource):
  24 + file_content = models.FileField(_('File'), upload_to = 'files/', validators = [validate_file_extension])
  25 +
  26 + class Meta:
  27 + verbose_name = _('File Link')
  28 + verbose_name_plural = _('File Links')
  29 +
  30 + def __str__(self):
  31 + return self.name
  32 +
  33 + def access_link(self):
  34 + return 'webpages:view'
  35 +
  36 + def update_link(self):
  37 + return 'webpages:update'
  38 +
  39 + def delete_link(self):
  40 + return 'webpages:delete'
file_link/templates/file_links/_form.html 0 → 100644
@@ -0,0 +1,338 @@ @@ -0,0 +1,338 @@
  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 + {% render_field form.file_content %}
  31 +
  32 + <div class="input-group common-file-input">
  33 + <input type="text" readonly="" class="form-control" placeholder="{% trans 'Choose your file...' %}">
  34 + <span class="input-group-btn input-group-sm">
  35 + <button type="button" class="btn btn-fab btn-fab-mini">
  36 + <i class="material-icons">attach_file</i>
  37 + </button>
  38 + </span>
  39 + </div>
  40 +
  41 + <div class="filedrag">
  42 + {% trans 'Click or drop the file here' %}<br />
  43 +
  44 + <small>{% trans 'The file could not exceed 10MB.' %}</small>
  45 + </div>
  46 +
  47 + <span id="helpBlock" class="help-block">{{ form.file_content.help_text }}</span>
  48 +
  49 + {% if form.file_content.errors %}
  50 + <div class="alert alert-danger alert-dismissible" role="alert">
  51 + <button type="button" class="close" data-dismiss="alert" aria-label="Close">
  52 + <span aria-hidden="true">&times;</span>
  53 + </button>
  54 + <ul>
  55 + {% for error in form.file_content.errors %}
  56 + <li>{{ error }}</li>
  57 + {% endfor %}
  58 + </ul>
  59 + </div>
  60 + {% endif %}
  61 + </div>
  62 +
  63 + <legend>{% trans 'Common resources settings' %}</legend>
  64 +
  65 + <div class="form-group{% if form.has_error %} has-error {% endif %} is-fileinput">
  66 + <label for="{{ form.brief_description.auto_id }}">{{ form.brief_description.label }}</label>
  67 + {% render_field form.brief_description class='form-control text_wysiwyg' %}
  68 +
  69 + <span id="helpBlock" class="help-block">{{ form.brief_description.help_text }}</span>
  70 +
  71 + {% if form.brief_description.errors %}
  72 + <div class="alert alert-danger alert-dismissible" role="alert">
  73 + <button type="button" class="close" data-dismiss="alert" aria-label="Close">
  74 + <span aria-hidden="true">&times;</span>
  75 + </button>
  76 + <ul>
  77 + {% for error in form.brief_description.errors %}
  78 + <li>{{ error }}</li>
  79 + {% endfor %}
  80 + </ul>
  81 + </div>
  82 + {% endif %}
  83 + </div>
  84 +
  85 + <div class="form-group{% if form.has_error %} has-error {% endif %} is-fileinput">
  86 + <label for="{{ form.tags.auto_id }}">{{ form.tags.label }}</label>
  87 + {% render_field form.tags class='form-control' data-role="tagsinput" %}
  88 +
  89 + <span id="helpBlock" class="help-block">{{ form.tags.help_text }}</span>
  90 +
  91 + {% if form.tags.errors %}
  92 + <div class="alert alert-danger alert-dismissible" role="alert">
  93 + <button type="button" class="close" data-dismiss="alert" aria-label="Close">
  94 + <span aria-hidden="true">&times;</span>
  95 + </button>
  96 + <ul>
  97 + {% for error in form.tags.errors %}
  98 + <li>{{ error }}</li>
  99 + {% endfor %}
  100 + </ul>
  101 + </div>
  102 + {% endif %}
  103 + </div>
  104 +
  105 + <div class="panel-group" id="professors_accordion" role="tablist" aria-multiselectable="true">
  106 + <div class="panel panel-info">
  107 + <div class="panel-heading">
  108 + <div class="row">
  109 + <div class="col-md-12">
  110 + <a data-parent="#professors_accordion" data-toggle="collapse" href="#notifications">
  111 + <h4 class="panel-title">
  112 + <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>
  113 + </h4>
  114 + </a>
  115 + </div>
  116 + </div>
  117 + </div>
  118 + <div id="notifications" class="panel-collapse collapse">
  119 +
  120 + <div class="notifies">
  121 + <div style="text-align:left">
  122 + {% render_field pendencies_form.id %}
  123 + {% render_field pendencies_form.resource %}
  124 + {% render_field pendencies_form.subject class='pend_subj' %}
  125 +
  126 + <div class="form-group{% if pendencies_form.has_error %} has-error {% endif %} row">
  127 + <label for="{{ pendencies_form.action.auto_id }}" class="pull-left action_label contol-label">
  128 + {% trans 'Action not performed by the user' %}:
  129 + </label>
  130 + <div class="col-md-3">
  131 + {% render_field pendencies_form.action class='form-control' %}
  132 + </div>
  133 +
  134 + <br clear="all" />
  135 +
  136 + <span id="helpBlock" class="help-block">{{ pendencies_form.action.help_text }}</span>
  137 +
  138 + {% if pendencies_form.action.errors %}
  139 + <div class="alert alert-danger alert-dismissible" role="alert">
  140 + <button type="button" class="close" data-dismiss="alert" aria-label="Close">
  141 + <span aria-hidden="true">&times;</span>
  142 + </button>
  143 + <ul>
  144 + {% for error in pendencies_form.action.errors %}
  145 + <li>{{ error }}</li>
  146 + {% endfor %}
  147 + </ul>
  148 + </div>
  149 + {% endif %}
  150 + </div>
  151 + <br clear="all" />
  152 + <div class="row">
  153 + <div class="col-md-12">
  154 + <p>{% trans 'Wished period' %}: </p>
  155 + </div>
  156 + </div>
  157 + <div class="form-group{% if pendencies_form.has_error %} has-error {% endif %} row">
  158 + <div class="col-lg-2 col-md-2 col-sm-2 col-xs-3 checkbox">
  159 + <label>
  160 + {% render_field pendencies_form.begin_date_check class="begin_date" %} {{ pendencies_form.begin_date.label }}
  161 + </label>
  162 + </div>
  163 + <div class="col-lg-4 col-md-4 col-sm-4 col-xs-4">
  164 + {% render_field pendencies_form.begin_date class='form-control datetime-picker begin_date_input' %}
  165 + </div>
  166 + </div>
  167 + <div class="row">
  168 + <span id="helpBlock" class="help-block">{{ pendencies_form.begin_date.help_text }}</span>
  169 +
  170 + {% if pendencies_form.begin_date.errors %}
  171 + <div class="alert alert-danger alert-dismissible" role="alert">
  172 + <button type="button" class="close" data-dismiss="alert" aria-label="Close">
  173 + <span aria-hidden="true">&times;</span>
  174 + </button>
  175 + <ul>
  176 + {% for error in pendencies_form.begin_date.errors %}
  177 + <li>{{ error }}</li>
  178 + {% endfor %}
  179 + </ul>
  180 + </div>
  181 + {% endif %}
  182 + </div>
  183 + <div class="form-group{% if pendencies_form.has_error %} has-error {% endif %} row">
  184 + <div class="col-lg-2 col-md-2 col-sm-2 col-xs-3 checkbox">
  185 + <label>
  186 + {% render_field pendencies_form.end_date_check class="end_date" %} {{ pendencies_form.end_date.label }}
  187 + </label>
  188 + </div>
  189 + <div class="col-lg-4 col-md-4 col-sm-4 col-xs-4">
  190 + {% render_field pendencies_form.end_date class='form-control datetime-picker end_date_input' %}
  191 + </div>
  192 + </div>
  193 + <div class="row">
  194 + <span id="helpBlock" class="help-block">{{ pendencies_form.end_date.help_text }}</span>
  195 +
  196 + {% if pendencies_form.end_date.errors %}
  197 + <div class="alert alert-danger alert-dismissible" role="alert">
  198 + <button type="button" class="close" data-dismiss="alert" aria-label="Close">
  199 + <span aria-hidden="true">&times;</span>
  200 + </button>
  201 + <ul>
  202 + {% for error in pendencies_form.end_date.errors %}
  203 + <li>{{ error }}</li>
  204 + {% endfor %}
  205 + </ul>
  206 + </div>
  207 + {% endif %}
  208 + </div>
  209 + </div>
  210 + </div>
  211 + </div>
  212 + </div>
  213 +
  214 + <div class="panel panel-info">
  215 + <div class="panel-heading">
  216 + <div class="row">
  217 + <div class="col-md-12">
  218 + <a data-parent="#professors_accordion" data-toggle="collapse" href="#students">
  219 + <h4 class="panel-title">
  220 + <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>
  221 + </h4>
  222 + </a>
  223 + </div>
  224 + </div>
  225 + </div>
  226 + <div id="students" class="panel-collapse collapse">
  227 + <div class="form-group{% if form.has_error %} has-error {% endif %}">
  228 + <div class=" checkbox">
  229 + <label for="{{ form.all_students.auto_id }}">
  230 + {% render_field form.all_students %} {{ form.all_students.label }}
  231 + </label>
  232 + </div>
  233 +
  234 + <span id="helpBlock" class="help-block">{{ form.all_students.help_text }}</span>
  235 +
  236 + {% if form.all_students.errors %}
  237 + <div class="alert alert-danger alert-dismissible" role="alert">
  238 + <button type="button" class="close" data-dismiss="alert" aria-label="Close">
  239 + <span aria-hidden="true">&times;</span>
  240 + </button>
  241 + <ul>
  242 + {% for error in form.all_students.errors %}
  243 + <li>{{ error }}</li>
  244 + {% endfor %}
  245 + </ul>
  246 + </div>
  247 + {% endif %}
  248 + </div>
  249 +
  250 + <p><em>{% trans 'Attribute students to webpage' %}:</em></p>
  251 + {% render_field form.students class='form-control' %}
  252 +
  253 + <span id="helpBlock" class="help-block">{{ form.students.help_text }}</span>
  254 +
  255 + {% if form.students.errors %}
  256 + <div class="alert alert-danger alert-dismissible" role="alert">
  257 + <button type="button" class="close" data-dismiss="alert" aria-label="Close">
  258 + <span aria-hidden="true">&times;</span>
  259 + </button>
  260 + <ul>
  261 + {% for error in form.students.errors %}
  262 + <li>{{ error }}</li>
  263 + {% endfor %}
  264 + </ul>
  265 + </div>
  266 + {% endif %}
  267 +
  268 + <br clear="all" />
  269 +
  270 + <p><em>{% trans 'Attribute groups to webpage' %}:</em></p>
  271 + {% render_field form.groups class='form-control' %}
  272 +
  273 + <span id="helpBlock" class="help-block">{{ form.groups.help_text }}</span>
  274 +
  275 + {% if form.groups.errors %}
  276 + <div class="alert alert-danger alert-dismissible" role="alert">
  277 + <button type="button" class="close" data-dismiss="alert" aria-label="Close">
  278 + <span aria-hidden="true">&times;</span>
  279 + </button>
  280 + <ul>
  281 + {% for error in form.groups.errors %}
  282 + <li>{{ error }}</li>
  283 + {% endfor %}
  284 + </ul>
  285 + </div>
  286 + {% endif %}
  287 + </div>
  288 + </div>
  289 + </div>
  290 +
  291 + <div class="form-group{% if form.has_error %} has-error {% endif %}">
  292 + <div class=" checkbox">
  293 + <label for="{{ form.visible.auto_id }}">
  294 + {% render_field form.visible %} {{ form.visible.label }}
  295 + </label>
  296 + </div>
  297 +
  298 + <span id="helpBlock" class="help-block">{{ form.visible.help_text }}</span>
  299 +
  300 + {% if form.visible.errors %}
  301 + <div class="alert alert-danger alert-dismissible" role="alert">
  302 + <button type="button" class="close" data-dismiss="alert" aria-label="Close">
  303 + <span aria-hidden="true">&times;</span>
  304 + </button>
  305 + <ul>
  306 + {% for error in form.visible.errors %}
  307 + <li>{{ error }}</li>
  308 + {% endfor %}
  309 + </ul>
  310 + </div>
  311 + {% endif %}
  312 + </div>
  313 +
  314 + <div class="col-md-12 col-lg-12 col-sm-12 col-xs-12">
  315 + <div class="text-center">
  316 + <input type="submit" value="{% trans 'Save' %}" class="btn btn-raised btn-success" />
  317 + </div>
  318 + </div>
  319 +</form>
  320 +<script type="text/javascript">
  321 + $(function() {
  322 + var begin_val = $('.begin_date_input').val(),
  323 + end_val = $('.end_date_input').val();
  324 +
  325 + if (begin_val != '') {
  326 + $(".begin_date").prop('checked', true);
  327 + }
  328 +
  329 + if (end_val != '') {
  330 + $(".end_date").prop('checked', true);
  331 + }
  332 +
  333 + {% if not pendencies_form.is_valid and pendencies_form.is_bound %}
  334 + $("#notifications").collapse('toggle');
  335 + {% endif %}
  336 + });
  337 +</script>
  338 +<script type="text/javascript" src="{% static 'js/resources.js' %}"></script>
0 \ No newline at end of file 339 \ No newline at end of file
file_link/templates/file_links/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 File Link' as bread %}
  21 + {% breadcrumb bread 'file_links: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 'file_links/_form.html' %}
  29 + </div>
  30 + </div>
  31 + </div>
  32 + <br clear="all" />
  33 + <br clear="all" />
  34 +{% endblock %}
file_link/tests.py 0 → 100644
@@ -0,0 +1,3 @@ @@ -0,0 +1,3 @@
  1 +from django.test import TestCase
  2 +
  3 +# Create your tests here.
file_link/urls.py 0 → 100644
@@ -0,0 +1,8 @@ @@ -0,0 +1,8 @@
  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 +]
file_link/views.py 0 → 100644
@@ -0,0 +1,114 @@ @@ -0,0 +1,114 @@
  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 +
  8 +from amadeus.permissions import has_subject_permissions, has_resource_permissions
  9 +
  10 +from topics.models import Topic
  11 +
  12 +from pendencies.forms import PendenciesForm
  13 +
  14 +from .forms import FileLinkForm
  15 +from .models import FileLink
  16 +
  17 +class CreateView(LoginRequiredMixin, generic.edit.CreateView):
  18 + login_url = reverse_lazy("users:login")
  19 + redirect_field_name = 'next'
  20 +
  21 + template_name = 'file_links/create.html'
  22 + form_class = FileLinkForm
  23 +
  24 + def dispatch(self, request, *args, **kwargs):
  25 + slug = self.kwargs.get('slug', '')
  26 + topic = get_object_or_404(Topic, slug = slug)
  27 +
  28 + if not has_subject_permissions(request.user, topic.subject):
  29 + return redirect(reverse_lazy('subjects:home'))
  30 +
  31 + return super(CreateView, self).dispatch(request, *args, **kwargs)
  32 +
  33 + def get(self, request, *args, **kwargs):
  34 + self.object = None
  35 +
  36 + form_class = self.get_form_class()
  37 + form = self.get_form(form_class)
  38 +
  39 + slug = self.kwargs.get('slug', '')
  40 + topic = get_object_or_404(Topic, slug = slug)
  41 +
  42 + pendencies_form = PendenciesForm(initial = {'subject': topic.subject.id, 'actions': [("", "-------"),("view", _("Visualize"))]})
  43 +
  44 + return self.render_to_response(self.get_context_data(form = form, pendencies_form = pendencies_form))
  45 +
  46 + def post(self, request, *args, **kwargs):
  47 + self.object = None
  48 +
  49 + form_class = self.get_form_class()
  50 + form = self.get_form(form_class)
  51 +
  52 + slug = self.kwargs.get('slug', '')
  53 + topic = get_object_or_404(Topic, slug = slug)
  54 +
  55 + pendencies_form = PendenciesForm(self.request.POST, initial = {'subject': topic.subject.id, 'actions': [("", "-------"),("view", _("Visualize"))]})
  56 +
  57 + if (form.is_valid() and pendencies_form.is_valid()):
  58 + return self.form_valid(form, pendencies_form)
  59 + else:
  60 + return self.form_invalid(form, pendencies_form)
  61 +
  62 + def get_initial(self):
  63 + initial = super(CreateView, self).get_initial()
  64 +
  65 + slug = self.kwargs.get('slug', '')
  66 +
  67 + topic = get_object_or_404(Topic, slug = slug)
  68 + initial['subject'] = topic.subject
  69 +
  70 + return initial
  71 +
  72 + def form_invalid(self, form, pendencies_form):
  73 + return self.render_to_response(self.get_context_data(form = form, pendencies_form = pendencies_form))
  74 +
  75 + def form_valid(self, form, pendencies_form):
  76 + self.object = form.save(commit = False)
  77 +
  78 + slug = self.kwargs.get('slug', '')
  79 + topic = get_object_or_404(Topic, slug = slug)
  80 +
  81 + self.object.show_window = True
  82 + self.object.topic = topic
  83 + self.object.order = topic.resource_topic.count() + 1
  84 +
  85 + if not self.object.topic.visible and not self.object.topic.repository:
  86 + self.object.visible = False
  87 +
  88 + self.object.save()
  89 +
  90 + pend_form = pendencies_form.save(commit = False)
  91 + pend_form.resource = self.object
  92 +
  93 + if not pend_form.action == "":
  94 + pend_form.save()
  95 +
  96 + return redirect(self.get_success_url())
  97 +
  98 + def get_context_data(self, **kwargs):
  99 + context = super(CreateView, self).get_context_data(**kwargs)
  100 +
  101 + context['title'] = _('Create File Link')
  102 +
  103 + slug = self.kwargs.get('slug', '')
  104 + topic = get_object_or_404(Topic, slug = slug)
  105 +
  106 + context['topic'] = topic
  107 + context['subject'] = topic.subject
  108 +
  109 + return context
  110 +
  111 + def get_success_url(self):
  112 + messages.success(self.request, _('The File Link "%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))
  113 +
  114 + return reverse_lazy('subjects:view', kwargs = {'slug': self.object.topic.subject.slug})
0 \ No newline at end of file 115 \ No newline at end of file
topics/templates/topics/list.html
@@ -51,8 +51,8 @@ @@ -51,8 +51,8 @@
51 <ul class="dropdown-menu"> 51 <ul class="dropdown-menu">
52 <li><a href="#"><i class="fa fa-video-camera"></i> {% trans 'Video Embed' %}</a></li> 52 <li><a href="#"><i class="fa fa-video-camera"></i> {% trans 'Video Embed' %}</a></li>
53 <li><a href="#"><i class="fa fa-comments-o"></i> {% trans 'Forum' %}</a></li> 53 <li><a href="#"><i class="fa fa-comments-o"></i> {% trans 'Forum' %}</a></li>
54 - <li><a href="#"><i class="fa fa-file-archive-o"></i> {% trans 'File Link' %}</a></li>  
55 - <li><a href="{% url 'webpages:create' topic.slug %}"><i class="fa fa-file-o"></i> {% trans 'Webpage' %}</a></li> 54 + <li><a href="{% url 'file_links:create' topic.slug %}"><i class="fa fa-file-archive-o"></i> {% trans 'File Link' %}</a></li>
  55 + <li><a href="{% url 'webpages:create' topic.slug %}"><i class="fa fa-file-code-o"></i> {% trans 'Webpage' %}</a></li>
56 <li><a href="#"><i class="fa fa-question-circle-o"></i> {% trans 'Questionary' %}</a></li> 56 <li><a href="#"><i class="fa fa-question-circle-o"></i> {% trans 'Questionary' %}</a></li>
57 </ul> 57 </ul>
58 </div> 58 </div>
webpage/models.py
1 from django.db import models 1 from django.db import models
2 -from autoslug.fields import AutoSlugField  
3 from django.utils.translation import ugettext_lazy as _ 2 from django.utils.translation import ugettext_lazy as _
4 3
5 from topics.models import Resource 4 from topics.models import Resource
webpage/templates/webpages/_form.html
@@ -343,104 +343,5 @@ @@ -343,104 +343,5 @@
343 $("#notifications").collapse('toggle'); 343 $("#notifications").collapse('toggle');
344 {% endif %} 344 {% endif %}
345 }); 345 });
346 -  
347 - $('#id_groups').multiSelect({  
348 - selectableHeader: "<input type='text' class='search-input category-search-users' autocomplete='off' placeholder=' '>",  
349 - selectionHeader: "<input type='text' class='search-input category-search-users' autocomplete='off' placeholder=''>",  
350 - afterInit: function(ms){  
351 - var that = this,  
352 - $selectableSearch = that.$selectableUl.prev(),  
353 - $selectionSearch = that.$selectionUl.prev(),  
354 - selectableSearchString = '#'+that.$container.attr('id')+' .ms-elem-selectable:not(.ms-selected)',  
355 - selectionSearchString = '#'+that.$container.attr('id')+' .ms-elem-selection.ms-selected';  
356 -  
357 - that.qs1 = $selectableSearch.quicksearch(selectableSearchString)  
358 - .on('keydown', function(e){  
359 - if (e.which === 40){  
360 - that.$selectableUl.focus();  
361 - return false;  
362 - }  
363 - });  
364 -  
365 - that.qs2 = $selectionSearch.quicksearch(selectionSearchString)  
366 - .on('keydown', function(e){  
367 - if (e.which == 40){  
368 - that.$selectionUl.focus();  
369 - return false;  
370 - }  
371 - });  
372 - },  
373 - afterSelect: function(){  
374 - this.qs1.cache();  
375 - this.qs2.cache();  
376 - },  
377 - afterDeselect: function(){  
378 - this.qs1.cache();  
379 - this.qs2.cache();  
380 - }  
381 - });// Used to create multi-select css style  
382 -  
383 - $('#id_students').multiSelect({  
384 - selectableHeader: "<input type='text' class='search-input category-search-users' autocomplete='off' placeholder=' '>",  
385 - selectionHeader: "<input type='text' class='search-input category-search-users' autocomplete='off' placeholder=''>",  
386 - afterInit: function(ms){  
387 - var that = this,  
388 - $selectableSearch = that.$selectableUl.prev(),  
389 - $selectionSearch = that.$selectionUl.prev(),  
390 - selectableSearchString = '#'+that.$container.attr('id')+' .ms-elem-selectable:not(.ms-selected)',  
391 - selectionSearchString = '#'+that.$container.attr('id')+' .ms-elem-selection.ms-selected';  
392 -  
393 - that.qs1 = $selectableSearch.quicksearch(selectableSearchString)  
394 - .on('keydown', function(e){  
395 - if (e.which === 40){  
396 - that.$selectableUl.focus();  
397 - return false;  
398 - }  
399 - });  
400 -  
401 - that.qs2 = $selectionSearch.quicksearch(selectionSearchString)  
402 - .on('keydown', function(e){  
403 - if (e.which == 40){  
404 - that.$selectionUl.focus();  
405 - return false;  
406 - }  
407 - });  
408 - },  
409 - afterSelect: function(){  
410 - this.qs1.cache();  
411 - this.qs2.cache();  
412 - },  
413 - afterDeselect: function(){  
414 - this.qs1.cache();  
415 - this.qs2.cache();  
416 - }  
417 - });// Used to create multi-select css style  
418 -  
419 - $('.collapse').on('show.bs.collapse', function (e) {  
420 - if($(this).is(e.target)){  
421 - var btn = $(this).parent().find('.fa-angle-right');  
422 -  
423 - btn.switchClass("fa-angle-right", "fa-angle-down", 250, "easeInOutQuad");  
424 - }  
425 - });  
426 -  
427 - $('.collapse').on('hide.bs.collapse', function (e) {  
428 - if($(this).is(e.target)){  
429 - var btn = $(this).parent().find('.fa-angle-down');  
430 -  
431 - btn.switchClass("fa-angle-down", "fa-angle-right", 250, "easeInOutQuad");  
432 - }  
433 - });  
434 -  
435 - $('.begin_date_input').on('click', function () {  
436 - var checkbox = $(this).parent().parent().find('.begin_date');  
437 -  
438 - $(checkbox).prop('checked', true);  
439 - });  
440 -  
441 - $('.end_date_input').on('click', function () {  
442 - var checkbox = $(this).parent().parent().find('.end_date');  
443 -  
444 - $(checkbox).prop('checked', true);  
445 - });  
446 -</script>  
447 \ No newline at end of file 346 \ No newline at end of file
  347 +</script>
  348 +<script type="text/javascript" src="{% static 'js/resources.js' %}"></script>
448 \ No newline at end of file 349 \ No newline at end of file
webpage/templates/webpages/create.html
@@ -10,7 +10,6 @@ @@ -10,7 +10,6 @@
10 {% block javascript %} 10 {% block javascript %}
11 {{block.super}} 11 {{block.super}}
12 <script type="text/javascript" src="{% static "js/bootstrap-tagsinput.js" %} "></script> 12 <script type="text/javascript" src="{% static "js/bootstrap-tagsinput.js" %} "></script>
13 - <script type="text/javascript" src="{% static "js/jquery.formset.js" %} "></script>  
14 {% endblock %} 13 {% endblock %}
15 14
16 {% block breadcrumbs %} 15 {% block breadcrumbs %}