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 | 968 | .mural .post_make .post-field h4 { |
969 | 969 | cursor: text; |
970 | 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 | 981 | \ No newline at end of file | ... | ... |
amadeus/static/css/themes/green.css
mural/forms.py
... | ... | @@ -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 | 42 | \ No newline at end of file | ... | ... |
... | ... | @@ -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 | 16 | raise ValidationError(_('File not supported.')) |
17 | 17 | |
18 | 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 | 20 | post = models.TextField(_('Post'), blank = True) |
21 | 21 | image = models.ImageField(verbose_name = _('Image'), null=True, blank = True, upload_to = 'posts/', validators = [validate_img_extension]) |
22 | 22 | user = models.ForeignKey(User, verbose_name = _('User'), related_name = "post_user", null = True) | ... | ... |
... | ... | @@ -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 | 29 | </div> |
30 | 30 | <div class="col-lg-11 col-md-11 col-sm-11 col-xs-11 post-field"> |
31 | 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 | 33 | </div> |
34 | 34 | </div> |
35 | 35 | </div> |
36 | 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 | 47 | </div> |
46 | 48 | <div class="col-md-3 col-sm-3 col-xs-3"> |
47 | 49 | </div> |
48 | 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 | 100 | {% endblock %} |
50 | 101 | \ No newline at end of file | ... | ... |
mural/urls.py
... | ... | @@ -3,4 +3,6 @@ from . import views |
3 | 3 | |
4 | 4 | urlpatterns = [ |
5 | 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 | 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 | 2 | from django.views import generic |
3 | 3 | from django.contrib import messages |
4 | 4 | from django.http import JsonResponse |
5 | +from django.template.loader import render_to_string | |
5 | 6 | from django.core.urlresolvers import reverse, reverse_lazy |
6 | 7 | from django.utils.translation import ugettext_lazy as _ |
7 | 8 | from django.contrib.auth.mixins import LoginRequiredMixin |
8 | 9 | from django.db.models import Q, Count |
9 | 10 | |
11 | +from users.models import User | |
12 | + | |
10 | 13 | from .models import GeneralPost, CategoryPost, SubjectPost, MuralVisualizations |
14 | +from .forms import GeneralPostForm | |
11 | 15 | |
12 | 16 | class GeneralIndex(LoginRequiredMixin, generic.ListView): |
13 | 17 | login_url = reverse_lazy("users:login") |
... | ... | @@ -39,3 +43,53 @@ class GeneralIndex(LoginRequiredMixin, generic.ListView): |
39 | 43 | context['mural_menu_active'] = 'subjects_menu_active' |
40 | 44 | |
41 | 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}) | ... | ... |