Commit 17e3fac49c0a1fd50866f6cee9ae1b5ea6a29ff3

Authored by Matheus Lins
2 parents f0c5784d 4a3e61f0

Merge branch 'dev' of https://github.com/amadeusproject/amadeuslms into dev

core/migrations/0001_initial.py
1 1 # -*- coding: utf-8 -*-
2   -# Generated by Django 1.10 on 2016-09-22 05:46
  2 +# Generated by Django 1.10 on 2016-09-30 15:39
3 3 from __future__ import unicode_literals
4 4  
5 5 import autoslug.fields
6   -from django.conf import settings
7 6 from django.db import migrations, models
8 7 import django.db.models.deletion
9 8  
... ... @@ -13,7 +12,6 @@ class Migration(migrations.Migration):
13 12 initial = True
14 13  
15 14 dependencies = [
16   - migrations.swappable_dependency(settings.AUTH_USER_MODEL),
17 15 ]
18 16  
19 17 operations = [
... ... @@ -33,7 +31,6 @@ class Migration(migrations.Migration):
33 31 name='Action_Resource',
34 32 fields=[
35 33 ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
36   - ('action', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='core.Action', verbose_name='Action_Applied')),
37 34 ],
38 35 options={
39 36 'verbose_name': 'Action_Resource',
... ... @@ -45,8 +42,6 @@ class Migration(migrations.Migration):
45 42 fields=[
46 43 ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
47 44 ('datetime', models.DateTimeField(auto_now_add=True, verbose_name='Date and Time of action')),
48   - ('action_resource', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='core.Action_Resource', verbose_name='Action_Resource')),
49   - ('user', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL, verbose_name='Actor')),
50 45 ],
51 46 options={
52 47 'verbose_name': 'Log',
... ... @@ -61,8 +56,6 @@ class Migration(migrations.Migration):
61 56 ('read', models.BooleanField(default=False, verbose_name='Read')),
62 57 ('datetime', models.DateTimeField(auto_now_add=True, verbose_name='Date and Time of action')),
63 58 ('action_resource', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='core.Action_Resource', verbose_name='Action_Resource')),
64   - ('actor', models.ForeignKey(null=True, on_delete=django.db.models.deletion.CASCADE, related_name='notification_Performer', to=settings.AUTH_USER_MODEL, verbose_name='Perfomer')),
65   - ('user', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='notification_Actor', to=settings.AUTH_USER_MODEL, verbose_name='User')),
66 59 ],
67 60 options={
68 61 'verbose_name': 'Notification',
... ... @@ -74,7 +67,7 @@ class Migration(migrations.Migration):
74 67 fields=[
75 68 ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
76 69 ('name', models.CharField(max_length=100, verbose_name='Name')),
77   - ('slug', autoslug.fields.AutoSlugField(editable=False, null=True, populate_from='name', unique=True, verbose_name='Slug')),
  70 + ('slug', autoslug.fields.AutoSlugField(editable=False, populate_from='name', unique=True, verbose_name='Slug')),
78 71 ('created_date', models.DateField(auto_now_add=True, verbose_name='Created Date')),
79 72 ('url', models.CharField(default='', max_length=100, verbose_name='URL')),
80 73 ],
... ... @@ -83,9 +76,4 @@ class Migration(migrations.Migration):
83 76 'verbose_name_plural': 'Resources',
84 77 },
85 78 ),
86   - migrations.AddField(
87   - model_name='action_resource',
88   - name='resource',
89   - field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='core.Resource', verbose_name='Resource'),
90   - ),
91 79 ]
... ...
core/migrations/0002_auto_20160930_1239.py 0 → 100644
... ... @@ -0,0 +1,50 @@
  1 +# -*- coding: utf-8 -*-
  2 +# Generated by Django 1.10 on 2016-09-30 15:39
  3 +from __future__ import unicode_literals
  4 +
  5 +from django.conf import settings
  6 +from django.db import migrations, models
  7 +import django.db.models.deletion
  8 +
  9 +
  10 +class Migration(migrations.Migration):
  11 +
  12 + initial = True
  13 +
  14 + dependencies = [
  15 + ('core', '0001_initial'),
  16 + migrations.swappable_dependency(settings.AUTH_USER_MODEL),
  17 + ]
  18 +
  19 + operations = [
  20 + migrations.AddField(
  21 + model_name='notification',
  22 + name='actor',
  23 + field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.CASCADE, related_name='notification_Performer', to=settings.AUTH_USER_MODEL, verbose_name='Perfomer'),
  24 + ),
  25 + migrations.AddField(
  26 + model_name='notification',
  27 + name='user',
  28 + field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='notification_Actor', to=settings.AUTH_USER_MODEL, verbose_name='User'),
  29 + ),
  30 + migrations.AddField(
  31 + model_name='log',
  32 + name='action_resource',
  33 + field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='core.Action_Resource', verbose_name='Action_Resource'),
  34 + ),
  35 + migrations.AddField(
  36 + model_name='log',
  37 + name='user',
  38 + field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL, verbose_name='Actor'),
  39 + ),
  40 + migrations.AddField(
  41 + model_name='action_resource',
  42 + name='action',
  43 + field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='core.Action', verbose_name='Action_Applied'),
  44 + ),
  45 + migrations.AddField(
  46 + model_name='action_resource',
  47 + name='resource',
  48 + field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='core.Resource', verbose_name='Resource'),
  49 + ),
  50 + ]
... ...
core/models.py
... ... @@ -6,20 +6,20 @@ from autoslug.fields import AutoSlugField
6 6  
7 7 class Action(models.Model):
8 8 """
9   - It represents an Action on the program by a User such as "create post",
  9 + It represents an Action on the program by a User such as "create post",
10 10 "visualize post", etc. It is supposed to be created everytime we want an aciton
11 11 """
12 12  
13 13 name = models.CharField(_('Name'), max_length = 100)
14 14 created_date = models.DateField(_('Created Date'), auto_now_add=True)
15   -
  15 +
16 16 class Meta:
17 17 verbose_name = "Action"
18 18 verbose_name_plural = "Actions"
19 19  
20 20 def __str__(self):
21 21 return self.name
22   -
  22 +
23 23  
24 24 class Resource(models.Model):
25 25 """
... ... @@ -27,38 +27,38 @@ class Resource(models.Model):
27 27 Example: Pool was answered (Resource: Pool), PDF was visualized(Resource: PDF).
28 28  
29 29 Attributes:
30   - @name: name of the resource affected, it will be unique because a resource can be affecte
  30 + @name: name of the resource affected, it will be unique because a resource can be affecte
31 31 by a huge amount of actions
32 32 @created_date: The date the resource was created
33 33 @link: Which URL made that resource able to find
34 34 """
35 35  
36 36 name = models.CharField(_('Name'), max_length =100)
37   - slug = AutoSlugField(_("Slug"), populate_from='name', unique=True, null=True)
  37 + slug = AutoSlugField(_("Slug"), populate_from='name', unique=True)
38 38 created_date = models.DateField(_('Created Date'), auto_now_add=True)
39 39 url = models.CharField(_('URL'), max_length =100, default="")
40 40  
41 41  
42 42 class Meta:
43 43 verbose_name = "Resource"
44   - verbose_name_plural = "Resources"
  44 + verbose_name_plural = "Resources"
45 45  
46 46 def __str__(self):
47 47 return self.name
48 48  
49 49  
50 50 class Action_Resource(models.Model):
51   -
  51 +
52 52 action = models.ForeignKey(Action , verbose_name= _('Action_Applied'))
53 53 resource = models.ForeignKey(Resource, verbose_name = _('Resource'))
54   -
  54 +
55 55 class Meta:
56 56 verbose_name = "Action_Resource"
57 57 verbose_name_plural = "Action_Resources"
58 58  
59 59 def __str__(self):
60 60 return ''.join([self.action.name, " / ", self.resource.name])
61   -
  61 +
62 62  
63 63 class Notification(models.Model):
64 64 """
... ... @@ -77,7 +77,7 @@ class Notification(models.Model):
77 77 datetime = models.DateTimeField(_("Date and Time of action"), auto_now_add = True)
78 78 action_resource = models.ForeignKey(Action_Resource, verbose_name = _('Action_Resource'))
79 79 actor = models.ForeignKey(User, related_name = _('%(class)s_Performer'), verbose_name= _('Perfomer'), null = True)
80   -
  80 +
81 81 class Meta:
82 82 verbose_name = _("Notification")
83 83 verbose_name_plural = _("Notifications")
... ...
core/static/js/base/amadeus.js
... ... @@ -134,6 +134,10 @@ This functions get the next 5 notifications from the user given a "step"(an amou
134 134 function getNotifications(step){
135 135 $.get('/getNotifications',
136 136 {'steps':step, 'amount': 5}, function(data){
137   - console.log(data);
  137 + $("#notification-dropdown").append(data);
  138 + $('#notification-see-more').remove();
  139 + var seemore = '<li><a onclick="getNotifications('+(step+5)+')"> <div id="notification-see-more" class="list-group-item"> <div class="row-content"><p class="list-group-item-text">See More</p> </div> </a></li>';
  140 + $("#notification-dropdown").append(seemore);
  141 + $("#notification-count").text(step+5);
138 142 });
139 143 }
... ...
core/templates/base.html
... ... @@ -58,14 +58,14 @@
58 58 <div class="navbar-collapse collapse navbar-responsive-collapse">
59 59 <ul class="nav navbar-nav navbar-right notifications">
60 60 <li class="" data-toggle="tooltip" data-placement="bottom" title data-original-title="notifications">
61   - <a class="dropdown-toggle" data-toggle="dropdown"> <span class="badge notification-count">{{notifications.count}}</span><i class="fa fa-bell" aria-hidden="true"></i></a>
  61 + <a class="dropdown-toggle" data-toggle="dropdown"> <span id="notification-count" class="badge notification-count">{{notifications.count}}</span><i class="fa fa-bell" aria-hidden="true"></i></a>
62 62 <ul id="notification-dropdown" class="dropdown-menu">
63 63 <li class="dropdown-header">Notifications</li>
64 64 {% include "notifications.html" %}
65 65  
66 66 <li>
67 67 <a onclick="getNotifications(5)">
68   - <div class="list-group-item">
  68 + <div id="notification-see-more" class="list-group-item">
69 69 <div class="row-content">
70 70 <p class="list-group-item-text">See More</p>
71 71 </div>
... ...
core/templates/notifications.html
  1 +
  2 +<!-- This templates is responsible to display the list of notifications at the top of the page. -->
1 3 {% for notification in notifications %}
2 4 {% if notification.actor %} <!-- if the notification has a user-->
3 5 <li>
... ...
courses/migrations/0001_initial.py
1 1 # -*- coding: utf-8 -*-
2   -# Generated by Django 1.10 on 2016-09-28 20:27
  2 +# Generated by Django 1.10 on 2016-09-30 15:39
3 3 from __future__ import unicode_literals
4 4  
5 5 import autoslug.fields
6   -from django.conf import settings
7 6 from django.db import migrations, models
8 7 import django.db.models.deletion
9 8  
... ... @@ -13,8 +12,7 @@ class Migration(migrations.Migration):
13 12 initial = True
14 13  
15 14 dependencies = [
16   - migrations.swappable_dependency(settings.AUTH_USER_MODEL),
17   - ('core', '0001_initial'),
  15 + ('core', '0002_auto_20160930_1239'),
18 16 ]
19 17  
20 18 operations = [
... ... @@ -23,7 +21,7 @@ class Migration(migrations.Migration):
23 21 fields=[
24 22 ('resource_ptr', models.OneToOneField(auto_created=True, on_delete=django.db.models.deletion.CASCADE, parent_link=True, primary_key=True, serialize=False, to='core.Resource')),
25 23 ('limit_date', models.DateTimeField(verbose_name='Deliver Date')),
26   - ('student', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL, verbose_name='student')),
  24 + ('all_students', models.BooleanField(default=False, verbose_name='All Students')),
27 25 ],
28 26 bases=('core.resource',),
29 27 ),
... ... @@ -55,21 +53,18 @@ class Migration(migrations.Migration):
55 53 ('init_date', models.DateField(verbose_name='Begin of Course Date')),
56 54 ('end_date', models.DateField(verbose_name='End of Course Date')),
57 55 ('image', models.ImageField(blank=True, upload_to='courses/', verbose_name='Image')),
58   - ('category', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='courses.Category', verbose_name='Category')),
59   - ('professors', models.ManyToManyField(related_name='courses_professors', to=settings.AUTH_USER_MODEL, verbose_name='Professors')),
60   - ('students', models.ManyToManyField(related_name='courses_student', to=settings.AUTH_USER_MODEL, verbose_name='Students')),
61 56 ],
62 57 options={
  58 + 'ordering': ('create_date', 'name'),
63 59 'verbose_name': 'Course',
64 60 'verbose_name_plural': 'Courses',
65   - 'ordering': ('create_date', 'name'),
66 61 },
67 62 ),
68 63 migrations.CreateModel(
69 64 name='Material',
70 65 fields=[
71 66 ('resource_ptr', models.OneToOneField(auto_created=True, on_delete=django.db.models.deletion.CASCADE, parent_link=True, primary_key=True, serialize=False, to='core.Resource')),
72   - ('student', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL, verbose_name='student')),
  67 + ('all_students', models.BooleanField(default=False, verbose_name='All Students')),
73 68 ],
74 69 bases=('core.resource',),
75 70 ),
... ... @@ -85,13 +80,11 @@ class Migration(migrations.Migration):
85 80 ('end_date', models.DateField(verbose_name='End of Subject Date')),
86 81 ('create_date', models.DateTimeField(auto_now_add=True, verbose_name='Creation Date')),
87 82 ('update_date', models.DateTimeField(auto_now=True, verbose_name='Date of last update')),
88   - ('course', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='subjects', to='courses.Course', verbose_name='Course')),
89   - ('professors', models.ManyToManyField(related_name='subjects', to=settings.AUTH_USER_MODEL, verbose_name='Professors')),
90 83 ],
91 84 options={
  85 + 'ordering': ('create_date', 'name'),
92 86 'verbose_name': 'Subject',
93 87 'verbose_name_plural': 'Subjects',
94   - 'ordering': ('create_date', 'name'),
95 88 },
96 89 ),
97 90 migrations.CreateModel(
... ... @@ -101,7 +94,6 @@ class Migration(migrations.Migration):
101 94 ('name', models.CharField(max_length=100, verbose_name='Name')),
102 95 ('slug', autoslug.fields.AutoSlugField(editable=False, populate_from='name', unique=True, verbose_name='Slug')),
103 96 ('description', models.TextField(blank=True, verbose_name='Description')),
104   - ('subjects', models.ManyToManyField(to='courses.Subject')),
105 97 ],
106 98 options={
107 99 'verbose_name': 'subject category',
... ... @@ -118,23 +110,11 @@ class Migration(migrations.Migration):
118 110 ('create_date', models.DateTimeField(auto_now_add=True, verbose_name='Creation Date')),
119 111 ('update_date', models.DateTimeField(auto_now=True, verbose_name='Date of last update')),
120 112 ('visible', models.BooleanField(default=False, verbose_name='Visible')),
121   - ('owner', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL, verbose_name='Owner')),
122   - ('subject', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='courses.Subject', verbose_name='Subject')),
123 113 ],
124 114 options={
  115 + 'ordering': ('create_date', 'name'),
125 116 'verbose_name': 'Topic',
126 117 'verbose_name_plural': 'Topics',
127   - 'ordering': ('create_date', 'name'),
128 118 },
129 119 ),
130   - migrations.AddField(
131   - model_name='material',
132   - name='topic',
133   - field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='courses.Topic', verbose_name='Topic'),
134   - ),
135   - migrations.AddField(
136   - model_name='activity',
137   - name='topic',
138   - field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='courses.Topic', verbose_name='Topic'),
139   - ),
140 120 ]
... ...
courses/migrations/0002_auto_20160930_1239.py 0 → 100644
... ... @@ -0,0 +1,80 @@
  1 +# -*- coding: utf-8 -*-
  2 +# Generated by Django 1.10 on 2016-09-30 15:39
  3 +from __future__ import unicode_literals
  4 +
  5 +from django.conf import settings
  6 +from django.db import migrations, models
  7 +import django.db.models.deletion
  8 +
  9 +
  10 +class Migration(migrations.Migration):
  11 +
  12 + initial = True
  13 +
  14 + dependencies = [
  15 + migrations.swappable_dependency(settings.AUTH_USER_MODEL),
  16 + ('courses', '0001_initial'),
  17 + ]
  18 +
  19 + operations = [
  20 + migrations.AddField(
  21 + model_name='topic',
  22 + name='owner',
  23 + field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL, verbose_name='Owner'),
  24 + ),
  25 + migrations.AddField(
  26 + model_name='topic',
  27 + name='subject',
  28 + field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='courses.Subject', verbose_name='Subject'),
  29 + ),
  30 + migrations.AddField(
  31 + model_name='subjectcategory',
  32 + name='subjects',
  33 + field=models.ManyToManyField(to='courses.Subject'),
  34 + ),
  35 + migrations.AddField(
  36 + model_name='subject',
  37 + name='course',
  38 + field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='subjects', to='courses.Course', verbose_name='Course'),
  39 + ),
  40 + migrations.AddField(
  41 + model_name='subject',
  42 + name='professors',
  43 + field=models.ManyToManyField(related_name='subjects', to=settings.AUTH_USER_MODEL, verbose_name='Professors'),
  44 + ),
  45 + migrations.AddField(
  46 + model_name='material',
  47 + name='students',
  48 + field=models.ManyToManyField(related_name='materials', to=settings.AUTH_USER_MODEL, verbose_name='Students'),
  49 + ),
  50 + migrations.AddField(
  51 + model_name='material',
  52 + name='topic',
  53 + field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='materials', to='courses.Topic', verbose_name='Topic'),
  54 + ),
  55 + migrations.AddField(
  56 + model_name='course',
  57 + name='category',
  58 + field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='courses.Category', verbose_name='Category'),
  59 + ),
  60 + migrations.AddField(
  61 + model_name='course',
  62 + name='professors',
  63 + field=models.ManyToManyField(related_name='courses_professors', to=settings.AUTH_USER_MODEL, verbose_name='Professors'),
  64 + ),
  65 + migrations.AddField(
  66 + model_name='course',
  67 + name='students',
  68 + field=models.ManyToManyField(related_name='courses_student', to=settings.AUTH_USER_MODEL, verbose_name='Students'),
  69 + ),
  70 + migrations.AddField(
  71 + model_name='activity',
  72 + name='students',
  73 + field=models.ManyToManyField(related_name='activities', to=settings.AUTH_USER_MODEL, verbose_name='Students'),
  74 + ),
  75 + migrations.AddField(
  76 + model_name='activity',
  77 + name='topic',
  78 + field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='activities', to='courses.Topic', verbose_name='Topic'),
  79 + ),
  80 + ]
... ...
courses/models.py
... ... @@ -88,17 +88,19 @@ It is one kind of possible resources available inside a Topic.
88 88 Activity is something that has a deadline and has to be delivered by the student
89 89 """
90 90 class Activity(Resource):
91   - topic = models.ForeignKey(Topic, verbose_name = _('Topic'))
  91 + topic = models.ForeignKey(Topic, verbose_name = _('Topic'), related_name='activities')
92 92 limit_date = models.DateTimeField(_('Deliver Date'))
93   - student = models.ForeignKey(User, verbose_name = _('student'))
  93 + students = models.ManyToManyField(User, verbose_name = _('Students'), related_name='activities')
  94 + all_students = models.BooleanField(_('All Students'), default=False)
94 95  
95 96  
96 97 """
97 98 It represents any Material inside a topic, be it a file, a link, etc.
98 99 """
99 100 class Material(Resource):
100   - topic = models.ForeignKey(Topic, verbose_name = _('Topic'))
101   - student = models.ForeignKey(User, verbose_name = _('student'))
  101 + topic = models.ForeignKey(Topic, verbose_name = _('Topic'), related_name='materials')
  102 + students = models.ManyToManyField(User, verbose_name = _('Students'), related_name='materials')
  103 + all_students = models.BooleanField(_('All Students'), default=False)
102 104  
103 105 """
104 106 It is a category for each subject.
... ... @@ -112,4 +114,3 @@ class SubjectCategory(models.Model):
112 114 class Meta:
113 115 verbose_name = _('subject category')
114 116 verbose_name_plural = _('subject categories')
115   -
... ...
forum/migrations/0001_initial.py
1 1 # -*- coding: utf-8 -*-
2   -# Generated by Django 1.10 on 2016-09-28 20:27
  2 +# Generated by Django 1.10 on 2016-09-30 15:39
3 3 from __future__ import unicode_literals
4 4  
5   -from django.conf import settings
6 5 from django.db import migrations, models
7 6 import django.db.models.deletion
8 7  
... ... @@ -12,8 +11,7 @@ class Migration(migrations.Migration):
12 11 initial = True
13 12  
14 13 dependencies = [
15   - migrations.swappable_dependency(settings.AUTH_USER_MODEL),
16   - ('courses', '0001_initial'),
  14 + ('courses', '0002_auto_20160930_1239'),
17 15 ]
18 16  
19 17 operations = [
... ... @@ -38,8 +36,6 @@ class Migration(migrations.Migration):
38 36 ('message', models.TextField(verbose_name='Post message')),
39 37 ('modification_date', models.DateTimeField(auto_now=True, verbose_name='Modification Date')),
40 38 ('post_date', models.DateTimeField(auto_now_add=True, verbose_name='Post Date')),
41   - ('forum', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='forum.Forum', verbose_name='Forum')),
42   - ('user', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL, verbose_name='Autor')),
43 39 ],
44 40 options={
45 41 'verbose_name': 'Post',
... ... @@ -54,7 +50,6 @@ class Migration(migrations.Migration):
54 50 ('modification_date', models.DateTimeField(auto_now=True, verbose_name='Modification Date')),
55 51 ('answer_date', models.DateTimeField(auto_now_add=True, verbose_name='Answer Date')),
56 52 ('post', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='forum.Post', verbose_name='Post')),
57   - ('user', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL, verbose_name='Autor')),
58 53 ],
59 54 options={
60 55 'verbose_name': 'Post Answer',
... ...
forum/migrations/0002_auto_20160930_1239.py 0 → 100644
... ... @@ -0,0 +1,35 @@
  1 +# -*- coding: utf-8 -*-
  2 +# Generated by Django 1.10 on 2016-09-30 15:39
  3 +from __future__ import unicode_literals
  4 +
  5 +from django.conf import settings
  6 +from django.db import migrations, models
  7 +import django.db.models.deletion
  8 +
  9 +
  10 +class Migration(migrations.Migration):
  11 +
  12 + initial = True
  13 +
  14 + dependencies = [
  15 + migrations.swappable_dependency(settings.AUTH_USER_MODEL),
  16 + ('forum', '0001_initial'),
  17 + ]
  18 +
  19 + operations = [
  20 + migrations.AddField(
  21 + model_name='postanswer',
  22 + name='user',
  23 + field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL, verbose_name='Autor'),
  24 + ),
  25 + migrations.AddField(
  26 + model_name='post',
  27 + name='forum',
  28 + field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='forum.Forum', verbose_name='Forum'),
  29 + ),
  30 + migrations.AddField(
  31 + model_name='post',
  32 + name='user',
  33 + field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL, verbose_name='Autor'),
  34 + ),
  35 + ]
... ...
forum/models.py
... ... @@ -41,6 +41,15 @@ class Post(models.Model):
41 41 def __str__(self):
42 42 return ''.join([self.user.name, " / ", str(self.post_date)])
43 43  
  44 + def is_modified(self):
  45 + create = self.post_date.strftime("%Y-%m-%d %H:%M:%S")
  46 + edit = self.modification_date.strftime("%Y-%m-%d %H:%M:%S")
  47 +
  48 + if create != edit:
  49 + return True
  50 +
  51 + return False
  52 +
44 53 """
45 54 It represents an answer to a forum's post
46 55 """
... ...
forum/static/js/forum.js
... ... @@ -50,6 +50,54 @@ function showForum(url, forum_id) {
50 50 $('#forumModal').modal();
51 51 }
52 52  
  53 +/*
  54 +*
  55 +* Function to load form to edit post
  56 +*
  57 +*/
  58 +function edit_post(url, post_id) {
  59 + $.ajax({
  60 + url: url,
  61 + success: function(data) {
  62 + $("#post_"+post_id).find(".post_content").hide();
  63 + $("#post_"+post_id).find(".post_content").after(data);
  64 +
  65 + var frm = $("#post_"+post_id).find(".edit_post_form");
  66 + frm.submit(function () {
  67 + $.ajax({
  68 + type: frm.attr('method'),
  69 + url: frm.attr('action'),
  70 + data: frm.serialize(),
  71 + success: function (data) {
  72 + $("#post_"+post_id).parent().after(data);
  73 + frm.parent().parent().remove();
  74 + },
  75 + error: function(data) {
  76 + console.log(frm.serialize());
  77 + console.log('Error');
  78 + }
  79 + });
  80 + return false;
  81 + });
  82 + }
  83 + });
  84 +}
  85 +
  86 +/*
  87 +*
  88 +* Function to cancel post edition
  89 +*
  90 +*/
  91 +function cancelEditPost(post_id) {
  92 + $("#post_"+post_id).find(".post_content").show();
  93 + $("#post_"+post_id).find(".edit_post_form").remove();
  94 +}
  95 +
  96 +/*
  97 +*
  98 +* Function to delete a post
  99 +*
  100 +*/
53 101 function delete_post(url, post) {
54 102 var csrftoken = getCookie('csrftoken');
55 103  
... ... @@ -74,36 +122,4 @@ function answer(id, url) {
74 122 });
75 123  
76 124 $("#post_"+id).find(".answer_post").show();
77   -}
78   -
79   -function showPosts(url, forum) {
80   - if ($("#collapse" + forum).hasClass('in')) {
81   - $("#collapse" + forum).collapse('hide');
82   - } else {
83   - $.ajax({
84   - url: url,
85   - data: {'forum': forum},
86   - success: function(data) {
87   - $("#collapse" + forum).find(".well").html(data);
88   - }
89   - });
90   -
91   - $("#collapse" + forum).collapse('show');
92   - }
93   -}
94   -
95   -function showPostsAnswers(url, post) {
96   - if ($("#collapse" + post).hasClass('in')) {
97   - $("#collapse" + post).collapse('hide');
98   - } else {
99   - $.ajax({
100   - url: url,
101   - data: {'post': post},
102   - success: function(data) {
103   - $("#collapse" + post).find(".well").html(data);
104   - }
105   - });
106   -
107   - $("#collapse" + post).collapse('show');
108   - }
109 125 }
110 126 \ No newline at end of file
... ...
forum/templates/post/post_list.html
... ... @@ -17,17 +17,24 @@
17 17 <i class="material-icons">more_horiz</i>
18 18 </a>
19 19 <ul class="dropdown-menu" aria-labelledby="dropdownMenu1">
20   - <li><a href="javascript:void(0)"><i class="material-icons">create</i> {% trans 'Edit' %}</a></li>
  20 + <li><a href="javascript:edit_post('{% url 'forum:update_post' post.id %}', '{{ post.id }}')"><i class="material-icons">create</i> {% trans 'Edit' %}</a></li>
21 21 <li><a href="javascript:javascript:delete_post('{% url 'forum:delete_post' post.id %}', '{{ post.id }}')"><i class="material-icons">delete_sweep</i> {% trans 'Remove' %}</a></li>
22 22 </ul>
23 23 </div>
24 24 {% endif %}
25 25 </div>
26 26 </h3>
27   - <div class="card-data">
28   - <p class="comment-date"><i class="fa fa-clock-o"></i> {{ post.post_date }}</p>
  27 + <div class="post_content">
  28 + <div class="card-data">
  29 + <p class="comment-date">
  30 + <i class="fa fa-clock-o"></i> {{ post.post_date }}
  31 + {% if post.is_modified %}
  32 + <em> - {% trans 'Edited' %}</em>
  33 + {% endif %}
  34 + </p>
  35 + </div>
  36 + <p class="comment-text">{{ post.message|linebreaks }}</p>
29 37 </div>
30   - <p class="comment-text">{{ post.message|linebreaks }}</p>
31 38 <div class="answer_post"></div>
32 39 </div>
33 40 </div>
... ...
forum/templates/post/post_render.html
... ... @@ -15,17 +15,24 @@
15 15 <i class="material-icons">more_horiz</i>
16 16 </a>
17 17 <ul class="dropdown-menu" aria-labelledby="dropdownMenu1">
18   - <li><a href="javascript:void(0)"><i class="material-icons">create</i> {% trans 'Edit' %}</a></li>
  18 + <li><a href="javascript:edit_post('{% url 'forum:update_post' post.id %}', '{{ post.id }}')"></li>
19 19 <li><a href="javascript:delete_post('{% url 'forum:delete_post' post.id %}', '{{ post.id }}')"><i class="material-icons">delete_sweep</i> {% trans 'Remove' %}</a></li>
20 20 </ul>
21 21 </div>
22 22 {% endif %}
23 23 </div>
24 24 </h3>
25   - <div class="card-data">
26   - <p class="comment-date"><i class="fa fa-clock-o"></i> {{ post.post_date }}</p>
  25 + <div class="post_content">
  26 + <div class="card-data">
  27 + <p class="comment-date">
  28 + <i class="fa fa-clock-o"></i> {{ post.post_date }}
  29 + {% if post.post_date != post.modifiction_date %}
  30 + <em> - {% trans 'Edited' %}</em>
  31 + {% endif %}
  32 + </p>
  33 + </div>
  34 + <p class="comment-text">{{ post.message|linebreaks }}</p>
27 35 </div>
28   - <p class="comment-text">{{ post.message|linebreaks }}</p>
29 36 <div class="answer_post"></div>
30 37 </div>
31 38 </div>
32 39 \ No newline at end of file
... ...
forum/templates/post/post_update_form.html 0 → 100644
... ... @@ -0,0 +1,37 @@
  1 +{% load i18n permission_tags list_post %}
  2 +{% load widget_tweaks %}
  3 +
  4 +<form class="edit_post_form" method="post" action="{% url 'forum:update_post' post.id %}" enctype="multipart/form-data">
  5 + {% csrf_token %}
  6 + {% for field in form %}
  7 + {% if field.field.widget.input_type == 'hidden' %}
  8 + {% render_field field class='form-control' %}
  9 + {% else %}
  10 + <div class="form-group {% if form.has_error %} has-error {% endif %} is-fileinput">
  11 + <div class="input-group">
  12 + {% render_field field class='form-control' placeholder="Post a message" %}
  13 + <span class="help-block">{{ field.help_text }}</span>
  14 + {% if field.errors %}
  15 + <div class="row">
  16 + <br />
  17 + <div class="alert alert-danger alert-dismissible" role="alert">
  18 + <button type="button" class="close" data-dismiss="alert" aria-label="Close">
  19 + <span aria-hidden="true">&times;</span>
  20 + </button>
  21 + <ul>
  22 + {% for error in field.errors %}
  23 + <li>{{ error }}</li>
  24 + {% endfor %}
  25 + </ul>
  26 + </div>
  27 + </div>
  28 + {% endif %}
  29 + </div>
  30 + </div>
  31 + <div class="pull-right">
  32 + <button type="button" onclick="cancelEditPost('{{ post.id }}')" class="btn btn-danger btn-raised">{% trans 'Cancel' %}</button>
  33 + <button type="submit" class="btn btn-primary btn-raised">{% trans 'Save changes' %}</button>
  34 + </div>
  35 + {% endif %}
  36 + {% endfor %}
  37 +</form>
0 38 \ No newline at end of file
... ...
forum/templatetags/list_post.py
... ... @@ -14,6 +14,6 @@ def list_posts(request, forum):
14 14 'request': request,
15 15 }
16 16  
17   - context['posts'] = Post.objects.filter(forum = forum)
  17 + context['posts'] = Post.objects.filter(forum = forum).order_by('post_date')
18 18  
19 19 return context
20 20 \ No newline at end of file
... ...
forum/urls.py
... ... @@ -6,10 +6,10 @@ from . import views
6 6 urlpatterns = [
7 7 url(r'^$', views.ForumIndex.as_view(), name='index'),
8 8 url(r'^create$', views.CreateForumView.as_view(), name='create'),
9   - url(r'^posts$', views.PostIndex.as_view(), name='posts'),
10 9 url(r'^create_post$', views.CreatePostView.as_view(), name='create_post'),
11   - url(r'^render_post/([\w_-]+)/$', views.render_post, name='render_post'),
  10 + url(r'^update_post/(?P<pk>[\w_-]+)/$', views.PostUpdateView.as_view(), name='update_post'),
12 11 url(r'^delete_post/(?P<pk>[\w_-]+)/$', views.PostDeleteView.as_view(), name='delete_post'),
  12 + url(r'^render_post/([\w_-]+)/$', views.render_post, name='render_post'),
13 13 url(r'^post_deleted/$', views.post_deleted, name='deleted_post'),
14 14 url(r'^post_answers$', views.PostAnswerIndex.as_view(), name='post_answers'),
15 15 url(r'^reply_post$', views.CreatePostAnswerView.as_view(), name='reply_post'),
... ...
forum/views.py
... ... @@ -36,20 +36,6 @@ class CreateForumView(LoginRequiredMixin, generic.edit.CreateView):
36 36 form_class = ForumForm
37 37 success_url = reverse_lazy('forum:index')
38 38  
39   -class PostIndex(LoginRequiredMixin, generic.ListView):
40   - login_url = reverse_lazy("core:home")
41   - redirect_field_name = 'next'
42   -
43   - template_name = "post/post_list.html"
44   - context_object_name = 'posts'
45   -
46   - def get_queryset(self):
47   - forum = get_object_or_404(Forum, slug = self.request.GET.get('forum', ''))
48   -
49   - context = Post.objects.filter(forum = forum)
50   -
51   - return context
52   -
53 39 class CreatePostView(LoginRequiredMixin, generic.edit.CreateView):
54 40 login_url = reverse_lazy("core:home")
55 41 redirect_field_name = 'next'
... ... @@ -77,6 +63,19 @@ def render_post(request, post):
77 63  
78 64 return render(request, "post/post_render.html", context)
79 65  
  66 +class PostUpdateView(LoginRequiredMixin, generic.UpdateView):
  67 + login_url = reverse_lazy("core:home")
  68 + redirect_field_name = 'next'
  69 +
  70 + form_class = PostForm
  71 + model = Post
  72 + template_name = "post/post_update_form.html"
  73 +
  74 + def get_success_url(self):
  75 + self.success_url = reverse('forum:render_post', args = (self.object.id, ))
  76 +
  77 + return self.success_url
  78 +
80 79 class PostDeleteView(LoginRequiredMixin, generic.DeleteView):
81 80 login_url = reverse_lazy("core:home")
82 81 redirect_field_name = 'next'
... ...
poll/admin.py
1 1 from django.contrib import admin
2 2  
3   -# Register your models here.
  3 +from .models import Poll, Answer
  4 +
  5 +class PollAdmin(admin.ModelAdmin):
  6 + list_display = ['name', 'slug','limit_date']
  7 + search_fields = ['name','slug']
  8 +
  9 +class AnswerAdmin(admin.ModelAdmin):
  10 + list_display = ['answer','order']
  11 + search_fields = ['answer']
  12 +
  13 +admin.site.register(Poll, PollAdmin)
  14 +admin.site.register(Answer, AnswerAdmin)
... ...
poll/forms.py 0 → 100644
... ... @@ -0,0 +1,35 @@
  1 +from django import forms
  2 +from django.utils.translation import ugettext_lazy as _
  3 +from users.models import User
  4 +from .models import Poll
  5 +
  6 +class PollForm(forms.ModelForm):
  7 +
  8 + def __init__(self, *args, **kwargs):
  9 + super(PollForm, self).__init__(*args, **kwargs)
  10 + self.fields["all_students"].required = False
  11 + self.fields["all_students"].initial = False
  12 + self.fields["students"].required = False
  13 +
  14 + def clean_all_students(self):
  15 + if('all_students' not in self.data):
  16 + if('students' in self.data):
  17 + return False
  18 + raise forms.ValidationError(_('It is required one these fields.'))
  19 + else:
  20 + all_students = self.data['all_students']
  21 + if(not all_students):
  22 + raise forms.ValidationError(_('It is required one these fields.'))
  23 + return True
  24 +
  25 +
  26 + class Meta:
  27 + model = Poll
  28 + fields = ['name','limit_date','students','all_students']
  29 +
  30 + widgets = {
  31 + 'name': forms.TextInput(attrs={'placeholder': 'Question?'}),
  32 + 'limit_date': forms.DateTimeInput(
  33 + attrs={'placeholder': 'Maximum date permited to resolve the poll'}),
  34 + 'student': forms.Select(),
  35 + }
... ...
poll/migrations/0001_initial.py
1 1 # -*- coding: utf-8 -*-
2   -# Generated by Django 1.10 on 2016-09-28 20:27
  2 +# Generated by Django 1.10 on 2016-09-30 15:40
3 3 from __future__ import unicode_literals
4 4  
5 5 from django.db import migrations, models
... ... @@ -11,7 +11,7 @@ class Migration(migrations.Migration):
11 11 initial = True
12 12  
13 13 dependencies = [
14   - ('courses', '0001_initial'),
  14 + ('courses', '0002_auto_20160930_1239'),
15 15 ]
16 16  
17 17 operations = [
... ... @@ -23,20 +23,19 @@ class Migration(migrations.Migration):
23 23 ('order', models.PositiveSmallIntegerField(verbose_name='Order')),
24 24 ],
25 25 options={
26   - 'verbose_name': 'Answer',
27 26 'verbose_name_plural': 'Answers',
28 27 'ordering': ('order',),
  28 + 'verbose_name': 'Answer',
29 29 },
30 30 ),
31 31 migrations.CreateModel(
32 32 name='Poll',
33 33 fields=[
34 34 ('activity_ptr', models.OneToOneField(auto_created=True, on_delete=django.db.models.deletion.CASCADE, parent_link=True, primary_key=True, serialize=False, to='courses.Activity')),
35   - ('question', models.CharField(max_length=300, verbose_name='Question')),
36 35 ],
37 36 options={
38   - 'verbose_name': 'Poll',
39 37 'verbose_name_plural': 'Polls',
  38 + 'verbose_name': 'Poll',
40 39 },
41 40 bases=('courses.activity',),
42 41 ),
... ...
poll/models.py
... ... @@ -6,7 +6,6 @@ from core.models import Resource
6 6 from courses.models import Activity
7 7  
8 8 class Poll(Activity):
9   - question = models.CharField(_('Question'), max_length = 300)
10 9  
11 10 class Meta:
12 11 #ordering = ('create_date','name')
... ... @@ -14,7 +13,7 @@ class Poll(Activity):
14 13 verbose_name_plural = _('Polls')
15 14  
16 15 def __str__(self):
17   - return str(self.question) + str("/") + str(self.topic)
  16 + return str(self.name) + str("/") + str(self.topic)
18 17  
19 18 class Answer(models.Model):
20 19 answer = models.CharField(_("Answer"), max_length = 200)
... ... @@ -27,4 +26,4 @@ class Answer(models.Model):
27 26 verbose_name_plural = _('Answers')
28 27  
29 28 def __str__(self):
30   - return str(self.question) + str("/") + str(self.topic)
  29 + return str(self.answer) + str("/") + str(self.poll)
... ...
poll/permissions.py 0 → 100644
... ... @@ -0,0 +1,12 @@
  1 +from rolepermissions.permissions import register_object_checker
  2 +from amadeus.roles import SystemAdmin
  3 +
  4 +@register_object_checker()
  5 +def edit_poll(role, user, poll):
  6 + if (role == SystemAdmin):
  7 + return True
  8 +
  9 + if (user in poll.topic.subject.professors.all()):
  10 + return True
  11 +
  12 + return False
... ...
poll/templates/poll/create_update.html 0 → 100644
... ... @@ -0,0 +1,206 @@
  1 +{% extends "topic/index.html" %}
  2 +
  3 +{% load i18n widget_tweaks dict_access static%}
  4 +
  5 +{% block style %}
  6 + <script src="https://code.jquery.com/ui/1.12.1/jquery-ui.js"></script>
  7 +{% endblock %}
  8 +
  9 +{% block content %}
  10 +<!-- Modal (remember to change the ids!!!) -->
  11 +<div class="modal fade" id="poll" tabindex="-1" role="dialog" aria-labelledby="myModalLabel">
  12 + <div class="modal-dialog" role="document">
  13 + <div class="modal-content">
  14 +
  15 + <!-- Modal Header -->
  16 + <div class="modal-header">
  17 +
  18 + <!-- Put your title here!!! -->
  19 + <h4 class="modal-title" id="myModalLabel">{% trans "Create a Poll" %}</h4>
  20 +
  21 + </div>
  22 + <!-- Modal Body -->
  23 + <div class="modal-body">
  24 +
  25 + <!-- Put ONLY your content here!!! -->
  26 + <div class="conteiner">
  27 + <div class="row form-group">
  28 + <div class="col-md-1">
  29 + </br>
  30 + <label><span class="glyphicon glyphicon-hand-right"></span></label>
  31 + </div>
  32 + <div class="col-md-10">
  33 + <div class="has-success">
  34 + <input form="form" type="text" name="{{form.name.name}}" {% if form.name.value != None %}value="{{form.name.value}}" {% endif %} class="form-control" placeholder='{% trans "Question?" %}'>
  35 + <span class="help-block">{% trans "A Question to be answered" %}</span>
  36 + </div>
  37 + </div>
  38 + {% if form.name.errors %}
  39 + <div class="col-md-10 not_submited">
  40 + </br>
  41 + <div class="alert alert-danger alert-dismissible" role="alert">
  42 + <button type="button" class="close" data-dismiss="alert" aria-label="Close">
  43 + <span aria-hidden="true">&times;</span>
  44 + </button>
  45 + <ul>
  46 + {% for error in form.name.errors %}
  47 + <li>{{ error }}</li>
  48 + {% endfor %}
  49 + </ul>
  50 + </div>
  51 + </div>
  52 + {% endif %}
  53 + </div>
  54 + <form id="form" class="" action="" method="post">
  55 + {% csrf_token %}
  56 + {% for key in keys %}
  57 + <div class="row form-group">
  58 + <div class="col-md-1">
  59 + </br>
  60 + <label><span class="glyphicon glyphicon-move"></span></label>
  61 + </div>
  62 + <div class="col-md-10">
  63 + <div class="has-success is-empty">
  64 + <input type="text" name="{{key}}" class="form-control" placeholder='{% trans "Answer" %}' value="{{ answers|value:key }}">
  65 + <span class="help-block">{% trans "Possible answer for the question" %}</span>
  66 + </div>
  67 + </div>
  68 + <div class="col-md-1">
  69 + </br>
  70 + <label><span class="glyphicon glyphicon-remove" onclick="this.parentNode.parentNode.parentNode.parentNode.removeChild(this.parentNode.parentNode.parentNode);"></span></label>
  71 + </div>
  72 + </div>
  73 + {% empty %}
  74 + <div class="row form-group">
  75 + <div class="col-md-1">
  76 + </br>
  77 + <label><span class="glyphicon glyphicon-move"></span></label>
  78 + </div>
  79 + <div class="col-md-10">
  80 + <div class="has-success is-empty">
  81 + <input type="text" name="1" class="form-control" placeholder='{% trans "Answer" %}'>
  82 + <span class="help-block">{% trans "Possible answer for the question" %}</span>
  83 + </div>
  84 + </div>
  85 + <div class="col-md-1">
  86 + </br>
  87 + <label><span class="glyphicon glyphicon-remove" onclick="this.parentNode.parentNode.parentNode.parentNode.removeChild(this.parentNode.parentNode.parentNode);"></span></label>
  88 + </div>
  89 + </div>
  90 + {% endfor %}
  91 + </form>
  92 + </br>
  93 + </div>
  94 + <button type="button" id="add" class="btn btn-primary btn-block btn-sm">add</button>
  95 + <div class="row form-group">
  96 + <label for="{{ form.limit_date.auto_id }}">{{ form.limit_date.label }}</label>
  97 + {% render_field form.limit_date class="form-control" form="form"%}
  98 + {# <input form="form" class="form-control" type="date" name="{{form.limit_date.name}}" {% if form.limit_date.value != None %}value="{% if form.limit_date.value.year %}{{form.limit_date.value|date:'Y-m-d'}}{% else %}{{form.limit_date.value}}{% endif %}"{% endif %}>#}
  99 + {% if form.limit_date.errors %}
  100 + <div class="not_submited">
  101 + </br>
  102 + <div class="alert alert-danger alert-dismissible" role="alert">
  103 + <button type="button" class="close" data-dismiss="alert" aria-label="Close">
  104 + <span aria-hidden="true">&times;</span>
  105 + </button>
  106 + <ul>
  107 + {% for error in form.limit_date.errors %}
  108 + <li>{{ error }}</li>
  109 + {% endfor %}
  110 + </ul>
  111 + </div>
  112 + </div>
  113 + {% endif %}
  114 + </div>
  115 +
  116 + <div class="row form-group">
  117 + <label for="{{ form.students.auto_id }}">{{ form.students.label }}</label>
  118 + {% render_field form.students class="form-control" form="form"%}
  119 + </div>
  120 + <div class="row form-group">
  121 + <div class="checkbox">
  122 + <label>
  123 + {% render_field form.all_students class="form-control" form="form" %}<span class="checkbox-material"><span class="check"></span></span> {{form.all_students.label }}
  124 + {# <input form="form" type="checkbox" name="{{form.all_students.name}}"><span class="checkbox-material"><span class="check"></span></span> {{ form.all_students.label }}#}
  125 + </label>
  126 + </div>
  127 + {% if form.all_students.errors %}
  128 + <div class="not_submited">
  129 + </br>
  130 + <div class="alert alert-danger alert-dismissible" role="alert">
  131 + <button type="button" class="close" data-dismiss="alert" aria-label="Close">
  132 + <span aria-hidden="true">&times;</span>
  133 + </button>
  134 + <ul>
  135 + {% for error in form.all_students.errors %}
  136 + <li>{{ error }}</li>
  137 + {% endfor %}
  138 + </ul>
  139 + </div>
  140 + </div>
  141 + {% endif %}
  142 +
  143 + {# <label for="{{ form.all_students.auto_id }}">{{ form.all_students.label }}</label>#}
  144 + {# {% render_field form.all_students class="form-control" form="form"%}#}
  145 + </div>
  146 +
  147 + </div>
  148 +
  149 + <!-- Modal Footer -->
  150 + <div class="modal-footer">
  151 +
  152 + <!-- Don't remove that!!! -->
  153 + <button type="button" class="btn btn-danger btn-raised" data-dismiss="modal">{% trans "Close" %}</button>
  154 +
  155 + <!-- Put curtom buttons here!!! -->
  156 + <button type="submite" id="button" form="form" class="btn btn-primary btn-raised">{% trans "Create" %}</button>
  157 + </div>
  158 +
  159 + </div>
  160 + </div>
  161 +</div>
  162 +<script type="text/javascript">
  163 +// Este js tem que ficar aqui se não a tag "trans" não vai funcionar
  164 +$(window).ready(function() { // utilizado para abrir o modal quando tiver tido algum erro no preenchimento do formulario
  165 + if($('.not_submited').length){
  166 + $('#poll').modal('show');
  167 + }
  168 +});
  169 +$( "#form" ).sortable({ // utilizado para fazer a re-organização das respostas
  170 + delay: 100,
  171 + distance: 5,
  172 + update: function( event, ui ) {
  173 + var cont = 1;
  174 + $("#form div div div input").each(function(){
  175 + $(this).attr('name',cont++);
  176 + });
  177 + },
  178 +});
  179 +name = 2;
  180 +$("#add").click(function() { // utilizado para adicionar um novo campo de resposta
  181 + //Obs: não funcionar se estiver importado no head, só funciona se estiver no final do arquivo
  182 + $("#form").append('\
  183 + <div class="row form-group">\
  184 + <div class="col-md-1">\
  185 + </br>\
  186 + <label><span class="glyphicon glyphicon-move"></span></label>\
  187 + </div>\
  188 + <div class="col-md-10">\
  189 + <div class="has-success is-empty">\
  190 + <input type="text" name="1" class="form-control" placeholder="{% trans "Answer" %}">\
  191 + <span class="help-block">{% trans "Possible answer for the question" %}</span>\
  192 + </div>\
  193 + </div>\
  194 + <div class="col-md-1">\
  195 + </br>\
  196 + <label><span class="glyphicon glyphicon-remove" onclick="this.parentNode.parentNode.parentNode.parentNode.removeChild(this.parentNode.parentNode.parentNode);"></span></label>\
  197 + </div>\
  198 + </div>');
  199 + var cont = 1;
  200 + $("#form div div div input").each(function(){
  201 + $(this).attr('name',cont++);
  202 + });
  203 +});
  204 +</script>
  205 +<a href="" data-toggle="modal" data-target="#poll">modal</a>
  206 +{% endblock content %}
... ...
poll/templates/poll/poll.html
... ... @@ -1,52 +0,0 @@
1   -{% extends "topic/index.html" %}
2   -
3   -{% load i18n %}
4   -
5   -{% block style %}
6   - <script src="https://code.jquery.com/ui/1.12.1/jquery-ui.js"></script>
7   -{% endblock %}
8   -
9   -{% block content %}
10   -<div class="col-md-8 col-md-offset-2">
11   - <div class="panel panel-primary">
12   - <div class="panel-heading">
13   - <h3 class="panel-title">
14   - <span class="glyphicon glyphicon-hand-right"></span> Question?</h3>
15   - </div>
16   - <div class="container-fluid">
17   - <form id="form" class="" action="" method="post">
18   - <div class="row form-group">
19   - <div class="col-md-1">
20   - </br>
21   - <label><a href=""><span class="glyphicon glyphicon-move"></span></a></label>
22   - </div>
23   - <div class="col-md-10">
24   - <div class="form-control-md has-success is-empty">
25   - <input type="text" class="form-control" placeholder="Email address default size">
26   - <span class="help-block">{% trans "Possible Answer" %}</span>
27   - </div>
28   - </div>
29   - <div class="col-md-1">
30   - </br>
31   - <label><a href=""><span class="glyphicon glyphicon-remove" onclick="this.parentNode.parentNode.parentNode.parentNode.removeChild(this.parentNode.parentNode.parentNode);"></span></a></label>
32   - </div>
33   - </div>
34   - </form>
35   - </br>
36   - </div>
37   -
38   - </div>
39   - <div class="panel-footer text-center">
40   - <button type="button" id="add" class="btn btn-primary btn-block btn-sm">add</button>
41   - </div>
42   - </div>
43   -<script type="text/javascript">
44   -$( "#form" ).sortable({
45   - delay: 100,
46   - distance: 5,
47   -});
48   -$("#add").click(function() {
49   - $("#form").append('<div class="row form-group"><div class="col-md-1"></br><label><a href=""><span class="glyphicon glyphicon-move"></span></a></label></div><div class="col-md-10"><div class="form-group-md has-success is-empty"><input type="text" class="form-control" placeholder="Email address default size"><span class="help-block">Please enter a valid email address</span></div></div><div class="col-md-1"></br><label><a href=""><span class="glyphicon glyphicon-remove" onclick="this.parentNode.parentNode.parentNode.parentNode.removeChild(this.parentNode.parentNode.parentNode);"></span></a></label></div></br></div>');
50   -});
51   -</script>
52   -{% endblock content %}
poll/templates/poll/view.html 0 → 100644
... ... @@ -0,0 +1,99 @@
  1 +{% extends "topic/index.html" %}
  2 +
  3 +{% load i18n widget_tweaks dict_access static%}
  4 +{% block content %}
  5 +<!-- Modal (remember to change the ids!!!) -->
  6 +<div class="modal fade" id="poll" tabindex="-1" role="dialog" aria-labelledby="myModalLabel">
  7 + <div class="modal-dialog" role="document">
  8 + <div class="modal-content">
  9 +
  10 + <!-- Modal Header -->
  11 + <div class="modal-header">
  12 +
  13 + <!-- Put your title here!!! -->
  14 + <h4 class="modal-title" id="myModalLabel">{{form.question}}</h4>
  15 +
  16 + </div>
  17 + <!-- Modal Body -->
  18 + <div class="modal-body">
  19 +
  20 + <!-- Put ONLY your content here!!! -->
  21 + <div class="conteiner">
  22 + </div>
  23 + <form id="form" method="post">
  24 + {% csrf_token %}
  25 + {% for key in keys %}
  26 + <div class="row form-group">
  27 + <div class="col-md-1">
  28 + </br>
  29 + <label><span class="glyphicon glyphicon-move"></span></label>
  30 + </div>
  31 + <div class="col-md-10">
  32 + <div class="has-success is-empty">
  33 + <input type="text" name="{{key}}" class="form-control" placeholder='{% trans "Answer" %}' value="{{ answers|value:key }}">
  34 + <span class="help-block">{% trans "Possible answer for the question" %}</span>
  35 + </div>
  36 + </div>
  37 + <div class="col-md-1">
  38 + </br>
  39 + <label><span class="glyphicon glyphicon-remove" onclick="this.parentNode.parentNode.parentNode.parentNode.removeChild(this.parentNode.parentNode.parentNode);"></span></label>
  40 + </div>
  41 + </div>
  42 + {% empty %}
  43 + <div class="row form-group">
  44 + <div class="col-md-1">
  45 + </br>
  46 + <label><span class="glyphicon glyphicon-move"></span></label>
  47 + </div>
  48 + <div class="col-md-10">
  49 + <div class="has-success is-empty">
  50 + <input type="text" name="1" class="form-control" placeholder='{% trans "Answer" %}'>
  51 + <span class="help-block">{% trans "Possible answer for the question" %}</span>
  52 + </div>
  53 + </div>
  54 + <div class="col-md-1">
  55 + </br>
  56 + <label><span class="glyphicon glyphicon-remove" onclick="this.parentNode.parentNode.parentNode.parentNode.removeChild(this.parentNode.parentNode.parentNode);"></span></label>
  57 + </div>
  58 + </div>
  59 + {% endfor %}
  60 + </form>
  61 + </br>
  62 + </div>
  63 + <button type="button" id="add" class="btn btn-primary btn-block btn-sm">add</button>
  64 + <div class="row form-group">
  65 + <input form="form" class="form-control" type="date" name="{{form.limit_date.name}}" {% if form.limit_date.value != None %}value="{% if form.limit_date.value.year %}{{form.limit_date.value|date:'Y-m-d'}}{% else %}{{form.limit_date.value}}{% endif %}"{% endif %}>
  66 + {% if form.limit_date.errors %}
  67 + <div class="not_submited">
  68 + </br>
  69 + <div class="alert alert-danger alert-dismissible" role="alert">
  70 + <button type="button" class="close" data-dismiss="alert" aria-label="Close">
  71 + <span aria-hidden="true">&times;</span>
  72 + </button>
  73 + <ul>
  74 + {% for error in form.limit_date.errors %}
  75 + <li>{{ error }}</li>
  76 + {% endfor %}
  77 + </ul>
  78 + </div>
  79 + </div>
  80 + {% endif %}
  81 + </div>
  82 +
  83 + </div>
  84 +
  85 + <!-- Modal Footer -->
  86 + <div class="modal-footer">
  87 +
  88 + <!-- Don't remove that!!! -->
  89 + <button type="button" class="btn btn-danger btn-raised" data-dismiss="modal">{% trans "Close" %}</button>
  90 +
  91 + <!-- Put curtom buttons here!!! -->
  92 + <button type="submite" id="button" form="form" class="btn btn-primary btn-raised">{% trans "Create" %}</button>
  93 + </div>
  94 +
  95 + </div>
  96 + </div>
  97 +</div>
  98 +<a href="" data-toggle="modal" data-target="#poll">modal</a>
  99 +{% endblock content %}
... ...
poll/templatetags/__init__.py 0 → 100644
poll/templatetags/dict_access.py 0 → 100644
... ... @@ -0,0 +1,13 @@
  1 +from django import template
  2 +
  3 +from forum.models import Forum
  4 +
  5 +register = template.Library()
  6 +
  7 +"""
  8 + Template tag to load all the foruns of a post
  9 +"""
  10 +
  11 +@register.filter
  12 +def value(dictionary, key):
  13 + return dictionary[key]
... ...
poll/urls.py
... ... @@ -3,6 +3,7 @@ from django.conf.urls import url
3 3 from . import views
4 4  
5 5 urlpatterns = [
6   - url(r'^to/poll/to/$', views.Poll.as_view(), name='poll'),
  6 + url(r'^create/(?P<slug>[\w\-_]+)/$', views.CreatePoll.as_view(), name='create_poll'), # topic slug
  7 + url(r'^update/(?P<slug>[\w\-_]+)/$', views.UpdatePoll.as_view(), name='update_poll'), # poll slug
7 8  
8 9 ]
... ...
poll/views.py
... ... @@ -8,31 +8,116 @@ from django.core.urlresolvers import reverse_lazy
8 8 from django.utils.translation import ugettext_lazy as _
9 9 from rolepermissions.verifications import has_role
10 10 from rolepermissions.verifications import has_object_permission
  11 +# from django.views.generic.edit import FormMixin
11 12  
12   -# from .forms import CourseForm, UpdateCourseForm, CategoryForm, SubjectForm,TopicForm
13   -# from .models import Course, Subject, Category,Topic, SubjectCategory
  13 +from .forms import PollForm
  14 +from .models import Poll, Answer
14 15 from core.mixins import NotificationMixin
15 16 from users.models import User
16   -from courses.models import Course
  17 +from courses.models import Course, Topic
17 18  
18   -class Poll(generic.TemplateView):
  19 +class CreatePoll(LoginRequiredMixin,generic.CreateView):
19 20  
20   - # login_url = reverse_lazy("core:home")
21   - # redirect_field_name = 'next'
22   - # model = Course
23   - # context_object_name = 'course'
24   - template_name = 'poll/poll.html'
25   - # queryset = Course.objects.all()
  21 + login_url = reverse_lazy("core:home")
  22 + redirect_field_name = 'next'
  23 + model = Poll
  24 + form_class = PollForm
  25 + context_object_name = 'poll'
  26 + template_name = 'poll/create_update.html'
  27 + success_url = reverse_lazy('core:home')
26 28  
27   - # def get_queryset(self):
28   - # return Course.objects.all()[0]
  29 + def form_invalid(self, form,**kwargs):
  30 + context = super(CreatePoll, self).form_invalid(form)
  31 + answers = {}
  32 + for key in self.request.POST:
  33 + if(key != 'csrfmiddlewaretoken' and key != 'name' and key != 'limit_date' and key != 'all_students' and key != 'students'):
  34 + answers[key] = self.request.POST[key]
  35 +
  36 + keys = sorted(answers)
  37 + context.context_data['answers'] = answers
  38 + context.context_data['keys'] = keys
  39 + return context
  40 +
  41 + def form_valid(self, form):
  42 + self.object = form.save(commit = False)
  43 + topic = get_object_or_404(Topic, slug = self.kwargs.get('slug'))
  44 + self.object.topic = topic
  45 + self.object.save()
  46 +
  47 + for key in self.request.POST:
  48 + if(key != 'csrfmiddlewaretoken' and key != 'name' and key != 'limit_date' and key != 'all_students' and key != 'students'):
  49 + answer = Answer(answer=self.request.POST[key],order=key,poll=self.object)
  50 + answer.save()
  51 +
  52 + return super(CreatePoll, self).form_valid(form)
  53 +
  54 + def get_context_data(self, **kwargs):
  55 + context = super(CreatePoll, self).get_context_data(**kwargs)
  56 + topic = get_object_or_404(Topic, slug = self.kwargs.get('slug'))
  57 + context['course'] = topic.subject.course
  58 + context['subject'] = topic.subject
  59 + context['subjects'] = topic.subject.course.subjects.all()
  60 + return context
  61 +
  62 +class UpdatePoll(LoginRequiredMixin,generic.UpdateView):
  63 +
  64 + login_url = reverse_lazy("core:home")
  65 + redirect_field_name = 'next'
  66 + model = Poll
  67 + form_class = PollForm
  68 + context_object_name = 'poll'
  69 + template_name = 'poll/create_update.html'
  70 + success_url = reverse_lazy('core:home')
  71 +
  72 + def dispatch(self, *args, **kwargs):
  73 + poll = get_object_or_404(Poll, slug = self.kwargs.get('slug'))
  74 + if(not has_object_permission('edit_poll', self.request.user, poll)):
  75 + return self.handle_no_permission()
  76 + return super(UpdatePoll, self).dispatch(*args, **kwargs)
  77 +
  78 + def get_object(self, queryset=None):
  79 + return get_object_or_404(Poll, slug = self.kwargs.get('slug'))
  80 +
  81 + def form_invalid(self, form,**kwargs):
  82 + context = super(UpdatePoll, self).form_invalid(form)
  83 + answers = {}
  84 + for key in self.request.POST:
  85 + if(key != 'csrfmiddlewaretoken' and key != 'name' and key != 'limit_date' and key != 'all_students' and key != 'students'):
  86 + answers[key] = self.request.POST[key]
  87 +
  88 + keys = sorted(answers)
  89 + context.context_data['answers'] = answers
  90 + context.context_data['keys'] = keys
  91 + return context
  92 +
  93 + def form_valid(self, form):
  94 + poll = self.object
  95 + poll = form.save(commit = False)
  96 + poll.answers.all().delete()
  97 + poll.save()
  98 +
  99 +
  100 + for key in self.request.POST:
  101 + if(key != 'csrfmiddlewaretoken' and key != 'name' and key != 'limit_date' and key != 'all_students' and key != 'students'):
  102 + answer = Answer(answer=self.request.POST[key],order=key,poll=poll)
  103 + answer.save()
  104 +
  105 + return super(UpdatePoll, self).form_valid(form)
29 106  
30 107 def get_context_data(self, **kwargs):
31   - context = super(Poll, self).get_context_data(**kwargs)
32   - course = Course.objects.all()[0]
33   - context['course'] = course
34   - context['subject'] = course.subjects.all()[0]
35   - context['subjects'] = course.subjects.all()
36   - # if (has_role(self.request.user,'system_admin')):
37   - # context['subjects'] = self.object.course.subjects.all()
  108 + context = super(UpdatePoll, self).get_context_data(**kwargs)
  109 + poll = self.object
  110 + context['course'] = poll.topic.subject.course
  111 + context['subject'] = poll.topic.subject
  112 + context['subjects'] = poll.topic.subject.course.subjects.all()
  113 +
  114 + answers = {}
  115 + for answer in poll.answers.all():
  116 + # print (key.answer)
  117 + answers[answer.order] = answer.answer
  118 +
  119 + keys = sorted(answers)
  120 + context['answers'] = answers
  121 + context['keys'] = keys
  122 +
38 123 return context
... ...
users/migrations/0001_initial.py
1 1 # -*- coding: utf-8 -*-
2   -# Generated by Django 1.10 on 2016-09-21 02:52
  2 +# Generated by Django 1.10 on 2016-09-30 15:40
3 3 from __future__ import unicode_literals
4 4  
5 5 import django.contrib.auth.models
... ...
users/templates/list_users.html
... ... @@ -66,7 +66,7 @@
66 66 <a href="javascript:void(0)" class="btn btn-primary btn-lg" data-toggle="modal" data-target="#DeleteModal">{% trans 'Delete' %}</a>
67 67 </div>
68 68 </div>
69   -
  69 +
70 70  
71 71 <!-- Modal -->
72 72 <div class="modal fade" id="DeleteModal" tabindex="-1" role="dialog" aria-labelledby="DeleteModalLabel">
... ... @@ -77,11 +77,11 @@
77 77 <h4 class="modal-title" id="DeleteModalLabel">Confirm delete</h4>
78 78 </div>
79 79 <div class="modal-body">
80   - Are you sure you want to delete?
  80 + Are you sure you want to delete the user <b>{{acc.name}}</b>?
81 81 </div>
82 82 <div class="modal-footer">
83   - <button type="button" class="btn btn-default" data-dismiss="modal">Cancel</button>
84   - <button type="button" class="btn btn-primary">Delete</button>
  83 + <button type="button" class="btn btn-default" data-dismiss="modal">Cancel</button>
  84 + <button type="button" class="btn btn-primary"> <a href="{% url 'users:delete' acc.username %}">Delete</a></button>
85 85 </div>
86 86 </div>
87 87 </div>
... ...
users/urls.py
... ... @@ -7,6 +7,7 @@ urlpatterns = [
7 7 url(r'^create/$', views.Create.as_view(), name='create'),
8 8 url(r'^edit/(?P<username>[\w_-]+)/$', views.Update.as_view(), name='update'),
9 9 url(r'^view/(?P<username>[\w_-]+)/$', views.View.as_view(), name='view'),
  10 + url(r'^delete/(?P<username>[\w_-]+)/$', views.delete, name='delete'),
10 11 url(r'^profile/$', views.Profile.as_view(), name='profile'),
11 12 url(r'^profile/editar/(?P<username>[\w_-]+)/$', views.EditProfile.as_view(), name='edit_profile'),
12 13 #
... ...
users/views.py
1   -from django.shortcuts import get_object_or_404
  1 +from django.shortcuts import get_object_or_404,redirect
2 2 from django.db.models import Q
3 3 from django.views import generic
4 4 from django.contrib import messages
... ... @@ -95,6 +95,14 @@ class View(LoginRequiredMixin, generic.DetailView):
95 95 slug_field = 'username'
96 96 slug_url_kwarg = 'username'
97 97  
  98 +def delete(request,username):
  99 + user = get_object_or_404(User,username = username)
  100 + user.delete()
  101 + messages.success(request,_("User deleted Successfully!"))
  102 + return redirect('users:manage')
  103 +
  104 +
  105 +
98 106 class UpdateUser(LoginRequiredMixin, generic.edit.UpdateView):
99 107  
100 108 allowed_roles = ['student']
... ...