Commit ab94e3df0bed4f5bd980bf8231a6990a6369beac

Authored by Sergio Oliveira
2 parents 2fedfd73 fd31cbb7

Merge branch 'settings_py'

.gitignore
... ... @@ -3,7 +3,7 @@
3 3 *.pot
4 4 *.pyc
5 5 *.pyo
6   -*.egg
  6 +*.egg*
7 7 local_settings.py
8 8 project_cfg.py
9 9  
... ...
colab/plugins/context_processors.py
... ... @@ -2,10 +2,10 @@
2 2 from django.conf import settings
3 3  
4 4  
5   -def proxied_apps(request):
6   - proxied_apps = {}
  5 +def colab_apps(request):
  6 + colab_apps = {}
7 7  
8 8 for app_name, app in settings.COLAB_APPS.items():
9   - proxied_apps[app_name] = app
  9 + colab_apps[app_name] = app
10 10  
11   - return {'proxy': proxied_apps}
  11 + return {'plugins': colab_apps}
... ...
colab/plugins/templates/plugins/menu_template.html
1 1 {% for title, links in menu_links.items %}
2 2 {% if links|length == 1 %}
3   - {% for text, link in links %}
  3 + {% for colab_url in links %}
4 4 <li>
5   - <a href="{{ link }}">{{ title }}</a>
  5 + <a href="{{ colab_url.url }}">{{ title }}</a>
6 6 </li>
7 7 {% endfor %}
8 8 {% else %}
9 9 <li class="dropdown">
10 10 <a href="#" class="dropdown-toggle" data-toggle="dropdown">{{ title }} <b class="caret"></b></a>
11 11 <ul class="dropdown-menu">
12   - {% for text, link in links %}
13   - <li><a href="{{ link }}">{{ text }}</a></li>
  12 + {% for colab_url in links %}
  13 + <li><a href="{{ colab_url.url }}">{{ colab_url.display }}</a></li>
14 14 {% endfor %}
15 15 </ul>
16 16 </li>
... ...
colab/plugins/templatetags/plugins.py
... ... @@ -3,45 +3,48 @@ from collections import OrderedDict
3 3 from django import template
4 4 from django.core.cache import cache
5 5 from django.template.loader import render_to_string
6   -from django.utils.translation import ugettext_lazy as _
  6 +from django.utils.translation import get_language
7 7  
8 8 register = template.Library()
9 9  
10 10  
11 11 @register.simple_tag(takes_context=True)
12 12 def plugins_menu(context):
  13 +
13 14 if context['user'].is_authenticated():
14 15 cache_key = 'colab-proxy-menu-authenticated'
15 16 else:
16 17 cache_key = 'colab-proxy-menu-anonymous'
17 18  
  19 + lang = get_language()
  20 + cache_key += '-{}'.format(lang)
  21 +
18 22 menu_from_cache = cache.get(cache_key)
19 23  
20 24 if menu_from_cache:
21 25 return menu_from_cache
22 26  
23 27 menu_links = OrderedDict()
24   - proxied_apps = context.get('proxy', {})
  28 + colab_apps = context.get('plugins', {})
25 29  
26   - for app_name, app in proxied_apps.items():
27   - if not app.get('menu'):
  30 + for app_name, app in colab_apps.items():
  31 + if not app.get('menu_urls'):
28 32 continue
29 33  
30   - menu = app.get('menu')
31   - title = menu.get('title', app_name)
32   - links = menu.get('links', tuple()).items()
33   - if context['user'].is_active:
34   - links += menu.get('auth_links', tuple()).items()
35   -
36   - if not links:
37   - continue
  34 + menu = app.get('menu_urls')
  35 + title = app.get('menu_title', app_name)
38 36  
39 37 if title not in menu_links:
40   - menu_links[_(title)] = []
  38 + menu_links[title] = []
  39 +
  40 + for colab_url in menu:
  41 + if not context['user'].is_active and colab_url.auth:
  42 + continue
  43 +
  44 + menu_links[title].append(colab_url)
41 45  
42   - for text, link in links:
43   - url = link
44   - menu_links[_(title)].append((_(text), url))
  46 + if not menu_links[title]:
  47 + del menu_links[title]
45 48  
46 49 menu = render_to_string('plugins/menu_template.html',
47 50 {'menu_links': menu_links})
... ...
colab/plugins/urls.py
... ... @@ -14,8 +14,8 @@ for app_name, app in settings.COLAB_APPS.items():
14 14 urls = app.get('urls')
15 15 if not urls.get('include'):
16 16 raise ImproperlyConfigured(undef_url_include_msg)
17   - print urls['include']
18 17 urlpatterns += patterns('',
19 18 url(urls.get('prefix', r''), include(urls['include'],
20 19 namespace=urls.get('namespace'))),
21   - )
22 20 \ No newline at end of file
  21 + )
  22 +
... ...
colab/plugins/utils/menu.py 0 → 100644
... ... @@ -0,0 +1,25 @@
  1 +from django.core.urlresolvers import reverse_lazy
  2 +
  3 +
  4 +class ColabUrl(object):
  5 + def __init__(self, display, url, auth):
  6 + self.display = display
  7 + self.url = url
  8 + self.auth = auth
  9 +
  10 +
  11 +def colab_url_factory(namespace):
  12 +
  13 + def url(display, viewname, namespace=namespace, args=tuple(),
  14 + kwargs={}, auth=False):
  15 +
  16 + if namespace:
  17 + rev_viewname = ':'.join((namespace, viewname))
  18 + else:
  19 + rev_viewname = viewname
  20 +
  21 + url = reverse_lazy(rev_viewname, args=args, kwargs=kwargs)
  22 +
  23 + return ColabUrl(display, url, auth)
  24 +
  25 + return url
... ...
colab/settings.py
... ... @@ -194,7 +194,7 @@ TEMPLATE_CONTEXT_PROCESSORS = (
194 194 'django.core.context_processors.request',
195 195 'django_mobile.context_processors.is_mobile',
196 196 'colab.super_archives.context_processors.mailarchive',
197   - 'colab.plugins.context_processors.proxied_apps',
  197 + 'colab.plugins.context_processors.colab_apps',
198 198 'colab.home.context_processors.robots',
199 199 'colab.home.context_processors.ribbon',
200 200 'colab.home.context_processors.google_analytics',
... ... @@ -280,8 +280,13 @@ CONVERSEJS_SHOW_ONLY_ONLINE_USERS = True
280 280 TASTYPIE_DEFAULT_FORMATS = ['json', ]
281 281  
282 282 from .utils.conf import load_yaml_settings
  283 +from .utils.conf import load_py_settings
  284 +from .utils.conf import load_colab_apps
  285 +
283 286 locals().update(load_yaml_settings())
284 287  
  288 +locals().update(load_py_settings())
  289 +
285 290 if locals().get('RAVEN_DSN', False):
286 291 RAVEN_CONFIG = {
287 292 'dsn': RAVEN_DSN + '?timeout=30', # noqa
... ... @@ -291,6 +296,8 @@ if locals().get(&#39;RAVEN_DSN&#39;, False):
291 296 BROWSERID_ENABLED = locals().get('BROWSERID_ENABLED') or False
292 297 SOCIAL_NETWORK_ENABLED = locals().get('SOCIAL_NETWORK_ENABLED') or False
293 298  
  299 +locals().update(load_colab_apps())
  300 +
294 301 COLAB_APPS = locals().get('COLAB_APPS') or {}
295 302 PROXIED_APPS = {}
296 303  
... ...
colab/utils/conf.py
1 1  
2 2 import os
  3 +import sys
  4 +
  5 +import warnings
  6 +
3 7 import yaml
4 8  
5 9 import yamlordereddictloader
6 10  
7 11 from django.core.exceptions import ImproperlyConfigured
8 12  
  13 +import importlib
  14 +
  15 +
  16 +USING_YAML_SETTINGS = False
  17 +
9 18  
10 19 class InaccessibleYAMLSettings(ImproperlyConfigured):
11 20 """Settings YAML is Inaccessible.
... ... @@ -29,22 +38,144 @@ def _load_yaml_file(yaml_path):
29 38  
30 39 def load_yaml_settings():
31 40 settings_dir = '/etc/colab/settings.d'
32   - yaml_path = os.getenv('COLAB_SETTINGS', '/etc/colab/settings.yaml')
  41 + yaml_path = os.getenv('COLAB_YAML_SETTINGS', '/etc/colab/settings.yaml')
33 42  
34   - if not os.path.exists(yaml_path):
35   - msg = "The yaml file {} does not exist".format(yaml_path)
36   - raise InaccessibleYAMLSettings(msg)
  43 + if os.path.exists(yaml_path):
  44 + global USING_YAML_SETTINGS
  45 + USING_YAML_SETTINGS = True
  46 + warnings.warn("YAML Settings file is deprecated. Use Py file instead.")
  47 + else:
  48 + return {}
37 49  
38 50 yaml_settings = _load_yaml_file(yaml_path)
39 51  
  52 + parse_yml_menus(yaml_settings)
  53 +
40 54 # Try to read settings from settings.d
41 55 if os.path.exists(settings_dir):
42 56 for file_name in os.listdir(settings_dir):
43 57 if file_name.endswith('.yaml') or file_name.endswith('yml'):
44 58 file_path = os.path.join(settings_dir, file_name)
45 59 yaml_settings_d = _load_yaml_file(file_path)
  60 +
  61 + parse_yml_menus(yaml_settings_d)
  62 +
46 63 yaml_settings.update(yaml_settings_d)
47 64  
48 65 return yaml_settings or {}
49 66  
50   -yaml_settings = load_yaml_settings()
  67 +
  68 +class InaccessiblePySettings(ImproperlyConfigured):
  69 + """Settings.py is Inaccessible.
  70 +
  71 + Check if the file exists and if you have read permissions."""
  72 +
  73 +
  74 +def _load_py_file(py_path, path):
  75 + original_path = sys.path
  76 +
  77 + sys.path = [path]
  78 + try:
  79 + py_settings = importlib.import_module(py_path)
  80 +
  81 + except IOError:
  82 + msg = ('Could not open settings file {}. Please '
  83 + 'check if the file exists and if user '
  84 + 'has read rights.').format(py_path)
  85 + raise InaccessiblePySettings(msg)
  86 +
  87 + except SyntaxError as excpt:
  88 + msg = ('Syntax Error: {}'.format(excpt))
  89 + raise InaccessiblePySettings(msg)
  90 +
  91 + finally:
  92 + sys.path = original_path
  93 +
  94 + py_setting = {var: getattr(py_settings, var) for var in dir(py_settings)
  95 + if not var.startswith('__')}
  96 +
  97 + return py_setting
  98 +
  99 +
  100 +def load_py_settings():
  101 + settings_dir = '/etc/colab/settings.d'
  102 + settings_file = os.getenv('COLAB_SETTINGS', '/etc/colab/settings.py')
  103 + settings_module = settings_file.split('.')[-2].split('/')[-1]
  104 + py_path = "/".join(settings_file.split('/')[:-1])
  105 +
  106 + global USING_YAML_SETTINGS
  107 + if not os.path.exists(py_path) and not USING_YAML_SETTINGS:
  108 + msg = "The py file {} does not exist".format(py_path)
  109 + raise InaccessiblePySettings(msg)
  110 + elif USING_YAML_SETTINGS:
  111 + return {}
  112 +
  113 + py_settings = _load_py_file(settings_module, py_path)
  114 +
  115 + # Try to read settings from settings.d
  116 +
  117 + if os.path.exists(settings_dir):
  118 + return py_settings
  119 + for file_name in os.listdir(settings_dir):
  120 + if file_name.endswith('.py'):
  121 + file_module = file_name.split('.')[0]
  122 + py_settings_d = _load_py_file(file_module, settings_dir)
  123 + py_settings.update(py_settings_d)
  124 +
  125 + return py_settings
  126 +
  127 +
  128 +def load_colab_apps():
  129 + plugins_dir = os.getenv('COLAB_PLUGINS', '/etc/colab/plugins.d/')
  130 +
  131 + global USING_YAML_SETTINGS
  132 + if USING_YAML_SETTINGS:
  133 + return {}
  134 +
  135 + COLAB_APPS = {}
  136 +
  137 + # Try to read settings from plugins.d
  138 + if os.path.exists(plugins_dir):
  139 + for file_name in os.listdir(plugins_dir):
  140 + if file_name.endswith('.py'):
  141 + file_module = file_name.split('.')[0]
  142 + py_settings_d = _load_py_file(file_module, plugins_dir)
  143 + fields = ['verbose_name', 'upstream', 'urls',
  144 + 'menu_urls', 'middlewares', 'dependencies',
  145 + 'context_processors']
  146 +
  147 + app_name = py_settings_d.get('name')
  148 + if not app_name:
  149 + warnings.warn("Plugin missing name variable")
  150 + continue
  151 +
  152 + COLAB_APPS[app_name] = {}
  153 + COLAB_APPS[app_name]['menu_title'] = \
  154 + py_settings_d.get('menu_title')
  155 +
  156 + for key in fields:
  157 + value = py_settings_d.get(key)
  158 + if value:
  159 + COLAB_APPS[app_name][key] = value
  160 +
  161 + return {'COLAB_APPS': COLAB_APPS}
  162 +
  163 +
  164 +def parse_yml_menus(yaml_settings):
  165 + if 'COLAB_APPS' in yaml_settings:
  166 + for key, plugin in yaml_settings['COLAB_APPS'].items():
  167 + if 'menu' in plugin:
  168 + parse_yml_tuples(yaml_settings['COLAB_APPS'][key]['menu'])
  169 +
  170 +
  171 +def parse_yml_tuples(menu):
  172 + dict_links = menu['links']
  173 + dict_auth_links = menu['auth_links']
  174 + menu['links'] = tuple()
  175 + menu['auth_links'] = tuple()
  176 +
  177 + for key, value in dict_links.items():
  178 + menu['links'] += ((key, value),)
  179 +
  180 + for key, value in dict_auth_links.items():
  181 + menu['auth_links'] += ((key, value),)
... ...
docs/source/user.rst
... ... @@ -14,41 +14,33 @@ Install
14 14  
15 15 Plugins
16 16 -------
17   -.. attribute:: COLAB_APPS
  17 +.. attribute:: name
18 18  
19   - :default: None
  19 +Declares the absolute name of the plugin app as a python import path. Example:
  20 +directory.something.someplugin
  21 +
  22 +.. attribute:: verbose_name
20 23  
21   - Describes the activated plugins and its configurations. It's necessary to describe
22   - for each app its name as the variable. The apps described here can be devided into
23   - two categories, that beeing, colab proxy apps and third-party apps.
24   - The upstream variable is only needed to colab proxy apps.
  24 +Delclare the description name of the plugin.
25 25  
26 26 .. attribute:: upstream
27 27  
28   -Declares the upstream server url of the proxy. Only declare if the plugin is a proxy.
  28 +Declares the upstream server url of the proxy. Only declare if the plugin is a
  29 +proxy.
29 30  
30   -dependecies
31   -+++++++++++
  31 +.. attribute:: middlewares
32 32  
33   - A list of the plugin dependecies that will be added to INSTALLED_APPS.
34   - This doesn't automatically install the python dependecies, only add to django apps.
  33 +Declares the middlewares of the plugin in a list format.
35 34  
  35 +.. attribute:: context_processors
36 36  
37   -menu
38   -++++
  37 +Declares the context processors of the plugin in a list format too.
39 38  
40   -.. attribute:: title
  39 +.. attribute:: dependency
41 40  
42   - Declares the menu title.
43   -.. attribute:: links
44   -
45   - Declares the menu items and its links.
46   -.. attribute:: auth_links
47   -
48   - Declares the menu items and its links when the user authenticated.
49   -.. attribute:: dependecies
50   -
51   -Declares a list of the plugin dependecies.
  41 +Declares the additional installed apps that this plugin depends on.
  42 +This doesn't automatically install the python dependecies, only add to django
  43 +apps.
52 44  
53 45 urls
54 46 ++++
... ... @@ -63,16 +55,35 @@ urls
63 55  
64 56 Declares the namespace for the url.
65 57  
66   -context_processors
67   -++++++++++++++++++
  58 +menu
  59 +++++
68 60  
69   - Declares the plugin context processors.
  61 +These variables defines the menu title and links of the plugin.
70 62  
71   -middlewares
72   -+++++++++++
  63 +.. attribute:: menu_title
  64 +
  65 + Declares the menu title.
  66 +.. attribute:: menu_links
  67 +
  68 + Declares the menu items and its links.
  69 + This should be a tuple object with several colab_url elements.
  70 + The colab_url_factory creates a factory for your links along with your
  71 + namespace.
  72 + The auth parameter indicates wether the link should only be displayed when
  73 + the user is logged in.
  74 +
  75 +Example:
  76 +
  77 +.. code-block:: python
73 78  
74   - Declares the plugin middlewares.
  79 + from colab.plugins.utils.menu import colab_url_factory
75 80  
  81 + url = colab_url_factory('plugin_app_name')
  82 +
  83 + menu_urls = (
  84 + url(display=_('Profile'), viewname='profile', kwargs={'path': '/profile/'}, auth=True),
  85 + url(display=_('Profile Two'), viewname='profile2', kwargs={'path': '/profile/2'}, auth=True),
  86 + )
76 87  
77 88 Extra Template Folders
78 89 ++++++++++++++++++++++
... ... @@ -113,12 +124,12 @@ SVN
113 124 .. TODO
114 125  
115 126 Social Networks
116   -++++
  127 ++++++++++++++++
117 128 .. attribute:: SOCIAL_NETWORK_ENABLED
118 129  
119 130 :default: False
120 131  
121   - When this variable is True, the social networks fields, like Facebook and
  132 + When this variable is True, the social networks fields, like Facebook and
122 133 Twitter, are added in user profile. By default, this fields are disabled.
123 134  
124 135 Auth
... ...
tests/config_settings.py 0 → 100644
... ... @@ -0,0 +1,11 @@
  1 +SECRET_KEY = 'ddddddddddddddddddddddddddddddddddddddddddddddddddddddaddddddddd'
  2 +
  3 +DATABASES = {
  4 + 'default': {
  5 + 'ENGINE': 'django.db.backends.postgresql_psycopg2',
  6 + 'HOST': 'localhost',
  7 + 'NAME': 'colab',
  8 + 'USER': 'colab',
  9 + 'PASSWORD': 'colab',
  10 + }
  11 +}
... ...
tests/plugins.d/gitlab.py 0 → 100644
... ... @@ -0,0 +1,39 @@
  1 +from django.utils.translation import ugettext_lazy as _
  2 +
  3 +name = 'colab.plugins.gitlab'
  4 +verbose_name = 'Gitlab Proxy'
  5 +
  6 +upstream = 'localhost'
  7 +#middlewares = []
  8 +
  9 +menu = {
  10 +'title': _('Code'),
  11 +'links': (
  12 + (_('Public Projects'), 'public/projects'),
  13 +),
  14 +'auth_links': (
  15 + (_('Profile'), 'profile'),
  16 + (_('New Project'), 'projects/new'),
  17 + (_('Projects'), 'dashboard/projects'),
  18 + (_('Groups'), 'profile/groups'),
  19 + (_('Issues'), 'dashboard/issues'),
  20 + (_('Merge Requests'), 'dashboard/merge_requests'),
  21 +
  22 +),
  23 +}
  24 +
  25 +
  26 +# dpaste:
  27 +# dependencies:
  28 +# - 'mptt'
  29 +# urls:
  30 +# include: 'dpaste.urls.dpaste'
  31 +# prefix: '^paste/'
  32 +# namespace: 'dpaste'
  33 +# menu:
  34 +# title: 'Dpaste'
  35 +# links:
  36 +# Public Projects: '/paste'
  37 +# auth_links:
  38 +# Profile: '/projects'
  39 +# New Project: '/projects/new'
... ...
tests/run.py
... ... @@ -4,7 +4,9 @@ import os
4 4 import sys
5 5  
6 6 os.environ['DJANGO_SETTINGS_MODULE'] = 'tests.settings'
7   -os.environ['COLAB_SETTINGS'] = 'tests/settings.yaml'
  7 +os.environ['COLAB_SETTINGS'] = 'tests/config_settings.py'
  8 +os.environ['COLAB_YAML_SETTINGS'] = 'tests/settings.yaml'
  9 +os.environ['COLAB_PLUGINS'] = 'tests/plugins.d'
8 10 os.environ['COVERAGE_PROCESS_START'] = '.coveragerc'
9 11 os.environ['REUSE_DB'] = '0'
10 12  
... ...
tests/settings.yaml
... ... @@ -1,73 +0,0 @@
1   -
2   -
3   -## Set to false in production
4   -DEBUG: true
5   -TEMPLATE_DEBUG: true
6   -
7   -## System admins
8   -ADMINS: &admin
9   - -
10   - - John Foo
11   - - john@example.com
12   - -
13   - - Mary Bar
14   - - mary@example.com
15   -
16   -MANAGERS: *admin
17   -
18   -COLAB_FROM_ADDRESS: '"Colab" <noreply@example.com>'
19   -SERVER_EMAIL: '"Colab" <noreply@example.com>'
20   -
21   -EMAIL_HOST: localhost
22   -EMAIL_PORT: 25
23   -EMAIL_SUBJECT_PREFIX: '[colab]'
24   -
25   -SECRET_KEY: 'hu8-)szdcjjsz%f02gt$5djbluxc$v0a%01l)di6oi)np7%8lu'
26   -
27   -# Must use it without trailing slash
28   -SITE_URL: 'http://localhost:8000'
29   -BROWSERID_AUDIENCES:
30   - - http://localhost:8000
31   -# - http://example.com
32   -# - https://example.org
33   -# - http://example.net
34   -
35   -ALLOWED_HOSTS:
36   - - localhost
37   -# - example.com
38   -# - example.org
39   -# - example.net
40   -
41   -### Uncomment to enable Broswer ID protocol for authentication
42   -# BROWSERID_ENABLED: True
43   -
44   -### Uncomment to enable Converse.js
45   -# CONVERSEJS_ENABLED: True
46   -
47   -### Uncomment to enable auto-registration
48   -# CONVERSEJS_AUTO_REGISTER: 'xmpp.example.com'
49   -
50   -## Database settings
51   -DATABASES:
52   - default:
53   - ENGINE: django.db.backends.postgresql_psycopg2
54   - HOST: localhost
55   - NAME: colab
56   - USER: colab
57   - PASSWORD: colab
58   -
59   -## Disable indexing
60   -ROBOTS_NOINDEX: false
61   -
62   -### Log errors to Sentry instance
63   -# RAVEN_DSN: 'http://public:secret@example.com/1'
64   -
65   -### Colab proxied apps
66   -COLAB_APPS:
67   - colab.plugins.gitlab:
68   - upstream: 'http://localhost:8090/gitlab/'
69   - private_token: ''
70   -# colab.plugins.trac:
71   -# upstream: 'http://localhost:5000/trac/'
72   -
73   -