]> git.openstreetmap.org Git - osqa.git/commitdiff
merge hernani -> trunk
authorjordan <jordan@0cfe37f9-358a-4d5e-be75-b63607b5c754>
Thu, 10 Mar 2011 22:22:06 +0000 (22:22 +0000)
committerjordan <jordan@0cfe37f9-358a-4d5e-be75-b63607b5c754>
Thu, 10 Mar 2011 22:22:06 +0000 (22:22 +0000)
git-svn-id: http://svn.osqa.net/svnroot/osqa/trunk@817 0cfe37f9-358a-4d5e-be75-b63607b5c754

24 files changed:
forum/authentication/__init__.py
forum/http_responses.py
forum/management/commands/module_command.py [new file with mode: 0644]
forum/models/question.py
forum/models/user.py
forum/modules/__init__.py
forum/modules/decorators.py
forum/modules/template_loader.py [new file with mode: 0644]
forum/registry.py
forum/skins/__init__.py
forum/templatetags/node_tags.py
forum/templatetags/ui_registry.py
forum/urls.py
forum/utils/userlinking.py
forum/views/auth.py
forum/views/meta.py
forum/views/readers.py
forum/views/vars.py [new file with mode: 0644]
forum/views/writers.py
forum_modules/openidauth/consumer.py
forum_modules/sximporter/importer.py
forum_modules/sximporter/views.py
settings.py
urls.py

index 8dbf825a525c2ab49c17a38834961ac6135706ce..79c47a8dd2e05908b96e2e7ea438a2cf72372576 100644 (file)
@@ -8,7 +8,8 @@ class ConsumerAndContext:
         self.id = id
         self._consumer = consumer
 
-        context.id = id
+        if context:
+            context.id = id
         self.context = context
 
     @property
@@ -27,7 +28,7 @@ contexts = dict([
         ])
 
 AUTH_PROVIDERS = dict([
-            (name, ConsumerAndContext(name, consumers[name], contexts[name])) for name in consumers.keys()
-            if name in contexts
+            (name, ConsumerAndContext(name, consumers[name], contexts.get(name, None))) for name in consumers.keys()
         ])
 
+
index 7fae3b69f3bd63b9d60fc93f91b733633e8af2a8..2ff4866fdb89a89e9efea173bb47e1f3e468ca9c 100644 (file)
@@ -24,4 +24,18 @@ class HttpResponseUnauthorized(HttpResponse):
             super(HttpResponseUnauthorized, self).__init__(
                 content=render_to_string('401.html', context_instance=RequestContext(request)),
                 status=401
-            )
\ No newline at end of file
+            )
+
+class HttpResponseNotFound(HttpResponse):
+    def __init__(self, request):
+        super(HttpResponseNotFound, self).__init__(
+            content=render_to_string('404.html', context_instance=RequestContext(request)),
+            status=404
+        )
+
+class HttpResponseIntServerError(HttpResponse):
+    def __init__(self, request):
+        super(HttpResponseIntServerError, self).__init__(
+            content=render_to_string('500.html', context_instance=RequestContext(request)),
+            status=500
+        )
\ No newline at end of file
diff --git a/forum/management/commands/module_command.py b/forum/management/commands/module_command.py
new file mode 100644 (file)
index 0000000..378d15a
--- /dev/null
@@ -0,0 +1,13 @@
+from django.core.management.base import BaseCommand, CommandError
+
+class Command(BaseCommand):
+
+    def handle(self, *args, **options):
+
+        for path in args:
+            m = __import__('forum_modules.%s' % path, globals(), locals(), ['forum_modules'])
+
+            if hasattr(m, 'run'):
+                run = getattr(m, 'run')
+                if callable(run):
+                    run()
index bef5bb511b544546ca8acf21ea4746ad4196efb5..ef4a37d814390a25f4de23734991570947db755c 100644 (file)
@@ -2,8 +2,6 @@ from base import *
 from tag import Tag
 from django.utils.translation import ugettext as _
 
-question_view = django.dispatch.Signal(providing_args=['instance', 'user'])
-
 class QuestionManager(NodeManager):
     def search(self, keywords):
         return False, self.filter(models.Q(title__icontains=keywords) | models.Q(body__icontains=keywords))
@@ -78,12 +76,6 @@ class Question(Node):
         
         return active_users
 
-def question_viewed(instance, **kwargs):
-    instance.extra_count += 1
-    instance.save()
-
-question_view.connect(question_viewed)
-
 
 class QuestionSubscription(models.Model):
     user = models.ForeignKey(User)
index 9d6324875f9273c6d9ab5da77290f7bb5c4645e9..d96426e2ad909db291bb6140d5377cda70686c5d 100644 (file)
@@ -15,12 +15,6 @@ from random import Random
 from django.utils.translation import ugettext as _
 import logging
 
-QUESTIONS_PER_PAGE_CHOICES = (
-(10, u'10'),
-(30, u'30'),
-(50, u'50'),
-)
-
 class AnonymousUser(DjangoAnonymousUser):
     reputation = 0
     
@@ -188,9 +182,6 @@ class User(BaseModel, DjangoUser):
             sub_settings = SubscriptionSettings(user=self)
             sub_settings.save()
 
-    def get_absolute_url(self):
-        return self.get_profile_url()
-
     def get_messages(self):
         messages = []
         for m in self.message_set.all():
index cbac6eddcad1bc2a5951da0d53e61aea2c0e1313..976d9eb448a05fd2b2fa78e741ee9e4c59ae8993 100644 (file)
@@ -1,29 +1,32 @@
 import os
 import types
-import re
 import logging
 
-from django.template import Template, TemplateDoesNotExist
-from django.conf import settings
-
 MODULES_PACKAGE = 'forum_modules'
 
-MODULES_FOLDER = os.path.join(os.path.dirname(__file__), '../../' + MODULES_PACKAGE)
+MODULES_FOLDER = None
+MODULE_LIST = []
+
 
-DISABLED_MODULES = getattr(settings, 'DISABLED_MODULES', [])
+def init_modules_engine(site_src_root, disabled_modules):
+    MODULES_FOLDER = os.path.join(site_src_root, MODULES_PACKAGE)
 
-MODULE_LIST = filter(lambda m: getattr(m, 'CAN_USE', True), [
-        __import__('forum_modules.%s' % f, globals(), locals(), ['forum_modules'])
-        for f in os.listdir(MODULES_FOLDER)
-        if os.path.isdir(os.path.join(MODULES_FOLDER, f)) and
-           os.path.exists(os.path.join(MODULES_FOLDER, "%s/__init__.py" % f)) and
-           not f in DISABLED_MODULES
-])
+    MODULE_LIST.extend(filter(lambda m: getattr(m, 'CAN_USE', True), [
+            __import__('forum_modules.%s' % f, globals(), locals(), ['forum_modules'])
+            for f in os.listdir(MODULES_FOLDER)
+            if os.path.isdir(os.path.join(MODULES_FOLDER, f)) and
+               os.path.exists(os.path.join(MODULES_FOLDER, "%s/__init__.py" % f)) and
+               not f in disabled_modules
+    ]))
 
 def get_modules_script(script_name):
     all = []
 
     for m in MODULE_LIST:
+        if hasattr(m, script_name):
+            all.append(getattr(m, script_name))
+            continue
+
         try:
             all.append(__import__('%s.%s' % (m.__name__, script_name), globals(), locals(), [m.__name__]))
         except ImportError, e:
@@ -32,7 +35,7 @@ def get_modules_script(script_name):
         except:
             import traceback
             msg = "Error importing %s from module %s: \n %s" % (
-                script_name, m.__name__, traceback.format_exc()
+                script_name, m, traceback.format_exc()
             )
             logging.error(msg)
 
@@ -88,25 +91,4 @@ def get_handler(name, default):
     all = get_all_handlers(name)
     return len(all) and all[0] or default
 
-module_template_re = re.compile('^modules\/(\w+)\/(.*)$')
-
-def module_templates_loader(name, dirs=None):
-    result = module_template_re.search(name)
-
-    if result is not None:
-        file_name = os.path.join(MODULES_FOLDER, result.group(1), 'templates', result.group(2))
-
-        if os.path.exists(file_name):
-            try:
-                f = open(file_name, 'r')
-                source = f.read()
-                f.close()
-                return (source, file_name)
-            except:
-                pass
-
-    raise TemplateDoesNotExist, name 
-
-module_templates_loader.is_usable = True
-
 from decorators import decorate, ReturnImediatelyException
index 3c53412dc0505c4c92bbe183a6709ca007dc5a34..11af9e3d1c4c1cd49e60b04db024a9344f7269f9 100644 (file)
@@ -69,7 +69,7 @@ class ReturnImediatelyException(Exception):
         self.ret = ret\r
 \r
 def _check_decoratable(origin, install=True):\r
-    if not isinstance(origin, DecoratableObject):\r
+    if not hasattr(origin, '_decoratable_obj'):\r
         if inspect.ismethod(origin) and not hasattr(origin, '_decoratable_obj'):\r
             decoratable = DecoratableObject(origin)\r
 \r
@@ -91,10 +91,18 @@ def _check_decoratable(origin, install=True):
         elif inspect.isfunction(origin):\r
             decoratable = DecoratableObject(origin)\r
 \r
+            def decorated(*args, **kwargs):\r
+                return decoratable(*args, **kwargs)\r
+\r
+            decorated._decoratable_obj = decoratable\r
+\r
             if install:\r
-                setattr(inspect.getmodule(origin), origin.__name__, decoratable)\r
+                setattr(inspect.getmodule(origin), origin.__name__, decorated)\r
+\r
+            decorated.__name__ = origin.__name__\r
+            decorated.__module__ = origin.__module__\r
 \r
-            return decoratable\r
+            return decorated\r
 \r
     return origin\r
 \r
@@ -103,7 +111,7 @@ def decorate(origin, needs_origin=True):
     origin = _check_decoratable(origin)\r
 \r
     def decorator(fn):\r
-        origin._decorate(fn, DecoratableObject.MODE_OVERRIDE, needs_origin=needs_origin)\r
+        origin._decoratable_obj._decorate(fn, DecoratableObject.MODE_OVERRIDE, needs_origin=needs_origin)\r
         \r
     return decorator\r
 \r
@@ -112,7 +120,7 @@ def _decorate_params(origin):
     origin = _check_decoratable(origin)\r
 \r
     def decorator(fn):\r
-        origin._decorate(fn, DecoratableObject.MODE_PARAMS)\r
+        origin._decoratable_obj._decorate(fn, DecoratableObject.MODE_PARAMS)\r
 \r
     return decorator\r
 \r
@@ -122,7 +130,7 @@ def _decorate_result(origin, needs_params=False):
     origin = _check_decoratable(origin)\r
 \r
     def decorator(fn):\r
-        origin._decorate(fn, DecoratableObject.MODE_RESULT, needs_params=needs_params)\r
+        origin._decoratable_obj._decorate(fn, DecoratableObject.MODE_RESULT, needs_params=needs_params)\r
 \r
     return decorator\r
 \r
@@ -131,7 +139,7 @@ decorate.result = _decorate_result
 def _decorate_with(fn):\r
     def decorator(origin):\r
         origin = _check_decoratable(origin)\r
-        origin._decorate(fn, DecoratableObject.MODE_OVERRIDE, needs_origin=True)\r
+        origin._decoratable_obj._decorate(fn, DecoratableObject.MODE_OVERRIDE, needs_origin=True)\r
         return origin\r
     return decorator\r
 \r
@@ -140,7 +148,7 @@ decorate.withfn = _decorate_with
 def _decorate_result_with(fn, needs_params=False):\r
     def decorator(origin):\r
         origin = _check_decoratable(origin)\r
-        origin._decorate(fn, DecoratableObject.MODE_RESULT, needs_params=needs_params)\r
+        origin._decoratable_obj._decorate(fn, DecoratableObject.MODE_RESULT, needs_params=needs_params)\r
         return origin\r
     return decorator\r
 \r
@@ -149,7 +157,7 @@ decorate.result.withfn = _decorate_result_with
 def _decorate_params_with(fn):\r
     def decorator(origin):\r
         origin = _check_decoratable(origin)\r
-        origin._decorate(fn, DecoratableObject.MODE_PARAMS)\r
+        origin._decoratable_obj._decorate(fn, DecoratableObject.MODE_PARAMS)\r
         return origin\r
     return decorator\r
 \r
diff --git a/forum/modules/template_loader.py b/forum/modules/template_loader.py
new file mode 100644 (file)
index 0000000..61cb44e
--- /dev/null
@@ -0,0 +1,44 @@
+import os, re
+
+from forum.skins import load_template_source as skins_template_loader, Template, BaseTemplateLoader
+from forum import modules
+
+MODULES_TEMPLATE_PREFIX = 'modules/'
+NO_OVERRIDE_TEMPLATE_PREFIX = 'no_override/'
+MODULES_TEMPLATE_FOLDER = 'templates'
+MODULES_TEMPLATE_OVERRIDES_FOLDER = 'template_overrides'
+
+TEMPLATE_OVERRIDE_LOOKUP_PATHS = [f for f in [
+        os.path.join(os.path.dirname(m.__file__), MODULES_TEMPLATE_OVERRIDES_FOLDER) for m in modules.MODULE_LIST
+    ] if os.path.exists(f)
+]
+
+class ModulesTemplateLoader(BaseTemplateLoader):
+
+    modules_re = re.compile('^%s(\w+)\/(.*)$' % MODULES_TEMPLATE_PREFIX)
+
+    def load_template_source(self, name, dirs=None):
+        template = None
+
+        if name.startswith(MODULES_TEMPLATE_PREFIX):
+            match = self.modules_re.search(name)
+            file_name = os.path.join(modules.MODULES_FOLDER, match.group(1), MODULES_TEMPLATE_FOLDER, match.group(2))
+
+            if os.path.exists(file_name):
+                template = Template(file_name)
+
+        elif name.startswith(NO_OVERRIDE_TEMPLATE_PREFIX):
+            return skins_template_loader.load_template_source(name[len(NO_OVERRIDE_TEMPLATE_PREFIX):], dirs)
+
+        else:
+            for override_path in TEMPLATE_OVERRIDE_LOOKUP_PATHS:
+                file_name = os.path.join(override_path, name)
+
+                if os.path.exists(file_name):
+                    template = Template(file_name)
+                    break
+
+
+        return template
+
+module_templates_loader = ModulesTemplateLoader()
index ff5a1157941eea91d21b19f5822fd89329bf6921..aedf5b0d1b0e1dbcc4d773ff8a4d4c3a021cfb87 100644 (file)
@@ -1,12 +1,19 @@
-from forum.modules import ui
+from forum.modules import ui, get_modules_script
 from django.utils.translation import ugettext as _
 from django.core.urlresolvers import reverse
 from django.template.defaultfilters import slugify
+from django.template import get_templatetags_modules
 from forum.templatetags.extra_tags import get_score_badge
 from forum.utils.html import cleanup_urls
 from forum import settings
 
 
+modules_template_tags = get_modules_script('templatetags')
+django_template_tags = get_templatetags_modules()
+
+for m in modules_template_tags:
+    django_template_tags.append(m.__name__)
+
 ui.register(ui.HEADER_LINKS,
             ui.Link(_('faq'), ui.Url('faq'), weight=400),
             ui.Link(_('about'), ui.Url('about'), weight=300),
index 7a7e0cdb7ca1d20624889f08c9586f55b857a117..33e0097cc147f15054f95ec997ffb596f0b47ef0 100644 (file)
 from django.conf import settings
-from django.template import loader
-from django.template.loaders import filesystem 
-from django.http import HttpResponse
+from django.template.loaders import filesystem
+from django.template import TemplateDoesNotExist, Template as DJTemplate
+from django.conf import settings as djsettings
 import os.path
 import logging
 
-#module for skinning osqa
-#at this point skin can be changed only in settings file
-#via OSQA_DEFAULT_SKIN variable
+UNEXISTENT_TEMPLATE = object()
 
-#note - Django template loaders use method django.utils._os.safe_join
-#to work on unicode file paths
-#here it is ignored because it is assumed that we won't use unicode paths
+SKINS_FOLDER = os.path.dirname(__file__)
+SKIN_TEMPLATES_FOLDER = 'templates'
+DEFAULT_SKIN_NAME = 'default'
+FORCE_DEFAULT_PREFIX = "%s/" % DEFAULT_SKIN_NAME
+
+
+class Template(object):
+
+    def __init__(self, file_name):
+        self._file_name = file_name
+        self._loaded = False
+
+    def _get_mtime(self):
+        return os.path.getmtime(self._file_name)
+
+    def _check_mtime(self):
+        if self._last_mtime is None:
+            return False
+
+        return self._last_mtime == self._get_mtime()
+
+    def _load(self):
+        try:
+            f = open(self._file_name, 'r')
+            self._source = f.read()
+            f.close()
+            self._loaded = True
+
+            self._last_mtime = self._get_mtime()
+        except:
+            self._loaded = False
+            self._last_mtime = None
+
+            raise
+
+    def return_tuple(self):
+        if not (self._loaded and self._check_mtime()):
+            try:
+                self._load()
+            except:
+                raise TemplateDoesNotExist, self._file_name
+
+        return self._source, self._file_name
+
+class BaseTemplateLoader(object):
+    is_usable = True
+
+    def __init__(self):
+        self.cache = {}
+
+    def __call__(self, name=None, dirs=None):
+        if name is None:
+            return self
+
+        return self.load_template(name, dirs)
+
+    def load_template(self, name, dirs=None):
+        if not djsettings.TEMPLATE_DEBUG:
+            if name in self.cache:
+                if self.cache[name] is UNEXISTENT_TEMPLATE:
+                    raise TemplateDoesNotExist, name
+
+                try:
+                    return self.cache[name].return_tuple()
+                except:
+                    del self.cache[name]
+
+        template = self.load_template_source(name, dirs)
+
+        if template is not None:
+            if not djsettings.DEBUG:
+                self.cache[name] = template
+
+            return template.return_tuple()
+        else:
+            if not djsettings.DEBUG:
+                self.cache[name] = UNEXISTENT_TEMPLATE
+
+            raise TemplateDoesNotExist, name
+
+    def load_template_source(self, name, dirs=None):
+        raise NotImplementedError
+
+
+class SkinsTemplateLoader(BaseTemplateLoader):
+
+    def load_template_source(self, name, dirs=None):
+
+        if name.startswith(FORCE_DEFAULT_PREFIX):
+
+            file_name = os.path.join(SKINS_FOLDER, DEFAULT_SKIN_NAME, SKIN_TEMPLATES_FOLDER, name[len(FORCE_DEFAULT_PREFIX):])
+
+            if os.path.exists(file_name):
+                return Template(file_name)
+            else:
+                return None
+
+        for skin in (settings.OSQA_DEFAULT_SKIN, DEFAULT_SKIN_NAME):
+            file_name = os.path.join(SKINS_FOLDER, skin, SKIN_TEMPLATES_FOLDER, name)
+
+            if os.path.exists(file_name):
+                return Template(file_name)
+
+        return None
+
+load_template_source = SkinsTemplateLoader()
 
-def load_template_source(name, dirs=None):
-    try:
-        tname = os.path.join(settings.OSQA_DEFAULT_SKIN,'templates',name)
-        return filesystem.load_template_source(tname,dirs)
-    except:
-        tname = os.path.join('default','templates',name)
-        return filesystem.load_template_source(tname,dirs)
-load_template_source.is_usable = True
 
 def find_media_source(url):
     """returns url prefixed with the skin name
@@ -56,5 +149,3 @@ def find_media_source(url):
                 return None
     return use_skin + '/' + url
 
-
-
index 360ffbf5e3a9d4119c120d540c6727f60e78c2d7..18e4522066a4c31e3d00ae19dae8ee8c1b968e51 100644 (file)
@@ -212,10 +212,10 @@ def comments(post, user):
 \r
 \r
 @register.inclusion_tag("node/contributors_info.html")\r
-def contributors_info(node):\r
+def contributors_info(node, verb=None):\r
     return {\r
-        'node_verb': (node.node_type == "question") and _("asked") or (\r
-                    (node.node_type == "answer") and _("answered") or _("posted")),\r
+        'node_verb': verb and verb or ((node.node_type == "question") and _("asked") or (\r
+                    (node.node_type == "answer") and _("answered") or _("posted"))),\r
         'node': node,\r
     }\r
 \r
index 21ba3bbdc0436ff2b2300df20e39252ec67ff62d..8b28e2c67c1aa063c96c95c168b5003ce813653d 100644 (file)
@@ -1,4 +1,5 @@
 from django import template
+from django.conf import settings
 from forum.modules import ui
 import logging
 
@@ -21,10 +22,11 @@ class LoadRegistryNode(template.Node):
                             result += separator
                         result += ui_object.render(context)
             except (KeyError, Exception), e:
-                import traceback
-                logging.error("Exception %s rendering ui objects %s: \n%s" % (
-                    e, ui_object, traceback.format_exc()
-                ))
+                if settings.DEBUG:
+                    import traceback
+                    logging.error("Exception %s rendering ui objects %s: \n%s" % (
+                        e, ui_object, traceback.format_exc()
+                    ))
 
         return result
 
index 209b3ea28cc0cd235b186c677005787b52978e56..fc64e28ac953c980a2dcb1272bd1ccd70c0f9a70 100644 (file)
@@ -216,5 +216,6 @@ urlpatterns += patterns('',
 
                         url(r'^feeds/rss[/]?$', app.readers.feed, name="latest_questions_feed"),
 
-                        url(r'^(?P<path>.+)$', app.meta.page, name="static_page"),
+                        #url(r'^.+$', app.meta.page, name="static_page"),
                         )
+
index da476ee9cc9bf58c07d5f1d34ecb74e1b07aee13..53a49dc05ecedab60e8153ed10639bc6026ed86d 100644 (file)
@@ -31,6 +31,8 @@ def auto_user_link(node, content):
             question = node.question
         elif node.answer:
             question = node.answer.question
+    else:
+        return content
     
     # Now we've got the root question. Let's get the list of active users.
     active_users = question.get_active_users()
index f8eb897d06b5ea15a44cd6c488bc86b63f55a271..38d86b2111ae4b146c1d38d4689768390949463f 100644 (file)
@@ -38,9 +38,9 @@ def signin_page(request):
     # If the referer is equal to the sign up page, e. g. if the previous login attempt was not successful we do not
     # change the sign in URL. The user should go to the same page.
     if not referer.replace(settings.APP_URL, '') == reverse('auth_signin'):
-        request.session['on_signin_url'] = referer
+        request.session[ON_SIGNIN_SESSION_ATTR] = referer
 
-    all_providers = [provider.context for provider in AUTH_PROVIDERS.values()]
+    all_providers = [provider.context for provider in AUTH_PROVIDERS.values() if provider.context]
 
     sort = lambda c1, c2: c1.weight - c2.weight
     can_show = lambda c: not request.user.is_authenticated() or c.show_to_logged_in_user
@@ -146,7 +146,7 @@ def process_provider_signin(request, provider):
             assoc = AuthKeyUserAssociation.objects.get(key=assoc_key)
             user_ = assoc.user
             return login_and_forward(request, user_)
-        except:
+        except AuthKeyUserAssociation.DoesNotExist:
             request.session['assoc_key'] = assoc_key
             request.session['auth_provider'] = provider
             return HttpResponseRedirect(reverse('auth_external_register'))
@@ -225,7 +225,7 @@ def external_register(request):
     return render_to_response('auth/complete.html', {
     'form1': form1,
     'email_feeds_form': email_feeds_form,
-    'provider':mark_safe(provider_context.human_name),
+    'provider':provider_context and mark_safe(provider_context.human_name) or _('unknown'),
     'login_type':provider_context.id,
     'gravatar_faq_url':reverse('faq') + '#gravatar',
     }, context_instance=RequestContext(request))
@@ -391,14 +391,14 @@ def login_and_forward(request, user, forward=None, message=None):
     request.user.message_set.create(message=message)
 
     if not forward:
-        forward = request.session.get('on_signin_url', reverse('index'))
+        forward = request.session.get(ON_SIGNIN_SESSION_ATTR, reverse('index'))
 
-    pending_data = request.session.get('pending_submission_data', None)
+    pending_data = request.session.get(PENDING_SUBMISSION_SESSION_ATTR, None)
 
     if pending_data and (user.email_isvalid or pending_data['type'] not in settings.REQUIRE_EMAIL_VALIDATION_TO):
         submission_time = pending_data['time']
         if submission_time < datetime.datetime.now() - datetime.timedelta(minutes=int(settings.HOLD_PENDING_POSTS_MINUTES)):
-            del request.session['pending_submission_data']
+            del request.session[PENDING_SUBMISSION_SESSION_ATTR]
         elif submission_time < datetime.datetime.now() - datetime.timedelta(minutes=int(settings.WARN_PENDING_POSTS_MINUTES)):
             user.message_set.create(message=(_("You have a %s pending submission.") % pending_data['data_name']) + " %s, %s, %s" % (
                 html.hyperlink(reverse('manage_pending_data', kwargs={'action': _('save')}), _("save it")),
index 536cf2603515cee8a6cfda653450691e57eff7a9..a0c5cede68f84b5d49c2c6cae90e0ee2abf489f7 100644 (file)
@@ -15,12 +15,13 @@ from django.db.models import Count
 from forum.forms import get_next_url
 from forum.models import Badge, Award, User, Page
 from forum.badges.base import BadgesMeta
+from forum.http_responses import HttpResponseNotFound, HttpResponseIntServerError
 from forum import settings
 from forum.utils.mail import send_template_email
 from django.utils.safestring import mark_safe
 from forum.templatetags.extra_filters import or_preview
 import decorators
-import re
+import re, sys, logging, traceback
 
 def favicon(request):
     return HttpResponseRedirect(str(settings.APP_FAVICON))
@@ -113,17 +114,19 @@ def badge(request, id, slug):
     'badge' : badge,
     }, context_instance=RequestContext(request))
 
-def page(request, path):
+def page(request):
+    path = request.path[1:]
+
     if path in settings.STATIC_PAGE_REGISTRY:
         try:
             page = Page.objects.get(id=settings.STATIC_PAGE_REGISTRY[path])
 
             if (not page.published) and (not request.user.is_superuser):
-                raise Http404
+                return HttpResponseNotFound(request)
         except:
-            raise Http404
+            return HttpResponseNotFound(request)
     else:
-        raise Http404
+        return HttpResponseNotFound(request)
 
     template = page.extra.get('template', 'default')
     sidebar = page.extra.get('sidebar', '')
@@ -160,3 +163,39 @@ def page(request, path):
     }, context_instance=RequestContext(request))
 
 
+def error_handler(request):
+
+    stacktrace = "".join(["\t\t%s\n" % l for l in traceback.format_exc().split("\n")])
+
+    try:
+        log_msg = """
+        error executing request:
+        PATH: %(path)s
+        USER: %(user)s
+        METHOD: %(method)s
+        POST PARAMETERS:
+        %(post)s
+        GET PARAMETERS:
+        %(get)s
+        HTTP HEADERS:
+        %(headers)s
+        COOKIES:
+        %(cookies)s
+        EXCEPTION INFO:
+        %(stacktrace)s
+        """ % {
+            'path': request.path,
+            'user': request.user.is_authenticated() and ("%s (%s)" % (request.user.username, request.user.id)) or "<anonymous>",
+            'method': request.method,
+            'post': request.POST and "".join(["\t\t%s: %s\n" % (k, v) for k, v in request.POST.items()]) or "None",
+            'get': request.GET and "".join(["\t\t%s: %s\n" % (k, v) for k, v in request.GET.items()]) or "None",
+            'cookies': request.COOKIES and "".join(["\t\t%s: %s\n" % (k, v) for k, v in request.COOKIES.items()]) or "None",
+            'headers': request.META and "".join(["\t\t%s: %s\n" % (k, v) for k, v in request.META.items()]) or "None",
+            'stacktrace': stacktrace
+        }
+    except:
+        log_msg = "error executing request:\n%s" % stacktrace
+
+
+    logging.error(log_msg)
+    return HttpResponseIntServerError(request)
index 9a6f66eaf2caaa78ec8439ea4e8715cad8948201..69edf3cfa9243df64602542f1ab8ec7af775ba33 100644 (file)
@@ -238,16 +238,14 @@ def tags(request):
     })
 
 def update_question_view_times(request, question):
-    if not 'last_seen_in_question' in request.session:
-        request.session['last_seen_in_question'] = {}
+    last_seen_in_question = request.session.get('last_seen_in_question', {})
 
-    last_seen = request.session['last_seen_in_question'].get(question.id, None)
+    last_seen = last_seen_in_question.get(question.id, None)
 
-    if (not last_seen) or last_seen < question.last_activity_at:
+    if (not last_seen) or (last_seen < question.last_activity_at):
         QuestionViewAction(question, request.user, ip=request.META['REMOTE_ADDR']).save()
-        request.session['last_seen_in_question'][question.id] = datetime.datetime.now()
-
-    request.session['last_seen_in_question'][question.id] = datetime.datetime.now()
+        last_seen_in_question[question.id] = datetime.datetime.now()
+        request.session['last_seen_in_question'] = last_seen_in_question
 
 def match_question_slug(id, slug):
     slug_words = slug.split('-')
diff --git a/forum/views/vars.py b/forum/views/vars.py
new file mode 100644 (file)
index 0000000..3ac5c2c
--- /dev/null
@@ -0,0 +1,2 @@
+ON_SIGNIN_SESSION_ATTR = 'on_signin_url'
+PENDING_SUBMISSION_SESSION_ATTR = 'pending_submission_data'
index dbc68c017c4c583160e453031e2400fef4b013d6..a5c5d704f817f926f28d5328842ed72ad02494e9 100644 (file)
@@ -18,6 +18,7 @@ from forum.models import *
 from forum.forms import get_next_url
 from forum.utils import html
 
+from vars import PENDING_SUBMISSION_SESSION_ATTR
 
 def upload(request):#ajax upload file to a question or answer
     class FileTypeNotAllow(Exception):
@@ -84,7 +85,7 @@ def ask(request):
 
                     return HttpResponseRedirect(question.get_absolute_url())
                 else:
-                    request.session['pending_submission_data'] = {
+                    request.session[PENDING_SUBMISSION_SESSION_ATTR] = {
                         'POST': request.POST,
                         'data_name': _("question"),
                         'type': 'ask',
@@ -233,7 +234,7 @@ def answer(request, id):
 
             return HttpResponseRedirect(answer.get_absolute_url())
         else:
-            request.session['pending_submission_data'] = {
+            request.session[PENDING_SUBMISSION_SESSION_ATTR] = {
                 'POST': request.POST,
                 'data_name': _("answer"),
                 'type': 'answer',
@@ -253,7 +254,7 @@ def answer(request, id):
 
 
 def manage_pending_data(request, action, forward=None):
-    pending_data = request.session.pop('pending_submission_data', None)
+    pending_data = request.session.pop(PENDING_SUBMISSION_SESSION_ATTR, None)
 
     if not pending_data:
         raise Http404
index cc1e97d78ef30c75bef87fd9258d623efd3fd5eb..4c3818cbc41c47c6fa43e2cfe6b20dcc551a003f 100644 (file)
@@ -26,6 +26,13 @@ class OpenIdAbstractAuthConsumer(AuthenticationConsumer):
         #'birthdate': 'http://axschema.org/birthDate',
     }
 
+    sreg_attributes = {
+        "required": {
+            "email": "email",
+            "nickname": "username"
+        }
+    }
+
     def get_user_url(self, request):
         try:
             return request.POST['openid_identifier']
@@ -50,23 +57,26 @@ class OpenIdAbstractAuthConsumer(AuthenticationConsumer):
         except DiscoveryFailure:
             raise InvalidAuthentication(_('Sorry, but your input is not a valid OpenId'))
 
-        #sreg = getattr(settings, 'OPENID_SREG', False)
+        sreg = getattr(self, 'sreg_attributes', False)
+
+        if sreg:
+            s = SRegRequest()
+
+            for k, attr_dic in sreg.items():
+                if k == "policy_url":
+                    s.policy_url = attr_dic
+                    continue
 
-        #if sreg:
-        #    s = SRegRequest()
-        #    for sarg in sreg:
-        #        if sarg.lower().lstrip() == "policy_url":
-        #            s.policy_url = sreg[sarg]
-        #        else:
-        #            for v in sreg[sarg].split(','):
-        #                s.requestField(field_name=v.lower().lstrip(), required=(sarg.lower().lstrip() == "required"))
-        #    auth_request.addExtension(s)
+                for attr_name in attr_dic.keys():
+                    s.requestField(field_name=attr_name, required=(k == "required"))
 
-        #auth_request.addExtension(SRegRequest(required=['email']))
+            auth_request.addExtension(s)
 
-        if request.session.get('force_email_request', True):
+        ax_schema = getattr(self, 'dataype2ax_schema', False)
+
+        if ax_schema and request.session.get('force_email_request', True):
             axr = AXFetchRequest()
-            for data_type, schema in self.dataype2ax_schema.items():
+            for data_type, schema in ax_schema.items():
                 if isinstance(schema, tuple):
                     axr.add(AttrInfo(schema[0], 1, True, schema[1]))
                 else:
@@ -94,31 +104,41 @@ class OpenIdAbstractAuthConsumer(AuthenticationConsumer):
         openid_response = consumer.complete(query_dict, url)
 
         if openid_response.status == SUCCESS:
-            if request.session.get('force_email_request', True):
-                try:
-                    ax = AXFetchResponse.fromSuccessResponse(openid_response)
 
-                    axargs = ax.getExtensionArgs()
+            consumer_data = {}
+
+            sreg_attrs = getattr(self, 'sreg_attributes', False)
+
+            if sreg_attrs:
+                sreg_response = SRegResponse.fromSuccessResponse(openid_response)
 
-                    ax_schema2data_type = dict([(s, t) for t, s in self.dataype2ax_schema.items()])
+                all_attrs = {}
+                [all_attrs.update(d) for k,d in sreg_attrs.items() if k != "policy_url"]
 
-                    available_types = dict([
-                        (ax_schema2data_type[s], re.sub('^type\.', '', n))
-                        for n, s in axargs.items() if s in ax_schema2data_type
-                    ])
+                for attr_name, local_name in all_attrs.items():
+                    if attr_name in sreg_response:
+                        consumer_data[local_name] = sreg_response[attr_name]
 
-                    available_data = dict([
-                        (t, axargs["value.%s.1" % s]) for t, s in available_types.items()
-                    ])
+            ax_schema = getattr(self, 'dataype2ax_schema', False)
+
+            if ax_schema:
+                ax = AXFetchResponse.fromSuccessResponse(openid_response)
+
+                axargs = ax.getExtensionArgs()
+
+                ax_schema2data_type = dict([(s, t) for t, s in ax_schema.items()])
+
+                available_types = dict([
+                    (ax_schema2data_type[s], re.sub('^type\.', '', n))
+                    for n, s in axargs.items() if s in ax_schema2data_type
+                ])
+
+                for t, s in available_types.items():
+                    if not t in consumer_data:
+                        consumer_data[t] = axargs["value.%s.1" % s]
                     
-                    request.session['auth_consumer_data'] = {
-                        'email': available_data.get('email', None),
-                    }
-
-                except Exception, e:
-                    pass
-                    #import sys, traceback
-                    #traceback.print_exc(file=sys.stdout)
+            request.session['auth_consumer_data'] = consumer_data
+
 
             return request.GET['openid.identity']
         elif openid_response.status == CANCEL:
index 0f7f812626e23968ff072493092218a5d984a383..8887f8096a6083b4fecaa305b7ad9ad8f316c758 100644 (file)
@@ -1,14 +1,11 @@
 # -*- coding: utf-8 -*-
 
-from xml.dom import minidom
-from datetime import datetime, timedelta
+from datetime import datetime
 import time
 import re
 import os
 import gc
 from django.utils.translation import ugettext as _
-from django.template.defaultfilters import slugify
-from forum.models.utils import dbsafe_encode
 from orm import orm
 
 from django.utils.encoding import force_unicode
@@ -167,13 +164,12 @@ class IdIncrementer():
 
 openidre = re.compile('^https?\:\/\/')
 def userimport(path, options):
-#users = readTable(dump, "Users")
 
     usernames = []
     openids = set()
     uidmapper = IdMapper()
-    #merged_users = []
 
+    authenticated_user = options.get('authenticated_user', None)
     owneruid = options.get('owneruid', None)
     #check for empty values
     if not owneruid:
@@ -185,14 +181,29 @@ def userimport(path, options):
         if sxu.get('id') == '-1':
             return
         #print "\n".join(["%s : %s" % i for i in sxu.items()])
+
         if int(sxu.get('id')) == int(owneruid):
-            osqau = orm.User.objects.get(id=1)
-            for assoc in orm.AuthKeyUserAssociation.objects.filter(user=osqau):
-                openids.add(assoc.key)
-            uidmapper[owneruid] = 1
-            uidmapper[-1] = 1
-            create = False
-        else:
+            if authenticated_user:
+                osqau = orm.User.objects.get(id=authenticated_user.id)
+
+                for assoc in orm.AuthKeyUserAssociation.objects.filter(user=osqau):
+                    openids.add(assoc.key)
+
+                uidmapper[owneruid] = osqau.id
+                uidmapper[-1] = osqau.id
+                create = False
+            else:
+                uidmapper[owneruid] = int(owneruid)
+                uidmapper[-1] = int(owneruid)
+
+
+        sxbadges = sxu.get('badgesummary', None)
+        badges = {'1':'0', '2':'0', '3':'0'}
+
+        if sxbadges:
+            badges.update(dict([b.split('=') for b in sxbadges.split()]))
+
+        if create:
             username = unicode(sxu.get('displayname',
                                sxu.get('displaynamecleaned', sxu.get('realname', final_username_attempt(sxu)))))[:30]
 
@@ -210,15 +221,8 @@ def userimport(path, options):
 
                     if not totest in usernames:
                         username = totest
-                        break          
-
-        sxbadges = sxu.get('badgesummary', None)
-        badges = {'1':'0', '2':'0', '3':'0'}
+                        break
 
-        if sxbadges:
-            badges.update(dict([b.split('=') for b in sxbadges.split()]))
-
-        if create:
             osqau = orm.User(
                     id           = sxu.get('id'),
                     username     = username,
@@ -312,7 +316,6 @@ def userimport(path, options):
     return uidmapper
 
 def tagsimport(dump, uidmap):
-#tags = readTable(dump, "Tags")
 
     tagmap = {}
 
@@ -354,18 +357,8 @@ def remove_post_state(name, post):
     post.state_string = "".join("(%s)" % s for s in re.findall('\w+', post.state_string) if s != name)
 
 def postimport(dump, uidmap, tagmap):
-#history = {}
-#accepted = {}
     all = []
 
-    #for h in readTable(dump, "PostHistory"):
-    #    if not history.get(h.get('postid'), None):
-    #        history[h.get('postid')] = []
-    #
-    #    history[h.get('postid')].append(h)
-
-    #posts = readTable(dump, "Posts")
-
     def callback(sxpost):
         nodetype = (sxpost.get('posttypeid') == '1') and "nodetype" or "answer"
 
@@ -441,7 +434,6 @@ def postimport(dump, uidmap, tagmap):
     return all
 
 def comment_import(dump, uidmap, posts):
-#comments = readTable(dump, "PostComments")
     currid = IdIncrementer(max(posts))
     mapping = {}
 
@@ -516,7 +508,6 @@ def create_and_activate_revision(post):
     post.save()
 
 def post_vote_import(dump, uidmap, posts):
-#votes = readTable(dump, "Posts2Votes")
     close_reasons = {}
 
     def close_callback(r):
@@ -646,7 +637,6 @@ def post_vote_import(dump, uidmap, posts):
 
 
 def comment_vote_import(dump, uidmap, comments):
-#votes = readTable(dump, "Comments2Votes")
     user2vote = []
     comments2score = {}
 
@@ -688,7 +678,6 @@ def comment_vote_import(dump, uidmap, comments):
 
 
 def badges_import(dump, uidmap, post_list):
-#node_ctype = orm['contenttypes.contenttype'].objects.get(name='node')
 
     sxbadges = {}
 
@@ -750,10 +739,19 @@ def badges_import(dump, uidmap, post_list):
     for badge in obadges.values():
         badge.save()
 
+def save_setting(k, v):
+    try:
+        kv = orm.KeyValue.objects.get(key=k)
+        kv.value = v
+    except:
+        kv = orm.KeyValue(key = k, value = v)
+
+    kv.save()
+
+
 def pages_import(dump, currid):
     currid = IdIncrementer(currid)
     registry = {}
-    #sx_pages = readTable(dump, "FlatPages")
 
     def callback(sxp):
         currid.inc()
@@ -800,8 +798,7 @@ def pages_import(dump, currid):
 
     readTable(dump, "FlatPages", callback)
 
-    kv = orm.KeyValue(key='STATIC_PAGE_REGISTRY', value=dbsafe_encode(registry))
-    kv.save()
+    save_setting('STATIC_PAGE_REGISTRY', dbsafe_encode(registry))
 
 sx2osqa_set_map = {
 u'theme.html.name': 'APP_TITLE',
@@ -832,28 +829,17 @@ def html_decode(html):
 
 
 def static_import(dump):
-#sx_sets = readTable(dump, "ThemeTextResources")
     sx_unknown = {}
 
     def callback(set):
         if unicode(set['name']) in sx2osqa_set_map:
-            try:
-                kv = orm.KeyValue.objects.get(key=sx2osqa_set_map[set['name']])
-                kv.value = dbsafe_encode(html_decode(set['value']))
-            except:
-                kv = orm.KeyValue(
-                        key = sx2osqa_set_map[set['name']],
-                        value = dbsafe_encode(html_decode(set['value']))
-                        )
-
-            kv.save()
+            save_setting(sx2osqa_set_map[set['name']], dbsafe_encode(html_decode(set['value'])))
         else:
             sx_unknown[set['name']] = html_decode(set['value'])
 
     readTable(dump, "ThemeTextResources", callback)
 
-    unknown = orm.KeyValue(key='SXIMPORT_UNKNOWN_SETS', value=dbsafe_encode(sx_unknown))
-    unknown.save()
+    save_setting('SXIMPORT_UNKNOWN_SETS', dbsafe_encode(sx_unknown))
 
 def disable_triggers():
     from south.db import db
index dcca241c3a49b081172a4a07904f1fc1326a2c81..ebaed2133e8c2957c814b991524e646a5202a780 100644 (file)
@@ -1,30 +1,38 @@
 from django.shortcuts import render_to_response\r
 from django.template import RequestContext\r
-from forum.views.admin import super_user_required\r
+from forum.http_responses import HttpResponseUnauthorized\r
+from forum.models import User\r
 import importer\r
 from zipfile import ZipFile\r
 import os\r
 \r
-@super_user_required\r
 def sximporter(request):\r
-    list = []\r
-    if request.method == "POST" and "dump" in request.FILES:\r
-        dump = ZipFile(request.FILES['dump'])\r
-        members = [f for f in dump.namelist() if f.endswith('.xml')]\r
-        extract_to = os.path.join(os.path.dirname(__file__), 'tmp')\r
-\r
-        if not os.path.exists(extract_to):\r
-            os.makedirs(extract_to)\r
-\r
-        for m in members:\r
-            f = open(os.path.join(extract_to, m), 'w')\r
-            f.write(dump.read(m))\r
-            f.close()\r
-\r
-        #dump.extractall(extract_to, members)\r
-        dump.close()\r
-        importer.sximport(extract_to, request.POST)\r
-\r
-    return render_to_response('modules/sximporter/page.html', {\r
-    'names': list\r
-    }, context_instance=RequestContext(request))\r
+    if (not User.objects.exists()) or (request.user.is_authenticated() and request.user.is_superuser):\r
+        list = []\r
+        if request.method == "POST" and "dump" in request.FILES:\r
+            dump = ZipFile(request.FILES['dump'])\r
+            members = [f for f in dump.namelist() if f.endswith('.xml')]\r
+            extract_to = os.path.join(os.path.dirname(__file__), 'tmp')\r
+\r
+            if not os.path.exists(extract_to):\r
+                os.makedirs(extract_to)\r
+\r
+            for m in members:\r
+                f = open(os.path.join(extract_to, m), 'w')\r
+                f.write(dump.read(m))\r
+                f.close()\r
+\r
+            #dump.extractall(extract_to, members)\r
+            dump.close()\r
+\r
+            options = dict([(k, v) for k, v in request.POST.items()])\r
+            options['authenticated_user'] = (request.user.is_authenticated() and (request.user,) or (None,))[0]\r
+\r
+            importer.sximport(extract_to, options)\r
+\r
+        return render_to_response('modules/sximporter/page.html', {\r
+        'names': list\r
+        }, context_instance=RequestContext(request))\r
+    else:\r
+        return HttpResponseUnauthorized(request)\r
+\r
index 6e7c106c3b886128e8366366b9e1f5f834b8504c..61c47a8279411bc8370c6baffba665e1960ec291 100644 (file)
@@ -1,5 +1,4 @@
 # encoding:utf-8
-# Django settings for lanai project.
 import os.path
 import sys
 
@@ -7,24 +6,18 @@ SITE_ID = 1
 
 ADMIN_MEDIA_PREFIX = '/admin_media/'
 SECRET_KEY = '$oo^&_m&qwbib=(_4m_n*zn-d=g#s0he5fx9xonnym#8p6yigm'
-# List of callables that know how to import templates from various sources.
-TEMPLATE_LOADERS = (
+
+TEMPLATE_LOADERS = [
     'django.template.loaders.filesystem.load_template_source',
     'django.template.loaders.app_directories.load_template_source',
-    'forum.modules.module_templates_loader',
+    'forum.modules.template_loader.module_templates_loader',
     'forum.skins.load_template_source',
-#     'django.template.loaders.eggs.load_template_source',
-)
+]
 
 MIDDLEWARE_CLASSES = [
-    #'django.middleware.gzip.GZipMiddleware',
     'django.contrib.sessions.middleware.SessionMiddleware',
-    #'django.middleware.locale.LocaleMiddleware',
-    #'django.middleware.cache.UpdateCacheMiddleware',
     'django.middleware.common.CommonMiddleware',
-    #'django.middleware.cache.FetchFromCacheMiddleware',
     'forum.middleware.extended_user.ExtendedUser',
-    #'django.middleware.sqlprint.SqlPrintingMiddleware',
     'forum.middleware.anon_user.ConnectToSessionMessagesMiddleware',
     'forum.middleware.request_utils.RequestUtils',
     'forum.middleware.cancel.CancelActionMiddleware',
@@ -33,28 +26,27 @@ MIDDLEWARE_CLASSES = [
     'django.middleware.transaction.TransactionMiddleware',
 ]
 
-TEMPLATE_CONTEXT_PROCESSORS = (
+TEMPLATE_CONTEXT_PROCESSORS = [
     'django.core.context_processors.request',
     'forum.context.application_settings',
-    #'django.core.context_processors.i18n',
-    'forum.user_messages.context_processors.user_messages',#must be before auth
-    'django.core.context_processors.auth', #this is required for admin
-)
+    'forum.user_messages.context_processors.user_messages',
+    'django.core.context_processors.auth',
+]
 
 ROOT_URLCONF = 'urls'
+APPEND_SLASH = True
 
 TEMPLATE_DIRS = (
     os.path.join(os.path.dirname(__file__),'forum','skins').replace('\\','/'),
 )
 
-#UPLOAD SETTINGS
+
 FILE_UPLOAD_TEMP_DIR = os.path.join(os.path.dirname(__file__), 'tmp').replace('\\','/')
 FILE_UPLOAD_HANDLERS = ("django.core.files.uploadhandler.MemoryFileUploadHandler",
  "django.core.files.uploadhandler.TemporaryFileUploadHandler",)
 DEFAULT_FILE_STORAGE = 'django.core.files.storage.FileSystemStorage'
-# for user upload
+
 ALLOW_FILE_TYPES = ('.jpg', '.jpeg', '.gif', '.bmp', '.png', '.tiff')
-# unit byte
 ALLOW_MAX_FILE_SIZE = 1024 * 1024
 
 # User settings
@@ -80,6 +72,19 @@ for path in app_url_split[1].split('/')[1:]:
 if FORCE_SCRIPT_NAME.endswith('/'):
     FORCE_SCRIPT_NAME = FORCE_SCRIPT_NAME[:-1]
 
+from forum import modules
+
+modules.init_modules_engine(SITE_SRC_ROOT, DISABLED_MODULES)
+
+[MIDDLEWARE_CLASSES.extend(
+        ["%s.%s" % (m.__name__, mc) for mc in getattr(m, 'MIDDLEWARE_CLASSES', [])]
+                          ) for m in modules.MODULE_LIST]
+
+[TEMPLATE_LOADERS.extend(
+        ["%s.%s" % (m.__name__, tl) for tl in getattr(m, 'TEMPLATE_LOADERS', [])]
+                          ) for m in modules.MODULE_LIST]
+
+
 INSTALLED_APPS = [
     'django.contrib.auth',
     'django.contrib.contenttypes',
diff --git a/urls.py b/urls.py
index 0355657c9d3d7d279c19009ea10398fa90c3da94..aa8bdf98d3e8051069fe9f05f873ea89c128d419 100644 (file)
--- a/urls.py
+++ b/urls.py
@@ -11,3 +11,7 @@ if 'rosetta' in settings.INSTALLED_APPS:
         url(r'^rosetta/', include('rosetta.urls')),
     )
 
+handler404 = 'forum.views.meta.page'
+handler500 = 'forum.views.meta.error_handler'
+
+