From 08948f69c835e27c0f90dea93d71901a5a62b0f5 Mon Sep 17 00:00:00 2001 From: hernani Date: Fri, 11 Mar 2011 19:50:09 +0000 Subject: [PATCH] merge hernani > trunk git-svn-id: http://svn.osqa.net/svnroot/osqa/trunk@821 0cfe37f9-358a-4d5e-be75-b63607b5c754 --- forum/authentication/__init__.py | 7 +- forum/http_responses.py | 16 ++- forum/management/commands/module_command.py | 13 ++ forum/models/question.py | 8 -- forum/models/user.py | 9 -- forum/modules/__init__.py | 52 +++----- forum/modules/decorators.py | 26 ++-- forum/modules/template_loader.py | 44 +++++++ forum/registry.py | 9 +- forum/skins/__init__.py | 129 +++++++++++++++++--- forum/templatetags/node_tags.py | 19 +-- forum/templatetags/ui_registry.py | 10 +- forum/urls.py | 3 +- forum/utils/userlinking.py | 2 + forum/views/auth.py | 16 +-- forum/views/meta.py | 49 +++++++- forum/views/readers.py | 20 +-- forum/views/vars.py | 2 + forum/views/writers.py | 7 +- forum_modules/openidauth/consumer.py | 86 ++++++++----- forum_modules/sximporter/importer.py | 90 ++++++-------- forum_modules/sximporter/views.py | 54 ++++---- settings.py | 44 ++++--- urls.py | 4 + 24 files changed, 468 insertions(+), 251 deletions(-) create mode 100644 forum/management/commands/module_command.py create mode 100644 forum/modules/template_loader.py create mode 100644 forum/views/vars.py diff --git a/forum/authentication/__init__.py b/forum/authentication/__init__.py index 8dbf825..79c47a8 100644 --- a/forum/authentication/__init__.py +++ b/forum/authentication/__init__.py @@ -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() ]) + diff --git a/forum/http_responses.py b/forum/http_responses.py index 7fae3b6..2ff4866 100644 --- a/forum/http_responses.py +++ b/forum/http_responses.py @@ -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 index 0000000..378d15a --- /dev/null +++ b/forum/management/commands/module_command.py @@ -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() diff --git a/forum/models/question.py b/forum/models/question.py index bef5bb5..ef4a37d 100644 --- a/forum/models/question.py +++ b/forum/models/question.py @@ -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) diff --git a/forum/models/user.py b/forum/models/user.py index 9d63248..d96426e 100644 --- a/forum/models/user.py +++ b/forum/models/user.py @@ -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(): diff --git a/forum/modules/__init__.py b/forum/modules/__init__.py index cbac6ed..976d9eb 100644 --- a/forum/modules/__init__.py +++ b/forum/modules/__init__.py @@ -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 diff --git a/forum/modules/decorators.py b/forum/modules/decorators.py index 3c53412..11af9e3 100644 --- a/forum/modules/decorators.py +++ b/forum/modules/decorators.py @@ -69,7 +69,7 @@ class ReturnImediatelyException(Exception): self.ret = ret def _check_decoratable(origin, install=True): - if not isinstance(origin, DecoratableObject): + if not hasattr(origin, '_decoratable_obj'): if inspect.ismethod(origin) and not hasattr(origin, '_decoratable_obj'): decoratable = DecoratableObject(origin) @@ -91,10 +91,18 @@ def _check_decoratable(origin, install=True): elif inspect.isfunction(origin): decoratable = DecoratableObject(origin) + def decorated(*args, **kwargs): + return decoratable(*args, **kwargs) + + decorated._decoratable_obj = decoratable + if install: - setattr(inspect.getmodule(origin), origin.__name__, decoratable) + setattr(inspect.getmodule(origin), origin.__name__, decorated) + + decorated.__name__ = origin.__name__ + decorated.__module__ = origin.__module__ - return decoratable + return decorated return origin @@ -103,7 +111,7 @@ def decorate(origin, needs_origin=True): origin = _check_decoratable(origin) def decorator(fn): - origin._decorate(fn, DecoratableObject.MODE_OVERRIDE, needs_origin=needs_origin) + origin._decoratable_obj._decorate(fn, DecoratableObject.MODE_OVERRIDE, needs_origin=needs_origin) return decorator @@ -112,7 +120,7 @@ def _decorate_params(origin): origin = _check_decoratable(origin) def decorator(fn): - origin._decorate(fn, DecoratableObject.MODE_PARAMS) + origin._decoratable_obj._decorate(fn, DecoratableObject.MODE_PARAMS) return decorator @@ -122,7 +130,7 @@ def _decorate_result(origin, needs_params=False): origin = _check_decoratable(origin) def decorator(fn): - origin._decorate(fn, DecoratableObject.MODE_RESULT, needs_params=needs_params) + origin._decoratable_obj._decorate(fn, DecoratableObject.MODE_RESULT, needs_params=needs_params) return decorator @@ -131,7 +139,7 @@ decorate.result = _decorate_result def _decorate_with(fn): def decorator(origin): origin = _check_decoratable(origin) - origin._decorate(fn, DecoratableObject.MODE_OVERRIDE, needs_origin=True) + origin._decoratable_obj._decorate(fn, DecoratableObject.MODE_OVERRIDE, needs_origin=True) return origin return decorator @@ -140,7 +148,7 @@ decorate.withfn = _decorate_with def _decorate_result_with(fn, needs_params=False): def decorator(origin): origin = _check_decoratable(origin) - origin._decorate(fn, DecoratableObject.MODE_RESULT, needs_params=needs_params) + origin._decoratable_obj._decorate(fn, DecoratableObject.MODE_RESULT, needs_params=needs_params) return origin return decorator @@ -149,7 +157,7 @@ decorate.result.withfn = _decorate_result_with def _decorate_params_with(fn): def decorator(origin): origin = _check_decoratable(origin) - origin._decorate(fn, DecoratableObject.MODE_PARAMS) + origin._decoratable_obj._decorate(fn, DecoratableObject.MODE_PARAMS) return origin return decorator diff --git a/forum/modules/template_loader.py b/forum/modules/template_loader.py new file mode 100644 index 0000000..61cb44e --- /dev/null +++ b/forum/modules/template_loader.py @@ -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() diff --git a/forum/registry.py b/forum/registry.py index ff5a115..aedf5b0 100644 --- a/forum/registry.py +++ b/forum/registry.py @@ -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), diff --git a/forum/skins/__init__.py b/forum/skins/__init__.py index 7a7e0cd..33e0097 100644 --- a/forum/skins/__init__.py +++ b/forum/skins/__init__.py @@ -1,26 +1,119 @@ 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 - - diff --git a/forum/templatetags/node_tags.py b/forum/templatetags/node_tags.py index aab4d99..01745be 100644 --- a/forum/templatetags/node_tags.py +++ b/forum/templatetags/node_tags.py @@ -92,11 +92,14 @@ def post_controls(post, user): # The other controls are visible only to authenticated users. if user.is_authenticated(): - edit_url = reverse('edit_' + post_type, kwargs={'id': post.id}) - if user.can_edit_post(post): - controls.append(post_control(_('edit'), edit_url)) - elif post_type == 'question' and user.can_retag_questions(): - controls.append(post_control(_('retag'), edit_url)) + try: + edit_url = reverse('edit_' + post_type, kwargs={'id': post.id}) + if user.can_edit_post(post): + controls.append(post_control(_('edit'), edit_url)) + elif post_type == 'question' and user.can_retag_questions(): + controls.append(post_control(_('retag'), edit_url)) + except: + pass if post_type == 'question': if post.nis.closed and user.can_reopen_question(post): @@ -206,10 +209,10 @@ def comments(post, user): @register.inclusion_tag("node/contributors_info.html") -def contributors_info(node): +def contributors_info(node, verb=None): return { - 'node_verb': (node.node_type == "question") and _("asked") or ( - (node.node_type == "answer") and _("answered") or _("posted")), + 'node_verb': verb and verb or ((node.node_type == "question") and _("asked") or ( + (node.node_type == "answer") and _("answered") or _("posted"))), 'node': node, } diff --git a/forum/templatetags/ui_registry.py b/forum/templatetags/ui_registry.py index 21ba3bb..8b28e2c 100644 --- a/forum/templatetags/ui_registry.py +++ b/forum/templatetags/ui_registry.py @@ -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 diff --git a/forum/urls.py b/forum/urls.py index f53ccae..be0d9f3 100644 --- a/forum/urls.py +++ b/forum/urls.py @@ -215,5 +215,6 @@ urlpatterns += patterns('', url(r'^feeds/rss[/]?$', app.readers.feed, name="latest_questions_feed"), - url(r'^(?P.+)$', app.meta.page, name="static_page"), + #url(r'^.+$', app.meta.page, name="static_page"), ) + diff --git a/forum/utils/userlinking.py b/forum/utils/userlinking.py index da476ee..53a49dc 100644 --- a/forum/utils/userlinking.py +++ b/forum/utils/userlinking.py @@ -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() diff --git a/forum/views/auth.py b/forum/views/auth.py index f8eb897..f17d6d2 100644 --- a/forum/views/auth.py +++ b/forum/views/auth.py @@ -30,7 +30,9 @@ from forum.models import AuthKeyUserAssociation, ValidationHash, Question, Answe from forum.actions import UserJoinsAction, EmailValidationAction from forum.models.action import ActionRepute + from forum.settings import REP_GAIN_BY_EMAIL_VALIDATION +from vars import ON_SIGNIN_SESSION_ATTR, PENDING_SUBMISSION_SESSION_ATTR def signin_page(request): referer = request.META.get('HTTP_REFERER', '/') @@ -38,9 +40,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 +148,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 +227,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 +393,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")), diff --git a/forum/views/meta.py b/forum/views/meta.py index 536cf26..a0c5ced 100644 --- a/forum/views/meta.py +++ b/forum/views/meta.py @@ -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 "", + '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) diff --git a/forum/views/readers.py b/forum/views/readers.py index 9a6f66e..991a6b6 100644 --- a/forum/views/readers.py +++ b/forum/views/readers.py @@ -102,13 +102,15 @@ def questions(request): @decorators.render('questions.html') def tag(request, tag): - questions = Question.objects.filter(tags__name=unquote(tag)) - if not questions: + try: + tag = Tag.active.get(name=unquote(tag)) + except Tag.DoesNotExist: raise Http404 + return question_list(request, - questions, + Question.objects.filter(tags=tag), mark_safe(_('questions tagged %(tag)s') % {'tag': tag}), None, mark_safe(_('Questions Tagged With %(tag)s') % {'tag': tag}), @@ -238,16 +240,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 index 0000000..3ac5c2c --- /dev/null +++ b/forum/views/vars.py @@ -0,0 +1,2 @@ +ON_SIGNIN_SESSION_ATTR = 'on_signin_url' +PENDING_SUBMISSION_SESSION_ATTR = 'pending_submission_data' diff --git a/forum/views/writers.py b/forum/views/writers.py index dbc68c0..a5c5d70 100644 --- a/forum/views/writers.py +++ b/forum/views/writers.py @@ -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 diff --git a/forum_modules/openidauth/consumer.py b/forum_modules/openidauth/consumer.py index cc1e97d..4c3818c 100644 --- a/forum_modules/openidauth/consumer.py +++ b/forum_modules/openidauth/consumer.py @@ -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: diff --git a/forum_modules/sximporter/importer.py b/forum_modules/sximporter/importer.py index 0f7f812..8887f80 100644 --- a/forum_modules/sximporter/importer.py +++ b/forum_modules/sximporter/importer.py @@ -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 diff --git a/forum_modules/sximporter/views.py b/forum_modules/sximporter/views.py index dcca241..ebaed21 100644 --- a/forum_modules/sximporter/views.py +++ b/forum_modules/sximporter/views.py @@ -1,30 +1,38 @@ from django.shortcuts import render_to_response from django.template import RequestContext -from forum.views.admin import super_user_required +from forum.http_responses import HttpResponseUnauthorized +from forum.models import User import importer from zipfile import ZipFile import os -@super_user_required def sximporter(request): - list = [] - if request.method == "POST" and "dump" in request.FILES: - dump = ZipFile(request.FILES['dump']) - members = [f for f in dump.namelist() if f.endswith('.xml')] - extract_to = os.path.join(os.path.dirname(__file__), 'tmp') - - if not os.path.exists(extract_to): - os.makedirs(extract_to) - - for m in members: - f = open(os.path.join(extract_to, m), 'w') - f.write(dump.read(m)) - f.close() - - #dump.extractall(extract_to, members) - dump.close() - importer.sximport(extract_to, request.POST) - - return render_to_response('modules/sximporter/page.html', { - 'names': list - }, context_instance=RequestContext(request)) + if (not User.objects.exists()) or (request.user.is_authenticated() and request.user.is_superuser): + list = [] + if request.method == "POST" and "dump" in request.FILES: + dump = ZipFile(request.FILES['dump']) + members = [f for f in dump.namelist() if f.endswith('.xml')] + extract_to = os.path.join(os.path.dirname(__file__), 'tmp') + + if not os.path.exists(extract_to): + os.makedirs(extract_to) + + for m in members: + f = open(os.path.join(extract_to, m), 'w') + f.write(dump.read(m)) + f.close() + + #dump.extractall(extract_to, members) + dump.close() + + options = dict([(k, v) for k, v in request.POST.items()]) + options['authenticated_user'] = (request.user.is_authenticated() and (request.user,) or (None,))[0] + + importer.sximport(extract_to, options) + + return render_to_response('modules/sximporter/page.html', { + 'names': list + }, context_instance=RequestContext(request)) + else: + return HttpResponseUnauthorized(request) + diff --git a/settings.py b/settings.py index 6e7c106..6a9112a 100644 --- a/settings.py +++ b/settings.py @@ -1,5 +1,4 @@ # encoding:utf-8 -# Django settings for lanai project. import os.path import sys @@ -7,54 +6,46 @@ 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', 'forum.middleware.admin_messages.AdminMessagesMiddleware', - #'recaptcha_django.middleware.ReCaptchaMiddleware', '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 +71,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 0355657..aa8bdf9 100644 --- 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' + + -- 2.39.5