Skip to content
This repository has been archived by the owner on Jan 19, 2022. It is now read-only.

Commit

Permalink
Merge pull request #52 from Wtower/improve-coverage-35
Browse files Browse the repository at this point in the history
Improve coverage
  • Loading branch information
Wtower committed Apr 6, 2016
2 parents 2e27437 + 884b544 commit ef2f603
Show file tree
Hide file tree
Showing 17 changed files with 283 additions and 174 deletions.
2 changes: 2 additions & 0 deletions .coveragerc
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
[run]
omit = */settings*.py,setup.py,manage.py
2 changes: 1 addition & 1 deletion .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ install:
- pip install coveralls

script:
coverage run manage.py test
coverage run --source='.' manage.py test ninecms

after_success:
coveralls
Expand Down
107 changes: 52 additions & 55 deletions README.rst
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,26 @@ NineCMS

Nine CMS is a Django app to manage content. Users can create content and publish it to paths.

Detailed documentation soon to be published.
.. image:: https://img.shields.io/travis/Wtower/django-ninecms/devel.svg
:target: https://travis-ci.org/Wtower/django-ninecms

.. image:: https://img.shields.io/coveralls/Wtower/django-ninecms/devel.svg
:target: https://coveralls.io/github/Wtower/django-ninecms

.. image:: https://img.shields.io/pypi/v/django-ninecms.svg
:target: https://pypi.python.org/pypi/django-ninecms
:alt: Latest PyPI version

.. image:: https://img.shields.io/pypi/dm/django-ninecms.svg
:target: https://pypi.python.org/pypi/django-ninecms
:alt: Number of PyPI downloads per month

Admin screenshot:

.. image:: https://raw.githubusercontent.com/Wtower/django-ninecms/master/docs/screenshots/index1.png

Detailed documentation soon to be published.

Objectives
----------

Expand Down Expand Up @@ -51,12 +67,9 @@ Features
- Admin interface with dashboard
- Utilities

- Libraries
- Character transliteration
- Serializers
- Custom tags
- Basic search functionality
- Template suggestions

- Bootstrap

Expand Down Expand Up @@ -94,7 +107,7 @@ New project guide
This is a full guide to create a new project. *Soon a Quick Guide will be added*.

There is also a project that can be used as an
`empty Django 9cms web site starter <http://www.github.com/Wtower/django-ninecms-starter>`_.
`Django 9cms web site boilerplate <http://www.github.com/Wtower/django-ninecms-starter>`_.

1. Create a new project

Expand All @@ -107,20 +120,20 @@ There is also a project that can be used as an

- Add the following to the ``requirements.txt`` file::

Django>=1.9.0
Django~=1.9.0
django-ninecms>=0.5.4

- And optionally::

coverage>=4.0.3
django-admin-bootstrapped>=2.5.6
coverage~=4.0.3
django-admin-bootstrapped~=2.5.6
django-admin-bootstrapped-plus>=0.1.1
django-bootstrap3>=6.2.2
django-debug-toolbar>=1.4.0
mysqlclient>=1.3.7
newrelic>=2.58.2.45
python3-memcached>=1.51
sqlparse>=0.1.18
django-bootstrap3~=7.0.1
django-debug-toolbar~=1.4.0
mysqlclient~=1.3.7
newrelic~=2.60.0.46
python3-memcached~=1.51
sqlparse~=0.1.18

- Then run::

Expand Down Expand Up @@ -419,9 +432,10 @@ Theming
Theming is easy and straightforward. Besides from developing a custom theme, it is easy to use any ready-made
HTML theme from the myriads available on the web.

*(Changes in v0.6.0)*

There is a ``base.html`` which gets extended by an ``index.html``. The base declares the doc type (HTML5),
loads scripts (from an indicative common pre-selected list as defined in settings) and defines blocks to extend
in index. For Drupal veterans it is the equivalent of ``html.tpl.php`` and it usually doesn't need to be overridden.
loads scripts, all defined in blocks.

The index file is the one that most probably needs to be overridden. You can check the base to see where each of
the following blocks appears. These are defined by order of appearance:
Expand All @@ -442,43 +456,33 @@ the following blocks appears. These are defined by order of appearance:
- ``body_scripts``: define any additional content at the bottom of the ``<body>``.
Here add additional scripts to be loaded in the end of the document.

Other important template is ``site-name.html``. This is a small template to define the site name, usually
an image with logo. Unlike Drupal7, we decided to keep such one-off settings hard-coded and simple rather than
dynamic in the db.

The templates ``block_content.html`` and ``block_static.html`` fine-tune how the content is displayed.
The former loads only for the main content node as presented in index. The latter is used for any static node blocks
as defined in the administration panel (db). Optionally override them to fine tune the fields present and therefore
to reduce the number of queries executed.
The index file is the default template that is used, but it can be extended to be used in page types
(see theme suggestions below).

In summary, override templates such as:

- ``index.html``
- ``site-name.html``
- ``block_content.html``
- ``block_static.html``
The templates in the ``ninecms/templates`` folder are examples of how to render specific contexts of blocks
and can be used either with ``{% include %}`` or can be copied into the custom templates directly.

Theme suggestions
-----------------

Add a file in the project's ``templates`` folder, with the following names, in order to override a 9cms template.
Each page type can have its own template. Ninecms chooses template for the page type
based in the template filename, in the following order:

- content: ``[block_content]_[page_type]_[node_id]`` (eg ``block_content_basic_5.html``)
- static node: ``[block_static]_[region]_[alias]`` (eg ``block_static_header_blog_1.html``)
- menu: ``[block_menu]_[region]_[menu.id]`` (eg ``block_menu_header_1.html``)
- signal (view): ``[block_signal]_[region]_[signal]`` (eg ``block_signal_header_random_video_node.html``)
- contact form: ``[block_contact]_[region]``
- language menu: ``[block_language]_[region]``
- ``page_[page_type.name]``
- ``[page_type.name]``
- ``index.html``

where ``[page_type.name]`` is the machine name of the page type,
eg. if the page type name is 'Basic Page' then this will be ``basic_page``.

Any combination of ``[]`` is allowed, eg. ``block_content_basic.html`` or ``block_content_5.html``.
Always append ``.html`` extension.
It is good to extend the template from index and use Django blocks at will.

Page types
----------

Page types are central to the organisation of a CMS content. In NineCMS, apart from logically organising content
to relevant page types, which can be done also with taxonomy terms, each page type can have a different page layout,
with different blocks specified as elements to different regions.
with different blocks.

Page types do not feature custom fields and thus cannot be used as the separation of entity-like models,
as eg. in Drupal. There is no intention to add such a feature as Django models can be very easily be added
Expand All @@ -504,8 +508,7 @@ The following replacement tokens can be used:
Block types
-----------

Additionally to content of any node, which is rendered anyway (unlike from eg. Drupal that has a content block),
the following block types are supported:
The following block types are supported:

- ``static``: Static content provided by linking to a node.
Unlike from Drupal concept of block that defines a text fields anyway.
Expand Down Expand Up @@ -566,22 +569,16 @@ Example of configuration of an ``editor`` group perms:
- Image: add, change, delete
- Page type specific permissions: add, change

Libraries
---------
Front-end libraries
-------------------

Libraries is a minor convenience feature (discussion open) that allows to easily integrate JS scripts in the template.
A small number of files are involved: ``settings``, ``templatetags``, ``base.html``.
The implementor may select to ignore libraries and override ``base.html`` or ``index.html`` blocks for
adding scripts anyway.
*(Changes in v0.6.0)*

Alternatively, use ``django-bower``. Bower is a front-end packages repository that by itself requires node.js,
but this package makes possible to use bower easily and install libraries easily. The downside is that proper
and sometimes plenty HTML still needs to be authored in templates, which is now handled in base.html.
Front-end package management is an important aspect of any site.
In NineCMS, Libraries had been a minor convenience feature to integrate front-end packages.
It has been removed because there are already several existing possibilities than can be easily used.

Second alternative is to create (in future) and use separate django packages, such as django-bootstrap3,
and other custom package for each major widely used js package. This is nice because it deals with the
above downside with custom template tags such as ``{% bootstrap_javascript %}``, but also deals with the
requirements issue. Downside is increased maintenance for the author of them.
An extension to NineCMS will soon be available for this matter. Alternatively, ``django-bower`` is good.

Image styles
------------
Expand Down
10 changes: 2 additions & 8 deletions ninecms/admin.py
Original file line number Diff line number Diff line change
Expand Up @@ -331,19 +331,13 @@ class ContentBlockAdmin(admin.ModelAdmin):

def page_types_list(self, obj):
""" Return a custom column with page types in which each block is an element
If page type is immediately repeated (same block more than once in one page type), add '+' instead
:param obj: a block object
:return: column output
"""
r = []
prev = 0
for page_type in obj.page_types.all():
if page_type.id != prev:
r.append('<a href="%s">%s</a>' % (
reverse('admin:ninecms_pagetype_change', args=(page_type.id,)), page_type))
else:
r[-1] += '+'
prev = page_type.id
r.append(
'<a href="%s">%s</a>' % (reverse('admin:ninecms_pagetype_change', args=(page_type.id,)), page_type))
return ', '.join(r)
page_types_list.allow_tags = True
page_types_list.short_description = _("Page types")
Expand Down
72 changes: 6 additions & 66 deletions ninecms/forms.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,54 +6,16 @@

from django import forms
from django.forms.models import inlineformset_factory
from django.contrib.admin.widgets import FilteredSelectMultiple
from django.contrib.auth.models import Group
from django.utils.translation import ugettext_lazy as _
from ninecms.models import Node, Image, File, Video, ContentBlock, PageType, TaxonomyTerm
from ninecms.utils.sanitize import sanitize, ModelSanitizeForm
from ninecms.utils.manytomany import ManyToManyModelForm, ModelBiMultipleChoiceField


class PageTypeForm(forms.ModelForm):
""" Override default page type form to show related blocks
https://www.lasolution.be/blog/related-manytomanyfield-django-admin-site.html
https://github.com/django/django/blob/master/django/contrib/admin/widgets.py#L24
"""
# @todo make parent generic class for reverse related m2m fields (use a new meta prop)
blocks = forms.ModelMultipleChoiceField(
ContentBlock.objects.all(),
widget=FilteredSelectMultiple("Blocks", True),
required=False,
)

def __init__(self, *args, **kwargs):
""" Initialize form
:param args
:param kwargs
:return: None
"""
super(PageTypeForm, self).__init__(*args, **kwargs)
if self.instance.pk:
self.initial['blocks'] = self.instance.blocks.values_list('pk', flat=True)
# from django.db.models import ManyToManyRel
# from django.contrib import admin
# rel = ManyToManyRel(ContentBlock, PageType)
# self.fields['blocks'].widget = RelatedFieldWidgetWrapper(self.fields['blocks'].widget, rel, admin.site)

def save(self, *args, **kwargs):
""" Handle saving of related blocks
:param args
:param kwargs
:return: instance
"""
instance = super(PageTypeForm, self).save(*args, **kwargs)
if instance.pk:
for block in instance.blocks.all():
if block not in self.cleaned_data['blocks']:
instance.blocks.remove(block)
for block in self.cleaned_data['blocks']:
if block not in instance.blocks.all():
instance.blocks.add(block)
return instance
class PageTypeForm(ManyToManyModelForm):
""" Override default page type form to show related blocks """
blocks = ModelBiMultipleChoiceField(ContentBlock.objects.all(), double_list="Blocks")

class Meta:
""" Meta class """
Expand All @@ -80,13 +42,9 @@ class ContentTypePermissionsForm(forms.Form):
)


class ContentNodeEditForm(forms.ModelForm):
class ContentNodeEditForm(ManyToManyModelForm):
""" Node edit or create form """
terms = forms.ModelMultipleChoiceField(
TaxonomyTerm.objects.all(),
widget=FilteredSelectMultiple("Terms", True),
required=False,
)
terms = ModelBiMultipleChoiceField(TaxonomyTerm.objects.all(), double_list="Terms")

def __init__(self, *args, **kwargs):
""" Get user object to check if has full_html permission
Expand All @@ -100,8 +58,6 @@ def __init__(self, *args, **kwargs):
except AttributeError:
self.current_user = kwargs.pop('user', None)
super(ContentNodeEditForm, self).__init__(*args, **kwargs)
if self.instance.pk:
self.initial['terms'] = self.instance.terms.values_list('pk', flat=True)

def clean(self):
""" Override clean form to bleach
Expand All @@ -117,22 +73,6 @@ def clean(self):
cleaned_data[field] = sanitize(cleaned_data[field], full_html=full_html)
return cleaned_data

def save(self, *args, **kwargs):
""" Handle saving of related terms
:param args
:param kwargs
:return: instance
"""
instance = super(ContentNodeEditForm, self).save(*args, **kwargs)
if instance.pk:
for term in instance.terms.all():
if term not in self.cleaned_data['terms']:
instance.terms.remove(term)
for term in self.cleaned_data['terms']:
if term not in instance.terms.all():
instance.terms.add(term)
return instance

class Meta:
""" Form model and fields """
model = Node
Expand Down
2 changes: 1 addition & 1 deletion ninecms/management/commands/check_updates.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ def handle(self, *args, **options):
:return: None
"""
with Capturing() as updates:
pip.main(['list', '--outdated'])
pip.main(['list', '--outdated', '--retries', '1'])
cache = caches['default']
if not updates: # pragma: nocover
cache.delete('updates')
Expand Down
16 changes: 14 additions & 2 deletions ninecms/migrations/0009_auto_20150924_1456.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ def transliterate_folder(field): # pragma: nocover
def migrate_path_file_name(apps, schema_editor): # pragma: nocover
""" Transliterate the directories of all saved files
Transliterate the corresponding field values
:param apps
:param app registry
:param schema_editor
:return: None
"""
Expand All @@ -46,12 +46,24 @@ def migrate_path_file_name(apps, schema_editor): # pragma: nocover
video.save()


# noinspection PyUnusedLocal
def reverse(apps, schema_editor): # pragma: nocover
"""
Reverse the above operation
Nothing to do here, transliterated folder names remain
:param apps: app registry
:param schema_editor
:return: None
"""
pass


class Migration(migrations.Migration):

dependencies = [
('ninecms', '0008_auto_20150819_1516'),
]

operations = [
migrations.RunPython(migrate_path_file_name),
migrations.RunPython(migrate_path_file_name, reverse),
]
Loading

0 comments on commit ef2f603

Please sign in to comment.