Merge branch 'feature-newstyles' into release-0.13
[snf-cloudcms] / cloudcms / models.py
1 # Copyright 2012 GRNET S.A. All rights reserved.
2 #
3 # Redistribution and use in source and binary forms, with or
4 # without modification, are permitted provided that the following
5 # conditions are met:
6 #
7 #   1. Redistributions of source code must retain the above
8 #      copyright notice, this list of conditions and the following
9 #      disclaimer.
10 #
11 #   2. Redistributions in binary form must reproduce the above
12 #      copyright notice, this list of conditions and the following
13 #      disclaimer in the documentation and/or other materials
14 #      provided with the distribution.
15 #
16 # THIS SOFTWARE IS PROVIDED BY GRNET S.A. ``AS IS'' AND ANY EXPRESS
17 # OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
18 # WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
19 # PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL GRNET S.A OR
20 # CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
21 # SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
22 # LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
23 # USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
24 # AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25 # LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
26 # ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
27 # POSSIBILITY OF SUCH DAMAGE.
28 #
29 # The views and conclusions contained in the software and
30 # documentation are those of the authors and should not be
31 # interpreted as representing official policies, either expressed
32 # or implied, of GRNET S.A.
33
34 import datetime
35
36 from django.db import models
37 from django.conf import settings
38 from django.contrib.sites import models as sites_models
39 from django.utils.translation import ugettext_lazy as _, ugettext, ungettext
40 from django.core.cache import cache
41 from django.utils import simplejson
42
43 from feincms import translations
44 from feincms.module.medialibrary.fields import MediaFileForeignKey
45 from feincms.module.medialibrary.models import MediaFile
46
47
48 class Application(models.Model):
49     """
50     Application object refers to the application each cms is deployed for.
51
52     Each cms deployment should contain at least one application object linked
53     to the site object the cms is deployed for.
54
55     Enabling cloudcms.context_processors.application in CONTEXT_PROCESSROS setting
56     let you can access the application object throughout the html templates.
57     """
58
59     MESSAGE_TYPE_CHOICES = (
60         ('NM','No Message'),
61         ('success','Success'),
62         ('error','Error'),
63         ('warning','Warning'),
64         ('info','Info'),
65     )
66  
67
68     code = models.CharField('Identifier', max_length=100, null=False, blank=False,
69             help_text="Just a codename of the application, to be used in "\
70                     "several places where no free text is allowed"\
71                     "(e.g. urls, paths, etc)")
72     title = models.CharField(max_length=255, null=False, blank=False,
73             help_text="The title of the application")
74
75     logo = MediaFileForeignKey(MediaFile, blank=True, null=True)
76     favicon = MediaFileForeignKey(MediaFile, blank=True, null=True,
77             related_name="as_favicon")
78     site = models.ForeignKey(sites_models.Site)
79     app_url = models.URLField(help_text="The url of the application UI (not "\
80             "the cms", verify_exists=False, blank=True, null=True)
81     index_url = models.CharField(max_length=255, default="/", blank=False,
82             null=False)
83     linked_in_username = models.CharField(max_length=255, blank=True)
84     twitter_username = models.CharField(max_length=255, blank=True)
85     facebook_username = models.CharField(max_length=255, blank=True)
86
87     show_twitter_feed_on_top = models.BooleanField(default=False)
88     extra_styles = models.TextField(default="", blank=True)
89
90     footer_top = models.TextField(default="", blank=True)
91     footer_bottom = models.TextField(default="", blank=True)
92
93     message_type = models.CharField(max_length=20, 
94                                     choices= MESSAGE_TYPE_CHOICES,
95                                     default = 'NM' )
96     custom_message = models.TextField(default="", blank=True)
97
98     @classmethod
99     def current(cls):
100         return cls.objects.get(site__pk=settings.SITE_ID)
101
102     def __unicode__(self):
103         return self.title
104
105
106 # http://stackoverflow.com/a/2680060/114435
107 dthandler = lambda obj: obj.isoformat() if isinstance(obj, datetime.datetime) else None
108
109 class Client(models.Model):
110     """
111     Model which refers to a service/application client. Client model contains
112     multiple ClientVersionSource to identify it's version downloads.
113     """
114     uid = models.CharField(max_length=255)
115     name = models.CharField(max_length=255)
116
117     def get_sources(self):
118         sources = {}
119         from cloudcms.clients import ClientVersions
120         for s in self.clientversionsource_set.all():
121             sources[s.os] = {'type': s.source_type,
122                              'args': [s.link]}
123
124         return ClientVersions(sources, cache_backend=cache)
125
126     def to_json(self):
127         return simplejson.dumps(list(self.get_sources().get_latest_versions()),
128                 default=dthandler)
129
130     def get_default_os(self):
131         try:
132             return self.clientversionsource_set.filter(default=True)[0].os
133         except IndexError:
134             return self.clientversionsource_set.filter()[0].os
135
136     def __unicode__(self):
137         return self.name
138
139
140 class ClientVersionSource(models.Model):
141     """
142     Client version source. source_type choices should map to
143     cloudcms.clients.SOURCE_TYPES.
144     """
145
146     default = models.BooleanField(default=True)
147     source_type = models.CharField(max_length=60,
148             choices=(('link','Link'),
149                      ('direct','Direct'),
150                      ('redmine_files','Redmine files')))
151     os = models.CharField(max_length=255)
152     link = models.CharField(max_length=255)
153     logo = MediaFileForeignKey(MediaFile, blank=True, null=True)
154     architecture = models.CharField(max_length=255, null=True, blank=True,
155             help_text="""Depending the source type this can be left empty and
156             let source type identify the architecture""")
157     client = models.ForeignKey(Client)
158     version_regex = models.CharField(max_length=255, help_text="""Regular expression to
159             match the version of the file based on retrieved source filenames
160             (used in redmine source types)""", null=True, blank=True)
161     file_regex = models.CharField(max_length=255, help_text="""Return only files that
162             match this expression""", null=True, blank=True)
163
164     def __unicode__(self):
165         return "[%s] %s" % (self.get_source_type_display(), self.os)
166
167
168 # hook for feincms configuration, is this appropriate place ??? who knows
169 from cloudcms.cms import *
170
171 class Service(models.Model, translations.TranslatedObjectMixin):
172     """
173     Service.
174     """
175
176     ordering = models.SmallIntegerField(_('ordering'), default=0)
177     image_faq = MediaFileForeignKey(MediaFile, blank=True, null=True, related_name='image_faq')
178     image_userguide = MediaFileForeignKey(MediaFile, blank=True, null=True,related_name='image_userguide')
179     class_name = models.CharField(_('class name'), max_length=100, blank=True)
180     
181     
182     class Meta:
183         verbose_name = _('service')
184         verbose_name_plural = _('services')
185         ordering = ['-ordering',]
186
187     objects = translations.TranslatedObjectManager()
188
189     def get_first_question(self):
190         try:
191             return self.faqs.filter(is_active=True)[0]
192         except:
193             return None
194         
195     def get_first_entry(self):
196         try:
197             return self.userguideentries.filter(is_active=True)[0]
198         except:
199             return None
200         
201     def __unicode__(self):
202         trans = translations.TranslatedObjectMixin.__unicode__(self)
203         return trans or _('Unnamed category')
204
205
206 class ServiceTranslation(translations.Translation(Service)):
207     """
208     Service translation
209     """
210     title = models.CharField(_('service title'), max_length=100)
211     slug = models.SlugField(_('slug'), unique=True)
212     description = models.CharField(_('description'), max_length=250, blank=True)
213     cms_page = models.ForeignKey(Page, null=True, blank=True)
214     title_faq = models.CharField(_('service title (faq section)'), max_length=100, blank=True)
215     title_userguide = models.CharField(_('service title (userguide section)'), max_length=100, blank=True)
216     
217     class Meta:
218         verbose_name = _('service translation')
219         verbose_name_plural = _('service translations')
220         ordering = ['title']
221
222     def __unicode__(self):
223         return self.title
224
225     def save(self, *args, **kwargs):
226         if not self.slug:
227             self.slug = slugify(self.title)
228
229         super(ServiceTranslation, self).save(*args, **kwargs)
230