Commit 0cc9739e1cffb07bf7b8cae476b8aa391c184d82
1 parent
3c80a5d8
Exists in
master
and in
5 other branches
Criado o procedimento de criar, atualizar e deletar um subject #35
Showing
10 changed files
with
224 additions
and
60 deletions
Show diff stats
@@ -0,0 +1,23 @@ | @@ -0,0 +1,23 @@ | ||
1 | +# -*- coding: utf-8 -*- | ||
2 | +# Generated by Django 1.10 on 2016-09-08 19:25 | ||
3 | +from __future__ import unicode_literals | ||
4 | + | ||
5 | +from django.db import migrations | ||
6 | + | ||
7 | + | ||
8 | +class Migration(migrations.Migration): | ||
9 | + | ||
10 | + dependencies = [ | ||
11 | + ('courses', '0008_auto_20160908_1332'), | ||
12 | + ] | ||
13 | + | ||
14 | + operations = [ | ||
15 | + migrations.AlterModelOptions( | ||
16 | + name='subject', | ||
17 | + options={'ordering': ('create_date',), 'verbose_name': 'Subject', 'verbose_name_plural': 'Subjects'}, | ||
18 | + ), | ||
19 | + migrations.AlterModelOptions( | ||
20 | + name='topic', | ||
21 | + options={'ordering': ('create_date',), 'verbose_name': 'Topic', 'verbose_name_plural': 'Topics'}, | ||
22 | + ), | ||
23 | + ] |
@@ -0,0 +1,25 @@ | @@ -0,0 +1,25 @@ | ||
1 | +{% extends 'subject/index.html' %} | ||
2 | + | ||
3 | +{% load static i18n permission_tags widget_tweaks %} | ||
4 | + | ||
5 | +{% block content %} | ||
6 | + | ||
7 | + <div class="panel panel-default"> | ||
8 | + <div class="panel-body"> | ||
9 | + <form class="form-group " method="post" action=""> | ||
10 | + {% csrf_token %} | ||
11 | + {% for field in form %} | ||
12 | + <div class="form-group {% if field.errors %} has-error{% endif %}"> | ||
13 | + <label for="{{ field.auto_id }}" class="control-label label-static"> {{ field.label }}</label> | ||
14 | + | ||
15 | + {% render_field field class='form-control' placeholder=field.label%} | ||
16 | + <span class="help-block">{{ field.help_text }}</span> | ||
17 | + </div> | ||
18 | + {% endfor %} | ||
19 | + <div class="col-lg-offset-4 col-lg-4"> | ||
20 | + <button type="submite" class="btn btn-raised btn-primary btn-lg btn-block">{% trans 'Create' %}</button> | ||
21 | + </div> | ||
22 | + </form> | ||
23 | + </div> | ||
24 | + </div> | ||
25 | +{% endblock content %} |
@@ -0,0 +1,18 @@ | @@ -0,0 +1,18 @@ | ||
1 | +{% extends 'subject/index.html' %} | ||
2 | + | ||
3 | +{% load static i18n permission_tags widget_tweaks %} | ||
4 | + | ||
5 | +{% block content %} | ||
6 | + | ||
7 | +<div class="panel panel-default"> | ||
8 | + <div class="panel-body"> | ||
9 | + <form action="" method="post"> | ||
10 | + {% csrf_token %} | ||
11 | + <h2>{% trans 'Are you sure you want to delete the category' %} "{{subject}}"?</h2> | ||
12 | + <input type="submit" class="btn btn-raised btn-success btn-lg" value="{% trans 'Yes' %}" /> | ||
13 | + <a href="{% url 'course:view_subject' subject.slug%}" class="btn btn-raised btn-danger btn-lg">{% trans 'No' %}</a> | ||
14 | + </form> | ||
15 | + </div> | ||
16 | +</div> | ||
17 | + | ||
18 | +{% endblock content %} |
courses/templates/subject/index.html
@@ -24,14 +24,31 @@ | @@ -24,14 +24,31 @@ | ||
24 | <a href="{% url 'course:view_subject' subject.slug%}" class="btn btn-default">{{subject}}</a> | 24 | <a href="{% url 'course:view_subject' subject.slug%}" class="btn btn-default">{{subject}}</a> |
25 | {% endfor %} | 25 | {% endfor %} |
26 | </div> | 26 | </div> |
27 | - | ||
28 | </div> | 27 | </div> |
28 | + | ||
29 | + {% if user|has_role:'system_admin' or user|has_role:'professor' %} | ||
30 | + <a href="{% url 'course:create_subject' course.slug %}" class="btn btn-primary btn-md btn-block">{% trans "Create Subject" %}</a> | ||
31 | + {% endif %} | ||
29 | {% endblock %} | 32 | {% endblock %} |
30 | 33 | ||
31 | {% block content %} | 34 | {% block content %} |
32 | <div class="panel panel-info"> | 35 | <div class="panel panel-info"> |
33 | <div class="panel-heading"> | 36 | <div class="panel-heading"> |
34 | - <h3 class="panel-title">{% trans "Presentation Subject" %}</h3> | 37 | + <div class="row"> |
38 | + <div class="col-md-7 col-sm-7"> | ||
39 | + <h3>{% trans "Presentation Subject" %}</h3> | ||
40 | + </div> | ||
41 | + <div class="col-md-2 col-sm-2"> | ||
42 | + {% if user|has_role:'system_admin' or user in subject.professors %} | ||
43 | + <a href="{% url 'course:update_subject' subject.slug%}" class="btn">{% trans "edit" %}</a> | ||
44 | + {% endif %} | ||
45 | + </div> | ||
46 | + <div class="col-md-3 col-sm-3"> | ||
47 | + {% if user|has_role:'system_admin' or user in subject.professors %} | ||
48 | + <a href="{% url 'course:delete_subject' subject.slug%}" class="btn">{% trans "delete" %}</a> | ||
49 | + {% endif %} | ||
50 | + </div> | ||
51 | + </div> | ||
35 | </div> | 52 | </div> |
36 | <div class="panel-body"> | 53 | <div class="panel-body"> |
37 | <p> | 54 | <p> |
@@ -0,0 +1,25 @@ | @@ -0,0 +1,25 @@ | ||
1 | +{% extends 'subject/index.html' %} | ||
2 | + | ||
3 | +{% load static i18n permission_tags widget_tweaks %} | ||
4 | + | ||
5 | +{% block content %} | ||
6 | + | ||
7 | + <div class="panel panel-default"> | ||
8 | + <div class="panel-body"> | ||
9 | + <form class="form-group " method="post" action=""> | ||
10 | + {% csrf_token %} | ||
11 | + {% for field in form %} | ||
12 | + <div class="form-group {% if field.errors %} has-error{% endif %}"> | ||
13 | + <label for="{{ field.auto_id }}" class="control-label label-static"> {{ field.label }}</label> | ||
14 | + | ||
15 | + {% render_field field class='form-control' placeholder=field.label%} | ||
16 | + <span class="help-block">{{ field.help_text }}</span> | ||
17 | + </div> | ||
18 | + {% endfor %} | ||
19 | + <div class="col-lg-offset-4 col-lg-4"> | ||
20 | + <button type="submite" class="btn btn-raised btn-primary btn-lg btn-block">{% trans 'Update' %}</button> | ||
21 | + </div> | ||
22 | + </form> | ||
23 | + </div> | ||
24 | + </div> | ||
25 | +{% endblock content %} |
courses/templates/topic/create.html
@@ -13,6 +13,7 @@ | @@ -13,6 +13,7 @@ | ||
13 | <label for="{{ field.auto_id }}" class="control-label label-static"> {{ field.label }}</label> | 13 | <label for="{{ field.auto_id }}" class="control-label label-static"> {{ field.label }}</label> |
14 | 14 | ||
15 | {% render_field field class='form-control' placeholder=field.label%} | 15 | {% render_field field class='form-control' placeholder=field.label%} |
16 | + <span class="help-block">{{ field.help_text }}</span> | ||
16 | </div> | 17 | </div> |
17 | {% endfor %} | 18 | {% endfor %} |
18 | <div class="col-lg-offset-4 col-lg-4"> | 19 | <div class="col-lg-offset-4 col-lg-4"> |
courses/templates/topic/update.html
@@ -13,6 +13,7 @@ | @@ -13,6 +13,7 @@ | ||
13 | <label for="{{ field.auto_id }}" class="control-label label-static"> {{ field.label }}</label> | 13 | <label for="{{ field.auto_id }}" class="control-label label-static"> {{ field.label }}</label> |
14 | 14 | ||
15 | {% render_field field class='form-control' placeholder=field.label%} | 15 | {% render_field field class='form-control' placeholder=field.label%} |
16 | + <span class="help-block">{{ field.help_text }}</span> | ||
16 | </div> | 17 | </div> |
17 | {% endfor %} | 18 | {% endfor %} |
18 | <div class="col-lg-offset-4 col-lg-4"> | 19 | <div class="col-lg-offset-4 col-lg-4"> |
courses/urls.py
@@ -14,11 +14,10 @@ urlpatterns = [ | @@ -14,11 +14,10 @@ urlpatterns = [ | ||
14 | url(r'^categories/edit/(?P<slug>[\w_-]+)/$', views.UpdateCatView.as_view(), name='update_cat'), | 14 | url(r'^categories/edit/(?P<slug>[\w_-]+)/$', views.UpdateCatView.as_view(), name='update_cat'), |
15 | url(r'^categories/(?P<slug>[\w_-]+)/$', views.ViewCat.as_view(), name='view_cat'), | 15 | url(r'^categories/(?P<slug>[\w_-]+)/$', views.ViewCat.as_view(), name='view_cat'), |
16 | url(r'^categories/delete/(?P<slug>[\w_-]+)/$', views.DeleteCatView.as_view(), name='delete_cat'), | 16 | url(r'^categories/delete/(?P<slug>[\w_-]+)/$', views.DeleteCatView.as_view(), name='delete_cat'), |
17 | - url(r'^course/(?P<slug>[\w_-]+)/subjects/$', views.SubjectsView.as_view(), name='view_subject'), | ||
18 | - url(r'^course/(?P<slug>[\w_-]+)/topics/create/$', views.CreateTopicView.as_view(), name='create_topic'), | ||
19 | - url(r'^course/(?P<slug>[\w_-]+)/topics/update/$', views.UpdateTopicView.as_view(), name='update_topic'), | ||
20 | - # url(r'^course/(?P<slug>[\w_-]+)/modules/create/$', views.CreateModView.as_view(), name='create_mods'), | ||
21 | - # url(r'^course/(?P<slug_course>[\w_-]+)/modules/edit/(?P<slug>[\w_-]+)/$', views.UpdateModView.as_view(), name='update_mods'), | ||
22 | - # url(r'^course/(?P<slug_course>[\w_-]+)/modules/delete/(?P<slug>[\w_-]+)/$', views.DeleteModView.as_view(), name='delete_mods'), | ||
23 | - # url(r'^course/(?P<slug>[\w_-]+)/subject$', views.ViewSubject.as_view(), name='view_subject'), | 17 | + url(r'^course/subjects/(?P<slug>[\w_-]+)/$', views.SubjectsView.as_view(), name='view_subject'), |
18 | + url(r'^course/topics/create/(?P<slug>[\w_-]+)/$', views.CreateTopicView.as_view(), name='create_topic'), | ||
19 | + url(r'^course/topics/update/(?P<slug>[\w_-]+)/$', views.UpdateTopicView.as_view(), name='update_topic'), | ||
20 | + url(r'^course/subjects/create/(?P<slug>[\w_-]+)/$', views.CreateSubjectView.as_view(), name='create_subject'), | ||
21 | + url(r'^course/subjects/update/(?P<slug>[\w_-]+)/$', views.UpdateSubjectView.as_view(), name='update_subject'), | ||
22 | + url(r'^course/subjects/delete/(?P<slug>[\w_-]+)/$', views.DeleteSubjectView.as_view(), name='delete_subject'), | ||
24 | ] | 23 | ] |
courses/views.py
@@ -8,6 +8,8 @@ from rolepermissions.mixins import HasRoleMixin | @@ -8,6 +8,8 @@ from rolepermissions.mixins import HasRoleMixin | ||
8 | from django.core.urlresolvers import reverse_lazy | 8 | from django.core.urlresolvers import reverse_lazy |
9 | from django.utils.translation import ugettext_lazy as _ | 9 | from django.utils.translation import ugettext_lazy as _ |
10 | from slugify import slugify | 10 | from slugify import slugify |
11 | +from rolepermissions.verifications import has_role | ||
12 | +from django.db.models import Q | ||
11 | 13 | ||
12 | from .forms import CourseForm, CategoryForm, SubjectForm,TopicForm | 14 | from .forms import CourseForm, CategoryForm, SubjectForm,TopicForm |
13 | from .models import Course, Subject, Category,Topic | 15 | from .models import Course, Subject, Category,Topic |
@@ -197,7 +199,10 @@ class SubjectsView(LoginRequiredMixin, generic.ListView): | @@ -197,7 +199,10 @@ class SubjectsView(LoginRequiredMixin, generic.ListView): | ||
197 | def get_queryset(self): | 199 | def get_queryset(self): |
198 | subject = get_object_or_404(Subject, slug = self.kwargs.get('slug')) | 200 | subject = get_object_or_404(Subject, slug = self.kwargs.get('slug')) |
199 | course = subject.course | 201 | course = subject.course |
200 | - return course.subjects.filter(visible=True) | 202 | + context = course.subjects.filter(visible=True) |
203 | + if (self.request.user in subject.professors.all() or has_role(self.request.user,'system_admin')): | ||
204 | + context = course.subjects.all() | ||
205 | + return context | ||
201 | 206 | ||
202 | def get_context_data(self, **kwargs): | 207 | def get_context_data(self, **kwargs): |
203 | subject = get_object_or_404(Subject, slug = self.kwargs.get('slug')) | 208 | subject = get_object_or_404(Subject, slug = self.kwargs.get('slug')) |
@@ -223,8 +228,9 @@ class CreateTopicView(LoginRequiredMixin, HasRoleMixin, generic.edit.CreateView) | @@ -223,8 +228,9 @@ class CreateTopicView(LoginRequiredMixin, HasRoleMixin, generic.edit.CreateView) | ||
223 | subject = get_object_or_404(Subject, slug = self.kwargs.get('slug')) | 228 | subject = get_object_or_404(Subject, slug = self.kwargs.get('slug')) |
224 | context['course'] = subject.course | 229 | context['course'] = subject.course |
225 | context['subject'] = subject | 230 | context['subject'] = subject |
226 | - context['subjects'] = subject.course.subjects.filter(visible=True) | ||
227 | - | 231 | + context['subjects'] = subject.course.subjects.filter(Q(visible=True) | Q(professors__in=[self.request.user])) |
232 | + if (has_role(self.request.user,'system_admin')): | ||
233 | + context['subjects'] = subject.course.subjects.all() | ||
228 | return context | 234 | return context |
229 | 235 | ||
230 | def form_valid(self, form): | 236 | def form_valid(self, form): |
@@ -237,18 +243,12 @@ class CreateTopicView(LoginRequiredMixin, HasRoleMixin, generic.edit.CreateView) | @@ -237,18 +243,12 @@ class CreateTopicView(LoginRequiredMixin, HasRoleMixin, generic.edit.CreateView) | ||
237 | 243 | ||
238 | return super(CreateTopicView, self).form_valid(form) | 244 | return super(CreateTopicView, self).form_valid(form) |
239 | 245 | ||
240 | - def render_to_response(self, context, **response_kwargs): | ||
241 | - messages.success(self.request, _('Module created successfully!')) | ||
242 | - | ||
243 | - return self.response_class(request=self.request, template=self.get_template_names(), context=context, using=self.template_engine) | ||
244 | - | ||
245 | class UpdateTopicView(LoginRequiredMixin, HasRoleMixin, generic.UpdateView): | 246 | class UpdateTopicView(LoginRequiredMixin, HasRoleMixin, generic.UpdateView): |
246 | 247 | ||
247 | allowed_roles = ['professor', 'system_admin','student'] | 248 | allowed_roles = ['professor', 'system_admin','student'] |
248 | login_url = reverse_lazy("core:home") | 249 | login_url = reverse_lazy("core:home") |
249 | redirect_field_name = 'next' | 250 | redirect_field_name = 'next' |
250 | template_name = 'topic/update.html' | 251 | template_name = 'topic/update.html' |
251 | - # model = Topic | ||
252 | form_class = TopicForm | 252 | form_class = TopicForm |
253 | 253 | ||
254 | def get_object(self, queryset=None): | 254 | def get_object(self, queryset=None): |
@@ -262,48 +262,81 @@ class UpdateTopicView(LoginRequiredMixin, HasRoleMixin, generic.UpdateView): | @@ -262,48 +262,81 @@ class UpdateTopicView(LoginRequiredMixin, HasRoleMixin, generic.UpdateView): | ||
262 | topic = get_object_or_404(Topic, slug = self.kwargs.get('slug')) | 262 | topic = get_object_or_404(Topic, slug = self.kwargs.get('slug')) |
263 | context['course'] = topic.subject.course | 263 | context['course'] = topic.subject.course |
264 | context['subject'] = topic.subject | 264 | context['subject'] = topic.subject |
265 | - context['subjects'] = topic.subject.course.subjects.filter(visible=True) | 265 | + context['subjects'] = topic.subject.course.subjects.filter(Q(visible=True) | Q(professors__in=[self.request.user])) |
266 | + if (has_role(self.request.user,'system_admin')): | ||
267 | + context['subjects'] = topic.subject.course.subjects.all() | ||
268 | + return context | ||
269 | + | ||
270 | +class CreateSubjectView(LoginRequiredMixin, HasRoleMixin, generic.edit.CreateView): | ||
271 | + | ||
272 | + allowed_roles = ['professor', 'system_admin'] | ||
273 | + login_url = reverse_lazy("core:home") | ||
274 | + redirect_field_name = 'next' | ||
275 | + template_name = 'subject/create.html' | ||
276 | + form_class = SubjectForm | ||
277 | + | ||
278 | + def get_success_url(self): | ||
279 | + return reverse_lazy('course:view_subject', kwargs={'slug' : self.object.slug}) | ||
280 | + | ||
281 | + def get_context_data(self, **kwargs): | ||
282 | + context = super(CreateSubjectView, self).get_context_data(**kwargs) | ||
283 | + course = get_object_or_404(Course, slug = self.kwargs.get('slug')) | ||
284 | + context['course'] = course | ||
285 | + context['subjects'] = course.subjects.filter(Q(visible=True) | Q(professors__in=[self.request.user])) | ||
286 | + if (has_role(self.request.user,'system_admin')): | ||
287 | + context['subjects'] = course.subjects.all() | ||
288 | + return context | ||
289 | + | ||
290 | + def form_valid(self, form): | ||
291 | + course = get_object_or_404(Course, slug = self.kwargs.get('slug')) | ||
292 | + | ||
293 | + self.object = form.save(commit = False) | ||
294 | + self.object.course = course | ||
295 | + self.object.professor = self.request.user | ||
296 | + self.object.save() | ||
266 | 297 | ||
298 | + return super(CreateSubjectView, self).form_valid(form) | ||
299 | + | ||
300 | + | ||
301 | +class UpdateSubjectView(LoginRequiredMixin, HasRoleMixin, generic.UpdateView): | ||
302 | + | ||
303 | + allowed_roles = ['professor', 'system_admin'] | ||
304 | + login_url = reverse_lazy("core:home") | ||
305 | + redirect_field_name = 'next' | ||
306 | + template_name = 'subject/update.html' | ||
307 | + form_class = SubjectForm | ||
308 | + | ||
309 | + def get_object(self, queryset=None): | ||
310 | + return get_object_or_404(Subject, slug = self.kwargs.get('slug')) | ||
311 | + | ||
312 | + def get_success_url(self): | ||
313 | + return reverse_lazy('course:view_subject', kwargs={'slug' : self.object.slug}) | ||
314 | + | ||
315 | + def get_context_data(self, **kwargs): | ||
316 | + context = super(UpdateSubjectView, self).get_context_data(**kwargs) | ||
317 | + context['course'] = self.object.course | ||
318 | + context['subject'] = self.object | ||
319 | + context['subjects'] = self.object.course.subjects.filter(Q(visible=True) | Q(professors__in=[self.request.user])) | ||
320 | + if (has_role(self.request.user,'system_admin')): | ||
321 | + context['subjects'] = self.object.course.subjects.all() | ||
267 | return context | 322 | return context |
268 | 323 | ||
269 | - # def form_valid(self, form): | ||
270 | - # self.object = form.save(commit = False) | ||
271 | - # self.object.slug = slugify(self.object.name) | ||
272 | - # self.object.save() | ||
273 | - # | ||
274 | - # return super(UpdateModView, self).form_valid(form) | ||
275 | - | ||
276 | - # def render_to_response(self, context, **response_kwargs): | ||
277 | - # messages.success(self.request, _('Module edited successfully!')) | ||
278 | - # | ||
279 | - # return self.response_class(request=self.request, template=self.get_template_names(), context=context, using=self.template_engine) | ||
280 | - | ||
281 | -# class DeleteModView(LoginRequiredMixin, HasRoleMixin, generic.DeleteView): | ||
282 | -# | ||
283 | -# allowed_roles = ['professor', 'system_admin'] | ||
284 | -# login_url = reverse_lazy("core:home") | ||
285 | -# redirect_field_name = 'next' | ||
286 | -# model = Module | ||
287 | -# template_name = 'module/delete.html' | ||
288 | -# | ||
289 | -# def get_success_url(self): | ||
290 | -# return reverse_lazy('course:manage_mods', kwargs={'slug' : self.object.course.slug}) | ||
291 | -# | ||
292 | -# def get_context_data(self, **kwargs): | ||
293 | -# course = get_object_or_404(Course, slug = self.kwargs.get('slug_course')) | ||
294 | -# context = super(DeleteModView, self).get_context_data(**kwargs) | ||
295 | -# context['course'] = course | ||
296 | -# | ||
297 | -# return context | ||
298 | -# | ||
299 | -# def render_to_response(self, context, **response_kwargs): | ||
300 | -# messages.success(self.request, _('Module deleted successfully!')) | ||
301 | -# | ||
302 | -# return self.response_class(request=self.request, template=self.get_template_names(), context=context, using=self.template_engine) | ||
303 | - | ||
304 | -# class ViewSubject(LoginRequiredMixin, generic.DetailView): | ||
305 | -# login_url = reverse_lazy("core:home") | ||
306 | -# redirect_field_name = 'next' | ||
307 | -# model = Course | ||
308 | -# template_name = 'subject/index.html' | ||
309 | -# context_object_name = 'course' | 324 | +class DeleteSubjectView(LoginRequiredMixin, HasRoleMixin, generic.DeleteView): |
325 | + | ||
326 | + allowed_roles = ['professor', 'system_admin'] | ||
327 | + login_url = reverse_lazy("core:home") | ||
328 | + redirect_field_name = 'next' | ||
329 | + model = Subject | ||
330 | + template_name = 'subject/delete.html' | ||
331 | + | ||
332 | + def get_context_data(self, **kwargs): | ||
333 | + context = super(DeleteSubjectView, self).get_context_data(**kwargs) | ||
334 | + context['course'] = self.object.course | ||
335 | + context['subject'] = self.object | ||
336 | + context['subjects'] = self.object.course.subjects.filter(Q(visible=True) | Q(professors__in=[self.request.user])) | ||
337 | + if (has_role(self.request.user,'system_admin')): | ||
338 | + context['subjects'] = self.object.course.subjects.all() | ||
339 | + return context | ||
340 | + | ||
341 | + def get_success_url(self): | ||
342 | + return reverse_lazy('course:view_subject', kwargs={'slug' : self.object.course.subjects.all()[0].slug}) |
@@ -0,0 +1,22 @@ | @@ -0,0 +1,22 @@ | ||
1 | +# -*- coding: utf-8 -*- | ||
2 | +# Generated by Django 1.10 on 2016-09-08 19:25 | ||
3 | +from __future__ import unicode_literals | ||
4 | + | ||
5 | +import django.core.validators | ||
6 | +from django.db import migrations, models | ||
7 | +import re | ||
8 | + | ||
9 | + | ||
10 | +class Migration(migrations.Migration): | ||
11 | + | ||
12 | + dependencies = [ | ||
13 | + ('users', '0011_auto_20160908_1108'), | ||
14 | + ] | ||
15 | + | ||
16 | + operations = [ | ||
17 | + migrations.AlterField( | ||
18 | + model_name='user', | ||
19 | + name='username', | ||
20 | + field=models.CharField(help_text='A short name that will be used to identify you in the platform and to access it', max_length=35, unique=True, validators=[django.core.validators.RegexValidator(re.compile('^[\\w.@+-]+$', 32), 'Type a valid username. This fields should only contain letters, numbers and the characteres: @/./+/-/_ .', 'invalid')], verbose_name='Login'), | ||
21 | + ), | ||
22 | + ] |