def user_insert(): form = UserForm() form.group.choices = get_groups() if not session['settings']['mail']: del form.send_info if form.validate_on_submit(): user_id = UserMapper.insert(form) flash(_('user created'), 'info') if session['settings']['mail'] and form.send_info.data: # pragma: no cover subject = _( 'Your account information for %(sitename)s', sitename=session['settings']['site_name']) body = _('Account information for %(username)s', username=form.username.data) + ' ' body += _('at') + ' ' + request.scheme + '://' + request.headers['Host'] + '\n\n' body += uc_first(_('username')) + ': ' + form.username.data + '\n' body += uc_first(_('password')) + ': ' + form.password.data + '\n' if send_mail(subject, body, form.email.data, False): flash( _('Sent account information mail to %(email)s.', email=form.email.data), 'info') else: flash( _('Failed to send account details to %(email)s.', email=form.email.data), 'error') return redirect(url_for('user_index')) if form.continue_.data == 'yes': return redirect(url_for('user_insert')) return redirect(url_for('user_view', id_=user_id)) return render_template('user/insert.html', form=form)
def build_table_form(class_name: str, linked_entities: Iterator) -> str: """ Returns a form with a list of entities with checkboxes""" from openatlas.models.entity import EntityMapper table = Table(Table.HEADERS[class_name] + ['']) linked_ids = [entity.id for entity in linked_entities] file_stats = get_file_stats() if class_name == 'file' else None if class_name == 'file': entities = EntityMapper.get_by_system_type('file', nodes=True) elif class_name == 'place': entities = EntityMapper.get_by_system_type('place', nodes=True, aliases=True) else: entities = EntityMapper.get_by_codes(class_name) for entity in entities: if entity.id in linked_ids: continue # Don't show already linked entries input_ = '<input id="selection-{id}" name="values" type="checkbox" value="{id}">'.format( id=entity.id) table.rows.append(get_base_table_data(entity, file_stats) + [input_]) if not table.rows: return uc_first(_('no entries')) return """ <form class="table" id="checkbox-form" method="post"> <input id="csrf_token" name="csrf_token" type="hidden" value="{token}"> <input id="checkbox_values" name="checkbox_values" type="hidden"> {table} <button name="form-submit" id="form-submit" type="submit">{add}</button> </form>""".format(add=uc_first(_('add')), token=generate_csrf(), table=table.display(class_name))
def export_csv(): path = app.config['EXPORT_FOLDER_PATH'] + '/csv' writeable = True if os.access(path, os.W_OK) else False form = ExportCsvForm() if form.validate_on_submit() and writeable: Export.export_csv(form) logger.log('info', 'database', 'CSV export') flash(_('data was exported as CSV'), 'info') return redirect(url_for('export_csv')) table = {'id': 'csv', 'header': ['name', 'size'], 'data': [], 'sort': 'sortList: [[0, 1]],headers: {0: { sorter: "text" }}'} for file in [f for f in os.listdir(path) if os.path.isfile(os.path.join(path, f))]: name = basename(file) if name == '.gitignore': continue link = '<a href="{url}">{label}</a>'.format(url=url_for('download_csv', filename=name), label=uc_first(_('download'))) data = [name, convert_size(os.path.getsize(path + '/' + name)), link] if is_authorized('admin') and writeable: confirm = ' onclick="return confirm(\'' + _('Delete %(name)s?', name=name) + '\')"' delete = '<a href="' + url_for('delete_csv', filename=name) delete += '" ' + confirm + '>' + uc_first(_('delete')) + '</a>' data.append(delete) table['data'].append(data) return render_template('export/export_csv.html', form=form, table=table, writeable=writeable)
def export_sql(): path = app.config['EXPORT_FOLDER_PATH'] + '/sql' writeable = True if os.access(path, os.W_OK) else False form = ExportSqlForm() if form.validate_on_submit() and writeable: if Export.export_sql(): logger.log('info', 'database', 'SQL export') flash(_('data was exported as SQL'), 'info') else: # pragma: no cover logger.log('error', 'database', 'SQL export failed') flash(_('SQL export failed'), 'error') return redirect(url_for('export_sql')) table = {'id': 'sql', 'header': ['name', 'size'], 'data': [], 'sort': 'sortList: [[0, 1]],headers: {0: { sorter: "text" }}'} for file in [f for f in os.listdir(path) if os.path.isfile(os.path.join(path, f))]: name = basename(file) if name == '.gitignore': continue url = url_for('download_sql', filename=name) data = [name, convert_size(os.path.getsize(path + '/' + name)), '<a href="' + url + '">' + uc_first(_('download')) + '</a>'] if is_authorized('admin') and writeable: confirm = ' onclick="return confirm(\'' + _('Delete %(name)s?', name=name) + '\')"' delete = '<a href="' + url_for('delete_sql', filename=name) delete += '" ' + confirm + '>' + uc_first(_('delete')) + '</a>' data.append(delete) table['data'].append(data) return render_template('export/export_sql.html', form=form, table=table, writeable=writeable)
def add_buttons(form: Any, name: str, entity: Union[Entity, None], origin: Optional[Entity] = None) -> FlaskForm: setattr(form, 'save', SubmitField(_('save') if entity else _('insert'))) if entity: return form if 'continue' in FORMS[name] and ( name in ['involvement', 'find', 'human_remains', 'type'] or not origin): setattr(form, 'insert_and_continue', SubmitField(uc_first(_('insert and continue')))) setattr(form, 'continue_', HiddenField()) insert_add = uc_first(_('insert and add')) + ' ' if name == 'place': setattr(form, 'insert_and_continue', SubmitField(uc_first(_('insert and continue')))) setattr(form, 'continue_', HiddenField()) setattr(form, 'insert_continue_sub', SubmitField(insert_add + _('feature'))) elif name == 'feature' and origin and origin.class_.name == 'place': setattr(form, 'insert_and_continue', SubmitField(uc_first(_('insert and continue')))) setattr(form, 'continue_', HiddenField()) setattr(form, 'insert_continue_sub', SubmitField(insert_add + _('stratigraphic unit'))) elif name == 'stratigraphic_unit': setattr(form, 'insert_and_continue', SubmitField(uc_first(_('insert and continue')))) setattr(form, 'continue_', HiddenField()) setattr(form, 'insert_continue_sub', SubmitField(insert_add + _('find'))) setattr(form, 'insert_continue_human_remains', SubmitField(insert_add + _('human remains'))) return form
def reset_confirm(code): # pragma: no cover user = UserMapper.get_by_reset_code(code) if not user: logger.log('info', 'auth', 'unknown reset code') flash(_('invalid password reset confirmation code'), 'error') abort(404) hours = session['settings']['reset_confirm_hours'] if datetime.datetime.now() > user.password_reset_date + datetime.timedelta(hours=hours): logger.log('info', 'auth', 'reset code expired') flash(_('This reset confirmation code has expired.'), 'error') abort(404) password = UserMapper.generate_password() user.password = bcrypt.hashpw(password.encode('utf-8'), bcrypt.gensalt()).decode('utf-8') user.password_reset_code = None user.password_reset_date = None user.login_failed_count = 0 user.update() subject = _('New password for %(sitename)s', sitename=session['settings']['site_name']) body = _('New password for %(username)s', username=user.username) + ' ' body += _('at') + ' ' + request.scheme + '://' + request.headers['Host'] + ':\n\n' body += uc_first(_('username')) + ': ' + user.username + '\n' body += uc_first(_('password')) + ': ' + password + '\n' if send_mail(subject, body, user.email, False): flash(_('Send new password mail to %(email)s.', email=user.email), 'info') else: flash(_('Failed to send password mail to %(email)s.', email=user.email), 'error') return redirect(url_for('login'))
def reset_confirm(code: str) -> str: # pragma: no cover user = UserMapper.get_by_reset_code(code) if not user: logger.log('info', 'auth', 'unknown reset code') flash(_('invalid password reset confirmation code'), 'error') abort(404) hours = session['settings']['reset_confirm_hours'] if datetime.datetime.now() > user.password_reset_date + datetime.timedelta( hours=hours): logger.log('info', 'auth', 'reset code expired') flash(_('This reset confirmation code has expired.'), 'error') abort(404) password = UserMapper.generate_password() user.password = bcrypt.hashpw(password.encode('utf-8'), bcrypt.gensalt()).decode('utf-8') user.password_reset_code = None user.password_reset_date = None user.login_failed_count = 0 user.update() subject = _('New password for %(sitename)s', sitename=session['settings']['site_name']) body = _('New password for %(username)s', username=user.username) + ' ' body += _('at') + ' ' + request.scheme + '://' + request.headers[ 'Host'] + ':\n\n' body += uc_first(_('username')) + ': ' + user.username + '\n' body += uc_first(_('password')) + ': ' + password + '\n' if send_mail(subject, body, user.email, False): flash(_('Send new password mail to %(email)s.', email=user.email), 'info') else: flash( _('Failed to send password mail to %(email)s.', email=user.email), 'error') return redirect(url_for('login'))
def admin_check_dates() -> str: # Get invalid date combinations (e.g. begin after end) tables = {'link_dates': Table(['link', 'domain', 'range']), 'involvement_dates': Table(['actor', 'event', 'class', 'involvement', 'description']), 'dates': Table(['name', 'class', 'type', 'system type', 'created', 'updated', 'description'])} for entity in DateMapper.get_invalid_dates(): tables['dates'].rows.append([link(entity), link(entity.class_), entity.print_base_type(), entity.system_type, format_date(entity.created), format_date(entity.modified), truncate_string(entity.description)]) for link_ in DateMapper.get_invalid_link_dates(): label = '' if link_.property.code == 'OA7': # pragma: no cover label = 'relation' elif link_.property.code == 'P107': # pragma: no cover label = 'member' elif link_.property.code in ['P11', 'P14', 'P22', 'P23']: label = 'involvement' url = url_for(label + '_update', id_=link_.id, origin_id=link_.domain.id) tables['link_dates'].rows.append(['<a href="' + url + '">' + uc_first(_(label)) + '</a>', link(link_.domain), link(link_.range)]) for link_ in DateMapper.invalid_involvement_dates(): event = link_.domain actor = link_.range update_url = url_for('involvement_update', id_=link_.id, origin_id=actor.id) data = ([link(actor), link(event), g.classes[event.class_.code].name, link_.type.name if link_.type else '', truncate_string(link_.description), '<a href="' + update_url + '">' + uc_first(_('edit')) + '</a>']) tables['involvement_dates'].rows.append(data) return render_template('admin/check_dates.html', tables=tables)
def export_csv() -> str: path = app.config['EXPORT_FOLDER_PATH'] + '/csv' writeable = True if os.access(path, os.W_OK) else False form = ExportCsvForm() if form.validate_on_submit() and writeable: Export.export_csv(form) logger.log('info', 'database', 'CSV export') flash(_('data was exported as CSV'), 'info') return redirect(url_for('export_csv')) table = Table(['name', 'size'], order='[[0, "desc"]]') for file in [ f for f in os.listdir(path) if os.path.isfile(os.path.join(path, f)) ]: name = basename(file) if name == '.gitignore': continue link = '<a href="{url}">{label}</a>'.format(url=url_for('download_csv', filename=name), label=uc_first( _('download'))) data = [name, convert_size(os.path.getsize(path + '/' + name)), link] if is_authorized('admin') and writeable: confirm = ' onclick="return confirm(\'' + _('Delete %(name)s?', name=name) + '\')"' delete = '<a href="' + url_for('delete_csv', filename=name) delete += '" ' + confirm + '>' + uc_first(_('delete')) + '</a>' data.append(delete) table.rows.append(data) return render_template('export/export_csv.html', form=form, table=table, writeable=writeable)
def settings_index(): form = TestMail() if form.validate_on_submit( ) and session['settings']['mail']: # pragma: no cover user = current_user subject = _('Test mail from %(site_name)s', site_name=session['settings']['site_name']) body = _('This test mail was sent by %(username)s', username=user.username) body += ' ' + _('at') + ' ' + request.headers['Host'] if send_mail(subject, body, form.receiver.data): flash( _('A test mail was sent to %(email)s.', email=form.receiver.data)) else: form.receiver.data = current_user.email settings = session['settings'] groups = OrderedDict([ ('general', OrderedDict([(_('site name'), settings['site_name']), (_('default language'), app.config['LANGUAGES'][settings['default_language']]), (_('default table rows'), settings['default_table_rows']), (_('log level'), app.config['LOG_LEVELS'][int(settings['log_level'])]), (_('debug mode'), uc_first(_('on')) if settings['debug_mode'] else uc_first(_('off')))])), ('mail', OrderedDict([ (_('mail'), uc_first(_('on')) if settings['mail'] else uc_first(_('off'))), (_('mail transport username'), settings['mail_transport_username']), (_('mail transport host'), settings['mail_transport_host']), (_('mail transport port'), settings['mail_transport_port']), (_('mail from email'), settings['mail_from_email']), (_('mail from name'), settings['mail_from_name']), (_('mail recipients feedback'), ';'.join(settings['mail_recipients_feedback'])) ])), ('authentication', OrderedDict([ (_('random password length'), settings['random_password_length']), (_('minimum password length'), settings['minimum_password_length']), (_('reset confirm hours'), settings['reset_confirm_hours']), (_('failed login tries'), settings['failed_login_tries']), (_('failed login forget minutes'), settings['failed_login_forget_minutes']) ])) ]) return render_template('settings/index.html', groups=groups, settings=settings, form=form)
class NewsLetterForm(FlaskForm): subject = StringField('', [InputRequired()], render_kw={ 'placeholder': uc_first(_('subject')), 'autofocus': True }) body = TextAreaField('', [InputRequired()], render_kw={'placeholder': uc_first(_('content'))}) save = SubmitField(_('send'))
def description(self, entity): if not entity.description: return '' text = entity.description.replace('\r\n', '<br />') label = util.uc_first(_('description')) if hasattr(entity, 'system_type') and entity.system_type == 'source content': label = util.uc_first(_('content')) html = """<p class="description-title">{label}</p> <div class="description more">{description}</div>""".format(label=label, description=text) return html
def data_table(self, data): html = '<div class="data-table">' for key, value in data: if value or value == 0: value = util.uc_first(_('no')) if value is False else value value = util.uc_first(_('yes')) if value is True else value html += '<div class="table-row"><div>' + util.uc_first(key) + '</div>' html += '<div class="table-cell">' + str(value) + '</div></div>' html += '</div>' return html
def display_move_form(self, form, root_name): html = '' for field in form: if type(field) is TreeField: html += '<p>' + root_name + ' ' + str(field) + '</p>' html += '<p><a class="button" id="select-all">' + util.uc_first(_('select all')) + '</a>' html += '<a class="button" id="select-none">' + util.uc_first(_('deselect all')) + '</a></p>' table = {'id': 'move', 'header': ['#', util.uc_first(_('selection'))], 'data': []} for item in form.selection: table['data'].append([item, item.label.text]) return html + util.pager(table)
def __call__(self, field: TreeField, **kwargs: Any) -> TreeSelect: from openatlas.models.node import NodeMapper selection = '' selected_ids = [] if field.data: field.data = field.data[0] if type( field.data) is list else field.data selection = g.nodes[int(field.data)].name selected_ids.append(g.nodes[int(field.data)].id) html = """ <input id="{name}-button" name="{name}-button" type="text" class="table-select {required}" onfocus="this.blur()" readonly="readonly" value="{selection}" placeholder="{change_label}"> <a id="{name}-clear" {clear_style} class="button" onclick="clearSelect('{name}');">{clear_label}</a> <div id="{name}-overlay" class="overlay"> <div id="{name}-dialog" class="overlay-container"> <input class="tree-filter" id="{name}-tree-search" placeholder="{filter}"> <div id="{name}-tree"></div> </div> </div> <script> $(document).ready(function () {{ createOverlay("{name}","{title}",false,); $("#{name}-tree").jstree({{ "core" : {{"check_callback" : true, 'data':[{tree_data}]}}, "search": {{"case_insensitive": true, "show_only_matches": true}}, "plugins" : ["search"], }}); $("#{name}-tree").on("select_node.jstree", function (e, data) {{ selectFromTree("{name}", data.node.id, data.node.text); }}); $("#{name}-tree-search").keyup(function() {{ if (this.value.length >= {min_chars}) {{ $("#{name}-tree").jstree("search", $(this).val()); }} else if (this.value.length == 0) {{ $("#{name}-tree").jstree("search", $(this).val()); $("#{name}-tree").jstree(true).show_all(); }} }}); }}); </script>""".format( filter=uc_first(_('type to search')), min_chars=session['settings']['minimum_jstree_search'], name=field.id, title=g.nodes[int(field.id)].name, change_label=uc_first(_('change')), clear_label=uc_first(_('clear')), selection=selection, tree_data=NodeMapper.get_tree_data(int(field.id), selected_ids), clear_style='' if selection else ' style="display: none;" ', required=' required' if field.flags.required else '') return super(TreeSelect, self).__call__(field, **kwargs) + html
def description(self, entity) -> str: if not entity.description: return '' text = entity.description.replace('\r\n', '<br />') label = util.uc_first(_('description')) if hasattr(entity, 'system_type') and entity.system_type == 'source content': label = util.uc_first(_('content')) html = """<h2>{label}</h2> <div class="description more">{description}</div>""".format( label=label, description=text) return html
def event_view(id_): event = EntityMapper.get_by_id(id_) event.set_dates() tables = { 'info': get_entity_data(event), 'file': {'id': 'files', 'data': [], 'header': app.config['TABLE_HEADERS']['file'] + [_('main image')]}, 'subs': {'id': 'sub-event', 'data': [], 'header': app.config['TABLE_HEADERS']['event']}, 'source': {'id': 'source', 'data': [], 'header': app.config['TABLE_HEADERS']['source']}, 'actor': {'id': 'actor', 'data': [], 'header': ['actor', 'class', 'involvement', 'first', 'last', 'description']}, 'reference': {'id': 'reference', 'data': [], 'header': app.config['TABLE_HEADERS']['reference'] + ['pages']}} for link_ in event.get_links(['P11', 'P14', 'P22', 'P23']): first = link_.first if not link_.first and event.first: first = '<span class="inactive" style="float:right">' + str(event.first) + '</span>' last = link_.last if not link_.last and event.last: last = '<span class="inactive" style="float:right">' + str(event.last) + '</span>' data = ([link(link_.range), g.classes[link_.range.class_.code].name, link_.type.name if link_.type else '', first, last, truncate_string(link_.description)]) if is_authorized('editor'): update_url = url_for('involvement_update', id_=link_.id, origin_id=event.id) unlink_url = url_for('link_delete', id_=link_.id, origin_id=event.id) + '#tab-actor' data.append('<a href="' + update_url + '">' + uc_first(_('edit')) + '</a>') data.append(display_remove_link(unlink_url, link_.range.name)) tables['actor']['data'].append(data) profile_image_id = event.get_profile_image_id() for link_ in event.get_links('P67', True): domain = link_.domain data = get_base_table_data(domain) if domain.view_name == 'file': # pragma: no cover extension = data[3].replace('.', '') data.append(get_profile_image_table_link(domain, event, extension, profile_image_id)) if not profile_image_id and extension in app.config['DISPLAY_FILE_EXTENSIONS']: profile_image_id = domain.id if domain.view_name not in ['source', 'file']: data.append(truncate_string(link_.description)) if is_authorized('editor'): update_url = url_for('reference_link_update', link_id=link_.id, origin_id=event.id) data.append('<a href="' + update_url + '">' + uc_first(_('edit')) + '</a>') if is_authorized('editor'): url = url_for('link_delete', id_=link_.id, origin_id=event.id) data.append(display_remove_link(url + '#tab-' + domain.view_name, domain.name)) tables[domain.view_name]['data'].append(data) for sub_event in event.get_linked_entities('P117', True): tables['subs']['data'].append(get_base_table_data(sub_event)) return render_template('event/view.html', event=event, tables=tables, profile_image_id=profile_image_id)
def reference_insert(code, origin_id=None): origin = EntityMapper.get_by_id(origin_id) if origin_id else None form_code = 'Information Carrier' if code == 'carrier' else uc_first(code) form = build_form(ReferenceForm, uc_first(form_code)) if origin: del form.insert_and_continue if form.validate_on_submit(): return redirect(save(form, code=code, origin=origin)) return render_template('reference/insert.html', form=form, code=code, origin=origin)
def add_note_tab(entity: Entity) -> Tab: tab = Tab('note', entity=entity) for note in current_user.get_notes_by_entity_id(entity.id): data = [ format_date(note['created']), uc_first(_('public')) if note['public'] else uc_first(_('private')), link(User.get_by_id(note['user_id'])), note['text'], f'<a href="{url_for("note_view", id_=note["id"])}">' f'{uc_first(_("view"))}</a>'] tab.table.rows.append(data) return tab
def note(self, entity: Entity) -> str: if not current_user.settings['module_notes'] or not util.is_authorized( 'contributor'): return '' # pragma no cover if not entity.note: url = url_for('note_insert', entity_id=entity.id) return '<p><a href="' + url + '">+ ' + util.uc_first( _('note')) + '</a></p>' url = url_for('note_update', entity_id=entity.id) html = '<h2>' + util.uc_first( _('note')) + '</h2><p>' + entity.note + '</p>' html += '<a href="' + url + '">' + util.uc_first(_('edit note')) + '</a>' return html
def data_table(self, data: Iterator) -> str: html = '<div class="data-table">' for key, value in data: if value or value == 0: value = util.uc_first(_('no')) if value is False else value value = util.uc_first(_('yes')) if value is True else value html += ''' <div class="table-row"> <div>{key}</div> <div class="table-cell">{value}</div> </div>'''.format(key=util.uc_first(key), value=value) html += '</div>' return html
def admin_check_similar() -> str: form = SimilarForm() choices = ['source', 'event', 'actor', 'place', 'feature', 'stratigraphic unit', 'find', 'reference', 'file'] form.classes.choices = [(x, uc_first(_(x))) for x in choices] table = Table(['name', uc_first(_('count'))]) if form.validate_on_submit(): for sample_id, sample in EntityMapper.get_similar_named(form).items(): html = link(sample['entity']) for entity in sample['entities']: html += '<br/>' + link(entity) table.rows.append([html, len(sample['entities']) + 1]) table = table if table.rows else 'none found' return render_template('admin/check_similar.html', table=table, form=form)
def __call__(self, field, **kwargs): from openatlas.models.node import NodeMapper selection = '' selected_ids = [] if field.data: field.data = field.data[0] if isinstance(field.data, list) else field.data selection = g.nodes[int(field.data)].name selected_ids.append(g.nodes[int(field.data)].id) try: hierarchy_id = int(field.id) except ValueError: hierarchy_id = NodeMapper.get_hierarchy_by_name(uc_first(field.id)).id root = g.nodes[hierarchy_id] html = """ <input id="{name}-button" name="{name}-button" type="text" class="table-select {required}" onfocus="this.blur()" readonly="readonly" value="{selection}" placeholder="{change_label}" /> <a id="{name}-clear" {clear_style} class="button" onclick="clearSelect('{name}');">{clear_label}</a> <div id="{name}-overlay" class="overlay"> <div id="{name}-dialog" class="overlay-container"> <input class="tree-filter" id="{name}-tree-search" placeholder="Filter" /> <div id="{name}-tree"></div> </div> </div> <script> $(document).ready(function () {{ createOverlay("{name}","{title}",false,); $("#{name}-tree").jstree({{ "core" : {{"check_callback" : true, 'data':[{tree_data}]}}, "search": {{"case_insensitive": true, "show_only_matches": true}}, "plugins" : ["search"], }}); $("#{name}-tree").on("select_node.jstree", function (e, data) {{ selectFromTree("{name}", data.node.id, data.node.text); }}); $("#{name}-tree-search").keyup(function() {{ $("#{name}-tree").jstree("search", $(this).val()); }}); }}); </script>""".format( name=field.id, title=root.name, change_label=uc_first(_('change')), clear_label=uc_first(_('clear')), selection=selection, tree_data=NodeMapper.get_tree_data(hierarchy_id, selected_ids), clear_style='' if selection else ' style="display: none;" ', required=' required' if field.flags.required else '') return super(TreeSelect, self).__call__(field, **kwargs) + html
def __call__(self, field, **kwargs): from openatlas.models.node import NodeMapper selection = '' selected_ids = [] if field.data: field.data = field.data[0] if type(field.data) is list else field.data selection = g.nodes[int(field.data)].name selected_ids.append(g.nodes[int(field.data)].id) html = """ <input id="{name}-button" name="{name}-button" type="text" class="table-select {required}" onfocus="this.blur()" readonly="readonly" value="{selection}" placeholder="{change_label}" /> <a id="{name}-clear" {clear_style} class="button" onclick="clearSelect('{name}');">{clear_label}</a> <div id="{name}-overlay" class="overlay"> <div id="{name}-dialog" class="overlay-container"> <input class="tree-filter" id="{name}-tree-search" placeholder="{filter}" /> <div id="{name}-tree"></div> </div> </div> <script> $(document).ready(function () {{ createOverlay("{name}","{title}",false,); $("#{name}-tree").jstree({{ "core" : {{"check_callback" : true, 'data':[{tree_data}]}}, "search": {{"case_insensitive": true, "show_only_matches": true}}, "plugins" : ["search"], }}); $("#{name}-tree").on("select_node.jstree", function (e, data) {{ selectFromTree("{name}", data.node.id, data.node.text); }}); $("#{name}-tree-search").keyup(function() {{ if (this.value.length >= {min_chars}) {{ $("#{name}-tree").jstree("search", $(this).val()); }} }}); }}); </script>""".format( filter=uc_first(_('type to search')), min_chars=session['settings']['minimum_jstree_search'], name=field.id, title=g.nodes[int(field.id)].name, change_label=uc_first(_('change')), clear_label=uc_first(_('clear')), selection=selection, tree_data=NodeMapper.get_tree_data(int(field.id), selected_ids), clear_style='' if selection else ' style="display: none;" ', required=' required' if field.flags.required else '') return super(TreeSelect, self).__call__(field, **kwargs) + html
def admin_orphans() -> str: header = ['name', 'class', 'type', 'system type', 'created', 'updated', 'description'] tables = {'orphans': Table(header), 'unlinked': Table(header), 'missing_files': Table(header), 'circular': Table(['entity']), 'nodes': Table(['name', 'root']), 'orphaned_files': Table(['name', 'size', 'date', 'ext'])} tables['circular'].rows = [[link(entity)] for entity in EntityMapper.get_circular()] for entity in EntityMapper.get_orphans(): name = 'unlinked' if entity.class_.code in app.config['CODE_CLASS'].keys() else 'orphans' tables[name].rows.append([link(entity), link(entity.class_), entity.print_base_type(), entity.system_type, format_date(entity.created), format_date(entity.modified), truncate_string(entity.description)]) for node in NodeMapper.get_orphans(): tables['nodes'].rows.append([link(node), link(g.nodes[node.root[-1]])]) # Get orphaned file entities (no corresponding file) file_ids = [] for entity in EntityMapper.get_by_system_type('file', nodes=True): file_ids.append(str(entity.id)) if not get_file_path(entity): tables['missing_files'].rows.append([link(entity), link(entity.class_), entity.print_base_type(), entity.system_type, format_date(entity.created), format_date(entity.modified), truncate_string(entity.description)]) # Get orphaned files (no corresponding entity) for file in os.scandir(app.config['UPLOAD_FOLDER_PATH']): name = file.name if name != '.gitignore' and splitext(file.name)[0] not in file_ids: confirm = ' onclick="return confirm(\'' + _('Delete %(name)s?', name=name) + '\')"' tables['orphaned_files'].rows.append([ name, convert_size(file.stat().st_size), format_date(datetime.datetime.utcfromtimestamp(file.stat().st_ctime)), splitext(name)[1], '<a href="' + url_for('download_file', filename=name) + '">' + uc_first( _('download')) + '</a>', '<a href="' + url_for('admin_file_delete', filename=name) + '" ' + confirm + '>' + uc_first(_('delete')) + '</a>']) return render_template('admin/orphans.html', tables=tables)
def display_move_form(self, form, root_name: str) -> str: html = '' for field in form: if type(field) is TreeField: html += '<p>' + root_name + ' ' + str(field) + '</p>' html += """ <p> <a class="button" id="select-all">{select_all}</a> <a class="button" id="select-none">{deselect_all}</a> </p>""".format(select_all=util.uc_first(_('select all')), deselect_all=util.uc_first(_('deselect all'))) table = Table(['#', util.uc_first(_('selection'))]) for item in form.selection: table.rows.append([item, item.label.text]) return html + table.display('move')
def index() -> str: tables = {'overview': Table(paging=False, defs='[{className: "dt-body-right", targets: 1}]'), 'bookmarks': Table(['name', 'class', 'first', 'last'], defs='[{className: "dt-body-right", targets: [2,3]}]'), 'notes': Table(['name', 'class', 'first', 'last', _('note')], defs='[{className: "dt-body-right", targets: [2,3]}]'), 'latest': Table(['name', 'class', 'first', 'last', 'date', 'user'], order='[[4, "desc"]]', defs='[{className: "dt-body-right", targets: [2,3]}]')} if current_user.is_authenticated and hasattr(current_user, 'bookmarks'): for entity_id in current_user.bookmarks: entity = EntityMapper.get_by_id(entity_id) tables['bookmarks'].rows.append([link(entity), g.classes[entity.class_.code].name, entity.first, entity.last, bookmark_toggle(entity.id, True)]) for entity_id, text in UserMapper.get_notes().items(): entity = EntityMapper.get_by_id(entity_id) tables['notes'].rows.append([link(entity), g.classes[entity.class_.code].name, entity.first, entity.last, truncate_string(text)]) for name, count in EntityMapper.get_overview_counts().items(): if count: count = format_number(count) if count else '' url = url_for(name + '_index') if name != 'find' else url_for('place_index') tables['overview'].rows.append([ '<a href="' + url + '">' + uc_first(_(name)) + '</a>', count]) for entity in EntityMapper.get_latest(8): tables['latest'].rows.append([ link(entity), g.classes[entity.class_.code].name, entity.first, entity.last, format_date(entity.created), link(logger.get_log_for_advanced_view(entity.id)['creator'])]) intro = ContentMapper.get_translation('intro') return render_template('index/index.html', intro=intro, tables=tables)
def reference_link_update(link_: Link, origin: Entity) -> Union[str, Response]: origin = Entity.get_by_id(origin.id) form = AddReferenceForm() del form.reference if form.validate_on_submit(): link_.description = form.page.data link_.update() flash(_('info update'), 'info') tab = link_.range.class_.view if origin.class_.view == 'reference' \ else 'reference' return redirect(f"{url_for('view', id_=origin.id)}#tab-{tab}") form.save.label.text = _('save') form.page.data = link_.description if link_.domain.class_.name == 'external_reference': form.page.label.text = uc_first(_('link text')) return render_template( 'display_form.html', form=form, crumbs=[ [_(origin.class_.view), url_for('index', view=origin.class_.view)], origin, link_.domain if link_.domain.id != origin.id else link_.range, _('edit')])
def hierarchy_update(id_: int) -> str: root = g.nodes[id_] if root.system: abort(403) form = build_form(HierarchyForm, 'hierarchy', root) # type: HierarchyForm form.forms.choices = NodeMapper.get_form_choices(root) if root.value_type: del form.multiple elif root.multiple: form.multiple.render_kw = {'disabled': 'disabled'} if form.validate_on_submit(): if form.name.data != root.name and NodeMapper.get_nodes(form.name.data): flash(_('error name exists'), 'error') return redirect(url_for('node_index') + '#tab-' + str(root.id)) save(form, root) flash(_('info update'), 'info') return redirect(url_for('node_index') + '#tab-' + str(root.id)) form.multiple = root.multiple table = Table(['form', 'count'], paging=False) for form_id, form_ in root.forms.items(): url = url_for('hierarchy_remove_form', id_=root.id, remove_id=form_id) link = '<a href="' + url + '">' + uc_first(_('remove')) + '</a>' count = NodeMapper.get_form_count(root, form_id) table.rows.append([form_['name'], format_number(count) if count else link]) return render_template('hierarchy/update.html', node=root, form=form, table=table, forms=[form.id for form in form.forms])
def hierarchy_update(id_): root = g.nodes[id_] if root.system: abort(403) form = build_form(HierarchyForm, 'hierarchy', root) form.forms.choices = NodeMapper.get_form_choices(root) if root.value_type: del form.multiple elif root.multiple: form.multiple.render_kw = {'disabled': 'disabled'} if form.validate_on_submit(): if form.name.data != root.name and NodeMapper.get_nodes(form.name.data): flash(_('error name exists'), 'error') return redirect(url_for('node_index') + '#tab-' + str(root.id)) save(form, root) flash(_('info update'), 'info') return redirect(url_for('node_index') + '#tab-' + str(root.id)) form.multiple = root.multiple table = {'id': 'used_forms', 'show_pager': False, 'data': [], 'sort': 'sortList: [[0, 0]]', 'header': ['form', 'count']} for form_id, form_ in root.forms.items(): url = url_for('hierarchy_remove_form', id_=root.id, remove_id=form_id) link = '<a href="' + url + '">' + uc_first(_('remove')) + '</a>' count = NodeMapper.get_form_count(root, form_id) table['data'].append([form_['name'], format_number(count) if count else link]) return render_template('hierarchy/update.html', node=root, form=form, table=table, forms=[form.id for form in form.forms])
def reference_add(id_: int, class_name: str) -> str: reference = EntityMapper.get_by_id(id_) form = getattr(openatlas.views.reference, 'Add' + uc_first(class_name) + 'Form')() if form.validate_on_submit(): property_code = 'P128' if reference.class_.code == 'E84' else 'P67' entity = EntityMapper.get_by_id(getattr(form, class_name).data) reference.link(property_code, entity, form.page.data) return redirect( url_for('reference_view', id_=reference.id) + '#tab-' + class_name) if reference.system_type == 'external reference': form.page.label.text = uc_first(_('link text')) return render_template('reference/add.html', reference=reference, form=form, class_name=class_name)
def check_single_type_duplicates() -> list: # Find entities with multiple types attached which should be single from openatlas.models.node import NodeMapper from openatlas.models.entity import EntityMapper data = [] for id_, node in g.nodes.items(): if not node.root and not node.multiple and not node.value_type: node_ids = NodeMapper.get_all_sub_ids(node) if node_ids: sql = """ SELECT domain_id FROM model.link WHERE property_code = 'P2' AND range_id IN %(node_ids)s GROUP BY domain_id HAVING COUNT(*) > 1;""" g.cursor.execute(sql, {'node_ids': tuple(node_ids)}) debug_model['link sql'] += 1 for row in g.cursor.fetchall(): offending_nodes = [] entity = EntityMapper.get_by_id(row.domain_id, nodes=True) for entity_node in entity.nodes: if g.nodes[entity_node.root[-1]].id == node.id: url = url_for('admin_delete_single_type_duplicate', entity_id=entity.id, node_id=entity_node.id) offending_nodes.append( '<a href="' + url + '">' + uc_first(_('remove')) + '</a> ' + entity_node.name) data.append([link(entity), entity.class_.name, link(g.nodes[id_]), '<br />'.join(offending_nodes)]) return data
def reference_view(id_): reference = EntityMapper.get_by_id(id_) tables = { 'info': get_entity_data(reference), 'file': {'id': 'files', 'data': [], 'header': app.config['TABLE_HEADERS']['file'] + ['page'] + [_('main image')]}} for name in ['source', 'event', 'actor', 'place', 'feature', 'stratigraphic-unit', 'find']: header = app.config['TABLE_HEADERS'][name] + ['page'] tables[name] = {'id': name, 'header': header, 'data': []} for link_ in reference.get_links('P67', True): domain = link_.domain data = get_base_table_data(domain) if is_authorized('editor'): url = url_for('link_delete', id_=link_.id, origin_id=reference.id) + '#tab-file' data.append(display_remove_link(url, domain.name)) tables['file']['data'].append(data) profile_image_id = reference.get_profile_image_id() for link_ in reference.get_links(['P67', 'P128']): range_ = link_.range data = get_base_table_data(range_) data.append(truncate_string(link_.description)) if range_.view_name == 'file': # pragma: no cover ext = data[3].replace('.', '') data.append(get_profile_image_table_link(range_, reference, ext, profile_image_id)) if not profile_image_id and ext in app.config['DISPLAY_FILE_EXTENSIONS']: profile_image_id = range_.id if is_authorized('editor'): url = url_for('reference_link_update', link_id=link_.id, origin_id=reference.id) data.append('<a href="' + url + '">' + uc_first(_('edit')) + '</a>') url = url_for('link_delete', id_=link_.id, origin_id=reference.id) data.append(display_remove_link(url + '#tab-' + range_.table_name, range_.name)) tables[range_.table_name]['data'].append(data) return render_template('reference/view.html', reference=reference, tables=tables, profile_image_id=profile_image_id)
def __init__(self, name: str, content: Optional[str] = None, table: Optional[Table] = None, buttons: Optional[List[str]] = None, entity: Optional['Entity'] = None) -> None: self.name = name self.content = content self.title = uc_first(_(name.replace('_', ' '))) self.entity = entity self.table = table if table else Table() id_ = None view = None class_ = None if entity: id_ = entity.id view = entity.class_.view class_ = entity.class_ self.table.header = g.table_headers[name] if name == 'reference' or entity and entity.class_.view == 'reference': self.table.header = self.table.header + ['page'] buttons = buttons if buttons else [] self.add_buttons(name, buttons, view, id_, class_) self.buttons = buttons \ if buttons and is_authorized('contributor') else []
def get_form_settings(form: Any, profile: bool = False) -> dict[str, str]: if isinstance(form, ProfileForm): return { _('name'): current_user.real_name, _('email'): current_user.email, _('show email'): str( _('on') if current_user.settings['show_email'] else _('off')), _('newsletter'): str( _('on') if current_user.settings['newsletter'] else _('off'))} settings = {} for field in form: if field.type in ['CSRFTokenField', 'HiddenField', 'SubmitField']: continue label = uc_first(field.label.text) if profile and field.name in current_user.settings: value = current_user.settings[field.name] elif field.name in g.settings: value = g.settings[field.name] else: # pragma: no cover value = '' # In case of a missing setting after an update if field.type in ['StringField', 'IntegerField']: settings[label] = value if field.type == 'BooleanField': # str() needed for templates settings[label] = str(_('on')) if value else str(_('off')) if field.type == 'SelectField': if isinstance(value, str) and value.isdigit(): value = int(value) settings[label] = dict(field.choices).get(value) if field.name in [ 'mail_recipients_feedback', 'file_upload_allowed_extension']: settings[label] = ' '.join(value) return settings
def check_single_type_duplicates() -> list: # Find entities with multiple types attached which should be single from openatlas.models.node import NodeMapper from openatlas.models.entity import EntityMapper data = [] for id_, node in g.nodes.items(): if not node.root and not node.multiple and not node.value_type: node_ids = NodeMapper.get_all_sub_ids(node) if node_ids: sql = """ SELECT domain_id FROM model.link WHERE property_code = 'P2' AND range_id IN %(node_ids)s GROUP BY domain_id HAVING COUNT(*) > 1;""" g.execute(sql, {'node_ids': tuple(node_ids)}) for row in g.cursor.fetchall(): offending_nodes = [] entity = EntityMapper.get_by_id(row.domain_id, nodes=True) for entity_node in entity.nodes: if g.nodes[entity_node.root[-1]].id == node.id: url = url_for( 'admin_delete_single_type_duplicate', entity_id=entity.id, node_id=entity_node.id) offending_nodes.append('<a href="' + url + '">' + uc_first(_('remove')) + '</a> ' + entity_node.name) data.append([ link(entity), entity.class_.name, link(g.nodes[id_]), '<br />'.join(offending_nodes) ]) return data
def index(): tables = { 'counts': {'id': 'overview', 'header': [], 'data': [], 'show_pager': False}, 'bookmarks': {'id': 'bookmarks', 'data': [], 'show_pager': False, 'header': ['name', 'class', 'first', 'last']}, 'latest': {'id': 'latest', 'data': [], 'show_pager': False, 'header': ['name', 'class', 'first', 'last', 'date', 'user']}} if current_user.is_authenticated and hasattr(current_user, 'bookmarks'): for entity_id in current_user.bookmarks: entity = EntityMapper.get_by_id(entity_id) tables['bookmarks']['data'].append([ link(entity), g.classes[entity.class_.code].name, entity.first, entity.last, bookmark_toggle(entity.id, True)]) for name, count in EntityMapper.get_overview_counts().items(): count = format_number(count) if count else '' tables['counts']['data'].append([ '<a href="' + url_for(name + '_index') + '">' + uc_first(_(name)) + '</a>', count]) for entity in EntityMapper.get_latest(8): tables['latest']['data'].append([ link(entity), g.classes[entity.class_.code].name, entity.first, entity.last, format_date(entity.created), link(logger.get_log_for_advanced_view(entity.id)['creator'])]) intro = ContentMapper.get_translation('intro') return render_template('index/index.html', intro=intro, tables=tables)
class MailForm(Form): mail = BooleanField(uc_first(_('mail'))) mail_transport_username = StringField(uc_first(_('mail transport username'))) mail_transport_host = StringField(uc_first(_('mail transport host'))) mail_transport_port = StringField(uc_first(_('mail transport port'))) mail_from_email = StringField(uc_first(_('mail from email')), [Email()]) mail_from_name = StringField(uc_first(_('mail from name'))) mail_recipients_feedback = StringField(uc_first(_('mail recipients feedback'))) save = SubmitField(uc_first(_('save')))
def __call__(self, field, **kwargs): file_stats = None class_ = 'place' if field.id in ['residence', 'begins_in', 'ends_in'] else field.id if class_ == 'place': aliases = current_user.settings['table_show_aliases'] entities = EntityMapper.get_by_system_type('place', nodes=True, aliases=aliases) elif class_ == 'reference': entities = EntityMapper.get_by_system_type('bibliography') + \ EntityMapper.get_by_system_type('edition') + \ EntityMapper.get_by_system_type('external reference') elif class_ == 'file': entities = EntityMapper.get_display_files() file_stats = get_file_stats() else: entities = EntityMapper.get_by_codes(class_) selection = '' table = Table(Table.HEADERS[class_]) for entity in entities: # Todo: don't show self e.g. at source if field.data and entity.id == int(field.data): selection = entity.name data = get_base_table_data(entity, file_stats) data[0] = """<a onclick="selectFromTable(this,'{name}', {entity_id})">{entity_name}</a> """.format(name=field.id, entity_id=entity.id, entity_name=truncate_string(entity.name, span=False)) data[0] = '<br />'.join([data[0]] + [ truncate_string(alias) for id_, alias in entity.aliases.items()]) table.rows.append(data) html = """ <input id="{name}-button" name="{name}-button" class="table-select {required}" type="text" placeholder="{change_label}" onfocus="this.blur()" readonly="readonly" value="{selection}"> <a id="{name}-clear" class="button" {clear_style} onclick="clearSelect('{name}');">{clear_label}</a> <div id="{name}-overlay" class="overlay"> <div id="{name}-dialog" class="overlay-container">{table}</div></div> <script>$(document).ready(function () {{createOverlay("{name}", "{title}");}});</script> """.format(name=field.id, title=_(field.id.replace('_', ' ')), change_label=uc_first(_('change')), clear_label=uc_first(_('clear')), table=table.display(field.id), selection=selection, clear_style='' if selection else ' style="display: none;" ', required=' required' if field.flags.required else '') return super(TableSelect, self).__call__(field, **kwargs) + html
def __call__(self, field, **kwargs): selection = '' class_ = field.id if class_ in ['residence', 'begins_in', 'ends_in']: class_ = 'place' header = app.config['TABLE_HEADERS'][class_] table = {'id': field.id, 'header': header, 'data': []} file_stats = None if class_ == 'place': entities = EntityMapper.get_by_system_type('place') elif class_ == 'reference': entities = EntityMapper.get_by_system_type('bibliography') + \ EntityMapper.get_by_system_type('edition') + \ EntityMapper.get_by_system_type('external reference') elif class_ == 'file': entities = EntityMapper.get_display_files() file_stats = get_file_stats() else: entities = EntityMapper.get_by_codes(class_) for entity in entities: # Todo: don't show self e.g. at source if field.data and entity.id == int(field.data): selection = entity.name data = get_base_table_data(entity, file_stats) data[0] = """<a onclick="selectFromTable(this,'{name}', {entity_id})">{entity_name}</a> """.format(name=field.id, entity_id=entity.id, entity_name=truncate_string(entity.name, span=False)) table['data'].append(data) html = """ <input id="{name}-button" name="{name}-button" class="table-select {required}" type="text" placeholder="{change_label}" onfocus="this.blur()" readonly="readonly" value="{selection}"> <a id="{name}-clear" class="button" {clear_style} onclick="clearSelect('{name}');">{clear_label}</a> <div id="{name}-overlay" class="overlay"> <div id="{name}-dialog" class="overlay-container">{pager}</div></div> <script>$(document).ready(function () {{createOverlay("{name}", "{title}");}});</script> """.format(name=field.id, title=_(field.id.replace('_', ' ')), change_label=uc_first(_('change')), clear_label=uc_first(_('clear')), pager=pager(table), selection=selection, clear_style='' if selection else ' style="display: none;" ', required=' required' if field.flags.required else '') return super(TableSelect, self).__call__(field, **kwargs) + html
def build_add_reference_form(class_: str) -> FlaskForm: class Form(FlaskForm): pass setattr(Form, class_, TableField(_(class_), [InputRequired()])) setattr(Form, 'page', StringField(_('page'))) setattr(Form, 'save', SubmitField(uc_first(_('insert')))) return Form()
def __call__(self, field, **kwargs): selection = '' class_ = field.id if class_ in ['residence', 'appears_first', 'appears_last']: class_ = 'place' header = app.config['TABLE_HEADERS'][class_] table = {'id': field.id, 'header': header, 'data': []} if class_ == 'place': entities = EntityMapper.get_by_system_type('place') elif class_ == 'reference': entities = EntityMapper.get_by_system_type('bibliography') + \ EntityMapper.get_by_system_type('edition') elif class_ == 'file': entities = EntityMapper.get_by_system_type('file') else: entities = EntityMapper.get_by_codes(class_) for entity in entities: # Todo: don't show self e.g. at source if field.data and entity.id == int(field.data): selection = entity.name data = get_base_table_data(entity) data[0] = """<a onclick="selectFromTable(this,'{name}', {entity_id})">{entity_name}</a> """.format( name=field.id, entity_id=entity.id, entity_name=truncate_string(entity.name, span=False)) table['data'].append(data) html = """ <input id="{name}-button" name="{name}-button" class="table-select {required}" type="text" placeholder="{change_label}" onfocus="this.blur()" readonly="readonly" value="{selection}"> <a id="{name}-clear" class="button" {clear_style} onclick="clearSelect('{name}');">{clear_label}</a> <div id="{name}-overlay" class="overlay"> <div id="{name}-dialog" class="overlay-container">{pager}</div></div> <script>$(document).ready(function () {{createOverlay("{name}", "{title}");}});</script> """.format( name=field.id, title=_(field.id.replace('_', ' ')), change_label=uc_first(_('change')), clear_label=uc_first(_('clear')), pager=pager(table), selection=selection, clear_style='' if selection else ' style="display: none;" ', required=' required' if field.flags.required else '') return super(TableSelect, self).__call__(field, **kwargs) + html
def reference_insert(code, origin_id=None): origin = EntityMapper.get_by_id(origin_id) if origin_id else None form = build_form(ReferenceForm, uc_first('Information Carrier' if code == 'carrier' else code)) if origin: del form.insert_and_continue if form.validate_on_submit(): return redirect(save(form, code=code, origin=origin)) return render_template('reference/insert.html', form=form, code=code, origin=origin)
def __call__(self, field: TreeField, **kwargs: Any) -> TreeMultiSelect: selection = '' selected_ids = [] root = g.nodes[int(field.id)] if field.data: # Somehow field.data can be a string after a failed form validation, so fix that below field.data = ast.literal_eval( field.data) if type(field.data) is str else field.data for entity_id in field.data: selected_ids.append(entity_id) selection += g.nodes[entity_id].name + '<br>' html = """ <span id="{name}-button" class="button">{change_label}</span> <div id="{name}-selection" style="text-align:left;">{selection}</div> <div id="{name}-overlay" class="overlay"> <div id="{name}-dialog" class="overlay-container"> <input class="tree-filter" id="{name}-tree-search" placeholder="{filter}"> <div id="{name}-tree"></div> </div> </div> <script> createOverlay("{name}", "{title}", true, "tree"); $("#{name}-tree").jstree({{ "core" : {{ "check_callback" : true, 'data':[{tree_data}] }}, "search": {{"case_insensitive": true, "show_only_matches": true}}, "plugins": ["search", "checkbox"], "checkbox": {{"three_state": false}} }}); $("#{name}-tree-search").keyup(function(){{ if (this.value.length >= {min_chars}) {{ $("#{name}-tree").jstree("search", $(this).val()); }} else if (this.value.length == 0) {{ $("#{name}-tree").jstree("search", $(this).val()); $("#{name}-tree").jstree(true).show_all(); }} }}); </script>""".format( filter=uc_first(_('type to search')), min_chars=session['settings']['minimum_jstree_search'], name=field.id, title=root.name, selection=selection, change_label=uc_first(_('change')), tree_data=NodeMapper.get_tree_data(int(field.id), selected_ids)) return super(TreeMultiSelect, self).__call__(field, **kwargs) + html
def display_delete_link(self, entity) -> str: """ Build a link to delete an entity with a JavaScript confirmation dialog.""" name = entity.name.replace('\'', '') confirm = 'onclick="return confirm(\'' + _('Delete %(name)s?', name=name) + '\')"' url = url_for(entity.view_name + '_delete', id_=entity.id) return '<a ' + confirm + ' href="' + url + '">' + util.uc_first( _('delete')) + '</a>'
def manual_link(self, wiki_site): # Creates a link to a manual page html = """ <p class="manual"> <a class="manual" href="{url}" rel="noopener" target="_blank"> <img style="height:14px;" src="/static/images/icons/book.png" alt='' /> {label} </a> </p>""".format(url='https://redmine.openatlas.eu/projects/uni/wiki/' + wiki_site, label=util.uc_first(_('manual'))) return html
def reference_add2(reference_id, class_name): """ Link an entity to reference coming from the reference.""" reference_ = EntityMapper.get_by_id(reference_id) form = getattr(openatlas.views.reference, 'Add' + uc_first(class_name) + 'Form')() if form.validate_on_submit(): property_code = 'P128' if reference_.class_.code == 'E84' else 'P67' entity = EntityMapper.get_by_id(getattr(form, class_name).data) reference_.link(property_code, entity, form.page.data) return redirect(url_for('reference_view', id_=reference_.id) + '#tab-' + class_name) return render_template('reference/add2.html', reference=reference_, form=form, class_name=class_name)
def __call__(self, field, **kwargs): selection = '' selected_ids = [] root = g.nodes[int(field.id)] if field.data: # Somehow field.data can be a string after a failed form validation, so fix that below field.data = ast.literal_eval(field.data) if type(field.data) is str else field.data for entity_id in field.data: selected_ids.append(entity_id) selection += g.nodes[entity_id].name + '<br />' html = """ <span id="{name}-button" class="button">{change_label}</span> <div id="{name}-selection" style="text-align:left;">{selection}</div> <div id="{name}-overlay" class="overlay"> <div id="{name}-dialog" class="overlay-container"> <input class="tree-filter" id="{name}-tree-search" placeholder="{filter}" /> <div id="{name}-tree"></div> </div> </div> <script> createOverlay("{name}", "{title}", true, "tree"); $("#{name}-tree").jstree({{ "core" : {{ "check_callback" : true, 'data':[{tree_data}] }}, "search": {{"case_insensitive": true, "show_only_matches": true}}, "plugins": ["search", "checkbox"], "checkbox": {{"three_state": false}} }}); $("#{name}-tree-search").keyup(function(){{ if (this.value.length >= {min_chars}) {{ $("#{name}-tree").jstree("search", $(this).val()); }} }}); </script>""".format( filter=uc_first(_('type to search')), min_chars=session['settings']['minimum_jstree_search'], name=field.id, title=root.name, selection=selection, change_label=uc_first(_('change')), tree_data=NodeMapper.get_tree_data(int(field.id), selected_ids)) return super(TreeMultiSelect, self).__call__(field, **kwargs) + html
def profile_index(): user = current_user data = {'info': [ (_('username'), user.username), (_('full name'), user.real_name), (_('email'), user.email), (_('show email'), uc_first(_('on')) if user.settings['show_email'] else uc_first(_('off'))), (_('newsletter'), uc_first(_('on')) if user.settings['newsletter'] else uc_first(_('off'))) ]} form = DisplayForm() if form.validate_on_submit(): user.settings['language'] = form.language.data user.settings['theme'] = form.theme.data user.settings['table_rows'] = form.table_rows.data user.settings['layout'] = form.layout.data g.cursor.execute('BEGIN') try: user.update_settings() g.cursor.execute('COMMIT') session['language'] = form.language.data flash(_('info update'), 'info') except Exception as e: # pragma: no cover g.cursor.execute('ROLLBACK') logger.log('error', 'database', 'transaction failed', e) flash(_('error transaction'), 'error') return redirect(url_for('profile_index')) form.language.data = user.settings['language'] form.theme.data = user.settings['theme'] form.table_rows.data = user.settings['table_rows'] form.layout.data = user.settings['layout'] data['display'] = [ (form.language.label, form.language), (str(form.table_rows.label) + display_tooltip(form.table_rows.description), form.table_rows), (str(form.layout.label) + display_tooltip(form.layout.description), form.layout)] return render_template('profile/index.html', data=data, form=form)
def content_index(): header = ['name'] for language in app.config['LANGUAGES'].keys(): header.append(language) header.append('text') table_content = {'id': 'content', 'header': header, 'data': []} for item, languages in ContentMapper.get_content().items(): url = url_for('content_view', item=item) content = ['<a href="' + url + '">' + util.uc_first(_(item)) + '</a>'] html_ok = '<img src="/static/images/icons/dialog-apply.png" alt="ok" \>' for language in app.config['LANGUAGES'].keys(): content.append(html_ok if languages[language] else '') content.append(languages[session['language']]) table_content['data'].append(content) return render_template('content/index.html', table_content=table_content)
def search_index(): classes = ['source', 'event', 'actor', 'place', 'reference'] form = SearchForm() form.classes.choices = [(x, uc_first(_(x))) for x in classes] form.classes.default = classes form.classes.process(request.form) table = {'data': []} if request.method == 'POST' and 'global-term' in request.form and request.form['global-term']: # Coming from global search form.term.data = request.form['global-term'] form.classes.data = classes table = build_search_table(form) if form.validate_on_submit(): table = build_search_table(form) return render_template('search/index.html', form=form, table=table)
def display_menu(self, origin): """ Returns html with the menu and mark appropriate item as selected.""" html = '' if current_user.is_authenticated: selected = origin.view_name if origin else '' items = ['overview', 'source', 'event', 'actor', 'place', 'reference', 'types', 'admin'] for item in items: if selected: css = 'active' if item == selected else '' else: css = 'active' if request.path.startswith('/' + item) or \ (item == 'overview' and request.path == '/') else '' html += '<div class="{css}"><a href="/{item}">{label}</a></div>'.format( css=css, item=item, label=util.uc_first(_(item))) return html
def print_base_type(self): from openatlas.models.node import NodeMapper if not self.view_name or self.view_name == 'actor': # actors have no base type return '' root_name = self.view_name.title() if self.view_name == 'reference': root_name = self.system_type.title() elif self.view_name == 'file': root_name = 'License' elif self.view_name == 'place': root_name = uc_first(self.system_type) if self.system_type == 'stratigraphic unit': root_name = 'Stratigraphic Unit' root_id = NodeMapper.get_hierarchy_by_name(root_name).id for node in self.nodes: if node.root and node.root[-1] == root_id: return node.name return ''
def source_view(id_): source = EntityMapper.get_by_id(id_) tables = { 'info': get_entity_data(source), 'text': {'id': 'translation', 'data': [], 'header': ['text', 'type', 'content']}, 'file': {'id': 'files', 'data': [], 'header': app.config['TABLE_HEADERS']['file'] + [_('main image')]}, 'reference': {'id': 'source', 'data': [], 'header': app.config['TABLE_HEADERS']['reference'] + ['page']}} for text in source.get_linked_entities('P73'): tables['text']['data'].append([ link(text), next(iter(text.nodes)).name if text.nodes else '', truncate_string(text.description)]) for name in ['actor', 'event', 'place', 'feature', 'stratigraphic-unit', 'find']: tables[name] = {'id': name, 'header': app.config['TABLE_HEADERS'][name], 'data': []} for link_ in source.get_links('P67'): range_ = link_.range data = get_base_table_data(range_) if is_authorized('editor'): url = url_for('link_delete', id_=link_.id, origin_id=source.id) data.append(display_remove_link(url + '#tab-' + range_.table_name, range_.name)) tables[range_.table_name]['data'].append(data) profile_image_id = source.get_profile_image_id() for link_ in source.get_links(['P67', 'P128'], True): domain = link_.domain data = get_base_table_data(domain) if domain.view_name == 'file': # pragma: no cover extension = data[3].replace('.', '') data.append(get_profile_image_table_link(domain, source, extension, profile_image_id)) if not profile_image_id and extension in app.config['DISPLAY_FILE_EXTENSIONS']: profile_image_id = domain.id if domain.view_name not in ['file']: data.append(link_.description) if is_authorized('editor'): update_url = url_for('reference_link_update', link_id=link_.id, origin_id=source.id) data.append('<a href="' + update_url + '">' + uc_first(_('edit')) + '</a>') if is_authorized('editor'): url = url_for('link_delete', id_=link_.id, origin_id=source.id) data.append(display_remove_link(url + '#tab-' + domain.view_name, domain.name)) tables[domain.view_name]['data'].append(data) return render_template('source/view.html', source=source, tables=tables, profile_image_id=profile_image_id)
def __call__(self, field, **kwargs): if field.data and type(field.data) is str: field.data = ast.literal_eval(field.data) selection = '' class_ = field.id if field.id != 'given_place' else 'place' table = Table(Table.HEADERS[class_]) # Make checkbox column sortable and show selected on top table.headers = 'headers:{' + str(len(table.header)) + ':{sorter:"checkbox"}},' table.sort = 'sortList:[[' + str(len(table.header)) + ', 0],[0, 0]],' if class_ == 'place': aliases = current_user.settings['table_show_aliases'] entities = EntityMapper.get_by_system_type('place', nodes=True, aliases=aliases) else: entities = EntityMapper.get_by_codes(class_) for entity in entities: selection += entity.name + '<br/>' if field.data and entity.id in field.data else '' data = get_base_table_data(entity) data[0] = re.sub(re.compile('<a.*?>'), '', data[0]) # Remove links data.append("""<input type="checkbox" id="{id}" {checked} value="{name}" class="multi-table-select">""".format( id=str(entity.id), name=entity.name, checked='checked = "checked"' if field.data and entity.id in field.data else '')) table.rows.append(data) html = """ <span id="{name}-button" class="button">{change_label}</span><br /> <div id="{name}-selection" class="selection" style="text-align:left;">{selection}</div> <div id="{name}-overlay" class="overlay"> <div id="{name}-dialog" class="overlay-container">{table}</div></div> <script> $(document).ready(function () {{createOverlay("{name}", "{title}", true);}}); </script>""".format(name=field.id, change_label=uc_first(_('change')), title=_(field.id.replace('_', ' ')), selection=selection, table=table.display(field.id, remove_rows=False)) return super(TableMultiSelect, self).__call__(field, **kwargs) + html
def __call__(self, field, **kwargs): if field.data and type(field.data) is str: field.data = ast.literal_eval(field.data) selection = '' class_ = field.id if field.id != 'given_place' else 'place' table = {'id': field.id, 'header': app.config['TABLE_HEADERS'][class_], 'data': []} # Make checkbox column sortable and show selected on top table['headers'] = 'headers: { ' + str(len(table['header'])) + ': { sorter: "checkbox" } }' table['sort'] = 'sortList: [[' + str(len(table['header'])) + ',0],[0,0]]' if class_ == 'place': entities = EntityMapper.get_by_system_type('place') else: entities = EntityMapper.get_by_codes(class_) for entity in entities: selection += entity.name + '<br/>' if field.data and entity.id in field.data else '' data = get_base_table_data(entity) data[0] = truncate_string(entity.name) # Replace entity link with entity name html = """<input type="checkbox" id="{id}" {checked} value="{name}" class="multi-table-select">""".format( id=str(entity.id), name=entity.name, checked='checked = "checked"' if field.data and entity.id in field.data else '') data.append(html) table['data'].append(data) html = """ <span id="{name}-button" class="button">{change_label}</span><br /> <div id="{name}-selection" class="selection" style="text-align:left;">{selection}</div> <div id="{name}-overlay" class="overlay"> <div id="{name}-dialog" class="overlay-container">{pager}</div></div> <script> $(document).ready(function () {{createOverlay("{name}", "{title}", true);}}); </script>""".format( name=field.id, change_label=uc_first(_('change')), title=_(field.id.replace('_', ' ')), selection=selection, pager=pager(table, remove_rows=False)) return super(TableMultiSelect, self).__call__(field, **kwargs) + html
def actor_view(id_): actor = EntityMapper.get_by_id(id_) actor.set_dates() objects = [] info = get_entity_data(actor) residence = actor.get_linked_entity('P74') if residence: object_ = residence.get_linked_entity('P53', True) objects.append(object_) info.append((uc_first(_('residence')), link(object_))) first = actor.get_linked_entity('OA8') if first: object_ = first.get_linked_entity('P53', True) objects.append(object_) info.append((uc_first(_('appears first')), link(object_))) last = actor.get_linked_entity('OA9') if last: object_ = last.get_linked_entity('P53', True) objects.append(object_) info.append((uc_first(_('appears last')), link(object_))) tables = { 'info': info, 'file': {'id': 'files', 'data': [], 'header': app.config['TABLE_HEADERS']['file'] + [_('main image')]}, 'source': {'id': 'source', 'data': [], 'header': app.config['TABLE_HEADERS']['source']}, 'reference': {'id': 'reference', 'data': [], 'header': app.config['TABLE_HEADERS']['reference'] + ['pages']}, 'event': {'id': 'event', 'data': [], 'header': ['event', 'class', 'involvement', 'first', 'last', 'description']}, 'relation': {'id': 'relation', 'data': [], 'sort': 'sortList:[[0,0]]', 'header': ['relation', 'actor', 'first', 'last', 'description']}, 'member_of': {'id': 'member_of', 'data': [], 'header': ['member of', 'function', 'first', 'last', 'description']}} profile_image_id = actor.get_profile_image_id() for link_ in actor.get_links('P67', True): domain = link_.domain data = get_base_table_data(domain) if domain.view_name == 'file': extension = data[3].replace('.', '') data.append(get_profile_image_table_link(domain, actor, extension, profile_image_id)) if not profile_image_id and extension in app.config['DISPLAY_FILE_EXTENSIONS']: profile_image_id = domain.id if domain.view_name not in ['source', 'file']: data.append(truncate_string(link_.description)) if is_authorized('editor'): update_url = url_for('reference_link_update', link_id=link_.id, origin_id=actor.id) data.append('<a href="' + update_url + '">' + uc_first(_('edit')) + '</a>') if is_authorized('editor'): url = url_for('link_delete', id_=link_.id, origin_id=actor.id) data.append(display_remove_link(url + '#tab-' + domain.view_name, domain.name)) tables[domain.view_name]['data'].append(data) # Todo: Performance - getting every place of every object for every event is very costly for link_ in actor.get_links(['P11', 'P14', 'P22', 'P23'], True): event = link_.domain first = link_.first place = event.get_linked_entity('P7') if place: objects.append(place.get_linked_entity('P53', True)) if not link_.first and event.first: first = '<span class="inactive" style="float:right">' + str(event.first) + '</span>' last = link_.last if not link_.last and event.last: last = '<span class="inactive" style="float:right">' + str(event.last) + '</span>' data = ([link(event), g.classes[event.class_.code].name, link_.type.name if link_.type else '', first, last, truncate_string(link_.description)]) if is_authorized('editor'): update_url = url_for('involvement_update', id_=link_.id, origin_id=actor.id) unlink_url = url_for('link_delete', id_=link_.id, origin_id=actor.id) + '#tab-event' data.append('<a href="' + update_url + '">' + uc_first(_('edit')) + '</a>') data.append(display_remove_link(unlink_url, link_.domain.name)) tables['event']['data'].append(data) for link_ in actor.get_links('OA7') + actor.get_links('OA7', True): if actor.id == link_.domain.id: type_ = link_.type.get_name_directed() if link_.type else '' related = link_.range else: type_ = link_.type.get_name_directed(True) if link_.type else '' related = link_.domain data = ([type_, link(related), link_.first, link_.last, truncate_string(link_.description)]) if is_authorized('editor'): update_url = url_for('relation_update', id_=link_.id, origin_id=actor.id) unlink_url = url_for('link_delete', id_=link_.id, origin_id=actor.id) + '#tab-relation' data.append('<a href="' + update_url + '">' + uc_first(_('edit')) + '</a>') data.append(display_remove_link(unlink_url, related.name)) tables['relation']['data'].append(data) for link_ in actor.get_links('P107', True): data = ([link(link_.domain), link_.type.name if link_.type else '', link_.first, link_.last, truncate_string(link_.description)]) if is_authorized('editor'): update_url = url_for('member_update', id_=link_.id, origin_id=actor.id) unlink_url = url_for('link_delete', id_=link_.id, origin_id=actor.id) + '#tab-member-of' data.append('<a href="' + update_url + '">' + uc_first(_('edit')) + '</a>') data.append(display_remove_link(unlink_url, link_.domain.name)) tables['member_of']['data'].append(data) if actor.class_.code in app.config['CLASS_CODES']['group']: tables['member'] = {'id': 'member', 'data': [], 'header': ['member', 'function', 'first', 'last', 'description']} for link_ in actor.get_links('P107'): data = ([link(link_.range), link_.type.name if link_.type else '', link_.first, link_.last, truncate_string(link_.description)]) if is_authorized('editor'): update_url = url_for('member_update', id_=link_.id, origin_id=actor.id) unlink_url = url_for('link_delete', id_=link_.id, origin_id=actor.id) + '#tab-member' data.append('<a href="' + update_url + '">' + uc_first(_('edit')) + '</a>') data.append(display_remove_link(unlink_url, link_.range.name)) tables['member']['data'].append(data) gis_data = GisMapper.get_all(objects) if objects else None if gis_data and gis_data['gisPointSelected'] == '[]': gis_data = None return render_template('actor/view.html', actor=actor, tables=tables, gis_data=gis_data, profile_image_id=profile_image_id)
def display_form(self, form, form_id=None, for_persons=False): multipart = 'enctype="multipart/form-data"' if hasattr(form, 'file') else '' if 'update' in request.path: if hasattr(form, 'save') and hasattr(form.save, 'label'): form.save.label.text = _('save') if hasattr(form, 'insert_and_continue'): del form.insert_and_continue id_attribute = ' id="' + form_id + '" ' if form_id else '' html = {'main': '', 'types': '', 'value_types': '', 'header': '', 'footer': ''} def display_value_type_fields(subs, html_=''): for sub_id in subs: sub = g.nodes[sub_id] field_ = getattr(form, str(sub_id)) html_ += """ <div class="table-row value-type-switch"> <div><label>{label}</label> {tooltip}</div> <div class="table-cell">{field}</div> </div> """.format(label=sub.name, tooltip=display_tooltip(sub.description), field=field_(class_='value-type')) html_ += display_value_type_fields(sub.subs) return html_ for field in form: if type(field) is ValueFloatField: continue class_ = 'required' if field.flags.required else '' class_ += ' integer' if type(field) is IntegerField else '' for validator in field.validators: class_ += ' email' if type(validator) is Email else '' errors = '' for error in field.errors: errors += util.uc_first(error) if field.type in ['TreeField', 'TreeMultiField']: hierarchy_id = int(field.id) node = g.nodes[hierarchy_id] label = node.name if node.name in app.config['BASE_TYPES']: label = util.uc_first(_('type')) if field.label.text == 'super': label = util.uc_first(_('super')) if node.value_type and 'is_node_form' not in form: html['value_types'] += """ <div class="table-row value-type-switch"> <div></div> <div class="table-cell"> <label style="font-weight:bold;">{label}</label> {tooltip} </div> </div> """.format(label=label, tooltip=display_tooltip(node.description)) html['value_types'] += display_value_type_fields(node.subs) continue else: type_field = """ <div class="table-row"> <div><label>{label}</label> {info}</div> <div class="table-cell">{field}</div> </div> """.format(label=label, field=str(field(class_=class_)) + errors, info='' if 'is_node_form' in form else display_tooltip(node.description)) if node.name in app.config['BASE_TYPES']: # base type should be above other fields html['types'] = type_field + html['types'] else: html['types'] += type_field continue if field.type in ['CSRFTokenField', 'HiddenField']: html['header'] += str(field) continue field.label.text = util.uc_first(field.label.text) field.label.text += ' *' if field.flags.required and form_id != 'login-form' else '' if field.id == 'description': html['footer'] += '<br />{label}<br />{text}<br />'.format( label=field.label, text=field(class_=class_)) continue if field.type == 'SubmitField': html['footer'] += str(field) continue if field.id.split('_', 1)[0] == 'date': # if it's a date field use a function to add dates if field.id == 'date_begin_year': html['footer'] += util.add_dates_to_form(form, for_persons) continue field.label.text += display_tooltip(field.description) errors = ' <span class="error">' + errors + ' </span>' if errors else '' if field.id in ('file', 'name'): html['header'] += '<div class="table-row"><div>' + str(field.label) + '</div>' html['header'] += '<div class="table-cell">' + str(field(class_=class_)) + errors html['header'] += '</div></div>' continue html['main'] += '<div class="table-row"><div>' + str(field.label) + '</div>' html['main'] += '<div class="table-cell">' + str(field(class_=class_)).replace('> ', '>') html['main'] += errors + '</div></div>' html_all = '<form method="post"' + id_attribute + ' ' + multipart + '>' html_all += '<div class="data-table">' if html['value_types']: values_html = """ <div class="table-row"> <div> <label>{values}</label> </div> <div class="table-cell value-type-switcher"> <span id="value-type-switcher" class="button">{show}</span> </div> </div>""".format(values=util.uc_first(_('values')), show=util.uc_first(_('show'))) html['value_types'] = values_html + html['value_types'] html_all += html['header'] + html['types'] + html['main'] + html['value_types'] + html['footer'] html_all += '</div></form>' return html_all
def display_delete_link(self, entity): """ Build a link to delete an entity with a JavaScript confirmation dialog.""" name = entity.name.replace('\'', '') confirm = 'onclick="return confirm(\'' + _('Delete %(name)s?', name=name) + '\')"' url = url_for(entity.view_name + '_delete', id_=entity.id) return '<a ' + confirm + ' href="' + url + '">' + util.uc_first(_('delete')) + '</a>'
def uc_first(self, string): return util.uc_first(string)