Skip to content
Snippets Groups Projects
Select Git revision
  • 3bfa3abee41afd4e870cddac5fd37ecbba63dac2
  • master default protected
  • intros
  • live_sources
  • bootstrap4
  • modules
6 results

edit.py

Blame
  • Forked from Video AG Infrastruktur / website
    Source project has a limited visibility.
    Code owners
    Assign users and groups as approvers for specific file changes. Learn more.
    edit.py 7.96 KiB
    from server import *
    
    # field types:
    # 	boolean
    # 	shortstring
    # 	text
    # 	datetime
    # 	duration
    # 	videotime
    editable_tables = {
    	'courses': {
    		'table': 'courses_data',
    		'idcolumn': 'id',
    		'editable_fields': {
    			'visible':	{'type': 'boolean', 'description': 'Wenn ein Kurs nicht sichtbar ist sind alle Videos davon nicht abrufbar.'},
    			'listed':	{'type': 'boolean', 'description': 'Soll die Veranstaltung auf der Hauptseite gelistet werden?'},
    			'title':	{'type': 'shortstring'},
    			'short':	{'type': 'shortstring', 'description': 'Abkürzung für die Veranstaltung, z.B. für den Drehplan'},
    			'handle':	{'type': 'shortstring'},
    			'organizer':	{'type': 'shortstring'},
    			'subject':	{'type': 'shortstring'},
    			'semester':	{'type': 'shortstring'},
    			'downloadable':	{'type': 'boolean', 'description': 'Hiermit kann der Download-Button disabled werden'},
    			'internal':	{'type': 'text'},
    			'responsible':	{'type': 'shortstring'},
    			'deleted':	{'type': 'boolean'},
    			'description':	{'type': 'text'},
    			'external':	{'type': 'boolean', 'description': 'Soll die Veranstaltung nicht im Drehplan angezeigt werden?'},
    			'coursechapters':	{'type': 'boolean', 'description': 'Sollen auf der Kursseite die Kapitelmarker der Videos angezeigt werden?'}},
    		'creationtime_fields': ['created_by', 'time_created', 'time_updated'] },
    	'lectures': {
    		'table': 'lectures_data',
    		'idcolumn': 'id',
    		'editable_fields': {
    			'visible':	{'type': 'boolean', 'description': 'Wenn eine lecture nicht sichtbar ist sind alle Videos davon nicht abrufbar'},
    			'title':	{'type': 'shortstring'},
    			'comment':	{'type': 'text'},
    			'internal':	{'type': 'text'},
    			'speaker':	{'type': 'shortstring'},
    			'place':	{'type': 'shortstring'},
    			'time':		{'type': 'datetime'},
    			'duration':	{'type': 'duration'},
    			'jumplist':	{'type': ''},
    			'deleted':	{'type': 'boolean'},
    			'live':		{'type': 'boolean', 'description': 'Ist ein Livestream geplant? Muss gesetzt sein damit der RTMP Stream zugeordnet wird.'},
    			'norecording': {'type': 'boolean', 'description:': 'Führt dazu, dass der Termin ausgegraut wird.'}},
    		'creationtime_fields': ['course_id', 'time_created', 'time_updated'] },
    	'videos': {
    		'table': 'videos_data',
    		'idcolumn': 'id',
    		'editable_fields': {
    			'visible':	{'type': 'boolean', 'description': 'Ein nicht sichtbares Video kann nicht abgerufen werden.'},
    			'deleted':	{'type': 'boolean'}},
    		'creationtime_fields': ['created_by', 'time_created', 'time_updated'] },
    	'chapters': {
    		'table': 'chapters',
    		'idcolumn': 'id',
    		'editable_fields': {
    			'time':		{'type': 'videotime'},
    			'text':		{'type': 'shortstring'},
    			'visible':	{'type': 'boolean'},
    			'deleted':	{'type': 'boolean'}},
    		'creationtime_fields': ['created_by', 'time_created', 'time_updated'] },
    	'announcements': {
    		'table': 'announcements',
    		'idcolumn': 'id',
    		'editable_fields': {
    			'text':		{'type': 'text'},
    			'level':	{'type': 'integer'},
    			'visible':	{'type': 'boolean'},
    			'deleted':	{'type': 'boolean'},
    			'time_publish':	{'type': 'datetime'},
    			'time_expire':	{'type': 'datetime'}},
    		'creationtime_fields': ['created_by', 'time_created', 'time_updated'] },
    	'featured': {
    		'table': 'featured',
    		'idcolumn': 'id',
    		'editable_fields':	{
    			'title':	{'type': 'shortstring'},
    			'text':		{'type': 'text'},
    			'internal':	{'type': 'text'},
    			'visible':	{'type': 'boolean'},
    			'deleted':	{'type': 'boolean'},
    			'param':	{'type': 'shortstring'},
    			'param2':	{'type': 'shortstring'},
    			'order':	{'type': 'integer' }},
    		'creationtime_fields': ['created_by', 'time_created', 'time_updated', 'type'] },
    	'perm': {
    		'table': 'perm',
    		'idcolumn': 'id',
    		'editable_fields': {
    			'type':		{'type': 'shortstring'},
    			'param1':	{'type': 'shortstring'},
    			'param2':	{'type': 'shortstring'},
    			'deleted':	{'type': 'boolean'}},
    		'creationtime_fields': ['course_id', 'lecture_id', 'video_id', 'created_by', 'time_created', 'time_updated'] },
    	'sorterrorlog': {
    		'table': 'sorterrorlog_data',
    		'idcolumn': 'id',
    		'editable_fields': {
    			'deleted':	{'type': 'boolean'}},
    		'creationtime_fields': ['time_created', 'time_updated'] }
    	}
    
    #parses the path to a dict, containing the table, id, field and field type
    @app.template_filter(name='parseeditpath')
    def parseeditpath(path):
    	table, id, column = path.split('.', 2)
    	assert table in editable_tables
    	assert column in editable_tables[table]['editable_fields']
    	type = editable_tables[table]['editable_fields'][column]['type']
    	return {'table': table, 'id': id, 'column': column, 'type': type, 'tableinfo': editable_tables[table]}
    
    @app.template_filter(name='getfielddescription')
    def getfielddescription(path):
    	p = parseeditpath(path)
    	desc = p['tableinfo']['editable_fields'][p['column']].get('description', '')
    	if desc != '':
    		desc = '<br>'+desc
    	return desc
    
    @app.route('/internal/edit', methods=['GET', 'POST'])
    @mod_required
    @csrf_protect
    def edit(prefix='', ignore=[]):
    	# All editable tables are expected to have a 'time_updated' field
    	ignore.append('ref')
    	ignore.append('prefix')
    	ignore.append('_csrf_token')
    	if not prefix and 'prefix' in request.args:
    		prefix = request.args['prefix']
    	changes = request.values.items()
    	if (request.method == 'POST') and (request.get_json()):
    		changes = request.get_json().items()
    	for key, val in changes:
    		if key in ignore:
    			continue
    		key = prefix+key
    		path = parseeditpath(key)
    		modify('INSERT INTO changelog (`table`,id_value, id_key, field, value_new, value_old, `when`, who, executed) \
    			VALUES (?,?,?,?,?,(SELECT `%s` FROM %s WHERE %s = ?),?,?,1)'%(path['column'], path['tableinfo']['table'], path['tableinfo']['idcolumn']),
    				path['table'], path['id'], path['tableinfo']['idcolumn'], path['column'], val, path['id'], datetime.now(), session['user']['dbid'])
    		modify('UPDATE %s SET `%s` = ?, time_updated = ? WHERE `%s` = ?'%(path['tableinfo']['table'], path['column'], path['tableinfo']['idcolumn']), val, datetime.now(),path['id'])
    		for func in edit_handlers.get(path['table'], {}).get(None, []):
    			func(path['table'], path['column'], val, path['id'])
    		for func in edit_handlers.get(path['table'], {}).get(path['column'], []):
    			func(path['table'], path['column'], val, path['id'])
    	if 'ref' in request.values:
    		return redirect(request.values['ref'])
    	return "OK", 200
    
    @app.route('/internal/new/<table>', methods=['GET', 'POST'])
    @mod_required
    @csrf_protect
    def create(table):
    	assert table in editable_tables
    	defaults = {'created_by': session['user']['dbid'], 'time_created': datetime.now(), 'time_updated': datetime.now()}
    	columns = []
    	values = []
    	for column, val in defaults.items():
    		if column in editable_tables[table]['creationtime_fields']:
    			columns.append(column)
    			values.append(val)
    	args = request.values.items()
    	if (request.method == 'POST') and (request.get_json()):
    		args = request.get_json().items()
    	for column, val in args:
    		if (column == 'ref') or (column == '_csrf_token'):
    			continue
    		assert column in list(editable_tables[table]['editable_fields'].keys())+editable_tables[table]['creationtime_fields']
    		assert column not in defaults
    		columns.append('`'+column+'`')
    		values.append(val)
    	id = modify('INSERT INTO %s (%s) VALUES (%s)'%(editable_tables[table]['table'],
    				','.join(columns), ','.join(['?']*len(values))), *values)
    	if 'ref' in request.values:
    		return redirect(request.values['ref'])
    	return str(id), 200
    
    @app.route('/internal/changelog')
    @register_navbar('Changelog', icon='book')
    @mod_required
    def changelog():
    	if 'page' in request.args:
    		page = max(0, int(request.args['page']))
    	else:
    		page = 0
    	if 'pagesize' in request.args:
    		pagesize = min(500, int(request.args['pagesize']))
    	else:
    		pagesize = 50
    	changelog = query('SELECT * FROM changelog LEFT JOIN users ON (changelog.who = users.id) ORDER BY `when` DESC LIMIT ? OFFSET ?', pagesize, page*pagesize)
    	pagecount = math.ceil(query('SELECT count(id) as count FROM changelog')[0]['count']/pagesize)
    	for entry in changelog:
    		entry['path'] = '.'.join([entry['table'], entry['id_value'], entry['field']])
    	return render_template('changelog.html', changelog=changelog, page=page, pagesize=pagesize, pagecount=pagecount)