diff options
author | Evgeny Fadeev <evgeny.fadeev@gmail.com> | 2012-09-26 12:43:49 -0400 |
---|---|---|
committer | Evgeny Fadeev <evgeny.fadeev@gmail.com> | 2012-09-26 12:43:49 -0400 |
commit | fcbecf69d25a5a1781127655b9dc37148252513d (patch) | |
tree | 5b8598a3fb2488f6f2a459827c4e265f3fb64df1 | |
parent | 74c55ff1382cc11eaa822bb0bf011ddd85797403 (diff) | |
parent | 3b0d0773664b8fa255e7b2ed3b468022e3455847 (diff) | |
download | askbot-fcbecf69d25a5a1781127655b9dc37148252513d.tar.gz askbot-fcbecf69d25a5a1781127655b9dc37148252513d.tar.bz2 askbot-fcbecf69d25a5a1781127655b9dc37148252513d.zip |
Merge branch 'master' into group-messaging
-rw-r--r-- | askbot/doc/source/changelog.rst | 3 | ||||
-rw-r--r-- | askbot/doc/source/intranet-setup.rst | 7 | ||||
-rw-r--r-- | askbot/middleware/forum_mode.py | 4 | ||||
-rw-r--r-- | askbot/migrations/0137_create_groups_from_relevant_tags.py | 2 | ||||
-rw-r--r-- | askbot/models/__init__.py | 82 | ||||
-rw-r--r-- | askbot/setup_templates/settings.py | 3 | ||||
-rw-r--r-- | askbot/setup_templates/settings.py.mustache | 3 | ||||
-rw-r--r-- | askbot/startup_procedures.py | 19 | ||||
-rw-r--r-- | askbot/tasks.py | 77 | ||||
-rw-r--r-- | askbot/templatetags/extra_filters_jinja.py | 11 | ||||
-rw-r--r-- | askbot/tests/templatefilter_tests.py | 4 |
11 files changed, 136 insertions, 79 deletions
diff --git a/askbot/doc/source/changelog.rst b/askbot/doc/source/changelog.rst index d055bf5e..82f46e32 100644 --- a/askbot/doc/source/changelog.rst +++ b/askbot/doc/source/changelog.rst @@ -3,6 +3,9 @@ Changes in Askbot Development version ------------------- +* Added setting `NOTIFICATION_DELAY_TIME` to use with enabled celery daemon (Adolfo) +* Added setting `ASKBOT_INTERNAL_IPS` - to allow anonymous access to + closed sites from dedicated IP addresses (Evgeny) * Moved default skin from `askbot/skins/default` to simply `askbot` (Evgeny) * Repost comment as answer (Adolfo) * Question list widget (Adolfo) diff --git a/askbot/doc/source/intranet-setup.rst b/askbot/doc/source/intranet-setup.rst index 224ffb89..2711b376 100644 --- a/askbot/doc/source/intranet-setup.rst +++ b/askbot/doc/source/intranet-setup.rst @@ -12,3 +12,10 @@ Please change the following settings in your ``settings.py`` file:: In addition, in the "live settings": * disable gravatar in "settings->User settings" + +If you would like to password/protect your site +(achievable via "access control settings" -> "allow only registered users..."), +and at the same time be able to have some dedicated service +to read your site without authentication, add +IP addresses of that service to a tuple ``ASKBOT_INTERNAL_IPS`` +in your ``settings.py`` file. diff --git a/askbot/middleware/forum_mode.py b/askbot/middleware/forum_mode.py index 7f1e29b1..d593a6f2 100644 --- a/askbot/middleware/forum_mode.py +++ b/askbot/middleware/forum_mode.py @@ -45,6 +45,10 @@ class ForumModeMiddleware(object): and request.user.is_anonymous()): resolver_match = ResolverMatch(resolve(request.path)) + internal_ips = getattr(settings, 'ASKBOT_INTERNAL_IPS', None) + if internal_ips and request.META['REMOTE_ADDR'] in internal_ips: + return None + if is_view_allowed(resolver_match.func): return diff --git a/askbot/migrations/0137_create_groups_from_relevant_tags.py b/askbot/migrations/0137_create_groups_from_relevant_tags.py index 8d9a55d2..0150fcbc 100644 --- a/askbot/migrations/0137_create_groups_from_relevant_tags.py +++ b/askbot/migrations/0137_create_groups_from_relevant_tags.py @@ -54,7 +54,7 @@ class Migration(DataMigration): from django.db import connection cursor = connection.cursor() cursor.execute( - 'DROP TRIGGER group_membership_tsv_update_trigger ' + 'DROP TRIGGER IF EXISTS group_membership_tsv_update_trigger ' 'ON askbot_groupmembership' ) diff --git a/askbot/models/__init__.py b/askbot/models/__init__.py index d8f30503..9c777ea7 100644 --- a/askbot/models/__init__.py +++ b/askbot/models/__init__.py @@ -3036,77 +3036,19 @@ def send_instant_notifications_about_activity_in_post( post = None, recipients = None, ): - """ - function called when posts are updated - newly mentioned users are carried through to reduce - database hits - """ - if post.is_approved() is False: - return - - if recipients is None: - return - - acceptable_types = const.RESPONSE_ACTIVITY_TYPES_FOR_INSTANT_NOTIFICATIONS - - if update_activity.activity_type not in acceptable_types: - return - - #calculate some variables used in the loop below - from askbot.skins.loaders import get_template - update_type_map = const.RESPONSE_ACTIVITY_TYPE_MAP_FOR_TEMPLATES - update_type = update_type_map[update_activity.activity_type] - origin_post = post.get_origin_post() - headers = mail.thread_headers( - post, - origin_post, - update_activity.activity_type - ) - - logger = logging.getLogger() - if logger.getEffectiveLevel() <= logging.DEBUG: - log_id = uuid.uuid1() - message = 'email-alert %s, logId=%s' % (post.get_absolute_url(), log_id) - logger.debug(message) - else: - log_id = None - - #send email for all recipients - for user in recipients: - - if user.is_blocked(): - continue - - reply_address, alt_reply_address = get_reply_to_addresses(user, post) - - subject_line, body_text = format_instant_notification_email( - to_user = user, - from_user = update_activity.user, - post = post, - reply_address = reply_address, - alt_reply_address = alt_reply_address, - update_type = update_type, - template = get_template('email/instant_notification.html') - ) - - headers['Reply-To'] = reply_address - try: - mail.send_mail( - subject_line=subject_line, - body_text=body_text, - recipient_list=[user.email], - related_object=origin_post, - activity_type=const.TYPE_ACTIVITY_EMAIL_UPDATE_SENT, - headers=headers, - raise_on_failure=True - ) - except askbot_exceptions.EmailNotSent, error: - logger.debug( - '%s, error=%s, logId=%s' % (user.email, error, log_id) - ) - else: - logger.debug('success %s, logId=%s' % (user.email, log_id)) + if not django_settings.CELERY_ALWAYS_EAGER: + cache_key = 'instant-notification-%d' % post.thread.id + old_task_id = cache.cache.get(cache_key) + if old_task_id: + from celery.task.control import revoke + revoke(old_task_id, terminate=True) + from askbot import tasks + result = tasks.send_instant_nofications.apply_async((update_activity, + post, recipients), + countdown = django_settings.NOTIFICATION_DELAY_TIME) + if not django_settings.CELERY_ALWAYS_EAGER: + cache.cache.set(cache_key, result.task_id, django_settings.NOTIFICATION_DELAY_TIME) def notify_author_of_published_revision( revision = None, was_approved = None, **kwargs diff --git a/askbot/setup_templates/settings.py b/askbot/setup_templates/settings.py index 8577281c..3d24fc5e 100644 --- a/askbot/setup_templates/settings.py +++ b/askbot/setup_templates/settings.py @@ -260,3 +260,6 @@ TINYMCE_DEFAULT_CONFIG = { 'theme_advanced_statusbar_location': 'bottom', 'height': '250' } + +#delayed notifications, time in seconds, 15 mins by default +NOTIFICATION_DELAY_TIME = 60 * 15 diff --git a/askbot/setup_templates/settings.py.mustache b/askbot/setup_templates/settings.py.mustache index 6a40969d..a800edec 100644 --- a/askbot/setup_templates/settings.py.mustache +++ b/askbot/setup_templates/settings.py.mustache @@ -262,3 +262,6 @@ TINYMCE_DEFAULT_CONFIG = { 'width': '723', 'height': '250' } + +#delayed notifications, time in seconds, 15 mins by default +NOTIFICATION_DELAY_TIME = 60 * 15 diff --git a/askbot/startup_procedures.py b/askbot/startup_procedures.py index a33f8047..091338e5 100644 --- a/askbot/startup_procedures.py +++ b/askbot/startup_procedures.py @@ -227,6 +227,25 @@ def test_celery(): """ broker_backend = getattr(django_settings, 'BROKER_BACKEND', None) broker_transport = getattr(django_settings, 'BROKER_TRANSPORT', None) + delay_time = getattr(django_settings, 'NOTIFICATION_DELAY_TIME', None) + delay_setting_info = 'The delay is in seconds - used to throttle ' + \ + 'instant notifications note that this delay will work only if ' + \ + 'celery daemon is running Please search about ' + \ + '"celery daemon setup" for details' + + if delay_time is None: + raise AskbotConfigError( + '\nPlease add to your settings.py\n' + \ + 'NOTIFICATION_DELAY_TIME = 60*15\n' + \ + delay_setting_info + ) + else: + if not isinstance(delay_time, int): + raise AskbotConfigError( + '\nNOTIFICATION_DELAY_TIME setting must have a numeric value\n' + \ + delay_setting_info + ) + if broker_backend is None: if broker_transport is None: diff --git a/askbot/tasks.py b/askbot/tasks.py index 4aa11798..01cd3223 100644 --- a/askbot/tasks.py +++ b/askbot/tasks.py @@ -19,6 +19,8 @@ That is the reason for having two types of methods here: """ import sys import traceback +import logging +import uuid from django.contrib.contenttypes.models import ContentType from django.template import Context @@ -29,6 +31,8 @@ from askbot import const from askbot import mail from askbot.models import Post, Thread, User, ReplyAddress from askbot.models.badges import award_badges_signal +from askbot.models import get_reply_to_addresses, format_instant_notification_email +from askbot import exceptions as askbot_exceptions # TODO: Make exceptions raised inside record_post_update_celery_task() ... # ... propagate upwards to test runner, if only CELERY_ALWAYS_EAGER = True @@ -94,7 +98,7 @@ def notify_author_of_published_revision_celery_task(revision): def record_post_update_celery_task( post_id, post_content_type_id, - newly_mentioned_user_id_list = None, + newly_mentioned_user_id_list = None, updated_by_id = None, timestamp = None, created = False, @@ -138,7 +142,7 @@ def record_question_visit( update_view_count = False): """celery task which records question visit by a person updates view counter, if necessary, - and awards the badges associated with the + and awards the badges associated with the question visit """ #1) maybe update the view count @@ -164,3 +168,72 @@ def record_question_visit( actor = user, context_object = question_post, ) + +@task() +def send_instant_nofications(update_activity=None, + post=None, recipients=None): + + if post.is_approved() is False: + return + + if recipients is None: + return + + acceptable_types = const.RESPONSE_ACTIVITY_TYPES_FOR_INSTANT_NOTIFICATIONS + + if update_activity.activity_type not in acceptable_types: + return + + #calculate some variables used in the loop below + from askbot.skins.loaders import get_template + update_type_map = const.RESPONSE_ACTIVITY_TYPE_MAP_FOR_TEMPLATES + update_type = update_type_map[update_activity.activity_type] + origin_post = post.get_origin_post() + headers = mail.thread_headers( + post, + origin_post, + update_activity.activity_type + ) + + logger = logging.getLogger() + if logger.getEffectiveLevel() <= logging.DEBUG: + log_id = uuid.uuid1() + message = 'email-alert %s, logId=%s' % (post.get_absolute_url(), log_id) + logger.debug(message) + else: + log_id = None + + + for user in recipients: + if user.is_blocked(): + continue + + reply_address, alt_reply_address = get_reply_to_addresses(user, post) + + subject_line, body_text = format_instant_notification_email( + to_user = user, + from_user = update_activity.user, + post = post, + reply_address = reply_address, + alt_reply_address = alt_reply_address, + update_type = update_type, + template = get_template('email/instant_notification.html') + ) + + headers['Reply-To'] = reply_address + try: + mail.send_mail( + subject_line=subject_line, + body_text=body_text, + recipient_list=[user.email], + related_object=origin_post, + activity_type=const.TYPE_ACTIVITY_EMAIL_UPDATE_SENT, + headers=headers, + raise_on_failure=True + ) + except askbot_exceptions.EmailNotSent, error: + logger.debug( + '%s, error=%s, logId=%s' % (user.email, error, log_id) + ) + else: + logger.debug('success %s, logId=%s' % (user.email, log_id)) diff --git a/askbot/templatetags/extra_filters_jinja.py b/askbot/templatetags/extra_filters_jinja.py index 62a41895..ba13166b 100644 --- a/askbot/templatetags/extra_filters_jinja.py +++ b/askbot/templatetags/extra_filters_jinja.py @@ -26,16 +26,19 @@ register = coffin_template.Library() @register.filter def absolutize_urls(text): - url_re1 = re.compile(r'(?P<prefix><img[^<]+src=)"(?P<url>/[^"]+)"', re.I) - url_re2 = re.compile(r"(?P<prefix><img[^<]+src=)'(?P<url>/[^']+)'", re.I) + #temporal fix for bad regex with wysiwyg editor + url_re1 = re.compile(r'(?P<prefix><img[^<]+src=)"(?P<url>[/\..][^"]+)"', re.I) + url_re2 = re.compile(r"(?P<prefix><img[^<]+src=)'(?P<url>[/\..][^']+)'", re.I) url_re3 = re.compile(r'(?P<prefix><a[^<]+href=)"(?P<url>/[^"]+)"', re.I) url_re4 = re.compile(r"(?P<prefix><a[^<]+href=)'(?P<url>/[^']+)'", re.I) - img_replacement = '\g<prefix>"%s\g<url>" style="max-width:500px;"' % askbot_settings.APP_URL + img_replacement = '\g<prefix>"%s/\g<url>" style="max-width:500px;"' % askbot_settings.APP_URL replacement = '\g<prefix>"%s\g<url>"' % askbot_settings.APP_URL text = url_re1.sub(img_replacement, text) text = url_re2.sub(img_replacement, text) text = url_re3.sub(replacement, text) - return url_re4.sub(replacement, text) + #temporal fix for bad regex with wysiwyg editor + return url_re4.sub(replacement, text).replace('%s//' % askbot_settings.APP_URL, + '%s/' % askbot_settings.APP_URL) TIMEZONE_STR = pytz.timezone( diff --git a/askbot/tests/templatefilter_tests.py b/askbot/tests/templatefilter_tests.py index 090be956..3902aad4 100644 --- a/askbot/tests/templatefilter_tests.py +++ b/askbot/tests/templatefilter_tests.py @@ -6,12 +6,12 @@ class AbsolutizeUrlsTests(TestCase): def setUp(self): askbot_settings.update('APP_URL', 'http://example.com') def test_absolutize_image_urls(self): - text = """<img class="junk" src="/some.gif"> <IMG SRC='/some.png'>""" + text = """<img class="junk" src="/some.gif"> <img class="junk" src="../../cat.gif"> <IMG SRC='/some.png'>""" #jinja register.filter decorator works in a weird way output = filters.absolutize_urls[0](text) self.assertEqual( output, - '<img class="junk" src="http://example.com/some.gif" style="max-width:500px;"> <IMG SRC="http://example.com/some.png" style="max-width:500px;">' + '<img class="junk" src="http://example.com/some.gif" style="max-width:500px;"> <img class="junk" src="http://example.com/../../cat.gif" style="max-width:500px;"> <IMG SRC="http://example.com/some.png" style="max-width:500px;">' ) def test_absolutize_anchor_urls(self): text = """<a class="junk" href="/something">link</a> <A HREF='/something'>link</A>""" |