SP Portal administrative interface
[cascardo/ipsilon.git] / ipsilon / util / config.py
index 5366a96..e426679 100644 (file)
@@ -1,7 +1,33 @@
-# Copyright (C) 2014  Ipsilon project Contributors, for licensee see COPYING
+# Copyright (C) 2014 Ipsilon project Contributors, for license see COPYING
 
 from ipsilon.util.log import Log
+import os
 import json
+import base64
+import imghdr
+import hashlib
+import cherrypy
+
+
+def name_from_image(image):
+    if image is None:
+        return None
+
+    fext = imghdr.what(None, base64.b64decode(image))
+    m = hashlib.sha1()
+    m.update(base64.b64decode(image))
+
+    return '%s.%s' % (m.hexdigest(), fext)
+
+
+def url_from_image(image):
+    if image is None:
+        return None
+
+    return '%s/cache/%s' % (
+        cherrypy.config.get('base.mount', ""),
+        name_from_image(image)
+    )
 
 
 class Config(Log):
@@ -70,18 +96,21 @@ class Config(Log):
 
 class Option(Log):
 
-    def __init__(self, name, description):
+    def __init__(self, name, description, readonly=False):
         self.name = name
         self.description = description
         self._default_value = None
         self._assigned_value = None
+        self._readonly = readonly
 
     def __repr__(self):
-        return "%s: %s {%s}, value = %s [def: %s]" % (self.__class__,
-                                                      self.name,
-                                                      self.description,
-                                                      self._assigned_value,
-                                                      self._default_value)
+        return "%s: %s {%s}, value = %s [def: %s] readonly=%s" % (
+            self.__class__,
+            self.name,
+            self.description,
+            self._assigned_value,
+            self._default_value,
+            self._readonly)
 
     def __str__(self):
         return '%s=%s' % (self.name, self.get_value())
@@ -109,15 +138,18 @@ class Option(Log):
         return None
 
     def _str_import_value(self, value):
-        if type(value) is not str:
+        if not isinstance(value, str):
             raise ValueError('Value must be string')
         self._assigned_value = value
 
+    def is_readonly(self):
+        return self._readonly
+
 
 class String(Option):
 
-    def __init__(self, name, description, default_value=None):
-        super(String, self).__init__(name, description)
+    def __init__(self, name, description, default_value=None, readonly=False):
+        super(String, self).__init__(name, description, readonly=readonly)
         self._default_value = str(default_value)
 
     def set_value(self, value):
@@ -130,10 +162,83 @@ class String(Option):
         self._str_import_value(value)
 
 
+class Image(Option):
+    """
+    An image has two components: the binary blob of the image itself and
+    the SHA1 sum of the image.
+
+    We only need the image blob when writing to the cache file or
+    updating the database.
+
+    For the purposes of the UI we only need the filename which is
+    the SHA1 sum of file type the blob + file type.
+    """
+
+    def __init__(self, name, description, default_value=None, readonly=False):
+        super(Image, self).__init__(name, description, readonly=readonly)
+        self._image = None
+
+        if default_value:
+            self._image = default_value
+
+        self._assigned_value = url_from_image(self._image)
+        self.__write_cache_file()
+
+    def set_value(self, value):
+        if value is None:
+            return None
+
+        if os.path.exists(self.__filename()):
+            try:
+                os.remove(self.__filename())
+            except IOError as e:
+                self.error('Error removing %s: %s' % (self.__filename(), e))
+
+        self._image = base64.b64encode(value)
+        self._assigned_value = url_from_image(value)
+
+    def export_value(self):
+        if self._image is None:
+            return None
+
+        self.__write_cache_file()
+        return base64.b64decode(self._image)
+
+    def import_value(self, value):
+        if value is None:
+            return None
+
+        if os.path.exists(self.__filename()):
+            try:
+                os.remove(self.__filename())
+            except IOError as e:
+                self.error('Error removing %s: %s' % (self.__filename(), e))
+        self._image = base64.b64encode(value)
+        self._assigned_value = url_from_image(self._image)
+        self.__write_cache_file()
+
+    def __filename(self):
+        if self._image is None:
+            return None
+
+        cdir = cherrypy.config.get('cache_dir', '/var/cache/ipsilon')
+
+        return '%s/%s' % (cdir, name_from_image(self._image))
+
+    def __write_cache_file(self):
+        if self._image is None:
+            return None
+
+        if not os.path.exists(self.__filename()):
+            with open(self.__filename(), 'w') as imagefile:
+                imagefile.write(base64.b64decode(self._image))
+
+
 class Template(Option):
 
-    def __init__(self, name, description, default_template=None):
-        super(Template, self).__init__(name, description)
+    def __init__(self, name, description, default_template=None,
+                 readonly=False):
+        super(Template, self).__init__(name, description, readonly=readonly)
         self._default_value = str(default_template)
 
     def set_value(self, value):
@@ -154,8 +259,8 @@ class Template(Option):
 
 class List(Option):
 
-    def __init__(self, name, description, default_list=None):
-        super(List, self).__init__(name, description)
+    def __init__(self, name, description, default_list=None, readonly=False):
+        super(List, self).__init__(name, description, readonly=readonly)
         if default_list:
             self._default_value = default_list
         else:
@@ -170,7 +275,7 @@ class List(Option):
         return None
 
     def import_value(self, value):
-        if type(value) is not str:
+        if not isinstance(value, str):
             raise ValueError('Value (type: %s) must be string' % type(value))
         self._assigned_value = [x.strip() for x in value.split(',')]
 
@@ -180,7 +285,7 @@ class ComplexList(List):
     def _check_value(self, value):
         if value is None:
             return
-        if type(value) is not list:
+        if not isinstance(value, list):
             raise ValueError('The value type must be a list, not "%s"' %
                              type(value))
 
@@ -194,7 +299,7 @@ class ComplexList(List):
         return None
 
     def import_value(self, value):
-        if type(value) is not str:
+        if not isinstance(value, str):
             raise ValueError('The value type must be a string, not "%s"' %
                              type(value))
         jsonval = json.loads(value)
@@ -206,11 +311,11 @@ class MappingList(ComplexList):
     def _check_value(self, value):
         if value is None:
             return
-        if type(value) is not list:
+        if not isinstance(value, list):
             raise ValueError('The value type must be a list, not "%s"' %
                              type(value))
         for v in value:
-            if type(v) is not list:
+            if not isinstance(v, list):
                 raise ValueError('Each element must be a list, not "%s"' %
                                  type(v))
             if len(v) != 2:
@@ -218,7 +323,7 @@ class MappingList(ComplexList):
                                  ' not %d' % len(v))
 
     def import_value(self, value):
-        if type(value) is not str:
+        if not isinstance(value, str):
             raise ValueError('Value (type: %s) must be string' % type(value))
         jsonval = json.loads(value)
         self.set_value(jsonval)
@@ -226,8 +331,9 @@ class MappingList(ComplexList):
 
 class Choice(Option):
 
-    def __init__(self, name, description, allowed=None, default=None):
-        super(Choice, self).__init__(name, description)
+    def __init__(self, name, description, allowed=None, default=None,
+                 readonly=False):
+        super(Choice, self).__init__(name, description, readonly=readonly)
         if allowed:
             self._allowed_values = list(allowed)
         else:
@@ -253,7 +359,7 @@ class Choice(Option):
         return '%s=%s' % (self.name, self.get_value())
 
     def set_value(self, value):
-        if type(value) is not list:
+        if not isinstance(value, list):
             value = [value]
         self._assigned_value = list()
         for val in value:
@@ -267,7 +373,7 @@ class Choice(Option):
             self._assigned_value = None
 
     def unset_value(self, value):
-        if type(value) is str:
+        if isinstance(value, str):
             value = [value]
         unset = list()
         for val in value:
@@ -295,8 +401,9 @@ class Choice(Option):
 
 class Pick(Option):
 
-    def __init__(self, name, description, allowed, default_value):
-        super(Pick, self).__init__(name, description)
+    def __init__(self, name, description, allowed, default_value,
+                 readonly=False):
+        super(Pick, self).__init__(name, description, readonly=readonly)
         self._allowed_values = list(allowed)
         if default_value not in self._allowed_values:
             raise ValueError('The default value is not in the allowed list')
@@ -320,12 +427,20 @@ class Pick(Option):
 
 class Condition(Pick):
 
-    def __init__(self, name, description, default_value=False):
+    def __init__(self, name, description, default_value=False,
+                 readonly=False):
+        # The db stores 1/0. Convert the passed-in value if
+        # necessary
+        if default_value in [u'1', 'True', True]:
+            default_value = True
+        else:
+            default_value = False
         super(Condition, self).__init__(name, description,
-                                        [True, False], default_value)
+                                        [True, False], default_value,
+                                        readonly=readonly)
 
     def import_value(self, value):
-        self._assigned_value = value == 'True'
+        self._assigned_value = value
 
 
 class ConfigHelper(Log):