Commit 7801bd50068387480e54d25ec90456402b6babab
1 parent
a90d38e6
Exists in
master
and in
3 other branches
Adding post creation
Showing
10 changed files
with
337 additions
and
9 deletions
Show diff stats
amadeus/static/css/base/amadeus.css
@@ -968,4 +968,13 @@ li.item .notify_badge { | @@ -968,4 +968,13 @@ li.item .notify_badge { | ||
968 | .mural .post_make .post-field h4 { | 968 | .mural .post_make .post-field h4 { |
969 | cursor: text; | 969 | cursor: text; |
970 | font-style: italic; | 970 | font-style: italic; |
971 | +} | ||
972 | + | ||
973 | +.post_action i { | ||
974 | + font-size: 45px; | ||
975 | +} | ||
976 | + | ||
977 | +.post-button { | ||
978 | + padding-right: inherit !important; | ||
979 | + padding-left: inherit !important; | ||
971 | } | 980 | } |
972 | \ No newline at end of file | 981 | \ No newline at end of file |
amadeus/static/css/themes/green.css
@@ -504,6 +504,10 @@ a.add-row { | @@ -504,6 +504,10 @@ a.add-row { | ||
504 | color: #CCCCCC; | 504 | color: #CCCCCC; |
505 | } | 505 | } |
506 | 506 | ||
507 | +.post_action i { | ||
508 | + color: #1d8fe0; | ||
509 | +} | ||
510 | + | ||
507 | @media(max-width: 768px) { | 511 | @media(max-width: 768px) { |
508 | .navbar .navbar-nav .dropdown .dropdown-menu li > a { | 512 | .navbar .navbar-nav .dropdown .dropdown-menu li > a { |
509 | color: #333333 !important; | 513 | color: #333333 !important; |
mural/forms.py
@@ -0,0 +1,41 @@ | @@ -0,0 +1,41 @@ | ||
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 .models import GeneralPost | ||
7 | + | ||
8 | +class Validation(forms.ModelForm): | ||
9 | + MAX_UPLOAD_SIZE = 5*1024*1024 | ||
10 | + | ||
11 | + def clean_post(self): | ||
12 | + post = self.cleaned_data.get('post', '') | ||
13 | + cleaned_post = strip_tags(post) | ||
14 | + | ||
15 | + if cleaned_post == '': | ||
16 | + self._errors['post'] = [_('This field is required.')] | ||
17 | + | ||
18 | + return ValueError | ||
19 | + | ||
20 | + return post | ||
21 | + | ||
22 | + def clean_image(self): | ||
23 | + image = self.cleaned_data.get('image', False) | ||
24 | + | ||
25 | + if image: | ||
26 | + if hasattr(image, '_size'): | ||
27 | + if image._size > self.MAX_UPLOAD_SIZE: | ||
28 | + self._errors['image'] = [_("The image is too large. It should have less than 5MB.")] | ||
29 | + | ||
30 | + return ValueError | ||
31 | + | ||
32 | + return image | ||
33 | + | ||
34 | +class GeneralPostForm(Validation): | ||
35 | + class Meta: | ||
36 | + model = GeneralPost | ||
37 | + fields = ['action', 'post', 'image'] | ||
38 | + widgets = { | ||
39 | + 'action': forms.RadioSelect, | ||
40 | + 'post': forms.Textarea | ||
41 | + } | ||
0 | \ No newline at end of file | 42 | \ No newline at end of file |
@@ -0,0 +1,20 @@ | @@ -0,0 +1,20 @@ | ||
1 | +# -*- coding: utf-8 -*- | ||
2 | +# Generated by Django 1.10 on 2017-02-04 19:25 | ||
3 | +from __future__ import unicode_literals | ||
4 | + | ||
5 | +from django.db import migrations, models | ||
6 | + | ||
7 | + | ||
8 | +class Migration(migrations.Migration): | ||
9 | + | ||
10 | + dependencies = [ | ||
11 | + ('mural', '0002_muralvisualizations'), | ||
12 | + ] | ||
13 | + | ||
14 | + operations = [ | ||
15 | + migrations.AlterField( | ||
16 | + model_name='mural', | ||
17 | + name='action', | ||
18 | + field=models.CharField(choices=[('comment', 'Comment'), ('help', 'Ask for Help')], max_length=100, verbose_name='Action'), | ||
19 | + ), | ||
20 | + ] |
mural/models.py
@@ -16,7 +16,7 @@ def validate_img_extension(value): | @@ -16,7 +16,7 @@ def validate_img_extension(value): | ||
16 | raise ValidationError(_('File not supported.')) | 16 | raise ValidationError(_('File not supported.')) |
17 | 17 | ||
18 | class Mural(KnowsChild): | 18 | class Mural(KnowsChild): |
19 | - action = models.CharField(_('Action'), max_length = 100, choices = (("comment", _("Comment")), ("help", _("Ask for Help"))), blank = True) | 19 | + action = models.CharField(_('Action'), max_length = 100, default = "comment", choices = (("comment", _("Comment")), ("help", _("Ask for Help")))) |
20 | post = models.TextField(_('Post'), blank = True) | 20 | post = models.TextField(_('Post'), blank = True) |
21 | image = models.ImageField(verbose_name = _('Image'), null=True, blank = True, upload_to = 'posts/', validators = [validate_img_extension]) | 21 | image = models.ImageField(verbose_name = _('Image'), null=True, blank = True, upload_to = 'posts/', validators = [validate_img_extension]) |
22 | user = models.ForeignKey(User, verbose_name = _('User'), related_name = "post_user", null = True) | 22 | user = models.ForeignKey(User, verbose_name = _('User'), related_name = "post_user", null = True) |
@@ -0,0 +1,142 @@ | @@ -0,0 +1,142 @@ | ||
1 | +{% load static i18n %} | ||
2 | +{% load widget_tweaks %} | ||
3 | + | ||
4 | +<div class="modal-dialog" role="document"> | ||
5 | + <div class="modal-content"> | ||
6 | + <div class="modal-body"> | ||
7 | + <form id="post-form" method="post" action="{{ form_url }}" enctype="multipart/form-data"> | ||
8 | + {% csrf_token %} | ||
9 | + <div class="row"> | ||
10 | + <div class="form-group{% if form.has_error %} has-error {% endif %} text-center col-md-6"> | ||
11 | + <label for="{{ form.action.0.id_for_label }}" class="text-center post_action"> | ||
12 | + <span class="radio"> | ||
13 | + <i class="fa fa-commenting-o"></i> | ||
14 | + <br clear="all" /> | ||
15 | + {{ form.action.0 }} | ||
16 | + </span> | ||
17 | + </label> | ||
18 | + </div> | ||
19 | + <div class="form-group{% if form.has_error %} has-error {% endif %} text-center col-md-6"> | ||
20 | + <label for="{{ form.action.1.id_for_label }}" class="text-center post_action"> | ||
21 | + <span class="radio"> | ||
22 | + <i class="fa fa-comments-o"></i> | ||
23 | + <br clear="all" /> | ||
24 | + {{ form.action.1 }} | ||
25 | + </span> | ||
26 | + </label> | ||
27 | + </div> | ||
28 | + </div> | ||
29 | + | ||
30 | + <div class="form-group{% if form.has_error %} has-error {% endif %}"> | ||
31 | + <label for="{{ form.post.auto_id }}">{{ form.post.label }} <span>*</span></label> | ||
32 | + {% render_field form.post class='form-control text_simple_wysiwyg' %} | ||
33 | + | ||
34 | + <span id="helpBlock" class="help-block">{{ form.post.help_text }}</span> | ||
35 | + | ||
36 | + {% if form.post.errors %} | ||
37 | + <div class="alert alert-danger alert-dismissible" role="alert"> | ||
38 | + <button type="button" class="close" data-dismiss="alert" aria-label="Close"> | ||
39 | + <span aria-hidden="true">×</span> | ||
40 | + </button> | ||
41 | + <ul> | ||
42 | + {% for error in form.post.errors %} | ||
43 | + <li>{{ error }}</li> | ||
44 | + {% endfor %} | ||
45 | + </ul> | ||
46 | + </div> | ||
47 | + {% endif %} | ||
48 | + </div> | ||
49 | + | ||
50 | + <div class="form-group{% if form.has_error %} has-error {% endif %} is-fileinput"> | ||
51 | + {% render_field form.image %} | ||
52 | + | ||
53 | + <div class="input-group common-file-input"> | ||
54 | + <input type="text" readonly="" class="form-control" placeholder="{% trans 'Choose your photo...' %}"> | ||
55 | + <span class="input-group-btn input-group-sm"> | ||
56 | + <button type="button" class="btn btn-fab btn-fab-mini"> | ||
57 | + <i class="material-icons">attach_file</i> | ||
58 | + </button> | ||
59 | + </span> | ||
60 | + </div> | ||
61 | + | ||
62 | + <div class="filedrag"> | ||
63 | + {% trans 'Click or drop the file here' %}<br /> | ||
64 | + | ||
65 | + <small>{% trans 'The file could not exceed 5MB.' %}</small> | ||
66 | + </div> | ||
67 | + | ||
68 | + <span id="helpBlock" class="help-block">{{ form.image.help_text }}</span> | ||
69 | + | ||
70 | + {% if form.image.errors %} | ||
71 | + <div class="alert alert-danger alert-dismissible" role="alert"> | ||
72 | + <button type="button" class="close" data-dismiss="alert" aria-label="Close"> | ||
73 | + <span aria-hidden="true">×</span> | ||
74 | + </button> | ||
75 | + <ul> | ||
76 | + {% for error in form.image.errors %} | ||
77 | + <li>{{ error }}</li> | ||
78 | + {% endfor %} | ||
79 | + </ul> | ||
80 | + </div> | ||
81 | + {% endif %} | ||
82 | + </div> | ||
83 | + </form> | ||
84 | + </div> | ||
85 | + <div class="modal-footer"> | ||
86 | + <div class="col-md-12"> | ||
87 | + <button type="submit" id="button" form="post-form" class="btn btn-success btn-raised post-button pull-left">{% trans "Save" %}</button> | ||
88 | + <button type="button" class="btn btn-raised btn-default pull-right" data-dismiss="modal">{% trans "Cancel" %}</button> | ||
89 | + </div> | ||
90 | + </div> | ||
91 | + </div> | ||
92 | +</div> | ||
93 | + | ||
94 | +<script type="text/javascript"> | ||
95 | + $(function () { | ||
96 | + $('.text_simple_wysiwyg').summernote({ | ||
97 | + dialogsInBody: true, | ||
98 | + height: 150, | ||
99 | + toolbar: [ | ||
100 | + // [groupName, [list of button]] | ||
101 | + ['style', ['bold', 'italic']], | ||
102 | + ['insert', ['link']] | ||
103 | + ] | ||
104 | + }); | ||
105 | + | ||
106 | + $.material.init(); | ||
107 | + | ||
108 | + if (window.File && window.FileList && window.FileReader) { | ||
109 | + Init(); | ||
110 | + } | ||
111 | + }); | ||
112 | + | ||
113 | + // initialize | ||
114 | + function Init() { | ||
115 | + var small = $("#id_image"), | ||
116 | + filedrag = $(".filedrag"), | ||
117 | + common = $(".common-file-input"); | ||
118 | + | ||
119 | + // file select | ||
120 | + small.on("change", FileSelectHandler); | ||
121 | + | ||
122 | + // is XHR2 available? | ||
123 | + var xhr = new XMLHttpRequest(); | ||
124 | + if (xhr.upload) { | ||
125 | + // file drop | ||
126 | + filedrag.on("drop", FileSelectHandler); | ||
127 | + filedrag.attr('style', 'display:block'); | ||
128 | + common.attr('style', 'display:none'); | ||
129 | + } | ||
130 | + } | ||
131 | + | ||
132 | + // file selection | ||
133 | + function FileSelectHandler(e) { | ||
134 | + var files = e.target.files || e.dataTransfer.files, | ||
135 | + parent = $(e.target.offsetParent); | ||
136 | + | ||
137 | + // process all File objects | ||
138 | + for (var i = 0, f; f = files[i]; i++) { | ||
139 | + parent.find('.filedrag').html(f.name); | ||
140 | + } | ||
141 | + } | ||
142 | +</script> |
mural/templates/mural/list.html
@@ -29,21 +29,72 @@ | @@ -29,21 +29,72 @@ | ||
29 | </div> | 29 | </div> |
30 | <div class="col-lg-11 col-md-11 col-sm-11 col-xs-11 post-field"> | 30 | <div class="col-lg-11 col-md-11 col-sm-11 col-xs-11 post-field"> |
31 | <div> | 31 | <div> |
32 | - <h4>{% trans 'Wish to make a new post?' %}</h4> | 32 | + <h4 data-url="{% url 'mural:create_general' %}">{% trans 'Wish to make a new post?' %}</h4> |
33 | </div> | 33 | </div> |
34 | </div> | 34 | </div> |
35 | </div> | 35 | </div> |
36 | </div> | 36 | </div> |
37 | 37 | ||
38 | - {% if posts.count > 0 %} | ||
39 | - {% else %} | ||
40 | - <div class="text-center no-subjects"> | ||
41 | - <i class="fa fa-list"></i> | ||
42 | - <h4>{% trans 'There are no posts in this mural yet.' %}</h4> | ||
43 | - </div> | ||
44 | - {% endif %} | 38 | + <div class="posts"> |
39 | + {% for post in posts %} | ||
40 | + {% include 'mural/_view.html' %} | ||
41 | + {% endfor %} | ||
42 | + </div> | ||
43 | + <div class="text-center no-subjects" {% if posts.count > 0 %} style="display:none" {% endif %}> | ||
44 | + <i class="fa fa-list"></i> | ||
45 | + <h4>{% trans 'There are no posts in this mural yet.' %}</h4> | ||
46 | + </div> | ||
45 | </div> | 47 | </div> |
46 | <div class="col-md-3 col-sm-3 col-xs-3"> | 48 | <div class="col-md-3 col-sm-3 col-xs-3"> |
47 | </div> | 49 | </div> |
48 | </div> | 50 | </div> |
51 | + | ||
52 | + <div class="modal fade" tabindex="-1" role="dialog" aria-labelledby="myModalLabel"></div> | ||
53 | + | ||
54 | + <script type="text/javascript"> | ||
55 | + $(function () { | ||
56 | + $(".post-field").click(function () { | ||
57 | + var url = $(this).find('h4').data('url'); | ||
58 | + | ||
59 | + $.ajax({ | ||
60 | + url: url, | ||
61 | + success: function (data) { | ||
62 | + $('.modal').html(data); | ||
63 | + | ||
64 | + setPostFormSubmit(); | ||
65 | + | ||
66 | + $('.modal').modal('show'); | ||
67 | + } | ||
68 | + }) | ||
69 | + }); | ||
70 | + }); | ||
71 | + | ||
72 | + function setPostFormSubmit() { | ||
73 | + var frm = $('#post-form'); | ||
74 | + | ||
75 | + frm.submit(function () { | ||
76 | + $.ajax({ | ||
77 | + type: frm.attr('method'), | ||
78 | + url: frm.attr('action'), | ||
79 | + data: frm.serialize(), | ||
80 | + dataType: "json", | ||
81 | + success: function (data) { | ||
82 | + $('.posts').prepend(data.view); | ||
83 | + | ||
84 | + $('.no-subjects').attr('style', 'display:none'); | ||
85 | + | ||
86 | + $('.modal').modal('hide'); | ||
87 | + | ||
88 | + alertify.success(data.message); | ||
89 | + }, | ||
90 | + error: function(data) { | ||
91 | + $(".modal").html(data.responseText); | ||
92 | + setPostFormSubmit(); | ||
93 | + } | ||
94 | + }); | ||
95 | + | ||
96 | + return false; | ||
97 | + }); | ||
98 | + } | ||
99 | + </script> | ||
49 | {% endblock %} | 100 | {% endblock %} |
50 | \ No newline at end of file | 101 | \ No newline at end of file |
mural/urls.py
@@ -3,4 +3,6 @@ from . import views | @@ -3,4 +3,6 @@ from . import views | ||
3 | 3 | ||
4 | urlpatterns = [ | 4 | urlpatterns = [ |
5 | url(r'^$', views.GeneralIndex.as_view(), name='manage_general'), | 5 | url(r'^$', views.GeneralIndex.as_view(), name='manage_general'), |
6 | + url(r'^create_gen/$', views.GeneralCreate.as_view(), name='create_general'), | ||
7 | + url(r'^render_post/([\w_-]+)/$', views.render_gen_post, name='render_post_general'), | ||
6 | ] | 8 | ] |
7 | \ No newline at end of file | 9 | \ No newline at end of file |
mural/views.py
@@ -2,12 +2,16 @@ from django.shortcuts import get_object_or_404, redirect, render | @@ -2,12 +2,16 @@ from django.shortcuts import get_object_or_404, redirect, render | ||
2 | from django.views import generic | 2 | from django.views import generic |
3 | from django.contrib import messages | 3 | from django.contrib import messages |
4 | from django.http import JsonResponse | 4 | from django.http import JsonResponse |
5 | +from django.template.loader import render_to_string | ||
5 | from django.core.urlresolvers import reverse, reverse_lazy | 6 | from django.core.urlresolvers import reverse, reverse_lazy |
6 | from django.utils.translation import ugettext_lazy as _ | 7 | from django.utils.translation import ugettext_lazy as _ |
7 | from django.contrib.auth.mixins import LoginRequiredMixin | 8 | from django.contrib.auth.mixins import LoginRequiredMixin |
8 | from django.db.models import Q, Count | 9 | from django.db.models import Q, Count |
9 | 10 | ||
11 | +from users.models import User | ||
12 | + | ||
10 | from .models import GeneralPost, CategoryPost, SubjectPost, MuralVisualizations | 13 | from .models import GeneralPost, CategoryPost, SubjectPost, MuralVisualizations |
14 | +from .forms import GeneralPostForm | ||
11 | 15 | ||
12 | class GeneralIndex(LoginRequiredMixin, generic.ListView): | 16 | class GeneralIndex(LoginRequiredMixin, generic.ListView): |
13 | login_url = reverse_lazy("users:login") | 17 | login_url = reverse_lazy("users:login") |
@@ -39,3 +43,53 @@ class GeneralIndex(LoginRequiredMixin, generic.ListView): | @@ -39,3 +43,53 @@ class GeneralIndex(LoginRequiredMixin, generic.ListView): | ||
39 | context['mural_menu_active'] = 'subjects_menu_active' | 43 | context['mural_menu_active'] = 'subjects_menu_active' |
40 | 44 | ||
41 | return context | 45 | return context |
46 | + | ||
47 | +class GeneralCreate(LoginRequiredMixin, generic.edit.CreateView): | ||
48 | + login_url = reverse_lazy("users:login") | ||
49 | + redirect_field_name = 'next' | ||
50 | + | ||
51 | + template_name = 'mural/_form.html' | ||
52 | + form_class = GeneralPostForm | ||
53 | + | ||
54 | + def form_invalid(self, form): | ||
55 | + context = super(GeneralCreate, self).form_invalid(form) | ||
56 | + context.status_code = 400 | ||
57 | + | ||
58 | + return context | ||
59 | + | ||
60 | + def form_valid(self, form): | ||
61 | + self.object = form.save(commit = False) | ||
62 | + | ||
63 | + self.object.user = self.request.user | ||
64 | + | ||
65 | + self.object.save() | ||
66 | + | ||
67 | + users = User.objects.all().exclude(id = self.request.user.id) | ||
68 | + entries = [] | ||
69 | + | ||
70 | + for user in users: | ||
71 | + entries.append(MuralVisualizations(viewed = False, user = user, post = self.object)) | ||
72 | + | ||
73 | + MuralVisualizations.objects.bulk_create(entries) | ||
74 | + | ||
75 | + return super(GeneralCreate, self).form_valid(form) | ||
76 | + | ||
77 | + def get_context_data(self, *args, **kwargs): | ||
78 | + context = super(GeneralCreate, self).get_context_data(*args, **kwargs) | ||
79 | + | ||
80 | + context['form_url'] = reverse_lazy("mural:create_general") | ||
81 | + | ||
82 | + return context | ||
83 | + | ||
84 | + def get_success_url(self): | ||
85 | + return reverse_lazy('mural:render_post_general', args = (self.object.id, )) | ||
86 | + | ||
87 | +def render_gen_post(request, post): | ||
88 | + post = get_object_or_404(GeneralPost, id = post) | ||
89 | + | ||
90 | + context = {} | ||
91 | + context['post'] = post | ||
92 | + | ||
93 | + html = render_to_string("mural/_view.html", context, request) | ||
94 | + | ||
95 | + return JsonResponse({'message': _('Your post was published successfully!'), 'view': html}) |