-
Notifications
You must be signed in to change notification settings - Fork 4.3k
add sysadmin dashboard for Studio #719
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
7ee34aa
f6b4799
d9cfa9f
5ae00e1
46e84f3
e304803
b7d70b7
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,113 @@ | ||
| """ | ||
| sysadmin - a custom page for MITx that they use to manage their instance. This page is enabled via | ||
| a FEATURE FLAG | ||
| """ | ||
| import subprocess | ||
| import logging | ||
|
|
||
| from django_future.csrf import ensure_csrf_cookie | ||
| from django.contrib.auth.decorators import login_required | ||
| from django.shortcuts import redirect | ||
| from django.conf import settings | ||
| from django.http import HttpResponse | ||
| from django.utils.translation import ugettext as _ | ||
| from django.http import Http404 | ||
|
|
||
| from xmodule.modulestore.django import modulestore | ||
|
|
||
| from mitxmako.shortcuts import render_to_response | ||
|
|
||
| #import pymongo | ||
| import time | ||
| import json | ||
|
|
||
|
|
||
| @login_required | ||
| @ensure_csrf_cookie | ||
| def sysadmin(request): | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
|
||
| """ | ||
| sysadmin page: for now, just list courses and allow deletion | ||
| """ | ||
| if (not request.user) or (not request.user.is_staff): | ||
| return redirect('login') | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Since this view is |
||
|
|
||
| if not settings.MITX_FEATURES.get('ENABLE_MITX_SYSADMIN_PAGE', False): | ||
| raise Http404 | ||
|
|
||
| collection = modulestore().collection | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This will still break when we move to split mongo |
||
|
|
||
| msg = '' | ||
| bdir = getattr(settings, 'DELETED_COURSE_BACKUPS_DIR', None) | ||
|
|
||
| action = request.GET.get('action', request.POST.get('action', '')) | ||
| course_id = request.GET.get('course_id', '') | ||
|
|
||
| if action=='delete': | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Shouldn't this use a request with
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. no - it's not a RESTful API; this action is from a button
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This style of |
||
| if not course_id: | ||
| msg += "<font color='red'>{0}</font>".format(_('Error - no course specified')) | ||
| else: | ||
| nrec = collection.find({'_id.course': course_id}).count() | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This will still delete courses from other orgs |
||
| if not 'really' in request.GET: | ||
| msg += _("Really delete course {0}?\n").format(course_id) | ||
| msg += _("{0} records for this course in the database").format(nrec) | ||
| logging.debug('Delete course %s requested' % course_id) | ||
| else: | ||
| msg += _("deleting {0}: ").format(course_id) | ||
| data = collection.find({'_id.course': course_id}) | ||
| fn = 'course-%s-dump-%s.json' % (course_id, time.ctime(time.time()).replace(' ','_')) | ||
| if bdir is not None: | ||
| with open('%s/%s' % (bdir, fn), 'w') as fp: | ||
| for d in data: | ||
| fp.write(json.dumps(d)+'\n') | ||
| collection.remove({'_id.course': course_id}) | ||
| msg += _("{0} records for {1} removed (backup file {2})").format(nrec, course_id, fn) | ||
| logging.debug('Course %s deleted!' % course_id) | ||
| action = "" | ||
|
|
||
| elif action=='dump': | ||
| if not course_id: | ||
| msg += "<font color='red'>{0}</font>".format(_("Error - no course specified")) | ||
| else: | ||
| data = collection.find({'_id.course': course_id}) | ||
| response = HttpResponse(mimetype='text/json') | ||
| fn = 'course-%s-dump-%s.json' % (course_id, time.ctime(time.time()).replace(' ','_')) | ||
| response['Content-Disposition'] = 'attachment; filename={0}'.format(fn) | ||
| data = collection.find({'_id.course': course_id}) | ||
| for d in data: | ||
| response.write(json.dumps(d)+'\n') | ||
| return response | ||
|
|
||
| elif action=='Add Course' and request.method=='POST': | ||
| ''' | ||
| Add course by running external script, given git URL provided in input form | ||
| ''' | ||
| giturl = request.POST.get('giturl', '') | ||
| acscript = getattr(settings, 'CMS_ADD_COURSE_SCRIPT', '') | ||
| cmd = '{0} "{1}"'.format(acscript, giturl) | ||
| logging.debug('Adding course with command: {0}'.format(cmd)) | ||
| ret = subprocess.Popen(cmd, shell=True, executable = "/bin/bash", | ||
| stdout=subprocess.PIPE, stderr=subprocess.PIPE).communicate() | ||
| ret = ''.join(ret) | ||
| msg = "<font color='red'>{0} {1}</font>".format(_("Added course from"), giturl) | ||
| msg += "<pre>{0}</pre>".format(ret.replace('<','<')) | ||
|
|
||
| #----------------------------------------------------------------------------- | ||
| # get list of courses | ||
|
|
||
| ctab = {} | ||
| idtab = {} | ||
| courses = collection.distinct('_id.course') | ||
| for course in courses: | ||
| cinfo = collection.find_one({'_id.course':course, '_id.category':'course'}) or {} | ||
| id = cinfo.get('_id',cinfo) | ||
| name = id.get('name',id) | ||
| ctab[course] = name | ||
| idtab[course] = id | ||
|
|
||
| context = {'ctab': ctab, | ||
| 'idtab': idtab, | ||
| 'msg': msg, | ||
| 'course_id': course_id, | ||
| 'action': action, | ||
| } | ||
| return render_to_response('sysadmin.html', context) | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,43 @@ | ||
| <%inherit file="base.html" /> | ||
| <%! from django.utils.translation import ugettext as _ %> | ||
|
|
||
| <%block name="content"> | ||
| <article> | ||
| % if action=="delete": | ||
| <font color="red">${_("Really delete {0}").format(course_id)}?</font> | ||
| <a href="/sysadmin">No (Cancel)</a> | ||
| <a style="float:right" href="/sysadmin?action=delete&course_id=${course_id}&really=1">${_("Yes")} </a> | ||
| % endif | ||
|
|
||
| <pre> | ||
| ${msg} | ||
| </pre> | ||
| <hr width="50%"/> | ||
| <table border="1"> | ||
| % for cid in sorted(ctab.iterkeys()): | ||
| <% | ||
| id = idtab[cid] | ||
| org = id.get('org','') | ||
| name = id.get('name','') | ||
| nameurl = name.replace(' ','_') | ||
| %> | ||
| <tr> | ||
| <td><a href="/${org}/${cid}/course/${nameurl}">${_("GO")}</a> | </td> | ||
| <td><a href="/${org}/${cid}/generate_export/${nameurl}">${_("tar.gz export")}</a> |</td> | ||
| <td><a href="/sysadmin?action=dump&course_id=${cid}">${_("db dump")}</a> |</td> | ||
| <td><a href="/sysadmin?action=delete&course_id=${cid}">${_("delete")}</a> |</td> | ||
| <td>${cid}</td> | ||
| <td>${name}</td> | ||
| </tr> | ||
| % endfor | ||
| </table> | ||
|
|
||
| <br/> | ||
| <hr width="75%" /> | ||
| <form name="sysadminform" method="POST" action="/sysadmin"> | ||
| <input type="hidden" name="csrfmiddlewaretoken" value="${ csrf_token }"> | ||
| git ssh url: <input size="60" name="giturl" /> | ||
| <input type="submit" name="action" value="${_("Add Course")}"/> | ||
| </form> | ||
| </article> | ||
| </%block> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please add a module level docstring describing what functionality this set of views implements.