model changes, user institution history, style changes, forgot passowrd functionality, etc.

master
msinkec 3 years ago
parent 7da73f7d6a
commit 788f3bc3eb

@ -13,6 +13,6 @@ WORKDIR /usr/src/portal-webapp
RUN apt-get update && apt-get -y install wkhtmltopdf && \ RUN apt-get update && apt-get -y install wkhtmltopdf && \
rm -rf /var/lib/apt/lists/* rm -rf /var/lib/apt/lists/*
RUN pip3 install --no-cache-dir pdfkit flask==1.1.4 flask-dropzone flask-log-request-id flask-login Flask-SQLAlchemy alembic flask-migrate==2.7.0 Flask-script psycopg2 gunicorn pdfkit Werkzeug==1.0.1, PyJWT RUN pip3 install --no-cache-dir pdfkit flask==1.1.4 flask-dropzone flask-log-request-id flask-login Flask-SQLAlchemy alembic flask-migrate==2.7.0 Flask-script psycopg2 gunicorn pdfkit Werkzeug==1.0.1 PyJWT
ENTRYPOINT ["./entrypoint.sh"] ENTRYPOINT ["./entrypoint.sh"]

136
app.py

@ -123,7 +123,8 @@ upload_handler_regular = portal.regular.UploadHandlerRegular(
MAIL_SUBJECT=MAIL_SUBJECT, MAIL_SUBJECT=MAIL_SUBJECT,
MAIL_BODY=MAIL_BODY, MAIL_BODY=MAIL_BODY,
CONTRACT_CLIENT_CONTACT=CONTRACT_CLIENT_CONTACT, CONTRACT_CLIENT_CONTACT=CONTRACT_CLIENT_CONTACT,
MAX_FILES_PER_UPLOAD=MAX_FILES_PER_UPLOAD MAX_FILES_PER_UPLOAD=MAX_FILES_PER_UPLOAD,
APP_SECRET_KEY=APP_SECRET_KEY
) )
upload_handler_solar = portal.solar.UploadHandlerSolar( upload_handler_solar = portal.solar.UploadHandlerSolar(
@ -136,7 +137,8 @@ upload_handler_solar = portal.solar.UploadHandlerSolar(
MAIL_SUBJECT=MAIL_SUBJECT, MAIL_SUBJECT=MAIL_SUBJECT,
MAIL_BODY=MAIL_BODY, MAIL_BODY=MAIL_BODY,
CONTRACT_CLIENT_CONTACT=CONTRACT_CLIENT_CONTACT, CONTRACT_CLIENT_CONTACT=CONTRACT_CLIENT_CONTACT,
MAX_FILES_PER_UPLOAD=MAX_FILES_PER_UPLOAD MAX_FILES_PER_UPLOAD=MAX_FILES_PER_UPLOAD,
APP_SECRET_KEY=APP_SECRET_KEY
) )
@ -217,6 +219,9 @@ def solar_register_post():
name = request.form.get('name') name = request.form.get('name')
email = request.form.get('email') email = request.form.get('email')
password = request.form.get('password') password = request.form.get('password')
institution_name = request.form.get('institution')
institution_role = request.form.get('role')
institution = portal.base.get_institution_obj_by_name(institution_name)
user = RegisteredUser.query.filter_by(email=email).first() user = RegisteredUser.query.filter_by(email=email).first()
@ -248,7 +253,17 @@ def solar_register_post():
flash('Predolgo geslo.') flash('Predolgo geslo.')
return redirect('/solar/register') return redirect('/solar/register')
portal.base.register_new_user(name, email, password, active=False) if not institution:
flash('Institucija ne obstaja.')
return redirect('/solar/register')
if institution_role not in ['coordinator', 'mentor', 'other']:
flash('Neveljavna vloga v instituciji.')
return redirect('/solar/register')
user_id = portal.base.register_new_user(name, email, password, active=False)
portal.base.add_user_to_institution(user_id, institution.id, institution_role)
flash('Uspešna registracija.') flash('Uspešna registracija.')
return redirect('/solar/login') return redirect('/solar/login')
@ -270,13 +285,20 @@ def logout():
def solar(text): def solar(text):
is_admin = current_user.role == 'admin' is_admin = current_user.role == 'admin'
current_user_institution = portal.base.get_user_institution(current_user.id) current_user_institution = portal.base.get_user_institution(current_user.id)
current_user_obj = portal.base.get_user_obj(current_user.get_id())
institution_contract = None
if current_user_institution: if current_user_institution:
current_user_institution_moderator = portal.base.is_institution_moderator(current_user.id, current_user_institution.id) current_user_institution_coordinator = portal.base.is_institution_coordinator(current_user.id, current_user_institution.id)
institution_contract = portal.base.get_institution_contract(current_user_institution.id)
else: else:
current_user_institution_moderator = False current_user_institution_coordinator = False
if text.startswith('oddaja/') or text == 'oddaja': if text.startswith('oddaja/') or text == 'oddaja':
return render_template('solar-oddaja.html', is_admin=is_admin, is_institution_moderator=current_user_institution_moderator) return render_template('solar-oddaja.html',
is_admin=is_admin,
institution=current_user_institution,
institution_contract=institution_contract,
is_institution_coordinator=current_user_institution_coordinator)
elif text.startswith('zgodovina/') or text == 'zgodovina': elif text.startswith('zgodovina/') or text == 'zgodovina':
upload_items = portal.solar.get_upload_history(current_user.id) upload_items = portal.solar.get_upload_history(current_user.id)
uploader_names = [] uploader_names = []
@ -289,58 +311,66 @@ def solar(text):
else: else:
institution_names.append(institution.name) institution_names.append(institution.name)
return render_template('solar-zgodovina.html', upload_history=upload_items, uploader_names=uploader_names, return render_template('solar-zgodovina.html', upload_history=upload_items, uploader_names=uploader_names,
institution_names=institution_names, is_admin=is_admin, is_institution_moderator=current_user_institution_moderator) institution_names=institution_names, is_admin=is_admin, is_institution_coordinator=current_user_institution_coordinator)
elif text.startswith('pogodbe/') or text == 'pogodbe': elif text.startswith('pogodbe-institucije/') or text.startswith('pogodbe-ucencistarsi/'):
# Check for ownload contract request. # Check for ownload contract request.
match = re.match('^pogodbe/([a-z0-9_]+\.pdf)$', text) match = re.match('^pogodbe-(institucije|ucencistarsi)/([a-z0-9_]+\.pdf)$', text)
if match: if match:
filename = match.group(1) contract_type = match.group(1)
filename = match.group(2)
if len(filename) < 10: if len(filename) < 10:
return '', 404 return '', 404
prefix = filename[:2] prefix = filename[:2]
suffix = filename[2:] suffix = filename[2:]
f_hash = filename.split('.')[0]
if contract_type == 'institucije':
actual_filename = portal.base.get_actual_institution_contract_filename(f_hash)
else:
actual_filename = portal.base.get_actual_studentparent_contract_filename(f_hash)
safe_path = safe_join(str(upload_handler_solar.get_uploads_subdir('contracts')), prefix, suffix) safe_path = safe_join(str(upload_handler_solar.get_uploads_subdir('contracts')), prefix, suffix)
try: try:
return send_file(safe_path, as_attachment=True) return send_file(safe_path, attachment_filename=actual_filename, as_attachment=True)
except FileNotFoundError: except FileNotFoundError:
return '', 404 return '', 404
elif text.startswith('pogodbe/') or text == 'pogodbe':
user_obj = portal.base.get_user_obj(current_user.get_id())
institution = portal.base.get_user_institution(user_obj.id)
contracts_students = [] contracts_students = []
contract_school = [] contract_school = []
enable_upload_school_contract = False enable_upload_school_contract = False
show_upload_form = False show_upload_form = False
collaborators = [] collaborators = []
if institution: cooperation_history = dict()
collaborators = portal.base.get_all_active_institution_users(institution.id) if current_user_institution:
collaborators = portal.base.get_all_active_institution_users(current_user_institution.id)
show_upload_form = True show_upload_form = True
contract_school = portal.solar.get_institution_contract(institution.id) contract_school = portal.solar.get_institution_contract(current_user_institution.id)
if portal.base.is_institution_moderator(user_obj.id, institution.id): cooperation_history = portal.base.get_institution_cooperation_history(current_user_institution.id)
contracts_students = portal.solar.get_institution_student_contracts(institution.id) if portal.base.is_institution_coordinator(current_user_obj.id, current_user_institution.id):
contracts_students = portal.solar.get_institution_student_contracts(current_user_institution.id)
enable_upload_school_contract = True enable_upload_school_contract = True
else: else:
contracts_students = portal.solar.get_institution_student_contracts(institution.id, user_obj.id) contracts_students = portal.solar.get_institution_student_contracts(current_user_institution.id, current_user_obj.id)
return render_template('solar-pogodbe.html', contracts_students=contracts_students, return render_template('solar-pogodbe.html', contracts_students=contracts_students,
contract_school=contract_school, contract_school=contract_school,
enable_upload_school_contract=enable_upload_school_contract, enable_upload_school_contract=enable_upload_school_contract,
show_upload_form=show_upload_form, show_upload_form=show_upload_form,
collaborators=collaborators, collaborators=collaborators,
is_admin=is_admin, is_institution_moderator=current_user_institution_moderator) cooperation_history=cooperation_history,
is_admin=is_admin, is_institution_coordinator=current_user_institution_coordinator)
elif text.startswith('admin/') or text == 'admin': elif text.startswith('admin/') or text == 'admin':
users = portal.base.get_all_active_users_join_institutions() users = portal.base.get_all_users_join_institutions()
inactive_users = portal.base.get_all_inactive_users() inactive_users = portal.base.get_all_users_join_institutions(active=False)
solar_institutions = portal.solar.get_all_institutions() solar_institutions = portal.solar.get_all_institutions()
cooperation_history = portal.base.get_cooperation_history()
if is_admin: if is_admin:
return render_template('solar-admin.html', users=users, return render_template('solar-admin.html', users=users, user_cooperation_history=cooperation_history,
institutions=solar_institutions, inactive_users=inactive_users) institutions=solar_institutions, inactive_users=inactive_users)
elif text.startswith('manage-institution/') or text == 'manage-institution': elif text.startswith('manage-institution/') or text == 'manage-institution':
institution = portal.base.get_user_institution(current_user.id) if portal.base.is_institution_coordinator(current_user.id, current_user_institution.id):
if portal.base.is_institution_moderator(current_user.id, institution.id):
solar_users = portal.base.get_all_active_users() solar_users = portal.base.get_all_active_users()
institution_users = portal.base.get_all_active_institution_users(institution.id) institution_users = portal.base.get_all_active_institution_users(current_user_institution.id)
return render_template('solar-manage-institution.html', users=solar_users, return render_template('solar-manage-institution.html', users=solar_users,
institution_users=institution_users) institution_users=institution_users)
return '', 404 return '', 404
@ -420,7 +450,7 @@ def solar_forgotpass():
def solar_sendresetpass(): def solar_sendresetpass():
email = request.form.get('email') email = request.form.get('email')
portal.base.send_resetpass_mail(email, upload_handler_regular.config) portal.base.send_resetpass_mail(email, upload_handler_solar.config)
flash('Povezava za ponastavitev gesla je bila poslana na vpisan email naslov.') flash('Povezava za ponastavitev gesla je bila poslana na vpisan email naslov.')
return redirect(redirect_url()) return redirect(redirect_url())
@ -428,7 +458,7 @@ def solar_sendresetpass():
@app.route('/solar/resetpass/<token>') @app.route('/solar/resetpass/<token>')
def solar_resetpass(token): def solar_resetpass(token):
user = portal.base.verify_reset_token(token) user = portal.base.verify_reset_token(token, upload_handler_solar.config['APP_SECRET_KEY'])
if not user: if not user:
return '', 404 return '', 404
@ -439,7 +469,7 @@ def solar_resetpass(token):
@app.route('/solar/resetpass/<token>', methods=['POST']) @app.route('/solar/resetpass/<token>', methods=['POST'])
def solar_resetpass_post(token): def solar_resetpass_post(token):
new_password = request.form.get('new_password') new_password = request.form.get('new_password')
user = portal.base.verify_reset_token(token) user = portal.base.verify_reset_token(token, upload_handler_solar.config['APP_SECRET_KEY'])
if not user: if not user:
return '', 404 return '', 404
@ -448,7 +478,8 @@ def solar_resetpass_post(token):
if rowcount == 0: if rowcount == 0:
return '', 404 return '', 404
return 'Ponastavitev gesla uspešna.' flash('Ponastavitev gesla je bila uspešna.')
return redirect('/solar/login')
@app.route('/solar/topuploads') @app.route('/solar/topuploads')
@ -460,9 +491,12 @@ def solar_topuploads_srednje():
@app.route('/solar/deluser', methods=['POST']) @app.route('/solar/deluser', methods=['POST'])
@login_required @login_required
def solar_del_user(): def solar_del_user():
# TODO: check if user is institution moderator for the added users institution or is an admin if not portal.base.is_admin(current_user.id):
# TODO: delete from "user", "user_institution_mapping", update "institution_contract" set user to NULL return '', 404
return '', 404 user_id = request.form.get('user_id')
portal.base.del_user(user_id)
flash('Uporabnik je bil odstranjen.')
return redirect(redirect_url())
@app.route('/<corpus_name>/addinstitution', methods=['POST']) @app.route('/<corpus_name>/addinstitution', methods=['POST'])
@login_required @login_required
@ -482,11 +516,8 @@ def add_institution(corpus_name):
flash('Predolgo ime.') flash('Predolgo ime.')
return redirect(redirect_url()) return redirect(redirect_url())
if not region: if not region in portal.solar.VALID_REGIONS:
flash('Prazno polje za regijo.') flash('Neveljavna vrednost za regijo.')
return redirect(redirect_url())
if len(region) > 100:
flash('Predolgi niz za regijo.')
return redirect(redirect_url()) return redirect(redirect_url())
institution_id = portal.base.add_institution(name, region) institution_id = portal.base.add_institution(name, region)
@ -506,37 +537,34 @@ def add_user_institution_mapping(corpus_name):
if institution: if institution:
institution_id = institution.id institution_id = institution.id
if not (portal.base.is_admin(current_user.id) or portal.base.is_institution_moderator(current_user.id, institution_id)): if not (portal.base.is_admin(current_user.id) or portal.base.is_institution_coordinator(current_user.id, institution_id)):
return '', 404 return '', 404
user_id = request.form['user_id'] user_id = request.form['user_id']
role = request.form['role'] role = request.form['role']
if role not in ['moderator', 'user']: if role not in ['coordinator', 'mentor', 'other']:
return '', 404 return '', 404
if portal.base.get_user_institution(user_id): if portal.base.get_user_institution(user_id):
flash('Uporabnik je že dodeljen instituciji. Dodeljevanje večim institucijam '\ flash('Uporabnik je že dodeljen instituciji.')
'zaenkrat ni implementirano.')
return redirect(redirect_url()) return redirect(redirect_url())
portal.base.add_user_to_institution(user_id, institution_id, role) portal.base.add_user_to_institution(user_id, institution_id, role)
flash('Uporabnik je bil dodeljen instituciji.') flash('Uporabnik je bil dodeljen instituciji.')
return redirect(redirect_url()) return redirect(redirect_url())
@app.route('/<corpus_name>/deluserfrominstitution', methods=['POST']) @app.route('/solar/deluserfrominstitution', methods=['POST'])
@login_required @login_required
def del_user_institution_mapping(corpus_name): def del_user_institution_mapping():
institution = portal.base.get_user_institution(current_user.id)
if not portal.base.is_admin(current_user.id) \
and not portal.base.is_institution_moderator(current_user.id, institution.id):
return '', 404
if not corpus_name in ENABLED_CORPUSES:
return '', 404
user_id = request.form['user_id'] user_id = request.form['user_id']
institution = portal.base.get_user_institution(user_id)
if not institution:
flash('Uporabnik ni član nobene institucije.')
return redirect(redirect_url())
if not portal.base.is_institution_member(user_id, institution.id): if not portal.base.is_admin(current_user.id) \
flash('Uporabnik ni član vaše institucije.') and not portal.base.is_institution_coordinator(current_user.id, institution.id):
flash('Nimate ustreznih pravic za odstranitev uporabnika iz institucije.')
return redirect(redirect_url()) return redirect(redirect_url())
portal.base.del_user_from_institution(user_id, institution.id) portal.base.del_user_from_institution(user_id, institution.id)

@ -0,0 +1,37 @@
"""Added UserCooperationHistory.
Revision ID: 4c132220432f
Revises: ff356b1ef661
Create Date: 2021-09-01 09:25:43.144096
"""
from alembic import op
import sqlalchemy as sa
# revision identifiers, used by Alembic.
revision = '4c132220432f'
down_revision = 'ff356b1ef661'
branch_labels = None
depends_on = None
def upgrade():
# ### commands auto generated by Alembic - please adjust! ###
op.create_table('user_cooperation',
sa.Column('id', sa.Integer(), nullable=False),
sa.Column('user', sa.Integer(), nullable=False),
sa.Column('institution', sa.Integer(), nullable=False),
sa.Column('role', sa.String(), nullable=False),
sa.Column('badge_text', sa.String(), nullable=True),
sa.ForeignKeyConstraint(['institution'], ['institution.id'], ),
sa.ForeignKeyConstraint(['user'], ['registered_user.id'], ),
sa.PrimaryKeyConstraint('id')
)
# ### end Alembic commands ###
def downgrade():
# ### commands auto generated by Alembic - please adjust! ###
op.drop_table('user_cooperation')
# ### end Alembic commands ###

@ -0,0 +1,28 @@
"""Added school year column to user history.
Revision ID: c60b7bfaaf85
Revises: 4c132220432f
Create Date: 2021-09-02 08:36:41.711040
"""
from alembic import op
import sqlalchemy as sa
# revision identifiers, used by Alembic.
revision = 'c60b7bfaaf85'
down_revision = '4c132220432f'
branch_labels = None
depends_on = None
def upgrade():
# ### commands auto generated by Alembic - please adjust! ###
op.add_column('user_cooperation', sa.Column('school_year', sa.String(), nullable=False))
# ### end Alembic commands ###
def downgrade():
# ### commands auto generated by Alembic - please adjust! ###
op.drop_column('user_cooperation', 'school_year')
# ### end Alembic commands ###

@ -0,0 +1,58 @@
"""Dropped out predavanje stuff, added contract initial file name.
Revision ID: ff356b1ef661
Revises: 5ba116fc7f06
Create Date: 2021-08-26 12:45:56.036966
"""
from alembic import op
import sqlalchemy as sa
from sqlalchemy.dialects import postgresql
# revision identifiers, used by Alembic.
revision = 'ff356b1ef661'
down_revision = '5ba116fc7f06'
branch_labels = None
depends_on = None
def upgrade():
# ### commands auto generated by Alembic - please adjust! ###
op.drop_table('stamps_solar')
op.drop_table('upload_predavanja')
op.add_column('contracts_solar', sa.Column('original_filename', sa.String(), nullable=True))
op.add_column('institution_contract', sa.Column('original_filename', sa.String(), nullable=True))
# ### end Alembic commands ###
def downgrade():
# ### commands auto generated by Alembic - please adjust! ###
op.drop_column('institution_contract', 'original_filename')
op.drop_column('contracts_solar', 'original_filename')
op.create_table('upload_predavanja',
sa.Column('id', sa.INTEGER(), autoincrement=True, nullable=False),
sa.Column('upload_hash', sa.VARCHAR(), autoincrement=False, nullable=False),
sa.Column('timestamp', postgresql.TIMESTAMP(), autoincrement=False, nullable=False),
sa.Column('name', sa.VARCHAR(), autoincrement=False, nullable=False),
sa.Column('address', sa.VARCHAR(), autoincrement=False, nullable=False),
sa.Column('subject', sa.VARCHAR(), autoincrement=False, nullable=False),
sa.Column('faculty', sa.VARCHAR(), autoincrement=False, nullable=False),
sa.Column('email', sa.VARCHAR(), autoincrement=False, nullable=False),
sa.Column('phone', sa.VARCHAR(), autoincrement=False, nullable=True),
sa.Column('keywords', sa.VARCHAR(), autoincrement=False, nullable=False),
sa.Column('file_contract', sa.VARCHAR(), autoincrement=False, nullable=True),
sa.Column('upload_file_hashes', postgresql.ARRAY(sa.VARCHAR()), autoincrement=False, nullable=True),
sa.Column('agree_publish_future', sa.TEXT(), autoincrement=False, nullable=False),
sa.Column('agree_machine_translation', sa.BOOLEAN(), autoincrement=False, nullable=False),
sa.Column('agree_news_cjvt', sa.BOOLEAN(), autoincrement=False, nullable=False),
sa.PrimaryKeyConstraint('id', name='upload_predavanja_pkey')
)
op.create_table('stamps_solar',
sa.Column('id', sa.INTEGER(), autoincrement=True, nullable=False),
sa.Column('institution', sa.INTEGER(), autoincrement=False, nullable=True),
sa.Column('name', sa.TEXT(), autoincrement=False, nullable=False),
sa.Column('file_logo', sa.TEXT(), autoincrement=False, nullable=True),
sa.ForeignKeyConstraint(['institution'], ['institution.id'], name='stamps_institution_fkey'),
sa.PrimaryKeyConstraint('id', name='stamps_pkey')
)
# ### end Alembic commands ###

@ -25,7 +25,7 @@ import jwt
from werkzeug.security import generate_password_hash from werkzeug.security import generate_password_hash
from . model import db, UploadRegular, UploadSolar, RegisteredUser, UserInstitutionMapping, Institution, InstitutionContract, CorpusAccess from . model import *
#REGEX_EMAIL = re.compile('^[a-z0-9]+[\._]?[a-z0-9]+[@]\w+[.]\w{2,3}$') #REGEX_EMAIL = re.compile('^[a-z0-9]+[\._]?[a-z0-9]+[@]\w+[.]\w{2,3}$')
@ -248,6 +248,33 @@ def get_user_institution(user_id):
return None return None
def get_institution_contract(institution_id):
return InstitutionContract.query.filter_by(institution=institution_id).first()
def get_institution_cooperation_history(institution_id):
#return CooperationToken.query.join(UserCooperationTokenMapping,
# UserCooperationTokenMapping.cooperation_token == CooperationToken.id).filter(UserCooperationTokenMapping.user == user_id).all()
#
res = dict()
uch_rows = UserCooperationHistory.query.filter_by(institution=institution_id).order_by(UserCooperationHistory.school_year.desc()).all()
for row in uch_rows:
if row.user not in res:
res[row.user] = {
'coordinator': [],
'moderator': [],
'other': []
}
res[row.user][row.role].append(row.school_year)
return res
def get_cooperation_history():
return UserCooperationHistory.query.all()
def has_user_corpus_access(user_id, corpus_name): def has_user_corpus_access(user_id, corpus_name):
user = RegisteredUser.query.filter_by(id=user_id).first() user = RegisteredUser.query.filter_by(id=user_id).first()
@ -281,6 +308,10 @@ def get_institution_obj(institution_id):
return Institution.query.filter_by(id=institution_id).first() return Institution.query.filter_by(id=institution_id).first()
def get_institution_obj_by_name(institution_name):
return Institution.query.filter_by(name=institution_name).first()
def register_new_user(name, email, password, active=True, admin=False): def register_new_user(name, email, password, active=True, admin=False):
model_obj = RegisteredUser( model_obj = RegisteredUser(
name=name, name=name,
@ -334,11 +365,18 @@ def activate_user(user_id):
def update_user_password(user_id, new_password): def update_user_password(user_id, new_password):
phash = generate_password_hash(new_password) phash = generate_password_hash(new_password)
rowcount = db.session.query(RegisteredUser).filter_by(id=user_id).update({'pass_hash': pass_hash}) rowcount = db.session.query(RegisteredUser).filter_by(id=user_id).update({'pass_hash': phash})
db.session.commit() db.session.commit()
return rowcount return rowcount
def del_user(user_id):
db.session.query(UserCooperationHistory).filter(UserCooperationHistory.user == user_id).delete()
db.session.query(UserInstitutionMapping).filter(UserInstitutionMapping.user == user_id).delete()
db.session.query(RegisteredUser).filter(RegisteredUser.id == user_id).delete()
db.session.commit()
def del_user_from_institution(user_id, institution_id): def del_user_from_institution(user_id, institution_id):
db.session.query(UserInstitutionMapping).filter(UserInstitutionMapping.institution == institution_id).filter(UserInstitutionMapping.user == user_id).delete() db.session.query(UserInstitutionMapping).filter(UserInstitutionMapping.institution == institution_id).filter(UserInstitutionMapping.user == user_id).delete()
db.session.commit() db.session.commit()
@ -347,24 +385,28 @@ def del_user_from_institution(user_id, institution_id):
def get_all_active_users(): def get_all_active_users():
return RegisteredUser.query.filter_by(active=True).order_by(RegisteredUser.id).all() return RegisteredUser.query.filter_by(active=True).order_by(RegisteredUser.id).all()
def get_all_inactive_users(): def get_all_inactive_users():
return RegisteredUser.query.filter_by(active=False).order_by(RegisteredUser.id).all() return RegisteredUser.query.filter_by(active=False).order_by(RegisteredUser.id).all()
def get_all_active_users_join_institutions():
def get_all_users_join_institutions(active=True):
#return RegisteredUser.query.filter_by(active=True).order_by(RegisteredUser.id).all() #return RegisteredUser.query.filter_by(active=True).order_by(RegisteredUser.id).all()
return db.session.query(RegisteredUser, UserInstitutionMapping).outerjoin(UserInstitutionMapping, return db.session.query(RegisteredUser, UserInstitutionMapping).outerjoin(UserInstitutionMapping,
RegisteredUser.id == UserInstitutionMapping.user).order_by(RegisteredUser.id).all() RegisteredUser.id == UserInstitutionMapping.user).filter(RegisteredUser.active == active).order_by(RegisteredUser.id).all()
def get_all_active_institution_users(institution_id): def get_all_active_institution_users(institution_id):
return RegisteredUser.query.filter_by(active=True).join(UserInstitutionMapping, return RegisteredUser.query.filter_by(active=True).join(UserInstitutionMapping,
RegisteredUser.id == UserInstitutionMapping.user).filter(UserInstitutionMapping.institution == institution_id).all() RegisteredUser.id == UserInstitutionMapping.user).filter(UserInstitutionMapping.institution == institution_id).all()
def is_institution_moderator(user_id, institution_id): def is_institution_coordinator(user_id, institution_id):
user_inst_mapping = UserInstitutionMapping.query.filter_by(user=user_id).filter_by(institution=institution_id).first() user_inst_mapping = UserInstitutionMapping.query.filter_by(user=user_id).filter_by(institution=institution_id).first()
if not user_inst_mapping: if not user_inst_mapping:
return False return False
if user_inst_mapping.role != 'moderator': if user_inst_mapping.role != 'coordinator':
return False return False
return True return True
@ -376,15 +418,34 @@ def is_institution_member(user_id, institution_id):
return True return True
def get_password_reset_token(email, expires=500): def get_actual_institution_contract_filename(f_hash):
contract = InstitutionContract.query.filter_by(file_contract=f_hash).first()
if contract:
return contract.original_filename
return None
def get_actual_studentparent_contract_filename(f_hash):
contract = ContractsSolar.query.filter_by(file_contract=f_hash).first()
if contract:
return contract.original_filename
return None
def get_password_reset_token(email, key, expires=600):
return jwt.encode({'reset_password': email, return jwt.encode({'reset_password': email,
'exp': time() + expires}, 'exp': int(time.time()) + expires},
key=os.getenv('APP_SECRET_KEY'), algorithm='HS256') key=key, algorithm='HS256')
def verify_reset_token(token): def verify_reset_token(token, key):
try: try:
email = jwt.decode(token, message = jwt.decode(token,
key=os.getenv('APP_SECRET_KEY'), algorithms=["HS256"])['reset_password'] key=key, algorithms=["HS256"])
email = message['reset_password']
exp = message['exp']
if int(time.time()) >= exp:
# Token timed out
return None
except Exception as e: except Exception as e:
logging.error(e) logging.error(e)
return return
@ -392,13 +453,12 @@ def verify_reset_token(token):
def send_resetpass_mail(email, config): def send_resetpass_mail(email, config):
jwt_token = get_password_reset_token(email) jwt_token = get_password_reset_token(email, config['APP_SECRET_KEY'])
text = ''' text = '''
Zahtevali ste ponastavitev gesla vašega uporabniškega računa. Zahtevali ste ponastavitev gesla vašega uporabniškega računa.
Geslo lahko ponastavite na naslednji povezavi: https://zbiranje.slovenscina.eu/solar/resetpass/{}'''.format( Geslo lahko ponastavite na naslednji povezavi: https://zbiranje.slovenscina.eu/solar/resetpass/{}'''.format(jwt_token)
'https://zbiranje.slovenscina.eu/solar/resetpass/{}'.format(jwt_token))
# Create a secure SSL context # Create a secure SSL context
context = ssl.create_default_context() context = ssl.create_default_context()

@ -28,25 +28,6 @@ class UploadRegular(db.Model):
corpus_name = db.Column(db.String, nullable=False) corpus_name = db.Column(db.String, nullable=False)
class UploadPredavanja(db.Model):
__tablename__ = 'upload_predavanja'
id = db.Column(db.Integer, primary_key=True)
upload_hash = db.Column(db.String, nullable=False)
timestamp = db.Column(db.DateTime, default=datetime.utcnow, nullable=False)
name = db.Column(db.String, nullable=False)
address = db.Column(db.String, nullable=False)
subject = db.Column(db.String, nullable=False)
faculty = db.Column(db.String, nullable=False)
email = db.Column(db.String, nullable=False)
phone = db.Column(db.String, nullable=True)
keywords = db.Column(db.String, nullable=False)
agree_publish_future = db.Column(db.String, nullable=False)
agree_machine_translation = db.Column(db.Boolean, default=False, nullable=False)
agree_news_cjvt = db.Column(db.Boolean, default=False, nullable=False)
file_contract = db.Column(db.String, nullable=True)
upload_file_hashes = db.Column(sqlalchemy.types.ARRAY(db.String), nullable=True)
class UploadSolar(db.Model): class UploadSolar(db.Model):
__tablename__ = 'upload_solar' __tablename__ = 'upload_solar'
id = db.Column(db.Integer, primary_key=True) id = db.Column(db.Integer, primary_key=True)
@ -65,14 +46,6 @@ class UploadSolar(db.Model):
upload_file_hashes = db.Column(sqlalchemy.types.ARRAY(db.String), nullable=True) upload_file_hashes = db.Column(sqlalchemy.types.ARRAY(db.String), nullable=True)
class StampsSolar(db.Model):
__tablename__ = 'stamps_solar'
id = db.Column(db.Integer, primary_key=True)
institution = db.Column(db.Integer, sqlalchemy.ForeignKey('institution.id'), nullable=True)
name = db.Column(db.String, nullable=False)
file_logo = db.Column(db.String, nullable=True)
class ContractsSolar(db.Model): class ContractsSolar(db.Model):
__tablename__ = 'contracts_solar' __tablename__ = 'contracts_solar'
id = db.Column(db.Integer, primary_key=True) id = db.Column(db.Integer, primary_key=True)
@ -80,6 +53,7 @@ class ContractsSolar(db.Model):
upload_user = db.Column(db.Integer, sqlalchemy.ForeignKey('registered_user.id'), nullable=True) upload_user = db.Column(db.Integer, sqlalchemy.ForeignKey('registered_user.id'), nullable=True)
timestamp = db.Column(db.DateTime, default=datetime.utcnow, nullable=False) timestamp = db.Column(db.DateTime, default=datetime.utcnow, nullable=False)
file_contract = db.Column(db.String, nullable=False) file_contract = db.Column(db.String, nullable=False)
original_filename = db.Column(db.String, nullable=True)
contract_type = db.Column(db.String, nullable=False) contract_type = db.Column(db.String, nullable=False)
@ -100,7 +74,29 @@ class UserInstitutionMapping(db.Model):
id = db.Column(db.Integer, primary_key=True) id = db.Column(db.Integer, primary_key=True)
user = db.Column(db.Integer, sqlalchemy.ForeignKey('registered_user.id'), nullable=False) user = db.Column(db.Integer, sqlalchemy.ForeignKey('registered_user.id'), nullable=False)
institution = db.Column(db.Integer, sqlalchemy.ForeignKey('institution.id'), nullable=False) institution = db.Column(db.Integer, sqlalchemy.ForeignKey('institution.id'), nullable=False)
role =db.Column(db.String, nullable=False) role = db.Column(db.String, nullable=False)
#class CooperationToken(db.Model):
# __tablename__ = 'cooperation_token'
# id = db.Column(db.Integer, primary_key=True)
# name = db.Column(db.String, nullable=False)
#
#
#class UserCooperationTokenMapping(db.Model):
# __tablename__ = 'user_cooperation_token_mapping'
# id = db.Column(db.Integer, primary_key=True)
# user = db.Column(db.Integer, sqlalchemy.ForeignKey('registered_user.id'), nullable=False)
# cooperation_token = db.Column(db.Integer, sqlalchemy.ForeignKey('cooperation_token.id'), nullable=False)
class UserCooperationHistory(db.Model):
__tablename__ = 'user_cooperation'
id = db.Column(db.Integer, primary_key=True)
user = db.Column(db.Integer, sqlalchemy.ForeignKey('registered_user.id'), nullable=False)
institution = db.Column(db.Integer, sqlalchemy.ForeignKey('institution.id'), nullable=False)
role = db.Column(db.String, nullable=False)
school_year = db.Column(db.String, nullable=False)
badge_text = db.Column(db.String, nullable=True)
class Institution(db.Model): class Institution(db.Model):
@ -124,4 +120,5 @@ class InstitutionContract(db.Model):
corpus = db.Column(db.String, nullable=False) corpus = db.Column(db.String, nullable=False)
timestamp = db.Column(db.DateTime, default=datetime.utcnow, nullable=False) timestamp = db.Column(db.DateTime, default=datetime.utcnow, nullable=False)
file_contract = db.Column(db.String, nullable=True) file_contract = db.Column(db.String, nullable=True)
original_filename = db.Column(db.String, nullable=True)

@ -13,6 +13,7 @@ VALID_PROGRAMS = {'OS', 'SSG', 'MGP', 'ZG', 'NPI', 'SPI', 'SSI', 'PTI'}
VALID_SUBJECTS = {'slo', 'drug-jez', 'drug-druz', 'drug-narav', 'drug-strok', 'drug-izb'} VALID_SUBJECTS = {'slo', 'drug-jez', 'drug-druz', 'drug-narav', 'drug-strok', 'drug-izb'}
VALID_TEXT_TYPES = {'esej-spis', 'prakticno', 'solski-test', 'delo-v-razredu'} VALID_TEXT_TYPES = {'esej-spis', 'prakticno', 'solski-test', 'delo-v-razredu'}
VALID_GRAMMAR_CORRECTIONS = {'popr-ne', 'brez-popr', 'popr-da'} VALID_GRAMMAR_CORRECTIONS = {'popr-ne', 'brez-popr', 'popr-da'}
VALID_REGIONS = {'CE', 'GO', 'KK', 'KP', 'KR', 'LJ', 'MB', 'MS', 'NM', 'PO', 'SG'}
MAXLEN_FORM = 150 MAXLEN_FORM = 150
@ -109,17 +110,18 @@ class UploadHandlerSolar(UploadHandler):
if user_institution_mapping is None: if user_institution_mapping is None:
return 'Vaš uporabnik ni dodeljen nobeni inštituciji.' return 'Vaš uporabnik ni dodeljen nobeni inštituciji.'
institution_id = user_institution_mapping.institution institution_id = user_institution_mapping.institution
is_institution_moderator = True if user_institution_mapping.role == 'moderator' else False is_institution_coordinator = True if user_institution_mapping.role == 'coordinator' else False
if contract_type == 'sola': if contract_type == 'sola':
if not is_institution_moderator: if not is_institution_coordinator:
return 'Vaš uporabnik nima pravic za nalaganje pogodbe s šolo.' return 'Vaš uporabnik nima pravic za nalaganje pogodbe s šolo.'
# TODO: insert institution contract # TODO: insert institution contract
model_obj = InstitutionContract( model_obj = InstitutionContract(
institution=institution_id, institution=institution_id,
corpus='solar', corpus='solar',
timestamp=timestamp, timestamp=timestamp,
file_contract=f_hash file_contract=f_hash,
original_filename=f_obj.filename
) )
self.store_model(model_obj) self.store_model(model_obj)
else: else:
@ -129,6 +131,7 @@ class UploadHandlerSolar(UploadHandler):
timestamp=timestamp, timestamp=timestamp,
file_contract=f_hash, file_contract=f_hash,
contract_type=contract_type, contract_type=contract_type,
original_filename=f_obj.filename
) )
self.store_model(model_obj) self.store_model(model_obj)

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

@ -142,33 +142,50 @@ label {
} }
.contract-item { .contract-item {
height: 50px; height: 65px;
margin: 5px; margin: 5px;
border: 0px; border: 0px;
border-bottom: 2px solid #c4c4c4; border-bottom: 2px solid #c4c4c4;
} }
.contract-item-icon {
height: 50%;
margin-top: 15px;
margin-left: 15px;
margin-right: 15px;
float: left;
}
.contract-item-title { .contract-item-title {
font-family: Roboto; font-family: Roboto;
font-style: normal; font-style: normal;
font-weight: normal; font-weight: normal;
font-size: 16px; font-size: 16px;
color: #46535b; color: #46535b;
padding-top: 10px;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
} }
.contract-item-button { .contract-item-button {
font-family: Roboto; font-family: Roboto;
font-style: normal; font-style: normal;
font-weight: normal; font-weight: normal;
font-size: 16px; font-size: 14px;
float: right;
margin-top: -5px;
} }
.contract-item-date { .contract-item-data {
font-family: Roboto; font-family: Roboto;
font-style: normal; font-style: normal;
font-weight: normal; font-weight: normal;
font-size: 10px; font-size: 10px;
color: #46535b; color: #46535b;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
} }
#collaborators-container { #collaborators-container {
@ -200,6 +217,33 @@ label {
margin: 25px; margin: 25px;
padding:5px; padding:5px;
border-radius: 10px; border-radius: 10px;
text-align: center;
}
.warning {
background: #ffcc00;
border-radius: 15px;
border: 0px;
font-family: Roboto;
font-style: normal;
font-weight: normal;
font-size: 16px;
margin: 20px;
padding: 10px;
text-align: center;
}
.message-notification {
background: #99cc33;
border-radius: 15px;
border: 0px;
font-family: Roboto;
font-style: normal;
font-weight: normal;
font-size: 16px;
margin: 20px;
padding: 10px;
text-align: center;
} }
#button-submit { #button-submit {
@ -554,6 +598,10 @@ select {
left: 25% !important; left: 25% !important;
} }
.history-item {
border-bottom: 1px solid grey;
}
.history-item-date { .history-item-date {
font-family: Roboto; font-family: Roboto;
font-style: normal; font-style: normal;
@ -598,6 +646,7 @@ select {
font-weight: normal; font-weight: normal;
font-size: 12px; font-size: 12px;
line-height: 14px; line-height: 14px;
margin-bottom: 5px;
color: #848C91; color: #848C91;
} }

@ -47,7 +47,7 @@
<label for="telefon">Telefon:</label> <label for="telefon">Telefon:</label>
<input type="text" id="telefon" name="telefon"/> <input type="text" id="telefon" name="telefon"/>
<div class="form-text">*Po kliku na gumb “Oddaj” se bo prikazala vsebina pogodobe o odstopu avtorskih pravic. Če se z vsebino strinjate, kliknite gumb “Pošlji”, da podatke posredujete v korpus, po e-pošti pa boste prejeli svoj izvod pogodbe.</div> <div class="form-text">*Po kliku na gumb “Oddaj” se bo prikazala vsebina pogodbe o odstopu avtorskih pravic. Če se z vsebino strinjate, kliknite gumb “Pošlji”, da podatke posredujete v korpus, po e-pošti pa boste prejeli svoj izvod pogodbe.</div>
<button id="button-submit" type="submit">Oddaj</button> <button id="button-submit" type="submit">Oddaj</button>
</div> </div>

@ -5,8 +5,8 @@
<title>Admin panel - Šolar</title> <title>Admin panel - Šolar</title>
<style> <style>
.tableFixHead { .tableFixHead {
overflow-y: auto; overflow-y: scroll;
height: 306px; max-height: 306px;
} }
.tableFixHead thead th { .tableFixHead thead th {
position: sticky; position: sticky;
@ -24,6 +24,9 @@
th { th {
background: #eee; background: #eee;
} }
h2 {
color: blue;
}
</style> </style>
</head> </head>
<body> <body>
@ -45,13 +48,13 @@
<input type="password" id="password" name="password"><br> <input type="password" id="password" name="password"><br>
<input type="submit" value="Dodaj"> <input type="submit" value="Dodaj">
</form> </form>
<!--<h3>Odstrani uporabnika</h3> <h3>Odstrani uporabnika</h3>
<form action="/solar/deluser" method="post"> <form action="/solar/deluser" method="post">
<label for="user_id">ID uporabnika:</label><br> <label for="user_id">ID uporabnika:</label><br>
<input type="text" id="user_id" name="user_id"><br> <input type="text" id="user_id" name="user_id"><br>
<input type="submit" value="Odstrani"> <input type="submit" value="Odstrani">
</form>--> </form>
<h3>Seznam vseh aktivnih uporabnikov</h3> <h3>Aktivni uporabniki</h3>
<div class="tableFixHead"> <div class="tableFixHead">
<table> <table>
<thead> <thead>
@ -60,6 +63,7 @@
<th>Ime in priimek</th> <th>Ime in priimek</th>
<th>Email</th> <th>Email</th>
<th>ID institucije</th> <th>ID institucije</th>
<th>Vloga v instituciji</th>
</tr> </tr>
</thead> </thead>
<tbody> <tbody>
@ -69,6 +73,7 @@
<td>{{item[0].name}}</td> <td>{{item[0].name}}</td>
<td>{{item[0].email}}</td> <td>{{item[0].email}}</td>
<td>{{item[1].institution}}</td> <td>{{item[1].institution}}</td>
<td>{{item[1].role}}</td>
{% endfor %} {% endfor %}
</table> </table>
</div> </div>
@ -80,11 +85,18 @@
<input type="text" id="institution_id" name="institution_id"><br> <input type="text" id="institution_id" name="institution_id"><br>
<label for="role">Vloga v instituciji:</label> <label for="role">Vloga v instituciji:</label>
<select name="role" id="role"> <select name="role" id="role">
<option value="moderator">Moderator</option> <option value="coordinator">Koordinator/-ka</option>
<option value="user">Uporabnik</option> <option value="mentor">Mentor/-ica</option>
<option value="other">Druga vloga</option>
</select> </select>
<input type="submit" value="Dodeli"> <input type="submit" value="Dodeli">
</form> </form>
<h3>Odstrani uporabnika iz institucije</h3>
<form action="/solar/deluserfrominstitution" method="post">
<label for="user_id">ID uporabnika:</label>
<input type="text" id="user_id" name="user_id"><br>
<input type="submit" value="Odstrani">
</form>
<div> </div> <div> </div>
<h2>Institucije</h2> <h2>Institucije</h2>
<h3>Dodaj institucijo</h3> <h3>Dodaj institucijo</h3>
@ -92,7 +104,19 @@
<label for="name">Naziv:</label> <label for="name">Naziv:</label>
<input type="text" id="name" name="name"><br> <input type="text" id="name" name="name"><br>
<label for="region">Regija:</label> <label for="region">Regija:</label>
<input type="text" id="region" name="region"><br> <select name="region" id="region">
<option value="CE">Celje</option>
<option value="GO">Nova Gorica</option>
<option value="KK">Krško</option>
<option value="KP">Koper</option>
<option value="KR">Kranj</option>
<option value="LJ">Ljubljana</option>
<option value="MB">Maribor</option>
<option value="MS">Murska Sobota</option>
<option value="NM">Novo mesto</option>
<option value="PO">Postojna</option>
<option value="SG">Slovenj Gradec</option>
</select>
<input type="submit" value="Dodaj"> <input type="submit" value="Dodaj">
</form> </form>
<h3>Seznam vseh instituticij</h3> <h3>Seznam vseh instituticij</h3>
@ -115,7 +139,7 @@
{% endfor %} {% endfor %}
</table> </table>
</div> </div>
<h3>Seznam uporabnikov, ki niso aktivirani</h3> <h3>Čakajoči uporabniki</h3>
<div class="tableFixHead"> <div class="tableFixHead">
<table> <table>
<thead> <thead>
@ -123,14 +147,18 @@
<th>ID</th> <th>ID</th>
<th>Ime in priimek</th> <th>Ime in priimek</th>
<th>Email</th> <th>Email</th>
<th>Institucija</th>
<th>Vloga v instituciji</th>
</tr> </tr>
</thead> </thead>
<tbody> <tbody>
{% for item in inactive_users %} {% for item in inactive_users %}
<tr> <tr>
<td>{{item.id}}</td> <td>{{item[0].id}}</td>
<td>{{item.name}}</td> <td>{{item[0].name}}</td>
<td>{{item.email}}</td> <td>{{item[0].email}}</td>
<td>{{item[1].institution}}</td>
<td>{{item[1].role}}</td>
</tr> </tr>
{% endfor %} {% endfor %}
</table> </table>
@ -141,4 +169,30 @@
<input type="text" id="id" name="id"><br> <input type="text" id="id" name="id"><br>
<input type="submit" value="Aktiviraj"> <input type="submit" value="Aktiviraj">
</form> </form>
<h2>Zgodovina sodelovanja</h2>
<div class="tableFixHead">
<table>
<thead>
<tr>
<th>ID</th>
<th>ID uporabnika</th>
<th>ID institucije</th>
<th>Vloga</th>
<th>Šolsko leto</th>
<th>Besedilo značke</th>
</tr>
</thead>
<tbody>
{% for item in user_cooperation_history %}
<tr>
<td>{{item.id}}</td>
<td>{{item.user}}</td>
<td>{{item.institution}}</td>
<td>{{item.role}}</td>
<td>{{item.school_year}}</td>
<td>{{item.badge_text}}</td>
</tr>
{% endfor %}
</table>
</div>
</body> </body>

@ -28,9 +28,8 @@
</div> </div>
</div> </div>
<button class="button-general" style="margin-top: 20px;">Pošlji povezavo za ponastavitev geslo</button> <button class="button-general" style="margin-top: 20px;">Pošlji</button>
</form> </form>
<a href="/solar/register" class="contract-item-button">Registracija</a>
</div> </div>
</div> </div>
</div> </div>

@ -8,11 +8,17 @@
<body> <body>
<div id="main-window"> <div id="main-window">
<div id="rect1"> <div id="rect1">
<div style="padding: 50px;"> <div style="padding: 25px;">
<div id="logo-container"> <div id="logo-container">
<img src="/static/image/logo.svg" alt="logo"/> <img src="/static/image/logo.svg" alt="logo"/>
</div> </div>
<h3 id="title" style="font-size: 27px; text-align: left;">Prijava - ŠOLAR</h3> <h1 id="title">Portal za oddajanje besedil</h1>
<h2 id="subtitle">Korpus Šolar</h2>
<div class="form-text"><em>Zbiranje besedil za korpus Šolar poteka po naslednjem postopku, ki prinaša tudi točke za napredovanje.</em></div>
<h3 id="title" style="font-size: 27px; text-align: left;">Prijava</h3>
<div> <div>
{% with messages = get_flashed_messages() %} {% with messages = get_flashed_messages() %}
{% if messages %} {% if messages %}
@ -24,22 +30,32 @@
<form method="POST" action="/solar/login"> <form method="POST" action="/solar/login">
<div> <div>
<div> <div>
<input type="email" name="email" placeholder="Email" autofocus=""> <img src="/static/image/user.png" alt="user" style="float: left; width: 10%;"/>
<input type="email" name="email" placeholder="Email" autofocus=""
style="float: right; width: 85%; margin-bottom: 20px; margin-top: 10px;">
</div> </div>
</div> </div>
<div> <div>
<div> <div>
<input type="password" name="password" placeholder="Geslo"> <img src="/static/image/password.png" alt="user" style="float: left; width: 10%; margin-top: 20px;"/>
<input type="password" name="password" placeholder="Geslo"
style="float: right; width: 85%; margin-bottom: 20px; margin-top: 10px;">
</div> </div>
</div> </div>
<div style="display:flex; flex-direction: row; justify-content: left; align-items: center"> <button class="button-general" style="margin-top: 30px; margin-bottom: 20px;">PRIJAVA</button>
<label>Zapomni prijavo</label>
<input style="width: 10%;" type="checkbox">
</div>
<button class="button-general" style="margin-top: 20px;">PRIJAVA</button>
</form> </form>
<a href="/solar/register" class="contract-item-button">Registracija</a> <a href="/solar/forgotpass" class="contract-item-button">Pozabljeno geslo</a>
<a href="/solar/register" class="contract-item-button"
style="-webkit-appearance: button;
-moz-appearance: button;
appearance: button;
margin-top: 80px;
text-decoration: none;
color: #46535b;
width: 100%;"
><img src="/static/image/register.png" alt="user" style="float: left; width: 15%; margin-right: 25px;"/>
Registracija<br><div style="font-size: 11px; margin-top: 10px;">Še nimate uporabniškega računa? Registrirajte se!</div></a>
</div> </div>
</div> </div>
</div> </div>

@ -5,8 +5,8 @@
<title>Upravljanje institucije - Šolar</title> <title>Upravljanje institucije - Šolar</title>
<style> <style>
.tableFixHead { .tableFixHead {
overflow-y: auto; overflow-y: scroll;
height: 206px; max-height: 306px;
} }
.tableFixHead thead th { .tableFixHead thead th {
position: sticky; position: sticky;
@ -24,12 +24,15 @@
th { th {
background: #eee; background: #eee;
} }
h2 {
color: blue;
}
</style> </style>
</head> </head>
<body> <body>
{% with messages = get_flashed_messages() %} {% with messages = get_flashed_messages() %}
{% if messages %} {% if messages %}
<div> <div style="background: blue;">
{{ messages[0] }} {{ messages[0] }}
</div> </div>
{% endif %} {% endif %}
@ -76,14 +79,15 @@
</table> </table>
</div> </div>
<br> <br>
<h3>Dodeli uporabnika instituciji</h3> <h3>Dodaj uporabnika instituciji</h3>
<form action="/solar/addusertoinstitution" method="post"> <form action="/solar/addusertoinstitution" method="post">
<label for="user_id">ID uporabnika:</label> <label for="user_id">ID uporabnika:</label>
<input type="text" id="user_id" name="user_id"><br> <input type="text" id="user_id" name="user_id"><br>
<label for="role">Vloga v instituciji:</label> <label for="role">Vloga v instituciji:</label>
<select name="role" id="role"> <select name="role" id="role">
<option value="moderator">Moderator</option> <option value="coordinator">Koordinator/-ka</option>
<option value="user">Uporabnik</option> <option value="mentor">Mentor/-ica</option>
<option value="other">Druga vloga</option>
</select> </select>
<input type="submit" value="Dodeli"> <input type="submit" value="Dodeli">
</form> </form>

@ -19,12 +19,13 @@
</head> </head>
<body> <body>
<a href="/solar/logout">Odjavi se</a> <a href="/solar/logout">Odjavi se</a>
{% if is_institution_moderator %} {% if is_institution_coordinator %}
<br><a href="/solar/manage-institution">Upravljaj z institucijo</a> <br><a href="/solar/manage-institution">Upravljaj z institucijo</a>
{% endif %} {% endif %}
{% if is_admin %} {% if is_admin %}
<br><a href="/solar/admin">Administracijski meni</a> <br><a href="/solar/admin">Administracijski meni</a>
{% endif %} {% endif %}
<br><a href="mailto:email@example.com">Pomoč</a>
<div class="bg"></div> <div class="bg"></div>
<div id="main-window"> <div id="main-window">
<div id="rect1"> <div id="rect1">
@ -41,6 +42,13 @@
<button id="button-pogodbe" class="selection-tab-button">POGODBE</button> <button id="button-pogodbe" class="selection-tab-button">POGODBE</button>
</div> </div>
{% if not institution %}
<div class="warning">Niste član nobene institucije!</div>
{% elif not institution_contract %}
<div class="warning">Pogodba s šolo še ni naložena!</div>
{% endif %}
<div id="data-confirm-notification" class="message-notification" style="display: none;">Prosimo, preverite in potrdite vnešene podatke.</div>
<label for="program">PROGRAM</label> <label for="program">PROGRAM</label>
<select id="program" name="program"> <select id="program" name="program">
<option value="OS" selected="selected">Osnovnošolski (OŠ)</option> <option value="OS" selected="selected">Osnovnošolski (OŠ)</option>
@ -109,7 +117,8 @@
<button id="button-submit" type="submit">Oddaj</button> <button id="button-submit" type="submit">Oddaj</button>
</div> </div>
<div class="dropzone-previews"></div> <div class="dropzone-previews">
</div>
</form> </form>
</div> </div>
@ -236,6 +245,7 @@ zagotovili vse potrebne informacije v skladu s predpisi o varstvu osebnih podatk
var btnPogodbe = document.getElementById("button-pogodbe"); var btnPogodbe = document.getElementById("button-pogodbe");
var elemTermsPopup = document.getElementById("popup-terms"); var elemTermsPopup = document.getElementById("popup-terms");
var termsScrollbox = document.getElementById("popup-terms-text"); var termsScrollbox = document.getElementById("popup-terms-text");
var dataConfirmNotification = document.getElementById("data-confirm-notification");
var scrollboxTriggered = false; var scrollboxTriggered = false;
var form = document.forms["my-dropzone"]; var form = document.forms["my-dropzone"];
@ -307,6 +317,9 @@ zagotovili vse potrebne informacije v skladu s predpisi o varstvu osebnih podatk
alert("Polje za predmet ne more biti prazno!"); alert("Polje za predmet ne more biti prazno!");
} else if (vrsta === "delo-v-razredu" && isEmptyOrSpaces(vrstaCustom)) { } else if (vrsta === "delo-v-razredu" && isEmptyOrSpaces(vrstaCustom)) {
alert("Polje za vrsto besedila ne more biti prazno!"); alert("Polje za vrsto besedila ne more biti prazno!");
} else if (dataConfirmNotification.style.display == "none") {
dataConfirmNotification.style.display = "inherit";
btnSubmit.textContent = "Potrdi";
} else { } else {
// Then make terms popup visible // Then make terms popup visible
btnSubmit.disabled = true; btnSubmit.disabled = true;
@ -325,6 +338,8 @@ zagotovili vse potrebne informacije v skladu s predpisi o varstvu osebnih podatk
// Clear fields and hide popup agian // Clear fields and hide popup agian
btnSubmit.disabled = false; btnSubmit.disabled = false;
elemTermsPopup.style.display = "none"; elemTermsPopup.style.display = "none";
dataConfirmNotification.style.display = "none";
btnSubmit.textContent = "Oddaj";
form.reset(); form.reset();
scrollboxTriggered = false; scrollboxTriggered = false;
}); });

@ -7,12 +7,13 @@
</head> </head>
<body> <body>
<a href="/solar/logout">Odjavi se</a> <a href="/solar/logout">Odjavi se</a>
{% if is_institution_moderator %} {% if is_institution_coordinator %}
<br><a href="/solar/manage-institution">Upravljaj z institucijo</a> <br><a href="/solar/manage-institution">Upravljaj z institucijo</a>
{% endif %} {% endif %}
{% if is_admin %} {% if is_admin %}
<br><a href="/solar/admin">Administracijski meni</a> <br><a href="/solar/admin">Administracijski meni</a>
{% endif %} {% endif %}
<br><a href="mailto:email@example.com">Pomoč</a>
<div class="bg"></div> <div class="bg"></div>
<div id="main-window"> <div id="main-window">
<div id="rect1"> <div id="rect1">
@ -29,20 +30,22 @@
</div> </div>
<div id="contract-container"> <div id="contract-container">
{% if contract_school %} {% if contract_school %}
<div class="contract-item" style="background-color: #ccffcc;"> <div class="contract-item">
<div class="contract-item-icon"></div> <img src="/static/image/contract.png" alt="contract" class="contract-item-icon"/>
<div class="contract-item-title">Pogodba s šolo</div> <div class="contract-item-title">Pogodba s šolo</div>
<div class="contract-item-date">DODANO: {{contract_school.timestamp}}</div> <div class="contract-item-data">{{contract_school.original_filename}}</div>
<a href="/solar/pogodbe/{{ contract_school.file_contract }}.pdf" class="contract-item-button">Prenesi</a> <div class="contract-item-data">DODANO: {{contract_school.timestamp}}</div>
<a href="/solar/pogodbe-institucije/{{ contract_school.file_contract }}.pdf" class="contract-item-button">Prenesi</a>
</div> </div>
</br> </br>
{% endif %} {% endif %}
{% for item in contracts_students %} {% for item in contracts_students %}
<div class="contract-item"> <div class="contract-item">
<div class="contract-item-icon"></div> <img src="/static/image/contract.png" alt="contract" class="contract-item-icon"/>
<div class="contract-item-title">Pogodba o prenosu lastništva</div> <div class="contract-item-title" style="font-size: 12px;">{{item.original_filename}}</div>
<div class="contract-item-date">DODANO: {{item.timestamp}}</div> <div class="contract-item-data">Pogodba o prenosu lastništva</div>
<a href="/solar/pogodbe/{{ item.file_contract }}.pdf" class="contract-item-button">Prenesi</a> <div class="contract-item-data">DODANO: {{item.timestamp}}</div>
<a href="/solar/pogodbe-ucencistarsi/{{ item.file_contract }}.pdf" class="contract-item-button">Prenesi</a>
</div> </div>
</br> </br>
{% endfor %} {% endfor %}
@ -78,15 +81,29 @@
<div id="collaborators-container"> <div id="collaborators-container">
<div class="container-title">Sodelujoči</div> <div class="container-title">Sodelujoči</div>
<div style="overflow-y: auto;"> <div style="overflow-y: auto;">
{% for item in collaborators %} {% for collaborator in collaborators %}
<div class="collaborators-item"> <div class="collaborators-item">
<div class="collaborators-item-name">{{item.name}}</div> <div class="collaborators-item-name">{{collaborator.name}}</div>
{% for history_item in cooperation_history %}
{% if collaborator.id == history_item.user %}
<div>{{history_item.badge_text}}</div>
{% endif %}
{% endfor %}
</div> </div>
{% endfor %} {% endfor %}
</div> </div>
</div> </div>
<div id="awards-container"> <div id="awards-container">
<div class="container-title">Nagrade</div> <div class="container-title">Sodelovanje v letih</div>
<div style="overflow-y: auto; margin: auto; width: 100%; height: 200px;">
{% for item in cooperation_history %}
<div style="border-bottom: 2px solid #c4c4c4; min-height: 50px;">
<img src="/static/image/star.png" alt="star" style="float: left; width: 40px;"/>
<div class="collaborators-item-name"
style="float: right; width: 250px; margin-bottom: 20px; margin-top: 10px; text-align: left; margin-left: 20px;">{{item.badge_text}}</div>
</div>
{% endfor %}
</div>
</div> </div>
</div> </div>
</div> </div>

@ -12,7 +12,12 @@
<div id="logo-container"> <div id="logo-container">
<img src="/static/image/logo.svg" alt="logo"/> <img src="/static/image/logo.svg" alt="logo"/>
</div> </div>
<h3 id="title" style="font-size: 27px; text-align: left;">Registracija - ŠOLAR</h3> <h1 id="title">Portal za oddajanje besedil</h1>
<h2 id="subtitle">Korpus Šolar</h2>
<div class="form-text"><em>Zbiranje besedil za korpus Šolar poteka po naslednjem postopku, ki prinaša tudi točke za napredovanje.</em></div>
<h3 id="title" style="font-size: 27px; text-align: left;">Registracija</h3>
<div> <div>
{% with messages = get_flashed_messages() %} {% with messages = get_flashed_messages() %}
{% if messages %} {% if messages %}
@ -39,6 +44,19 @@
<input type="password" name="password" placeholder="Geslo"> <input type="password" name="password" placeholder="Geslo">
</div> </div>
</div> </div>
<div>
<div>
<input type="institution" name="institution" placeholder="Naziv institucije" autofocus="">
</div>
</div>
<label for="role">Vloga v instituciji</label>
<select id="role" name="role" >
<option value="coordinator">Koordinator/-ka</option>
<option value="mentor">Mentor/-ica</option>
<option value="other">Druga vloga</option>
</select>
<button class="button-general" style="margin-top: 20px;">REGISTRACIJA</button> <button class="button-general" style="margin-top: 20px;">REGISTRACIJA</button>
</form> </form>
</div> </div>

@ -12,7 +12,7 @@
<div id="logo-container"> <div id="logo-container">
<img src="/static/image/logo.svg" alt="logo"/> <img src="/static/image/logo.svg" alt="logo"/>
</div> </div>
<h3 id="title" style="font-size: 27px; text-align: left;">Prijava - ŠOLAR</h3> <h3 id="title" style="font-size: 27px; text-align: left;">Ponastavitev gesla - ŠOLAR</h3>
<div> <div>
{% with messages = get_flashed_messages() %} {% with messages = get_flashed_messages() %}
{% if messages %} {% if messages %}
@ -21,25 +21,14 @@
</div> </div>
{% endif %} {% endif %}
{% endwith %} {% endwith %}
<form method="POST" action="/solar/login"> <form method="POST" action="">
<div> <div>
<div> <div>
<input type="email" name="email" placeholder="Email" autofocus=""> <input type="password" name="new_password" placeholder="Novo geslo">
</div> </div>
</div> </div>
<button class="button-general" style="margin-top: 20px;">PONASTAVI</button>
<div>
<div>
<input type="password" name="password" placeholder="Geslo">
</div>
</div>
<div style="display:flex; flex-direction: row; justify-content: left; align-items: center">
<label>Zapomni prijavo</label>
<input style="width: 10%;" type="checkbox">
</div>
<button class="button-general" style="margin-top: 20px;">PRIJAVA</button>
</form> </form>
<a href="/solar/register" class="contract-item-button">Registracija</a>
</div> </div>
</div> </div>
</div> </div>

@ -8,12 +8,13 @@
</head> </head>
<body> <body>
<a href="/solar/logout">Odjavi se</a> <a href="/solar/logout">Odjavi se</a>
{% if is_institution_moderator %} {% if is_institution_coordinator %}
<br><a href="/solar/manage-institution">Upravljaj z institucijo</a> <br><a href="/solar/manage-institution">Upravljaj z institucijo</a>
{% endif %} {% endif %}
{% if is_admin %} {% if is_admin %}
<br><a href="/solar/admin">Administracijski meni</a> <br><a href="/solar/admin">Administracijski meni</a>
{% endif %} {% endif %}
<br><a href="mailto:email@example.com">Pomoč</a>
<div class="bg"></div> <div class="bg"></div>
<div id="main-window"> <div id="main-window">
<div id="rect1"> <div id="rect1">
@ -35,43 +36,45 @@
<div class="history-item-uploader">{{ uploader_names[loop.index - 1] }}</div> <div class="history-item-uploader">{{ uploader_names[loop.index - 1] }}</div>
<div class="history-item-filecount">Št. datotek: {{ item.upload_file_hashes|length }}</div> <div class="history-item-filecount">Št. datotek: {{ item.upload_file_hashes|length }}</div>
<div class="history-item-desc"> <div class="history-item-desc">
[
{% set began = False %} {% set began = False %}
{% if institution_names[loop.index - 1] %} {% if institution_names[loop.index - 1] %}
{% if began %}|{% endif %} {{ institution_names[loop.index - 1] }} {% if began %}, {% endif %} {{ institution_names[loop.index - 1] }}
{% set began = True %} {% set began = True %}
{% endif %} {% endif %}
{% if item.program %} {% if item.program %}
{% if began %}|{% endif %} {{ item.program }} {% if began %}, {% endif %} {{ item.program }}
{% set began = True %} {% set began = True %}
{% endif %} {% endif %}
{% if item.subject %} {% if item.subject %}
{% if began %}|{% endif %} {{ item.subject }} {% if began %}, {% endif %} {{ item.subject }}
{% set began = True %} {% set began = True %}
{% endif %} {% endif %}
{% if item.subject_custom %} {% if item.subject_custom %}
{% if began %}|{% endif %} {{ item.subject_custom }} {% if began %}, {% endif %} {{ item.subject_custom }}
{% set began = True %} {% set began = True %}
{% endif %} {% endif %}
{% if item.grade %} {% if item.grade %}
{% if began %}|{% endif %} {{ item.grade }} {% if began %}, {% endif %} {{ item.grade }}
{% set began = True %} {% set began = True %}
{% endif %} {% endif %}
{% if item.text_type %} {% if item.text_type %}
{% if began %}|{% endif %} {{ item.text_type }} {% if began %}, {% endif %} {{ item.text_type }}
{% set began = True %} {% set began = True %}
{% endif %} {% endif %}
{% if item.text_type_custom %} {% if item.text_type_custom %}
{% if began %}|{% endif %} {{ item.text_type_custom }} {% if began %}, {% endif %} {{ item.text_type_custom }}
{% set began = True %} {% set began = True %}
{% endif %} {% endif %}
{% if item.school_year %} {% if item.school_year %}
{% if began %}|{% endif %} {{ item.school_year }} {% if began %}, {% endif %} {{ item.school_year }}
{% set began = True %} {% set began = True %}
{% endif %} {% endif %}
{% if item.grammar_corrections %} {% if item.grammar_corrections %}
{% if began %}|{% endif %} {{ item.grammar_corrections }} {% if began %}, {% endif %} {{ item.grammar_corrections }}
{% set began = True %} {% set began = True %}
{% endif %} {% endif %}
]
</div> </div>
</div> </div>
</br> </br>

Loading…
Cancel
Save