--- /dev/null
+# Copyright 2012 GRNET S.A. All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or
+# without modification, are permitted provided that the following
+# conditions are met:
+#
+# 1. Redistributions of source code must retain the above
+# copyright notice, this list of conditions and the following
+# disclaimer.
+#
+# 2. Redistributions in binary form must reproduce the above
+# copyright notice, this list of conditions and the following
+# disclaimer in the documentation and/or other materials
+# provided with the distribution.
+#
+# THIS SOFTWARE IS PROVIDED BY GRNET S.A. ``AS IS'' AND ANY EXPRESS
+# OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL GRNET S.A OR
+# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
+# USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+# AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
+# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+# POSSIBILITY OF SUCH DAMAGE.
+#
+# The views and conclusions contained in the software and
+# documentation are those of the authors and should not be
+# interpreted as representing official policies, either expressed
+# or implied, of GRNET S.A.
+
+
+from django.contrib import admin
+from django.utils.translation import ugettext_lazy as _
+
+from feincms.translations import admin_translationinline, short_language_code
+from feincms.admin import tree_editor, item_editor
+
+from cloudcmsguide.models import UserGuideEntry
+
+
+class UserGuideEntryAdmin(item_editor.ItemEditor, tree_editor.TreeEditor):
+
+ date_hierarchy = 'published_on'
+ list_display = ['title', 'is_active', 'published_on', 'service', 'author']
+ list_editable = ['is_active']
+ list_filter = ['is_active', 'is_featured', 'author']
+ search_fields = ['title', 'slug']
+ raw_id_fields = ['parent']
+
+ prepopulated_fields = {
+ 'slug': ('title',),
+ }
+
+ fieldsets = [
+ [None, {
+ 'fields': [
+ ('is_active', 'published_on'),
+ ('title', 'slug'),
+ 'parent',
+ 'service',
+ 'author',
+ ]
+ }],
+ item_editor.FEINCMS_CONTENT_FIELDSET,
+ ]
+
+ def import_from_sphinx(self, request):
+ return import_from_sphinx(request)
+
+
+admin.site.register(UserGuideEntry, UserGuideEntryAdmin)
+
--- /dev/null
+# Copyright 2012 GRNET S.A. All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or
+# without modification, are permitted provided that the following
+# conditions are met:
+#
+# 1. Redistributions of source code must retain the above
+# copyright notice, this list of conditions and the following
+# disclaimer.
+#
+# 2. Redistributions in binary form must reproduce the above
+# copyright notice, this list of conditions and the following
+# disclaimer in the documentation and/or other materials
+# provided with the distribution.
+#
+# THIS SOFTWARE IS PROVIDED BY GRNET S.A. ``AS IS'' AND ANY EXPRESS
+# OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL GRNET S.A OR
+# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
+# USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+# AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
+# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+# POSSIBILITY OF SUCH DAMAGE.
+#
+# The views and conclusions contained in the software and
+# documentation are those of the authors and should not be
+# interpreted as representing official policies, either expressed
+# or implied, of GRNET S.A.
+
+
+from datetime import datetime
+
+from django.db import models
+from django.conf import settings
+from django.contrib.auth.models import User
+from django.utils.translation import ugettext_lazy as _, ugettext, ungettext
+from django.template.loader import render_to_string
+from django.core import urlresolvers
+
+from feincms import translations
+from feincms.models import Base
+from feincms.module.page.models import Page
+from feincms.content.application.models import reverse
+from feincms.content.richtext.models import RichTextContent
+from feincms.content.section.models import SectionContent
+from feincms.module.medialibrary.fields import MediaFileForeignKey
+from feincms.module.medialibrary.models import MediaFile
+from feincms.module.page.extensions.navigation import NavigationExtension
+from feincms.module.page.extensions.navigation import PagePretender
+from feincms.content.application.models import ApplicationContent
+from feincms.models import Base, create_base_model
+
+from cloudcms.cms_utils import get_app_page
+
+class UserGuideEntryManager(models.Manager):
+
+ def active(self):
+ return self.filter(is_active=True)
+
+ def latest(self, limit=3):
+ return self.filter()[:limit]
+
+
+def get_guide_page():
+ """
+ Returns Page model that has been associated with userguide application
+ """
+ return get_app_page(Page, "cloudcmsguide")
+
+
+try:
+ # MPTT 0.4
+ from mptt.models import MPTTModel
+ mptt_register = False
+ Base = create_base_model(MPTTModel)
+except ImportError:
+ # MPTT 0.3
+ mptt_register = True
+
+class UserGuideEntry(Base):
+ """
+ User guide entry
+ """
+ is_active = models.BooleanField(_('is active'), default=True)
+ is_featured = models.BooleanField(_('is featured'), default=False)
+
+ title = models.CharField(_('title'), max_length=100)
+ slug = models.SlugField(_('slug'), max_length=100, unique_for_date='published_on')
+ author = models.ForeignKey(User, related_name='guide_pages', verbose_name=_('author'))
+ language = models.CharField(max_length=255, choices=settings.LANGUAGES)
+
+ published_on = models.DateTimeField(_('published on'), blank=True, null=True, default=datetime.now,
+ help_text=_('Will be filled in automatically when entry gets published.'))
+ last_changed = models.DateTimeField(_('last change'), auto_now=True, editable=False)
+
+ service = models.ForeignKey('cloudcms.Service', verbose_name=_('service'),
+ related_name='userguideentries', null=True, blank=False)
+
+ parent = models.ForeignKey('self', verbose_name=_('Parent'), blank=True, null=True, related_name='children')
+
+ objects = UserGuideEntryManager()
+
+ class Meta:
+ get_latest_by = 'published_on'
+ ordering = ['service', '-published_on']
+ verbose_name = _('User guide entry')
+ verbose_name_plural = _('User guide entries')
+
+ def __unicode__(self):
+ return self.title
+
+ def get_absolute_url(self):
+ try:
+ r = reverse('cloudcmsguide_entry_detail', 'cloudcmsguide.urls', (),
+ {
+ 'service': self.service.translation.slug,
+ 'slug': self.slug,
+ })
+ except Exception, e:
+ print e
+ return ""
+
+ # ugly hack to fix proper application reverse url
+ GUIDE_URL = ""
+ try:
+ GUIDE_URL = get_guide_page().get_navigation_url()
+ except Exception, e:
+ pass
+
+ if r.startswith(GUIDE_URL):
+ return r
+ else:
+ return GUIDE_URL + r.lstrip('/')
+
+ def back_url(self):
+ return get_guide_page().get_navigation_url()
+
+if mptt_register: # MPTT 0.3 legacy support
+ mptt.register(Page)
+
+UserGuideEntry.register_extensions(
+ 'changedate',
+ 'seo'
+)
--- /dev/null
+# Copyright 2012 GRNET S.A. All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or
+# without modification, are permitted provided that the following
+# conditions are met:
+#
+# 1. Redistributions of source code must retain the above
+# copyright notice, this list of conditions and the following
+# disclaimer.
+#
+# 2. Redistributions in binary form must reproduce the above
+# copyright notice, this list of conditions and the following
+# disclaimer in the documentation and/or other materials
+# provided with the distribution.
+#
+# THIS SOFTWARE IS PROVIDED BY GRNET S.A. ``AS IS'' AND ANY EXPRESS
+# OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL GRNET S.A OR
+# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
+# USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+# AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
+# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+# POSSIBILITY OF SUCH DAMAGE.
+#
+# The views and conclusions contained in the software and
+# documentation are those of the authors and should not be
+# interpreted as representing official policies, either expressed
+# or implied, of GRNET S.A.
+
+
+from django.db.models import Max
+from django.contrib.sitemaps import Sitemap
+from cloudcmsguide.models import UserGuideEntry
+
+class UserGuideSitemap(Sitemap):
+
+ def items(self):
+ return UserGuideEntry.objects.filter(is_active=True)
+
--- /dev/null
+{% load applicationcontent_tags pagination_tags i18n %}
+
+<div class="guide">
+ {% for service in services %}
+ <div class="guide-category">
+ <h2>{{ service.translation.title|upper }}</h2>
+ <ul>
+ {% for e in service.userguideentries.active %}
+ <li {% if e == entry %}class="current"{% endif %}>
+ <a href="{{ e.get_absolute_url }}">{{ e.title }}</a>
+ </li>
+ {% endfor %}
+ </ul>
+ </div>
+ {% endfor %}
+</div>
--- /dev/null
+{% load applicationcontent_tags i18n %}
+
+{% fragment request "sidecol" %}
+{% include "cloudcmsguide/archive.html" %}
+{% endfragment %}
+
+{% fragment request "maincol" %}
+<div class="user-guide-entry">
+ <h2>{{ entry.title|upper }}</h2>
+ <div class="content">
+ {% for content in entry.content.main %}
+ {{ content.render }}
+ {% endfor %}
+ </div>
+ <div class="bottom-content backlink">
+ <a href="{{ entry.back_url }}">{% trans "< Back to User guide" %}</a>
+ </div>
+</div>
+{% endfragment %}
--- /dev/null
+# Copyright 2012 GRNET S.A. All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or
+# without modification, are permitted provided that the following
+# conditions are met:
+#
+# 1. Redistributions of source code must retain the above
+# copyright notice, this list of conditions and the following
+# disclaimer.
+#
+# 2. Redistributions in binary form must reproduce the above
+# copyright notice, this list of conditions and the following
+# disclaimer in the documentation and/or other materials
+# provided with the distribution.
+#
+# THIS SOFTWARE IS PROVIDED BY GRNET S.A. ``AS IS'' AND ANY EXPRESS
+# OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL GRNET S.A OR
+# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
+# USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+# AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
+# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+# POSSIBILITY OF SUCH DAMAGE.
+#
+# The views and conclusions contained in the software and
+# documentation are those of the authors and should not be
+# interpreted as representing official policies, either expressed
+# or implied, of GRNET S.A.
+
+
+"""
+This file demonstrates two different styles of tests (one doctest and one
+unittest). These will both pass when you run "manage.py test".
+
+Replace these with more appropriate tests for your application.
+"""
+
+from django.test import TestCase
+
+class SimpleTest(TestCase):
+ def test_basic_addition(self):
+ """
+ Tests that 1 + 1 always equals 2.
+ """
+ self.failUnlessEqual(1 + 1, 2)
+
+__test__ = {"doctest": """
+Another way to test that 1 + 1 is equal to 2.
+
+>>> 1 + 1 == 2
+True
+"""}
+
--- /dev/null
+# Copyright 2012 GRNET S.A. All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or
+# without modification, are permitted provided that the following
+# conditions are met:
+#
+# 1. Redistributions of source code must retain the above
+# copyright notice, this list of conditions and the following
+# disclaimer.
+#
+# 2. Redistributions in binary form must reproduce the above
+# copyright notice, this list of conditions and the following
+# disclaimer in the documentation and/or other materials
+# provided with the distribution.
+#
+# THIS SOFTWARE IS PROVIDED BY GRNET S.A. ``AS IS'' AND ANY EXPRESS
+# OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL GRNET S.A OR
+# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
+# USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+# AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
+# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+# POSSIBILITY OF SUCH DAMAGE.
+#
+# The views and conclusions contained in the software and
+# documentation are those of the authors and should not be
+# interpreted as representing official policies, either expressed
+# or implied, of GRNET S.A.
+
+
+from django.conf.urls.defaults import patterns, include, url
+from django.conf import settings
+from django.contrib import admin
+
+admin.autodiscover()
+
+urlpatterns = patterns('cloudcmsguide.views',
+ url(r'^$', 'index', name='cloudcmsguide_entries_archive'),
+ url(r'^(?P<service>[\w]+)-(?P<slug>[-\w]+)/$',
+ 'detail', name='cloudcmsguide_entry_detail'),
+)
+
--- /dev/null
+# Copyright 2012 GRNET S.A. All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or
+# without modification, are permitted provided that the following
+# conditions are met:
+#
+# 1. Redistributions of source code must retain the above
+# copyright notice, this list of conditions and the following
+# disclaimer.
+#
+# 2. Redistributions in binary form must reproduce the above
+# copyright notice, this list of conditions and the following
+# disclaimer in the documentation and/or other materials
+# provided with the distribution.
+#
+# THIS SOFTWARE IS PROVIDED BY GRNET S.A. ``AS IS'' AND ANY EXPRESS
+# OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL GRNET S.A OR
+# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
+# USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+# AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
+# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+# POSSIBILITY OF SUCH DAMAGE.
+#
+# The views and conclusions contained in the software and
+# documentation are those of the authors and should not be
+# interpreted as representing official policies, either expressed
+# or implied, of GRNET S.A.
+
+
+from django.conf import settings
+from django.views.generic.simple import direct_to_template
+from django.http import HttpResponseRedirect, Http404
+
+from cloudcms.models import Service
+from cloudcmsguide.models import UserGuideEntry
+
+def index(request):
+ return archive(request)
+
+def archive(request):
+ """
+ Display entries list
+ """
+ services = Service.objects.all()
+
+ return direct_to_template(request,
+ "cloudcmsguide/archive.html", {'services': services})
+
+def detail(request, service, slug):
+ """
+ Display detailed entry.
+ """
+ entry = UserGuideEntry.objects.get(slug=slug)
+ services = Service.objects.all()
+
+ return direct_to_template(request,
+ "cloudcmsguide/detail.html", {'entry': entry,
+ 'services': services})
+