1 from datetime import datetime, timedelta
2 import os, time, csv, random
4 from django.shortcuts import render_to_response, get_object_or_404
5 from django.core.urlresolvers import reverse
6 from django.http import HttpResponseRedirect, HttpResponse, Http404
7 from forum.http_responses import HttpResponseUnauthorized
8 from django.template import RequestContext
9 from django.utils.translation import ugettext as _
10 from django.utils import simplejson
11 from django.db import models
12 from forum.settings.base import Setting
13 from forum.forms import MaintenanceModeForm, PageForm, NodeManFilterForm
14 from forum.settings.forms import SettingsSetForm
15 from forum.utils import pagination
17 from forum.models import Question, Answer, User, Node, Action, Page, NodeState
18 from forum.models.node import NodeMetaClass
19 from forum.actions import NewPageAction, EditPageAction, PublishAction
20 from forum import settings
24 def super_user_required(fn):
25 def wrapper(request, *args, **kwargs):
26 if request.user.is_authenticated() and request.user.is_superuser:
27 return fn(request, *args, **kwargs)
29 return HttpResponseUnauthorized(request)
35 def wrapper(request, *args, **kwargs):
36 res = fn(request, *args, **kwargs)
37 if isinstance(res, tuple):
38 template, context = res
39 context['basetemplate'] = settings.DJSTYLE_ADMIN_INTERFACE and "osqaadmin/djstyle_base.html" or "osqaadmin/base.html"
40 context['allsets'] = Setting.sets
41 context['othersets'] = sorted(
42 [s for s in Setting.sets.values() if not s.name in
43 ('basic', 'users', 'email', 'paths', 'extkeys', 'repgain', 'minrep', 'voting', 'accept', 'badges', 'about', 'faq', 'sidebar',
44 'form', 'moderation', 'css', 'headandfoot', 'head', 'view', 'urls')]
45 , lambda s1, s2: s1.weight - s2.weight)
47 context['tools'] = TOOLS
49 unsaved = request.session.get('previewing_settings', {})
50 context['unsaved'] = set([getattr(settings, s).set.name for s in unsaved.keys() if hasattr(settings, s)])
52 return render_to_response(template, context, context_instance=RequestContext(request))
58 def admin_tools_page(name, label):
66 class ActivityPaginatorContext(pagination.PaginatorContext):
68 super (ActivityPaginatorContext, self).__init__('ADMIN_RECENT_ACTIVITY', pagesizes=(20, 40, 80), default_pagesize=40)
71 def dashboard(request):
72 return ('osqaadmin/dashboard.html', pagination.paginated(request, ("recent_activity", ActivityPaginatorContext()), {
73 'settings_pack': unicode(settings.SETTINGS_PACK),
74 'statistics': get_statistics(),
75 'recent_activity': get_recent_activity(),
76 'flagged_posts': get_flagged_posts(),
80 def interface_switch(request):
81 if request.GET and request.GET.get('to', None) and request.GET['to'] in ('default', 'djstyle'):
82 settings.DJSTYLE_ADMIN_INTERFACE.set_value(request.GET['to'] == 'djstyle')
84 return HttpResponseRedirect(reverse('admin_index'))
87 def statistics(request):
88 today = datetime.now()
89 last_month = today - timedelta(days=30)
91 last_month_questions = Question.objects.filter_state(deleted=False).filter(added_at__gt=last_month
92 ).order_by('added_at').values_list(
93 'added_at', flat=True)
95 last_month_n_questions = Question.objects.filter_state(deleted=False).filter(added_at__lt=last_month).count()
96 qgraph_data = simplejson.dumps([
97 (time.mktime(d.timetuple()) * 1000, i + last_month_n_questions)
98 for i, d in enumerate(last_month_questions)
101 last_month_users = User.objects.filter(date_joined__gt=last_month
102 ).order_by('date_joined').values_list('date_joined', flat=True)
104 last_month_n_users = User.objects.filter(date_joined__lt=last_month).count()
106 ugraph_data = simplejson.dumps([
107 (time.mktime(d.timetuple()) * 1000, i + last_month_n_users)
108 for i, d in enumerate(last_month_users)
111 return 'osqaadmin/statistics.html', {
114 'id': 'questions_graph',
115 'caption': _("Questions Graph"),
118 'id': 'userss_graph',
119 'caption': _("Users Graph"),
126 def tools_page(request, name):
127 if not name in TOOLS:
130 return TOOLS[name](request)
134 def settings_set(request, set_name):
135 set = Setting.sets.get(set_name, {})
136 current_preview = request.session.get('previewing_settings', {})
142 form = SettingsSetForm(set, data=request.POST, files=request.FILES)
145 if 'preview' in request.POST:
146 current_preview.update(form.cleaned_data)
147 request.session['previewing_settings'] = current_preview
149 return HttpResponseRedirect(reverse('index'))
152 current_preview.pop(s.name, None)
154 request.session['previewing_settings'] = current_preview
156 if not 'reset' in request.POST:
158 request.user.message_set.create(message=_("'%s' settings saved succesfully") % set_name)
160 if set_name in ('minrep', 'badges', 'repgain'):
161 settings.SETTINGS_PACK.set_value("custom")
163 return HttpResponseRedirect(reverse('admin_set', args=[set_name]))
165 form = SettingsSetForm(set, unsaved=current_preview)
167 return 'osqaadmin/set.html', {
169 'markdown': set.markdown,
173 def get_default(request, set_name, var_name):
174 set = Setting.sets.get(set_name, None)
175 if set is None: raise Http404
177 setting = dict([(s.name, s) for s in set]).get(var_name, None)
178 if setting is None: raise Http404
182 if request.is_ajax():
183 return HttpResponse(setting.default)
185 return HttpResponseRedirect(reverse('admin_set', kwargs={'set_name': set_name}))
188 def get_recent_activity():
189 return Action.objects.order_by('-action_date')
191 def get_flagged_posts():
192 return Action.objects.filter(canceled=False, action_type="flag").order_by('-action_date')[0:30]
194 def get_statistics():
196 'total_users': User.objects.all().count(),
197 'users_last_24': User.objects.filter(date_joined__gt=(datetime.now() - timedelta(days=1))).count(),
198 'total_questions': Question.objects.filter_state(deleted=False).count(),
199 'questions_last_24': Question.objects.filter_state(deleted=False).filter(
200 added_at__gt=(datetime.now() - timedelta(days=1))).count(),
201 'total_answers': Answer.objects.filter_state(deleted=False).count(),
202 'answers_last_24': Answer.objects.filter_state(deleted=False).filter(
203 added_at__gt=(datetime.now() - timedelta(days=1))).count(),
207 def go_bootstrap(request):
208 #todo: this is the quick and dirty way of implementing a bootstrap mode
210 from forum_modules.default_badges import settings as dbsets
211 dbsets.POPULAR_QUESTION_VIEWS.set_value(100)
212 dbsets.NOTABLE_QUESTION_VIEWS.set_value(200)
213 dbsets.FAMOUS_QUESTION_VIEWS.set_value(300)
214 dbsets.NICE_ANSWER_VOTES_UP.set_value(2)
215 dbsets.NICE_QUESTION_VOTES_UP.set_value(2)
216 dbsets.GOOD_ANSWER_VOTES_UP.set_value(4)
217 dbsets.GOOD_QUESTION_VOTES_UP.set_value(4)
218 dbsets.GREAT_ANSWER_VOTES_UP.set_value(8)
219 dbsets.GREAT_QUESTION_VOTES_UP.set_value(8)
220 dbsets.FAVORITE_QUESTION_FAVS.set_value(1)
221 dbsets.STELLAR_QUESTION_FAVS.set_value(3)
222 dbsets.DISCIPLINED_MIN_SCORE.set_value(3)
223 dbsets.PEER_PRESSURE_MAX_SCORE.set_value(-3)
224 dbsets.CIVIC_DUTY_VOTES.set_value(15)
225 dbsets.PUNDIT_COMMENT_COUNT.set_value(10)
226 dbsets.SELF_LEARNER_UP_VOTES.set_value(2)
227 dbsets.STRUNK_AND_WHITE_EDITS.set_value(10)
228 dbsets.ENLIGHTENED_UP_VOTES.set_value(2)
229 dbsets.GURU_UP_VOTES.set_value(4)
230 dbsets.NECROMANCER_UP_VOTES.set_value(2)
231 dbsets.NECROMANCER_DIF_DAYS.set_value(30)
232 dbsets.TAXONOMIST_USE_COUNT.set_value(5)
236 settings.REP_TO_VOTE_UP.set_value(0)
237 settings.REP_TO_VOTE_DOWN.set_value(15)
238 settings.REP_TO_FLAG.set_value(15)
239 settings.REP_TO_COMMENT.set_value(0)
240 settings.REP_TO_LIKE_COMMENT.set_value(0)
241 settings.REP_TO_UPLOAD.set_value(0)
242 settings.REP_TO_CREATE_TAGS.set_value(0)
243 settings.REP_TO_CLOSE_OWN.set_value(60)
244 settings.REP_TO_REOPEN_OWN.set_value(120)
245 settings.REP_TO_RETAG.set_value(150)
246 settings.REP_TO_EDIT_WIKI.set_value(200)
247 settings.REP_TO_EDIT_OTHERS.set_value(400)
248 settings.REP_TO_CLOSE_OTHERS.set_value(600)
249 settings.REP_TO_DELETE_COMMENTS.set_value(400)
250 settings.REP_TO_VIEW_FLAGS.set_value(30)
252 settings.INITIAL_REP.set_value(1)
253 settings.MAX_REP_BY_UPVOTE_DAY.set_value(300)
254 settings.REP_GAIN_BY_UPVOTED.set_value(15)
255 settings.REP_LOST_BY_DOWNVOTED.set_value(1)
256 settings.REP_LOST_BY_DOWNVOTING.set_value(0)
257 settings.REP_GAIN_BY_ACCEPTED.set_value(25)
258 settings.REP_GAIN_BY_ACCEPTING.set_value(5)
259 settings.REP_LOST_BY_FLAGGED.set_value(2)
260 settings.REP_LOST_BY_FLAGGED_3_TIMES.set_value(30)
261 settings.REP_LOST_BY_FLAGGED_5_TIMES.set_value(100)
263 settings.SETTINGS_PACK.set_value("bootstrap")
265 request.user.message_set.create(message=_('Bootstrap mode enabled'))
266 return HttpResponseRedirect(reverse('admin_index'))
269 def go_defaults(request):
270 for setting in Setting.sets['badges']:
272 for setting in Setting.sets['minrep']:
274 for setting in Setting.sets['repgain']:
277 settings.SETTINGS_PACK.set_value("default")
279 request.user.message_set.create(message=_('All values reverted to defaults'))
280 return HttpResponseRedirect(reverse('admin_index'))
284 def recalculate_denormalized(request):
285 for n in Node.objects.all():
287 n.score = n.votes.aggregate(score=models.Sum('value'))['score']
288 if not n.score: n.score = 0
291 for u in User.objects.all():
292 u.reputation = u.reputes.aggregate(reputation=models.Sum('value'))['reputation']
295 request.user.message_set.create(message=_('All values recalculated'))
296 return HttpResponseRedirect(reverse('admin_index'))
299 def maintenance(request):
301 if 'close' in request.POST or 'adjust' in request.POST:
302 form = MaintenanceModeForm(request.POST)
305 settings.MAINTAINANCE_MODE.set_value({
306 'allow_ips': form.cleaned_data['ips'],
307 'message': form.cleaned_data['message']})
309 if 'close' in request.POST:
310 message = _('Maintenance mode enabled')
312 message = _('Settings adjusted')
314 request.user.message_set.create(message=message)
316 return HttpResponseRedirect(reverse('admin_maintenance'))
317 elif 'open' in request.POST:
318 settings.MAINTAINANCE_MODE.set_value(None)
319 request.user.message_set.create(message=_("Your site is now running normally"))
320 return HttpResponseRedirect(reverse('admin_maintenance'))
322 form = MaintenanceModeForm(initial={'ips': request.META['REMOTE_ADDR'],
323 'message': _('Currently down for maintenance. We\'ll be back soon')})
325 return ('osqaadmin/maintenance.html', {'form': form, 'in_maintenance': settings.MAINTAINANCE_MODE.value is not None
330 def flagged_posts(request):
331 return ('osqaadmin/flagged_posts.html', {
332 'flagged_posts': get_flagged_posts(),
336 def static_pages(request):
337 pages = Page.objects.all()
339 return ('osqaadmin/static_pages.html', {
344 def edit_page(request, id=None):
346 page = get_object_or_404(Page, id=id)
351 form = PageForm(page, request.POST)
354 if form.has_changed():
356 page = NewPageAction(user=request.user, ip=request.META['REMOTE_ADDR']).save(data=form.cleaned_data
359 EditPageAction(user=request.user, node=page, ip=request.META['REMOTE_ADDR']).save(
360 data=form.cleaned_data)
362 if ('publish' in request.POST) and (not page.published):
363 PublishAction(user=request.user, node=page, ip=request.META['REMOTE_ADDR']).save()
364 elif ('unpublish' in request.POST) and page.published:
365 page.nstate.published.cancel(ip=request.META['REMOTE_ADDR'])
367 return HttpResponseRedirect(reverse('admin_edit_page', kwargs={'id': page.id}))
370 form = PageForm(page)
373 published = page.published
377 return ('osqaadmin/edit_page.html', {
380 'published': published
385 @admin_tools_page(_("nodeman"), _("Node management"))
386 def node_management(request):
387 nodes = Node.objects.all()
390 filter_form = NodeManFilterForm(request.GET)
392 filter_form = NodeManFilterForm({'node_type': 'all', 'state_type': 'any'})
394 if filter_form.is_valid():
395 data = filter_form.cleaned_data
397 if data['node_type'] != 'all':
398 nodes = nodes.filter(node_type=data['node_type'])
400 if (data['state_type'] != 'any'):
401 nodes = nodes.filter_state(**{str(data['state_type']): True})
406 if data['text_in'] == 'title' or data['text_in'] == 'both':
407 filter = models.Q(title__icontains=data['text'])
409 if data['text_in'] == 'body' or data['text_in'] == 'both':
410 sec_filter = models.Q(body__icontains=data['text'])
412 filter = filter | sec_filter
417 nodes = nodes.filter(filter)
420 node_types = [('all', _("all"))] + [(k, n.friendly_name) for k, n in NodeMetaClass.types.items()]
421 state_types = NodeState.objects.filter(node__in=nodes).values_list('state_type', flat=True).distinct('state_type')
423 return ('osqaadmin/nodeman.html', pagination.paginated(request, ("nodes", ActivityPaginatorContext()), {
425 'node_types': node_types,
426 'state_types': state_types,
427 'filter_form': filter_form,