diff options
-rw-r--r-- | askbot/conf/__init__.py | 1 | ||||
-rw-r--r-- | askbot/conf/external_keys.py | 35 | ||||
-rw-r--r-- | askbot/conf/ldap.py | 93 | ||||
-rw-r--r-- | askbot/conf/site_settings.py | 1 | ||||
-rw-r--r-- | askbot/deps/django_authopenid/backends.py | 100 | ||||
-rw-r--r-- | askbot/deps/django_authopenid/views.py | 38 | ||||
-rw-r--r-- | askbot/doc/source/changelog.rst | 2 | ||||
-rw-r--r-- | askbot/doc/source/contributors.rst | 1 | ||||
-rw-r--r-- | askbot/management/commands/initialize_ldap_logins.py | 68 | ||||
-rw-r--r-- | askbot/models/badges.py | 5 | ||||
-rw-r--r-- | askbot/models/post.py | 6 | ||||
-rw-r--r-- | askbot/models/repute.py | 18 | ||||
-rw-r--r-- | askbot/setup_templates/settings.py.mustache | 2 | ||||
-rw-r--r-- | askbot/skins/default/templates/main_page/tab_bar.html | 2 | ||||
-rw-r--r-- | askbot/skins/default/templates/user_profile/user_edit.html | 19 | ||||
-rw-r--r-- | askbot/startup_procedures.py | 60 | ||||
-rw-r--r-- | askbot/utils/html.py | 4 | ||||
-rw-r--r-- | askbot/views/meta.py | 3 | ||||
-rw-r--r-- | askbot/views/users.py | 12 |
19 files changed, 282 insertions, 188 deletions
diff --git a/askbot/conf/__init__.py b/askbot/conf/__init__.py index de1eeccc..dff91d8e 100644 --- a/askbot/conf/__init__.py +++ b/askbot/conf/__init__.py @@ -9,6 +9,7 @@ import askbot.conf.flatpages import askbot.conf.site_settings import askbot.conf.license import askbot.conf.external_keys +import askbot.conf.ldap import askbot.conf.skin_general_settings import askbot.conf.sidebar_main import askbot.conf.sidebar_question diff --git a/askbot/conf/external_keys.py b/askbot/conf/external_keys.py index a673534a..24a43265 100644 --- a/askbot/conf/external_keys.py +++ b/askbot/conf/external_keys.py @@ -53,6 +53,8 @@ settings.register( ) ) + + settings.register( livesettings.StringValue( EXTERNAL_KEYS, @@ -160,36 +162,3 @@ settings.register( description=_('ident.ca consumer secret'), ) ) - -settings.register( - livesettings.BooleanValue( - EXTERNAL_KEYS, - 'USE_LDAP_FOR_PASSWORD_LOGIN', - description=_('Use LDAP authentication for the password login'), - defaut=False - ) -) - -settings.register( - livesettings.StringValue( - EXTERNAL_KEYS, - 'LDAP_PROVIDER_NAME', - description=_('LDAP service provider name') - ) -) - -settings.register( - livesettings.StringValue( - EXTERNAL_KEYS, - 'LDAP_URL', - description=_('URL for the LDAP service') - ) -) - -settings.register( - livesettings.LongStringValue( - EXTERNAL_KEYS, - 'HOW_TO_CHANGE_LDAP_PASSWORD', - description=_('Explain how to change LDAP password') - ) -) diff --git a/askbot/conf/ldap.py b/askbot/conf/ldap.py new file mode 100644 index 00000000..077ff792 --- /dev/null +++ b/askbot/conf/ldap.py @@ -0,0 +1,93 @@ +"""Settings for LDAP login for Askbot""" +from askbot.conf.settings_wrapper import settings +from askbot.conf.super_groups import EXTERNAL_SERVICES +from askbot.deps import livesettings +from django.utils.translation import ugettext as _ + +LDAP_SETTINGS = livesettings.ConfigurationGroup( + 'LDAP_SETTINGS', + _('LDAP login configuration'), + super_group = EXTERNAL_SERVICES + ) + +settings.register( + livesettings.BooleanValue( + LDAP_SETTINGS, + 'USE_LDAP_FOR_PASSWORD_LOGIN', + description=_('Use LDAP authentication for the password login'), + defaut=False + ) +) + +settings.register( + livesettings.StringValue( + LDAP_SETTINGS, + 'LDAP_URL', + description=_('LDAP URL'), + default="ldap://<host>:<port>" + ) +) + +settings.register( + livesettings.StringValue( + LDAP_SETTINGS, + 'LDAP_BASEDN', + description=_('LDAP BASE DN') + ) +) + +settings.register( + livesettings.StringValue( + LDAP_SETTINGS, + 'LDAP_SEARCH_SCOPE', + description=_('LDAP Search Scope'), + default="subs" + ) +) + +settings.register( + livesettings.StringValue( + LDAP_SETTINGS, + 'LDAP_USERID_FIELD', + description=_('LDAP Server USERID field name'), + default="uid" + ) +) + +settings.register( + livesettings.StringValue( + LDAP_SETTINGS, + 'LDAP_COMMONNAME_FIELD', + description=_('LDAP Server "Common Name" field name'), + default="cn" + ) +) + +settings.register( + livesettings.StringValue( + LDAP_SETTINGS, + 'LDAP_EMAIL_FIELD', + description=_('LDAP Server EMAIL field name'), + default="mail" + ) +) + +# May be necessary, but not handled properly. +# --> Commenting out until handled properly in backends.ldap_authenticate() +#settings.register( +# livesettings.StringValue( +# LDAP_SETTINGS, +# 'LDAP_PROXYDN', +# description=_('LDAP PROXY DN'), +# default="" +# ) +#) +# +#settings.register( +# livesettings.StringValue( +# LDAP_SETTINGS, +# 'LDAP_PROXYDN_PASSWORD', +# description=_('LDAP PROXY DN Password'), +# defalut="", +# ) +#) diff --git a/askbot/conf/site_settings.py b/askbot/conf/site_settings.py index 8cd73b3d..c64ea952 100644 --- a/askbot/conf/site_settings.py +++ b/askbot/conf/site_settings.py @@ -63,7 +63,6 @@ settings.register( livesettings.StringValue( QA_SITE_SETTINGS, 'APP_URL', - default='http://askbot.org', description=_( 'Base URL for your Q&A forum, must start with ' 'http or https' diff --git a/askbot/deps/django_authopenid/backends.py b/askbot/deps/django_authopenid/backends.py index 9f8f1dfd..f3d8f64b 100644 --- a/askbot/deps/django_authopenid/backends.py +++ b/askbot/deps/django_authopenid/backends.py @@ -9,6 +9,84 @@ from django.core.exceptions import ImproperlyConfigured from django.utils.translation import ugettext as _ from askbot.deps.django_authopenid.models import UserAssociation from askbot.deps.django_authopenid import util +from askbot.conf import settings as askbot_settings + +log = logging.getLogger('configuration') + + +def ldap_authenticate(username, password): + """ + Authenticate using ldap + + python-ldap must be installed + http://pypi.python.org/pypi/python-ldap/2.4.6 + """ + import ldap + user_information = None + try: + ldap_session = ldap.initialize(askbot_settings.LDAP_URL) + ldap_session.protocol_version = ldap.VERSION3 + user_filter = "({0}={1})".format(askbot_settings.LDAP_USERID_FIELD, + username) + # search ldap directory for user + res = ldap_session.search_s(askbot_settings.LDAP_BASEDN, ldap.SCOPE_SUBTREE, user_filter, None) + if res: # User found in LDAP Directory + user_dn = res[0][0] + user_information = res[0][1] + ldap_session.simple_bind_s(user_dn, password) # <-- will throw ldap.INVALID_CREDENTIALS if fails + ldap_session.unbind_s() + + exact_username = user_information[askbot_settings.LDAP_USERID_FIELD][0] + + # Assuming last, first order + # --> may be different + last_name, first_name = user_information[askbot_settings.LDAP_COMMONNAME_FIELD][0].rsplit(" ", 1) + email = user_information[askbot_settings.LDAP_EMAIL_FIELD][0] + try: + user = User.objects.get(username__exact=exact_username) + # always update user profile to synchronize with ldap server + user.set_password(password) + user.first_name = first_name + user.last_name = last_name + user.email = email + user.save() + except User.DoesNotExist: + # create new user in local db + user = User() + user.username = exact_username + user.set_password(password) + user.first_name = first_name + user.last_name = last_name + user.email = email + user.is_staff = False + user.is_superuser = False + user.is_active = True + user.save() + + log.info('Created New User : [{0}]'.format(exact_username)) + return user + else: + # Maybe a user created internally (django admin user) + try: + user = User.objects.get(username__exact=username) + if user.check_password(password): + return user + else: + return None + except User.DoesNotExist: + return None + + except ldap.INVALID_CREDENTIALS, e: + return None # Will fail login on return of None + except ldap.LDAPError, e: + log.error("LDAPError Exception") + log.exception(e) + return None + except Exception, e: + log.error("Unexpected Exception Occurred") + log.exception(e) + return None + class AuthBackend(object): """Authenticator's authentication backend class @@ -22,15 +100,14 @@ class AuthBackend(object): def authenticate( self, - username = None,#for 'password' - password = None,#for 'password' + username = None,#for 'password' and 'ldap' + password = None,#for 'password' and 'ldap' user_id = None,#for 'force' provider_name = None,#required with all except email_key openid_url = None, email_key = None, oauth_user_id = None,#used with oauth facebook_user_id = None,#user with facebook - ldap_user_id = None,#for ldap wordpress_url = None, # required for self hosted wordpress wp_user_id = None, # required for self hosted wordpress method = None,#requried parameter @@ -40,6 +117,7 @@ class AuthBackend(object): from the signature of the function call """ login_providers = util.get_enabled_login_providers() + assoc = None # UserAssociation not needed for ldap if method == 'password': if login_providers[provider_name]['type'] != 'password': raise ImproperlyConfigured('login provider must use password') @@ -156,14 +234,7 @@ class AuthBackend(object): return None elif method == 'ldap': - try: - assoc = UserAssociation.objects.get( - openid_url = ldap_user_id, - provider_name = provider_name - ) - user = assoc.user - except UserAssociation.DoesNotExist: - return None + user = ldap_authenticate(username, password) elif method == 'wordpress_site': try: @@ -180,9 +251,10 @@ class AuthBackend(object): else: raise TypeError('only openid and password supported') - #update last used time - assoc.last_used_timestamp = datetime.datetime.now() - assoc.save() + if assoc: + #update last used time + assoc.last_used_timestamp = datetime.datetime.now() + assoc.save() return user def get_user(self, user_id): diff --git a/askbot/deps/django_authopenid/views.py b/askbot/deps/django_authopenid/views.py index bb0b4986..22be8460 100644 --- a/askbot/deps/django_authopenid/views.py +++ b/askbot/deps/django_authopenid/views.py @@ -310,30 +310,26 @@ def signin(request): password_action = login_form.cleaned_data['password_action'] if askbot_settings.USE_LDAP_FOR_PASSWORD_LOGIN: assert(password_action == 'login') - ldap_provider_name = askbot_settings.LDAP_PROVIDER_NAME username = login_form.cleaned_data['username'] - if util.ldap_check_password( - username, - login_form.cleaned_data['password'] - ): - user = authenticate( - ldap_user_id = username, - provider_name = ldap_provider_name, - method = 'ldap' - ) - if user is not None: - login(request, user) - return HttpResponseRedirect(next_url) - else: - return finalize_generic_signin( - request = request, - user = user, - user_identifier = username, - login_provider_name = ldap_provider_name, - redirect_url = next_url + password = login_form.cleaned_data['password'] + # will be None if authentication fails + user = authenticate( + username=username, + password=password, + method = 'ldap' ) + if user is not None: + login(request, user) + return HttpResponseRedirect(next_url) else: - login_form.set_password_login_error() + return finalize_generic_signin( + request = request, + user = user, + user_identifier = username, + login_provider_name = ldap_provider_name, + redirect_url = next_url + ) + else: if password_action == 'login': user = authenticate( diff --git a/askbot/doc/source/changelog.rst b/askbot/doc/source/changelog.rst index a8e695a6..68d3c6b1 100644 --- a/askbot/doc/source/changelog.rst +++ b/askbot/doc/source/changelog.rst @@ -20,6 +20,8 @@ Development version (not released yet) * Added test on maximum length of title working for utf-8 text (Evgeny) * Added caching and invalidation to the question page (Evgeny) * Added a management command delete_contextless_activities (Evgeny) +* LDAP login configuration (github user `monkut <https://github.com/monkut>`_) +* Check order of middleware classes (Daniel Mican) 0.7.39 (Jan 11, 2012) --------------------- diff --git a/askbot/doc/source/contributors.rst b/askbot/doc/source/contributors.rst index a0b91518..eea61b31 100644 --- a/askbot/doc/source/contributors.rst +++ b/askbot/doc/source/contributors.rst @@ -35,6 +35,7 @@ Programming and documentation * `hjwp <https://github.com/hjwp>`_ * `Jacob Oscarson <http://www.aspektratio.net>`_ * `Radim Řehůřek <https://github.com/piskvorky>`_ +* `monkut <https://github.com/monkut>`_ Translations ------------ diff --git a/askbot/management/commands/initialize_ldap_logins.py b/askbot/management/commands/initialize_ldap_logins.py deleted file mode 100644 index 96ee74e5..00000000 --- a/askbot/management/commands/initialize_ldap_logins.py +++ /dev/null @@ -1,68 +0,0 @@ -"""Management command to create LDAP login method for all users. -Please see description of the command in its ``help_text``. -""" -import datetime -from django.core.management.base import CommandError -from django.utils.translation import ugettext as _ -from askbot.management import NoArgsJob -from askbot import models -from askbot.deps.django_authopenid.models import UserAssociation -from askbot.conf import settings as askbot_settings - -def create_ldap_login_for_user(user): - """a unit job that creates LDAP account record for - the user, assuming that his or her LDAP user name - is the same as the user name on the forum site. - If the record already exists, LDAP provider name - will be updated according to the live setting, - otherwise a new record will be created. - Always returns ``True``. - """ - ldap_url = askbot_settings.LDAP_URL - ldap_provider_name = askbot_settings.LDAP_PROVIDER_NAME - if '' in (ldap_url, ldap_provider_name): - raise CommandError( - 'Please, first set up LDAP settings ' - 'at url /settings/EXTERNAL_KEYS,' - 'relative to the base url of your forum site' - ) - try: - assoc = UserAssociation.objects.get( - openid_url = user.username, - user = user - ) - except UserAssociation.DoesNotExist: - assoc = UserAssociation( - openid_url = user.username, - user = user - ) - assoc.provider_name = ldap_provider_name - assoc.last_used_timestamp = datetime.datetime.now() - assoc.save() - return True - -class Command(NoArgsJob): - """definition of the job that - runs through all users and creates LDAP login - methods, assuming that LDAP user ID's are the same - as values ``~askbot.User.username`` - """ - help = _( - 'This command may help you migrate to LDAP ' - 'password authentication by creating a record ' - 'for LDAP association with each user account. ' - 'There is an assumption that ldap user id\'s are ' - 'the same as user names registered at the site. ' - 'Before running this command it is necessary to ' - 'set up LDAP parameters in the "External keys" section ' - 'of the site settings.' - ) - def __init__(self, *args, **kwargs): - self.batches = ({ - 'title': 'Initializing LDAP logins for all users: ', - 'query_set': models.User.objects.all(), - 'function': create_ldap_login_for_user, - 'changed_count_message': 'Created LDAP logins for %d users', - 'nothing_changed_message': 'All users already have LDAP login methods' - },) - super(Command, self).__init__(*args, **kwargs) diff --git a/askbot/models/badges.py b/askbot/models/badges.py index b909b7e1..61149df3 100644 --- a/askbot/models/badges.py +++ b/askbot/models/badges.py @@ -878,6 +878,11 @@ def init_badges(): #from the __init__ function? for key in BADGES.keys(): get_badge(key).get_stored_data() + #remove any badges from the database + #that are no longer in the BADGES dictionary + BadgeData.objects.exclude( + slug__in = map(slugify, BADGES.keys()) + ).delete() award_badges_signal = Signal( providing_args=[ diff --git a/askbot/models/post.py b/askbot/models/post.py index d9cd9a5e..2b78e7a1 100644 --- a/askbot/models/post.py +++ b/askbot/models/post.py @@ -486,8 +486,8 @@ class Post(models.Model): if self.is_comment(): #todo: implement a custom delete method on these #all this should pack into Activity.responses.filter( somehow ).delete() - activity_types = const.RESPONSE_ACTIVITY_TYPES_FOR_DISPLAY - activity_types += (const.TYPE_ACTIVITY_MENTION,) + #activity_types = const.RESPONSE_ACTIVITY_TYPES_FOR_DISPLAY + #activity_types += (const.TYPE_ACTIVITY_MENTION,) #todo: not very good import in models of other models #todo: potentially a circular import from askbot.models.user import Activity @@ -495,7 +495,7 @@ class Post(models.Model): activities = Activity.objects.filter( content_type = comment_content_type, object_id = self.id, - activity_type__in = activity_types + #activity_type__in = activity_types ) recipients = set() diff --git a/askbot/models/repute.py b/askbot/models/repute.py index dce907ac..c9c1b8bf 100644 --- a/askbot/models/repute.py +++ b/askbot/models/repute.py @@ -13,25 +13,27 @@ class BadgeData(models.Model): awarded_count = models.PositiveIntegerField(default=0) awarded_to = models.ManyToManyField(User, through='Award', related_name='badges') + def _get_meta_data(self): + """retrieves badge metadata stored + in a file""" + from askbot.models import badges + return badges.get_badge(self.slug) + @property def name(self): - from askbot.models import badges - return badges.get_badge(self.slug).name + return self._get_meta_data().name @property def description(self): - from askbot.models import badges - return badges.get_badge(self.slug).description + return self._get_meta_data().description @property def css_class(self): - from askbot.models import badges - return badges.get_badge(self.slug).css_class + return self._get_meta_data().css_class def get_type_display(self): - from askbot.models import badges #todo - rename "type" -> "level" in this model - return badges.get_badge(self.slug).get_level_display() + return self._get_meta_data().get_level_display() class Meta: app_label = 'askbot' diff --git a/askbot/setup_templates/settings.py.mustache b/askbot/setup_templates/settings.py.mustache index 19fd3c61..71ccf0f2 100644 --- a/askbot/setup_templates/settings.py.mustache +++ b/askbot/setup_templates/settings.py.mustache @@ -222,7 +222,7 @@ djcelery.setup_loader() DOMAIN_NAME = '{{domain_name}}' CSRF_COOKIE_NAME = '{{domain_name}}_csrf' -CSRF_COOKIE_DOMAIN = DOMAIN_NAME +CSRF_COOKIE_DOMAIN = DOMAIN_NAME #note that this can be edited STATIC_ROOT = os.path.join(PROJECT_ROOT, "static") STATICFILES_DIRS = (os.path.join(ASKBOT_ROOT, 'skins'),) diff --git a/askbot/skins/default/templates/main_page/tab_bar.html b/askbot/skins/default/templates/main_page/tab_bar.html index e08232bb..8b666155 100644 --- a/askbot/skins/default/templates/main_page/tab_bar.html +++ b/askbot/skins/default/templates/main_page/tab_bar.html @@ -1,6 +1,6 @@ {% import "macros.html" as macros %} {% load extra_filters_jinja %} -{% cache 0 "scope_sort_tabs" search_tags request.user scope sort query context.page language_code %} +{% cache 0 "scope_sort_tabs" search_tags request.user author_name scope sort query context.page language_code %} <a class="rss" {% if feed_url %} href="{{settings.APP_URL}}{{feed_url}}" diff --git a/askbot/skins/default/templates/user_profile/user_edit.html b/askbot/skins/default/templates/user_profile/user_edit.html index 94a1d58d..7735ba93 100644 --- a/askbot/skins/default/templates/user_profile/user_edit.html +++ b/askbot/skins/default/templates/user_profile/user_edit.html @@ -40,7 +40,7 @@ <td> {% if settings.EDITABLE_SCREEN_NAME %} {{ form.username }} - <span class="form-error"></span> {{ form.username.errors }} </td> + <span class="form-error"> {{ form.username.errors }} </span></td> {% else %} {{ view_user.username }} {% endif %} @@ -53,8 +53,7 @@ <td> {% if settings.EDITABLE_EMAIL %} {{ form.email }} - <span class="form-error"></span> - {{ form.email.errors }} + <span class="form-error">{{ form.email.errors }}</span> {% else %} {{ view_user.email }} {% trans %}(cannot be changed){% endtrans %} @@ -63,27 +62,27 @@ </tr> <tr> <td>{{ form.realname.label_tag() }}:</td> - <td>{{ form.realname }} <span class="form-error"></span> {{ form.realname.errors }} </td> + <td>{{ form.realname }} <span class="form-error"> {{ form.realname.errors }} </span></td> </tr> <tr> <td>{{ form.website.label_tag() }}:</td> - <td>{{ form.website }} <span class="form-error"></span> {{ form.website.errors }} </td> + <td>{{ form.website }} <span class="form-error"> {{ form.website.errors }} </span></td> </tr> <tr> <td>{{ form.city.label_tag() }}:</td> - <td>{{ form.city }} <span class="form-error"></span> {{ form.city.errors }} </td> + <td>{{ form.city }} <span class="form-error"> {{ form.city.errors }} </span></td> </tr> <tr> <td>{{ form.country.label_tag() }}:</td> - <td>{{ form.country }} <span class="form-error"></span> {{ form.country.errors }} </td> + <td>{{ form.country }} <span class="form-error"> {{ form.country.errors }} </span></td> </tr> <tr> <td>{{ form.show_country.label_tag() }}:</td> - <td>{{ form.show_country }} <span class="form-error"></span> {{ form.show_country.errors }} </td> + <td>{{ form.show_country }} <span class="form-error"> {{ form.show_country.errors }} </span></td> </tr> <tr> <td>{{ form.birthday.label_tag() }}:</td> - <td>{{ form.birthday }} <span class="form-error"></span> {{ form.birthday.errors }} </td> + <td>{{ form.birthday }} <span class="form-error"> {{ form.birthday.errors }} </span></td> </tr> <tr> <td></td> @@ -95,7 +94,7 @@ </tr> <tr> <td style="vertical-align:top">{{ form.about.label_tag() }}:</td> - <td>{{ form.about }} <span class="form-error"></span> {{ form.about.errors }} </td> + <td>{{ form.about }} <span class="form-error"> {{ form.about.errors }} </span></td> </tr> </table> <div style="margin:30px 0 60px 0"> diff --git a/askbot/startup_procedures.py b/askbot/startup_procedures.py index 4c76be2c..b4b36e35 100644 --- a/askbot/startup_procedures.py +++ b/askbot/startup_procedures.py @@ -109,7 +109,7 @@ def test_middleware(): installed in the django settings.py file. If that is not the case - raises an AskbotConfigError exception. """ - required_middleware = ( + required_middleware = [ 'django.contrib.sessions.middleware.SessionMiddleware', 'django.middleware.common.CommonMiddleware', 'django.contrib.auth.middleware.AuthenticationMiddleware', @@ -117,37 +117,47 @@ def test_middleware(): 'askbot.middleware.forum_mode.ForumModeMiddleware', 'askbot.middleware.cancel.CancelActionMiddleware', 'django.middleware.transaction.TransactionMiddleware', - 'askbot.middleware.view_log.ViewLogMiddleware', - ) + ] if 'debug_toolbar' in django_settings.INSTALLED_APPS: - required_middleware += ( + required_middleware.append( 'debug_toolbar.middleware.DebugToolbarMiddleware', ) - - installed_middleware_set = set(django_settings.MIDDLEWARE_CLASSES) - missing_middleware_set = set(required_middleware) - installed_middleware_set - - if missing_middleware_set: - error_message = """\n\nPlease add the following middleware (listed after this message) + required_middleware.extend([ + 'askbot.middleware.view_log.ViewLogMiddleware', + 'askbot.middleware.spaceless.SpacelessMiddleware', + ]) + found_middleware = [x for x in django_settings.MIDDLEWARE_CLASSES + if x in required_middleware] + if found_middleware != required_middleware: + # either middleware is out of order or it's missing an item + missing_middleware_set = set(required_middleware) - set(found_middleware) + middleware_text = '' + if missing_middleware_set: + error_message = """\n\nPlease add the following middleware (listed after this message) to the MIDDLEWARE_CLASSES variable in your site settings.py file. -The order the middleware records may be important, please take a look at the example in +The order the middleware records is important, please take a look at the example in https://github.com/ASKBOT/askbot-devel/blob/master/askbot/setup_templates/settings.py:\n\n""" - middleware_text = format_as_text_tuple_entries(missing_middleware_set) + middleware_text = format_as_text_tuple_entries(missing_middleware_set) + else: + # middleware is out of order + error_message = """\n\nPlease check the order of middleware closely. +The order the middleware records is important, please take a look at the example in +https://github.com/ASKBOT/askbot-devel/blob/master/askbot/setup_templates/settings.py +for the correct order.\n\n""" raise AskbotConfigError(error_message + middleware_text) #middleware that was used in the past an now removed - canceled_middleware = ( - 'askbot.deps.recaptcha_django.middleware.ReCaptchaMiddleware', - ) - #'debug_toolbar.middleware.DebugToolbarMiddleware', - - remove_middleware_set = set(canceled_middleware) \ - & installed_middleware_set - if remove_middleware_set: + canceled_middleware = [ + 'askbot.deps.recaptcha_django.middleware.ReCaptchaMiddleware' + ] + + invalid_middleware = [x for x in canceled_middleware + if x in django_settings.MIDDLEWARE_CLASSES] + if invalid_middleware: error_message = """\n\nPlease remove the following middleware entries from the list of MIDDLEWARE_CLASSES in your settings.py - these are not used any more:\n\n""" - middleware_text = format_as_text_tuple_entries(remove_middleware_set) + middleware_text = format_as_text_tuple_entries(invalid_middleware) raise AskbotConfigError(error_message + middleware_text) def try_import(module_name, pypi_package_name): @@ -356,7 +366,11 @@ def test_staticfiles(): askbot_root = os.path.dirname(askbot.__file__) skin_dir = os.path.abspath(os.path.join(askbot_root, 'skins')) - if skin_dir not in map(os.path.abspath, django_settings.STATICFILES_DIRS): + + # django_settings.STATICFILES_DIRS can have strings or tuples + staticfiles_dirs = [d[1] if isinstance(d, tuple) else d + for d in django_settings.STATICFILES_DIRS] + if skin_dir not in map(os.path.abspath, staticfiles_dirs): errors.append( 'Add to STATICFILES_DIRS list of your settings.py file:\n' " '%s'," % skin_dir @@ -368,7 +382,7 @@ def test_staticfiles(): 'Directory specified with settning ASKBOT_EXTRA_SKINS_DIR ' 'must exist and contain your custom skins for askbot.' ) - if extra_skins_dir not in django_settings.STATICFILES_DIRS: + if extra_skins_dir not in staticfiles_dirs: errors.append( 'Add ASKBOT_EXTRA_SKINS_DIR to STATICFILES_DIRS entry in ' 'your settings.py file.\n' diff --git a/askbot/utils/html.py b/askbot/utils/html.py index f6c168fb..f91fa980 100644 --- a/askbot/utils/html.py +++ b/askbot/utils/html.py @@ -28,10 +28,10 @@ class HTMLSanitizerMixin(sanitizer.HTMLSanitizerMixin): class HTMLSanitizer(tokenizer.HTMLTokenizer, HTMLSanitizerMixin): def __init__(self, stream, encoding=None, parseMeta=True, useChardet=True, - lowercaseElementName=True, lowercaseAttrName=True): + lowercaseElementName=True, lowercaseAttrName=True, **kwargs): tokenizer.HTMLTokenizer.__init__(self, stream, encoding, parseMeta, useChardet, lowercaseElementName, - lowercaseAttrName) + lowercaseAttrName, **kwargs) def __iter__(self): for token in tokenizer.HTMLTokenizer.__iter__(self): diff --git a/askbot/views/meta.py b/askbot/views/meta.py index ac06b7e0..b2f09cf4 100644 --- a/askbot/views/meta.py +++ b/askbot/views/meta.py @@ -6,7 +6,7 @@ This module contains a collection of views displaying all sorts of secondary and from django.shortcuts import render_to_response, get_object_or_404 from django.core.urlresolvers import reverse from django.template import RequestContext, Template -from django.http import HttpResponseRedirect, HttpResponse +from django.http import HttpResponseRedirect, HttpResponse, Http404 from django.core.urlresolvers import reverse from django.utils.translation import ugettext as _ from django.views import static @@ -127,6 +127,7 @@ def badges(request):#user status/reputation system def badge(request, id): #todo: supplement database data with the stuff from badges.py badge = get_object_or_404(BadgeData, id=id) + badge_recipients = User.objects.filter( award_user__badge = badge ).annotate( diff --git a/askbot/views/users.py b/askbot/views/users.py index 154df7d8..c625aeab 100644 --- a/askbot/views/users.py +++ b/askbot/views/users.py @@ -354,8 +354,16 @@ def user_stats(request, user, context): for award in user_awards: # Fetch content object if award.content_type_id == post_type.id: - award.content_object = awarded_posts_map[award.object_id] - award.content_object_is_post = True + #here we go around a possibility of awards + #losing the content objects when the content + #objects are deleted for some reason + awarded_post = awarded_posts_map.get(award.object_id, None) + if awarded_post is not None: + #protect from awards that are associated with deleted posts + award.content_object = awarded_post + award.content_object_is_post = True + else: + award.content_object_is_post = False else: award.content_object_is_post = False |