summaryrefslogtreecommitdiffstats
path: root/askbot/utils/decorators.py
blob: 1cf2005957c45ddb4977411abec61c7ff0f5cef9 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
import hotshot
import time
import os
import datetime
import functools
import inspect
import logging
from django.conf import settings
from django.core import exceptions as django_exceptions
from django.core.urlresolvers import reverse
from django.core.exceptions import ImproperlyConfigured
from django.http import HttpResponse, HttpResponseForbidden, Http404
from django.http import HttpResponseRedirect
from django.utils import simplejson
from django.utils.translation import ugettext as _
from django.utils.encoding import smart_str
from askbot import exceptions as askbot_exceptions
from askbot.conf import settings as askbot_settings
from askbot.utils import url_utils
from askbot.utils.html import site_url
from askbot import get_version

def auto_now_timestamp(func):
    """decorator that will automatically set
    argument named timestamp to the "now" value if timestamp == None

    if there is no timestamp argument, then exception is raised
    """
    @functools.wraps(func)
    def decorated_func(*arg, **kwarg):
        timestamp = kwarg.get('timestamp', None)
        if timestamp is None:
            kwarg['timestamp'] = datetime.datetime.now()
        return func(*arg, **kwarg)
    return decorated_func


def ajax_login_required(view_func):
    @functools.wraps(view_func)
    def wrap(request, *args, **kwargs):
        if request.user.is_authenticated():
            return view_func(request, *args, **kwargs)
        else:
            json = simplejson.dumps({'login_required':True})
            return HttpResponseForbidden(json, mimetype='application/json')
    return wrap


def anonymous_forbidden(view_func):
    @functools.wraps(view_func)
    def wrapper(request, *args, **kwargs):
        if request.user.is_anonymous():
            raise askbot_exceptions.LoginRequired()
        return view_func(request, *args, **kwargs)
    return wrapper


def get_only(view_func):
    @functools.wraps(view_func)
    def wrapper(request, *args, **kwargs):
        if request.method != 'GET':
            raise django_exceptions.PermissionDenied(
                'request method %s is not supported for this function' % \
                request.method
            )
        return view_func(request, *args, **kwargs)
    return wrapper


def post_only(view_func):
    @functools.wraps(view_func)
    def wrapper(request, *args, **kwargs):
        if request.method != 'POST':
            raise django_exceptions.PermissionDenied(
                'request method %s is not supported for this function' % \
                request.method
            )
        return view_func(request, *args, **kwargs)
    return wrapper

def ajax_only(view_func):
    @functools.wraps(view_func)
    def wrapper(request, *args, **kwargs):
        if not request.is_ajax():
            raise Http404
        try:
            data = view_func(request, *args, **kwargs)
            if data is None:
                data = {}
        except Exception, e:
            #todo: also check field called "message"
            if hasattr(e, 'messages'):
                if len(e.messages) > 1:
                    message = u'<ul>' + \
                        u''.join( 
                            map(lambda v: u'<li>%s</li>' % v, e.messages)
                        ) + \
                        u'</ul>'
                else:
                    message = e.messages[0]
            else:
                message = unicode(e)
            if message == '':
                message = _('Oops, apologies - there was some error')
            logging.debug(message)
            data = {
                'message': message,
                'success': 0
            }
            return HttpResponse(simplejson.dumps(data), mimetype='application/json')

        if isinstance(data, HttpResponse):#is this used?
            data.mimetype = 'application/json'
            return data
        else:
            data['success'] = 1
            json = simplejson.dumps(data)
            return HttpResponse(json, mimetype='application/json')
    return wrapper

def check_authorization_to_post(func_or_message):

    message = _('Please login to post')
    if not inspect.isfunction(func_or_message):
        message = unicode(func_or_message)

    def decorator(view_func):
        @functools.wraps(view_func)
        def wrapper(request, *args, **kwargs):
            if request.user.is_anonymous():
                #todo: expand for handling ajax responses
                if askbot_settings.ALLOW_POSTING_BEFORE_LOGGING_IN == False:
                    request.user.message_set.create(message = message)
                    params = 'next=%s' % request.path
                    return HttpResponseRedirect(url_utils.get_login_url() + '?' + params)
            return view_func(request, *args, **kwargs)
        return wrapper

    if inspect.isfunction(func_or_message):
        return decorator(func_or_message)
    else:
        return decorator

try:
    PROFILE_LOG_BASE = settings.PROFILE_LOG_BASE
except:
    PROFILE_LOG_BASE = "/tmp"

def profile(log_file):
    """Profile some callable.

    This decorator uses the hotshot profiler to profile some callable (like
    a view function or method) and dumps the profile data somewhere sensible
    for later processing and examination.

    It takes one argument, the profile log name. If it's a relative path, it
    places it under the PROFILE_LOG_BASE. It also inserts a time stamp into the 
    file name, such that 'my_view.prof' become 'my_view-20100211T170321.prof', 
    where the time stamp is in UTC. This makes it easy to run and compare 
    multiple trials.     

    http://code.djangoproject.com/wiki/ProfilingDjango
    """

    if not os.path.isabs(log_file):
        log_file = os.path.join(PROFILE_LOG_BASE, log_file)

    def _outer(f):
        def _inner(*args, **kwargs):
            # Add a timestamp to the profile output when the callable
            # is actually called.
            (base, ext) = os.path.splitext(log_file)
            base = base + "-" + time.strftime("%Y%m%dT%H%M%S", time.gmtime())
            final_log_file = base + ext

            prof = hotshot.Profile(final_log_file)
            try:
                ret = prof.runcall(f, *args, **kwargs)
            finally:
                prof.close()
            return ret

        return _inner
    return _outer

def check_spam(field):
    '''Decorator to check if there is spam in the form'''

    def decorator(view_func):
        @functools.wraps(view_func)
        def wrapper(request, *args, **kwargs):

            if askbot_settings.USE_AKISMET and askbot_settings.AKISMET_API_KEY == "":
                raise ImproperlyConfigured('You have not set AKISMET_API_KEY')

            if askbot_settings.USE_AKISMET and request.method == "POST":
                comment = smart_str(request.POST[field])
                data = {'user_ip': request.META["REMOTE_ADDR"],
                        'user_agent': request.environ['HTTP_USER_AGENT'],
                        'comment_author': smart_str(request.user.username),
                        }
                if request.user.is_authenticated():
                    data.update({'comment_author_email': request.user.email})

                from akismet import Akismet
                api = Akismet(
                    askbot_settings.AKISMET_API_KEY, 
                    smart_str(site_url(reverse('questions'))), 
                    "Askbot/%s" % get_version()
                )

                if api.comment_check(comment, data, build_data=False):
                    logging.debug(
                        'Spam detected in %s post at: %s',
                        request.user.username,
                        datetime.datetime.now()
                    )
                    spam_message = _(
                        'Spam was detected on your post, sorry '
                        'for if this is a mistake'
                    )
                    if request.is_ajax():
                        return HttpResponseForbidden(
                                spam_message, 
                                mimetype="application/json"
                            )
                    else:
                        request.user.message_set.create(message=spam_message)
                        return HttpResponseRedirect(reverse('index'))

            return view_func(request, *args, **kwargs)
        return wrapper

    return decorator

def admins_only(view_func):
    @functools.wraps(view_func)
    def decorator(request, *args, **kwargs):
        if request.user.is_anonymous():
            raise django_exceptions.PermissionDenied()
        if not request.user.is_administrator_or_moderator():
            raise django_exceptions.PermissionDenied(
            _('This function is limited to moderators and administrators')
        )
        return view_func(request, *args, **kwargs)
    return decorator