diff --git a/mo/jobs/submit.py b/mo/jobs/submit.py
index 7675c534cc6e3d5640bb4112fbb7f7cabb521464..e53beac142df65ebbc97dcea4e6f8405b09e5bd1 100644
--- a/mo/jobs/submit.py
+++ b/mo/jobs/submit.py
@@ -121,7 +121,16 @@ def parse_feedback_name(name: str) -> Optional[UploadFeedback]:
         )
 
     # Formát jmen, pod kterými se ukládají jednotlivě stahovaná řešení
-    m = re.match(r'(?P<task>.+)-reseni-(?P<paper_id>\d+)\.', name)
+    m = re.match(r'(?P<task>[^_]+)_(reseni|opravene)_(?P<user_id>\d+)_', name)
+    if m:
+        return UploadFeedback(
+            file_name=name,
+            task_code=m['task'],
+            user_id=int(m['user_id']),
+        )
+
+    # Formát jmen, pod kterými se dříve ukládala jednotlivě stahovaná řešení
+    m = re.match(r'(?P<task>.+)-(reseni|opravene)-(?P<paper_id>\d+)\.', name)
     if m:
         paper = db.get_session().query(db.Paper).get(int(m['paper_id']))
         if paper:
diff --git a/mo/web/org_contest.py b/mo/web/org_contest.py
index c135b139eba4cef91162e77b1f2b40892d7309da..ca0e026f455a57a119f5ec6d16e5b893e99d76e3 100644
--- a/mo/web/org_contest.py
+++ b/mo/web/org_contest.py
@@ -609,6 +609,7 @@ class SetFinalForm(FlaskForm):
 @app.route('/org/contest/c/<int:contest_id>/site/<int:site_id>/submit/<int:user_id>/<int:task_id>/', methods=('GET', 'POST'))
 def org_submit_list(contest_id: int, user_id: int, task_id: int, site_id: Optional[int] = None):
     sc = get_solution_context(contest_id, user_id, task_id, site_id)
+    assert sc.user is not None
     sess = db.get_session()
 
     self_url = url_for('org_submit_list', contest_id=contest_id, user_id=user_id, task_id=task_id, site_id=site_id)
@@ -733,13 +734,6 @@ def org_submit_list(contest_id: int, user_id: int, task_id: int, site_id: Option
                       .order_by(db.PointsHistory.points_at.desc())
                       .all())
 
-    def paper_link(paper: db.Paper) -> str:
-        return url_for('org_submit_paper',
-                       contest_id=sc.contest.contest_id,
-                       paper_id=paper.paper_id,
-                       site_id=site_id,
-                       filename=mo.web.util.task_paper_filename(paper))
-
     return render_template(
         'org_submit_list.html',
         sc=sc,
@@ -748,7 +742,7 @@ def org_submit_list(contest_id: int, user_id: int, task_id: int, site_id: Option
         fb_papers=fb_papers,
         points_history=points_history,
         for_site=(site_id is not None),
-        paper_link=paper_link,
+        paper_link=lambda p: mo.web.util.org_paper_link(sc.contest, sc.site, sc.user, p),
         form=form,
         set_final_form=set_final_form,
     )
@@ -763,7 +757,7 @@ def org_submit_paper(contest_id: int, paper_id: int, filename: str, site_id: Opt
     if not paper:
         raise werkzeug.exceptions.NotFound()
 
-    if filename != mo.web.util.task_paper_filename(paper):
+    if not filename.endswith('.pdf'):
         raise werkzeug.exceptions.NotFound()
 
     get_solution_context(contest_id, paper.for_user, paper.for_task, site_id)
@@ -860,17 +854,10 @@ def org_contest_task(contest_id: int, task_id: int, site_id: Optional[int] = Non
     ):
         paper_counts[(user_id, type.name)] = count
 
-    def paper_link(paper: db.Paper) -> str:
-        return url_for('org_submit_paper',
-                       contest_id=sc.contest.contest_id,
-                       paper_id=paper.paper_id,
-                       site_id=site_id,
-                       filename=mo.web.util.task_paper_filename(paper))
-
     return render_template(
         "org_contest_task.html",
         sc=sc, rows=rows, paper_counts=paper_counts,
-        paper_link=paper_link,
+        paper_link=lambda u, p: mo.web.util.org_paper_link(sc.contest, sc.site, u, p),
         can_upload=sc.allow_upload_feedback,
         points_form=points_form, request_form=request.form,
     )
@@ -924,20 +911,13 @@ def org_contest_solutions(id: int, site_id: Optional[int] = None):
     for s in sols:
         task_sols[s.task_id][s.user_id] = s
 
-    def paper_link(paper: db.Paper) -> str:
-        return url_for('org_submit_paper',
-                       contest_id=sc.contest.contest_id,
-                       paper_id=paper.paper_id,
-                       site_id=site_id,
-                       filename=mo.web.util.task_paper_filename(paper))
-
     return render_template(
         'org_contest_solutions.html',
         contest=sc.contest, site=sc.site, sc=sc,
         pions=pions, tasks=tasks, tasks_sols=task_sols, paper_counts=paper_counts,
         can_upload=sc.allow_upload_feedback,
         can_edit_points=sc.allow_edit_points,
-        paper_link=paper_link,
+        paper_link=lambda u, p: mo.web.util.org_paper_link(sc.contest, sc.site, u, p),
     )
 
 
@@ -1120,14 +1100,9 @@ def org_contest_user(contest_id: int, user_id: int):
     ):
         paper_counts[(task_id, type.name)] = count
 
-    def paper_link(paper: db.Paper) -> str:
-        return url_for('org_submit_paper',
-                       contest_id=sc.contest.contest_id,
-                       paper_id=paper.paper_id,
-                       filename=mo.web.util.task_paper_filename(paper))
-
     return render_template(
         'org_contest_user.html',
         sc=sc, pant=pant, task_sols=task_sols,
-        paper_link=paper_link, paper_counts=paper_counts,
+        paper_link=lambda u, p: mo.web.util.org_paper_link(sc.contest, None, u, p),
+        paper_counts=paper_counts,
     )
diff --git a/mo/web/org_score.py b/mo/web/org_score.py
index 5ca3fd02332c3c815882c53b16b3152754146488..a9de01eecda04b76f4ee91b6d2989a54077eeaf7 100644
--- a/mo/web/org_score.py
+++ b/mo/web/org_score.py
@@ -39,10 +39,12 @@ class OrderCell(Cell):
 
 class SolPointsCell(Cell):
     contest_id: int
+    user: db.User
     sol: Optional[db.Solution]
 
-    def __init__(self, contest_id: int):
+    def __init__(self, contest_id: int, user: db.User):
         self.contest_id = contest_id
+        self.user = user
         self.sol = None
 
     def __str__(self) -> str:
@@ -64,20 +66,10 @@ class SolPointsCell(Cell):
             points = str(self.sol.points)
 
         if self.sol.final_feedback_obj:
-            url = url_for(
-                'org_submit_paper',
-                contest_id=self.contest_id,
-                paper_id=self.sol.final_feedback,
-                filename=mo.web.util.task_paper_filename(self.sol.final_feedback_obj)
-            )
+            url = mo.web.util.org_paper_link(self.contest_id, None, self.user, self.sol.final_feedback_obj)
             return f'<td><a href="{url}" title="Zobrazit finální opravu">{points}</a>'
         elif self.sol.final_submit_obj:
-            url = url_for(
-                'org_submit_paper',
-                contest_id=self.contest_id,
-                paper_id=self.sol.final_submit,
-                filename=mo.web.util.task_paper_filename(self.sol.final_submit_obj)
-            )
+            url = mo.web.util.org_paper_link(self.contest_id, None, self.user, self.sol.final_submit_obj)
             return f'<td><a href="{url}" title="Zobrazit finální řešení od účastníka">{points}</a>'
         else:
             return f'<td>{points}'  # no paper no link
@@ -190,7 +182,7 @@ def org_score(round_id: Optional[int] = None, contest_id: Optional[int] = None):
             'birth_year':   pant.birth_year,
         })
         for task in tasks:
-            row.keys[f'task_{task.task_id}'] = SolPointsCell(contest_id=pion.contest_id)
+            row.keys[f'task_{task.task_id}'] = SolPointsCell(contest_id=pion.contest_id, user=user)
         rows_map[user.user_id] = row
     for sol in sols:
         rows_map[sol.user_id].keys[f'task_{sol.task_id}'].set_sol(sol)
diff --git a/mo/web/templates/org_contest_solutions.html b/mo/web/templates/org_contest_solutions.html
index 5f2cda25c33ef0f5efe3a65d31229ac3fcd389ec..5606f4e6c1966a19b0b43315b419cd457716760e 100644
--- a/mo/web/templates/org_contest_solutions.html
+++ b/mo/web/templates/org_contest_solutions.html
@@ -52,7 +52,7 @@ konkrétní úlohu. Symbol <b>+</b> značí, že existuje více verzí dostupný
 					{% set p = sol.final_submit_obj %}
 					{% set late = p.check_deadline(round) %}
 					<td class="sol {% if late %}sol-late{% endif %}">
-						<a href="{{ paper_link(p) }}" title="{{ p.uploaded_at|timeformat }} - {{ p.pages|inflected('stránka', 'stránky', 'stránek') }}{% if late %} - {{ late }}{% endif %}">🖺</a>
+						<a href="{{ paper_link(u, p) }}" title="{{ p.uploaded_at|timeformat }} - {{ p.pages|inflected('stránka', 'stránky', 'stránek') }}{% if late %} - {{ late }}{% endif %}">🖺</a>
 						{% set key = (u.user_id, task.task_id, "solution") %}
 						{% if key in paper_counts and paper_counts[key] > 1 %}
 							<b title="Celkem {{ paper_counts[key]|inflected('verze', 'verze', 'verzí') }}">+</b>
@@ -63,7 +63,7 @@ konkrétní úlohu. Symbol <b>+</b> značí, že existuje více verzí dostupný
 					<td class="sol">
 						{% if sol.final_feedback_obj %}
 						{% set p = sol.final_feedback_obj %}
-						<a href="{{ paper_link(p) }}" title="{{ p.uploaded_at|timeformat }} - {{ p.pages|inflected('stránka', 'stránky', 'stránek') }}">🖺</a>
+						<a href="{{ paper_link(u, p) }}" title="{{ p.uploaded_at|timeformat }} - {{ p.pages|inflected('stránka', 'stránky', 'stránek') }}">🖺</a>
 						{% set key = (u.user_id, task.task_id, "feedback") %}
 						{% if key in paper_counts and paper_counts[key] > 1 %}
 							<b title="Celkem {{ paper_counts[key]|inflected('verze', 'verze', 'verzí') }}">+</b>
diff --git a/mo/web/templates/parts/org_solution_table.html b/mo/web/templates/parts/org_solution_table.html
index 63387b8480e58c7a939d0eee2ff54663a87948ed..93d622e8d1e99aa631956006292bc7480753c0c0 100644
--- a/mo/web/templates/parts/org_solution_table.html
+++ b/mo/web/templates/parts/org_solution_table.html
@@ -35,7 +35,7 @@ finální (ve výchozím stavu poslední nahrané).{% endif %}
 			{% set p = sol.final_submit_obj %}
 			{% set late = p.check_deadline(round) %}
 			{% if late %}<span class='sol-late' title="{{ late }}"><b>⚠</b></span> {% endif %}
-			<a href='{{ paper_link(p) }}'>
+			<a href='{{ paper_link(u, p) }}'>
 				{{- p.uploaded_at|timeformat }} ({{ p.pages|inflected('strana', 'strany', 'stran') }})
 			</a>
 			{% if p.uploaded_by_obj != u %}
@@ -48,7 +48,7 @@ finální (ve výchozím stavu poslední nahrané).{% endif %}
 		    {% else %}–{% endif %}
 		<td>{% if sol.final_feedback_obj %}
 			{% set p = sol.final_feedback_obj %}
-			<a title="nahrál {{ p.uploaded_by_obj.full_name() }}" href='{{ paper_link(p) }}'>
+			<a title="nahrál {{ p.uploaded_by_obj.full_name() }}" href='{{ paper_link(u, p) }}'>
 				{{ (p.uploaded_at if p else None)|timeformat }} ({{ p.pages|inflected('strana', 'strany', 'stran') }})
 			</a>
 			{% set key = (obj.task_id if for_user else obj.user_id, "feedback") %}
diff --git a/mo/web/util.py b/mo/web/util.py
index f021d878e20400ca16c6a68628d396a4c2af3f6b..56dc2e9b494bbeb79f825932bde89c7c98d6f397 100644
--- a/mo/web/util.py
+++ b/mo/web/util.py
@@ -1,8 +1,9 @@
-from flask import Response, send_file
+from flask import Response, send_file, url_for
 from flask_wtf import FlaskForm
 import os
 from sqlalchemy.orm.query import Query
-from typing import Tuple
+from typing import Tuple, Optional, Union
+import unicodedata
 import werkzeug.exceptions
 import werkzeug.utils
 import wtforms
@@ -49,19 +50,39 @@ def send_task_statement(round: db.Round) -> Response:
         raise werkzeug.exceptions.NotFound()
 
 
-def task_paper_filename(paper: db.Paper) -> str:
-    """Doporučujeme preloadovat task."""
+def _task_paper_filename(user: db.User, paper: db.Paper) -> str:
+    # Tato jména parsuje dávkový upload
 
-    task = paper.task
+    full_name = user.full_name()
+    ascii_name = (unicodedata.normalize('NFD', full_name)
+                  .encode('ascii', 'ignore')
+                  .decode('utf-8'))
 
-    secure_task_code = werkzeug.utils.secure_filename(task.code)
     if paper.type == db.PaperType.solution:
-        return f'{secure_task_code}-reseni-{paper.paper_id}.pdf'
+        typ = 'reseni'
     elif paper.type == db.PaperType.feedback:
-        return f'{secure_task_code}-opravene-{paper.paper_id}.pdf'
+        typ = 'opravene'
     else:
         assert False
 
+    fn = f'{paper.task.code}_{typ}_{user.user_id}_{paper.paper_id}_{ascii_name}.pdf'
+    return werkzeug.utils.secure_filename(fn)
+
+
+def org_paper_link(contest_or_id: Union[db.Contest, int],
+                   site: Optional[db.Place],
+                   user: db.User,
+                   paper: db.Paper) -> str:
+    """Doporučujeme preloadovat paper.task."""
+    if isinstance(contest_or_id, db.Contest):
+        contest_or_id = contest_or_id.contest_id
+
+    return url_for('org_submit_paper',
+                   contest_id=contest_or_id,
+                   paper_id=paper.paper_id,
+                   site_id=site.place_id if site else None,
+                   filename=_task_paper_filename(user, paper))
+
 
 def send_task_paper(paper: db.Paper) -> Response:
     file = os.path.join(mo.util.data_dir('submits'), paper.file_name)