summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--askbot/doc/source/changelog.rst3
-rw-r--r--askbot/doc/source/intranet-setup.rst7
-rw-r--r--askbot/middleware/forum_mode.py4
-rw-r--r--askbot/migrations/0137_create_groups_from_relevant_tags.py2
-rw-r--r--askbot/models/__init__.py82
-rw-r--r--askbot/setup_templates/settings.py3
-rw-r--r--askbot/setup_templates/settings.py.mustache3
-rw-r--r--askbot/startup_procedures.py19
-rw-r--r--askbot/tasks.py77
-rw-r--r--askbot/templatetags/extra_filters_jinja.py11
-rw-r--r--askbot/tests/templatefilter_tests.py4
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>"""