summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorEvgeny Fadeev <evgeny.fadeev@gmail.com>2010-11-17 21:19:29 -0500
committerEvgeny Fadeev <evgeny.fadeev@gmail.com>2010-11-17 21:19:29 -0500
commit8a36f07e815659f3c14578e8d0c57fc75d3f887c (patch)
tree9ac2a2d2d839cb144dc753b2a94d0c0fef80a958
parentd3860a0092e075cf24180a3a8c68cfb07283f1df (diff)
downloadaskbot-8a36f07e815659f3c14578e8d0c57fc75d3f887c.tar.gz
askbot-8a36f07e815659f3c14578e8d0c57fc75d3f887c.tar.bz2
askbot-8a36f07e815659f3c14578e8d0c57fc75d3f887c.zip
made both media and templates resolve from custom skin directory, changed setting ASKBOT_EXTRA_SKIN_DIRS to ASKBOT_EXTRA_SKINS_DIR, now it has to be a string
-rw-r--r--askbot/__init__.py2
-rw-r--r--askbot/skins/loaders.py10
-rw-r--r--askbot/skins/utils.py138
-rw-r--r--askbot/urls.py79
-rw-r--r--askbot/views/meta.py14
5 files changed, 149 insertions, 94 deletions
diff --git a/askbot/__init__.py b/askbot/__init__.py
index 8098eeda..fb6dfad9 100644
--- a/askbot/__init__.py
+++ b/askbot/__init__.py
@@ -22,7 +22,7 @@ def get_version():
"""returns version of the askbot app
this version is meaningful for pypi only
"""
- return '0.6.29'
+ return '0.6.31'
#todo: maybe send_mail functions belong to models
#or the future API
diff --git a/askbot/skins/loaders.py b/askbot/skins/loaders.py
index 241897c9..f264c546 100644
--- a/askbot/skins/loaders.py
+++ b/askbot/skins/loaders.py
@@ -43,14 +43,8 @@ class SkinEnvironment(CoffinEnvironment):
"""
loaders = list()
skin_name = askbot_settings.ASKBOT_DEFAULT_SKIN
- skin_dirs = utils.get_skin_dirs()
-
- template_dirs = list()
- for dir in skin_dirs:
- template_dirs.append(os.path.join(dir, skin_name, 'templates'))
- if skin_name != 'default':
- for dir in skin_dirs:
- template_dirs.append(os.path.join(dir, 'default', 'templates'))
+ skin_dirs = utils.get_available_skins(selected = skin_name).values()
+ template_dirs = [os.path.join(skin_dir, 'templates') for skin_dir in skin_dirs]
loaders.append(jinja_loaders.FileSystemLoader(template_dirs))
return loaders
diff --git a/askbot/skins/utils.py b/askbot/skins/utils.py
index 259424da..b244da00 100644
--- a/askbot/skins/utils.py
+++ b/askbot/skins/utils.py
@@ -1,34 +1,90 @@
+"""utilities dealing with resolution of skin components
+
+the lookup resolution process for templates and media works as follows:
+* look up item in selected skin
+* if not found look in 'default'
+* the look in 'common'
+* raise an exception
+"""
import os
import logging
from django.conf import settings as django_settings
+from django.utils.datastructures import SortedDict
+
+class MediaNotFound(Exception):
+ """raised when media file is not found"""
+ pass
+
+def get_skins_from_dir(directory):
+ """returns sorted dict with skin data, like get_available_skins
+ but from a specific directory
+ """
+ skins = SortedDict()
+ for item in sorted(os.listdir(directory)):
+ item_dir = os.path.join(directory, item)
+ if os.path.isdir(item_dir):
+ skins[item] = item_dir
+ return skins
+
+def get_available_skins(selected=None):
+ """selected is a name of preferred skin
+ if it's None, then information about all skins will be returned
+ otherwise, only data about selected, default and common skins
+ will be returned
+
+ selected skin is guaranteed to be the first item in the dictionary
+
+ """
+ skins = SortedDict()
+ if hasattr(django_settings, 'ASKBOT_EXTRA_SKINS_DIR'):
+ skins.update(get_skins_from_dir(django_settings.ASKBOT_EXTRA_SKINS_DIR))
+
+ stock_dir = os.path.normpath(os.path.dirname(__file__))
+ stock_skins = get_skins_from_dir(stock_dir)
+ default_dir = stock_skins.pop('default')
+ common_dir = stock_skins.pop('common')
+
+ skins.update(stock_skins)
+
+ if selected:
+ if selected in skins:
+ selected_dir = skins[selected]
+ skins.clear()
+ skins[selected] = selected_dir
+ else:
+ assert(selected == 'default')
+ skins = SortedDict()
-def get_skin_dirs():
- #todo: handle case of multiple skin directories
- d = os.path.dirname
- n = os.path.normpath
- j = os.path.join
- f = os.path.isfile
- skin_dirs = []
- skin_dirs.append( n(j(d(d(__file__)), 'skins')) )
- if hasattr(django_settings, 'ASKBOT_EXTRA_SKIN_DIRS'):
- skin_dirs += django_settings.ASKBOT_EXTRA_SKIN_DIRS
- return skin_dirs
+ #re-insert default and common as last two items
+ skins['default'] = default_dir
+ skins['common'] = common_dir
+ return skins
+
+
+def get_path_to_skin(skin):
+ """returns path to directory in the list of
+ available skin directories that contains another
+ directory called skin
+
+ it is assumed that all skins are named uniquely
+ """
+ skin_dirs = get_available_skins()
+ return skin_dirs.get(skin, None)
def get_skin_choices():
- #todo: expand this to handle custom skin directories
- skin_list = ['default']
- for directory in get_skin_dirs():
- items = os.listdir(directory)
- for i in items:
- item_path = os.path.join(directory, i)
- if not os.path.isdir(item_path):
- continue
- if i == 'common':
- continue
- if i not in skin_list:
- skin_list.append(i)
-
- return [(i,i) for i in skin_list]
+ """returns a tuple for use as a set of
+ choices in the form"""
+ skin_names = list(reversed(get_available_skins().keys()))
+ skin_names.remove('common')
+ return zip(skin_names, skin_names)
+
+def resolve_skin_for_media(media=None, preferred_skin = None):
+ #see if file exists, if not, try skins 'default', then 'common'
+ available_skins = get_available_skins(selected = preferred_skin).items()
+ for skin_name, skin_dir in available_skins:
+ if os.path.isfile(os.path.join(skin_dir, 'media', media)):
+ return skin_name
+ raise MediaNotFound(media)
def get_media_url(url):
"""returns url prefixed with the skin name
@@ -38,6 +94,8 @@ def get_media_url(url):
if file is not found - returns None
and logs an error message
"""
+ #import datetime
+ #before = datetime.datetime.now()
url = unicode(url)
while url[0] == '/': url = url[1:]
#todo: handles case of multiple skin directories
@@ -66,7 +124,7 @@ def get_media_url(url):
else:
logging.critical('missing media resource %s' % url)
- #2) if it does not exist - look in skins
+ #2) if it does not exist in uploaded files directory - look in skins
#purpose of this try statement is to determine
#which skin is currently used
@@ -81,24 +139,14 @@ def get_media_url(url):
use_skin = 'default'
resource_revision = None
- skins = get_skin_dirs()[0]
-
- #see if file exists, if not, try skins 'default', then 'common'
- file_path = os.path.join(skins, use_skin, 'media', url)
- if not os.path.isfile(file_path):
- file_path = os.path.join(skins, 'default', 'media', url)
- if os.path.isfile(file_path):
- use_skin = 'default'
- else:
- file_path = os.path.join(skins, 'common', 'media', url)
- if os.path.isfile(file_path):
- use_skin = 'common'
- else:
- log_message = 'missing media resource %s in skin %s' \
- % (url, use_skin)
- logging.critical(log_message)
- use_skin = ''
- return None
+ #determine from which skin take the media file
+ try:
+ use_skin = resolve_skin_for_media(media=url, preferred_skin = use_skin)
+ except MediaNotFound, e:
+ log_message = 'missing media resource %s in skin %s' \
+ % (url, use_skin)
+ logging.critical(log_message)
+ return None
url = use_skin + '/media/' + url
url = '///' + django_settings.ASKBOT_URL + 'm/' + url
@@ -111,4 +159,6 @@ def get_media_url(url):
if resource_revision:
url += '?v=%d' % resource_revision
+ #after = datetime.datetime.now()
+ #print after - before
return url
diff --git a/askbot/urls.py b/askbot/urls.py
index 321010d1..7063b599 100644
--- a/askbot/urls.py
+++ b/askbot/urls.py
@@ -5,7 +5,7 @@ import os.path
from django.conf.urls.defaults import url, patterns, include
from django.conf.urls.defaults import handler500, handler404
from django.contrib import admin
-from askbot import views as app
+from askbot import views
from askbot.feed import RssLastestQuestionsFeed
from askbot.sitemap import QuestionsSitemap
from django.utils.translation import ugettext as _
@@ -21,7 +21,7 @@ sitemaps = {
APP_PATH = os.path.dirname(__file__)
urlpatterns = patterns('',
- url(r'^$', app.readers.index, name='index'),
+ url(r'^$', views.readers.index, name='index'),
url(
r'^sitemap.xml$',
'django.contrib.sitemaps.views.sitemap',
@@ -29,9 +29,8 @@ urlpatterns = patterns('',
name='sitemap'
),
url(
- r'^m/(?P<path>.*)$',
- 'django.views.static.serve',
- {'document_root': os.path.join(APP_PATH,'skins').replace('\\','/')},
+ r'^m/(?P<skin>[^/]+)/media/(?P<resource>.*)$',
+ views.meta.media,
name='askbot_media',
),
url(
@@ -40,159 +39,159 @@ urlpatterns = patterns('',
{'document_root': os.path.join(settings.PROJECT_ROOT, 'askbot', 'upfiles').replace('\\','/')},
name='uploaded_file',
),
- url(r'^%s$' % _('about/'), app.meta.about, name='about'),
- url(r'^%s$' % _('faq/'), app.meta.faq, name='faq'),
- url(r'^%s$' % _('privacy/'), app.meta.privacy, name='privacy'),
- url(r'^%s$' % _('logout/'), app.meta.logout, name='logout'),
+ url(r'^%s$' % _('about/'), views.meta.about, name='about'),
+ url(r'^%s$' % _('faq/'), views.meta.faq, name='faq'),
+ url(r'^%s$' % _('privacy/'), views.meta.privacy, name='privacy'),
+ url(r'^%s$' % _('logout/'), views.meta.logout, name='logout'),
url(
r'^%s(?P<id>\d+)/%s$' % (_('answers/'), _('comments/')),
- app.writers.answer_comments,
+ views.writers.answer_comments,
name='answer_comments'
),
url(
r'^%s(?P<id>\d+)/%s$' % (_('answers/'), _('edit/')),
- app.writers.edit_answer,
+ views.writers.edit_answer,
name='edit_answer'
),
url(
r'^%s(?P<id>\d+)/%s$' % (_('answers/'), _('revisions/')),
- app.readers.revisions,
+ views.readers.revisions,
kwargs = {'object_name': 'Answer'},
name='answer_revisions'
),
url(
r'^%s$' % _('questions/'),
- app.readers.questions,
+ views.readers.questions,
name='questions'
),
url(
r'^%s%s$' % (_('questions/'), _('ask/')),
- app.writers.ask,
+ views.writers.ask,
name='ask'
),
url(
r'^%s(?P<id>\d+)/%s$' % (_('questions/'), _('edit/')),
- app.writers.edit_question,
+ views.writers.edit_question,
name='edit_question'
),
url(
r'^%s(?P<id>\d+)/%s$' % (_('questions/'), _('retag/')),
- app.writers.retag_question,
+ views.writers.retag_question,
name='retag_question'
),
url(
r'^%s(?P<id>\d+)/%s$' % (_('questions/'), _('close/')),
- app.commands.close,
+ views.commands.close,
name='close'
),
url(
r'^%s(?P<id>\d+)/%s$' % (_('questions/'), _('reopen/')),
- app.commands.reopen,
+ views.commands.reopen,
name='reopen'
),
url(
r'^%s(?P<id>\d+)/%s$' % (_('questions/'), _('answer/')),
- app.writers.answer,
+ views.writers.answer,
name='answer'
),
url(
r'^%s(?P<id>\d+)/%s$' % (_('questions/'), _('vote/')),
- app.commands.vote,
+ views.commands.vote,
name='vote'
),
url(
r'^%s(?P<id>\d+)/%s$' % (_('questions/'), _('revisions/')),
- app.readers.revisions,
+ views.readers.revisions,
kwargs = {'object_name': 'Question'},
name='question_revisions'
),
url(
r'^%s(?P<id>\d+)/%s$' % (_('questions/'), _('comments/')),
- app.writers.question_comments,
+ views.writers.question_comments,
name='question_comments'
),
url(
r'^%s$' % _('command/'),
- app.commands.ajax_command,
+ views.commands.ajax_command,
name='call_ajax'
),
url(
r'^%s(?P<object_id>\d+)/%s(?P<comment_id>\d+)/%s$'\
% (_('questions/'), _('comments/'),_('delete/')),
- app.writers.delete_comment,
+ views.writers.delete_comment,
kwargs={'commented_object_type':'question'},
name='delete_question_comment'
),
url(
r'^%s(?P<object_id>\d+)/%s(?P<comment_id>\d+)/%s$'\
% (_('answers/'), _('comments/'),_('delete/')),
- app.writers.delete_comment,
+ views.writers.delete_comment,
kwargs={'commented_object_type':'answer'},
name='delete_answer_comment'
),
#place general question item in the end of other operations
url(
r'^%s(?P<id>\d+)/' % _('question/'),
- app.readers.question,
+ views.readers.question,
name='question'
),
url(
r'^%s$' % _('tags/'),
- app.readers.tags,
+ views.readers.tags,
name='tags'
),
url(
r'^%s%s(?P<tag>[^/]+)/$' % (_('mark-tag/'),_('interesting/')),
- app.commands.mark_tag,
+ views.commands.mark_tag,
kwargs={'reason':'good','action':'add'},
name='mark_interesting_tag'
),
url(
r'^%s%s(?P<tag>[^/]+)/$' % (_('mark-tag/'),_('ignored/')),
- app.commands.mark_tag,
+ views.commands.mark_tag,
kwargs={'reason':'bad','action':'add'},
name='mark_ignored_tag'
),
url(
r'^%s(?P<tag>[^/]+)/$' % _('unmark-tag/'),
- app.commands.mark_tag,
+ views.commands.mark_tag,
kwargs={'action':'remove'},
name='mark_ignored_tag'
),
url(
r'^%s$' % _('users/'),
- app.users.users,
+ views.users.users,
name='users'
),
#todo: rename as user_edit, b/c that's how template is named
url(
r'^%s(?P<id>\d+)/%s$' % (_('users/'), _('edit/')),
- app.users.edit_user,
+ views.users.edit_user,
name='edit_user'
),
url(
r'^%s(?P<id>\d+)/(?P<slug>.+)/$' % _('users/'),
- app.users.user,
+ views.users.user,
name='user_profile'
),
url(
r'^%s$' % _('badges/'),
- app.meta.badges,
+ views.meta.badges,
name='badges'
),
url(
r'^%s(?P<id>\d+)//*' % _('badges/'),
- app.meta.badge,
+ views.meta.badge,
name='badge'
),
url(
r'^%s%s$' % (_('messages/'), _('markread/')),
- app.commands.read_message,
+ views.commands.read_message,
name='read_message'
),
url(
r'^manage_inbox/$',
- app.commands.manage_inbox,
+ views.commands.manage_inbox,
name='manage_inbox'
),
url(
@@ -201,9 +200,9 @@ urlpatterns = patterns('',
{'feed_dict': feeds},
name='feeds'
),
- url( r'^%s$' % _('upload/'), app.writers.upload, name='upload'),
- url(r'^%s$' % _('search/'), app.readers.search, name='search'),
- url(r'^%s$' % _('feedback/'), app.meta.feedback, name='feedback'),
+ url( r'^%s$' % _('upload/'), views.writers.upload, name='upload'),
+ url(r'^%s$' % _('search/'), views.readers.search, name='search'),
+ url(r'^%s$' % _('feedback/'), views.meta.feedback, name='feedback'),
(r'^%s' % _('account/'), include('askbot.deps.django_authopenid.urls')),
(r'^i18n/', include('django.conf.urls.i18n')),
#url(r'^feeds/rss/$', RssLastestQuestionsFeed, name="latest_questions_feed"),
diff --git a/askbot/views/meta.py b/askbot/views/meta.py
index f4793ba7..d7ce7f4e 100644
--- a/askbot/views/meta.py
+++ b/askbot/views/meta.py
@@ -7,12 +7,14 @@ from django.shortcuts import render_to_response, get_object_or_404
from django.core.urlresolvers import reverse
from django.template import RequestContext
from django.http import HttpResponseRedirect, HttpResponse
-from askbot.forms import FeedbackForm
from django.core.urlresolvers import reverse
from django.utils.translation import ugettext as _
+from django.views import static
+from askbot.forms import FeedbackForm
from askbot.utils.forms import get_next_url
from askbot.models import Badge, Award
from askbot.skins.loaders import ENV
+from askbot import skins
import askbot
def generic_view(request, template = None):
@@ -128,3 +130,13 @@ def badge(request, id):
context = RequestContext(request, data)
return HttpResponse(template.render(context))
+def media(request, skin, resource):
+ """view that serves static media from any skin
+ uses django static serve view, where document root is
+ adjusted according to the current skin selection
+
+ in production this views should be by-passed via server configuration
+ for the better efficiency of serving static files
+ """
+ dir = skins.utils.get_path_to_skin(skin)
+ return static.serve(request, '/media/' + resource, document_root = dir)