Show More
@@ -446,6 +446,16 b' def includeme(config):' | |||||
446 | renderer='json_ext') |
|
446 | renderer='json_ext') | |
447 |
|
447 | |||
448 | config.add_route( |
|
448 | config.add_route( | |
|
449 | name='repo_files_replace_binary', | |||
|
450 | pattern='/{repo_name:.*?[^/]}/replace_binary/{commit_id}/{f_path:.*}', | |||
|
451 | repo_route=True) | |||
|
452 | config.add_view( | |||
|
453 | RepoFilesView, | |||
|
454 | attr='repo_files_replace_file', | |||
|
455 | route_name='repo_files_replace_binary', request_method='POST', | |||
|
456 | renderer='json_ext') | |||
|
457 | ||||
|
458 | config.add_route( | |||
449 | name='repo_files_create_file', |
|
459 | name='repo_files_create_file', | |
450 | pattern='/{repo_name:.*?[^/]}/create_file/{commit_id}/{f_path:.*}', |
|
460 | pattern='/{repo_name:.*?[^/]}/create_file/{commit_id}/{f_path:.*}', | |
451 | repo_route=True) |
|
461 | repo_route=True) |
@@ -924,6 +924,26 b' class TestModifyFilesWithWebInterface(ob' | |||||
924 | tip = repo.get_commit(commit_idx=-1) |
|
924 | tip = repo.get_commit(commit_idx=-1) | |
925 | assert tip.message == 'I committed' |
|
925 | assert tip.message == 'I committed' | |
926 |
|
926 | |||
|
927 | def test_replace_binary_file_view_commit_changes(self, backend, csrf_token): | |||
|
928 | repo = backend.create_repo() | |||
|
929 | backend.ensure_file(b"vcs/nodes.docx", content=b"PREVIOUS CONTENT'") | |||
|
930 | ||||
|
931 | response = self.app.post( | |||
|
932 | route_path('repo_files_replace_binary', | |||
|
933 | repo_name=repo.repo_name, | |||
|
934 | commit_id=backend.default_head_id, | |||
|
935 | f_path='vcs/nodes.docx'), | |||
|
936 | params={ | |||
|
937 | 'message': 'I committed', | |||
|
938 | 'csrf_token': csrf_token, | |||
|
939 | }, | |||
|
940 | upload_files=[('files_upload', 'vcs/nodes.docx', b'SOME CONTENT')], | |||
|
941 | status=200) | |||
|
942 | assert_session_flash( | |||
|
943 | response, 'Successfully committed 1 new file') | |||
|
944 | tip = repo.get_commit(commit_idx=-1) | |||
|
945 | assert tip.message == 'I committed' | |||
|
946 | ||||
927 | def test_edit_file_view_commit_changes_default_message(self, backend, |
|
947 | def test_edit_file_view_commit_changes_default_message(self, backend, | |
928 | csrf_token): |
|
948 | csrf_token): | |
929 | repo = backend.create_repo() |
|
949 | repo = backend.create_repo() | |
@@ -950,6 +970,48 b' class TestModifyFilesWithWebInterface(ob' | |||||
950 | tip = repo.get_commit(commit_idx=-1) |
|
970 | tip = repo.get_commit(commit_idx=-1) | |
951 | assert tip.message == 'Edited file vcs/nodes.py via RhodeCode Enterprise' |
|
971 | assert tip.message == 'Edited file vcs/nodes.py via RhodeCode Enterprise' | |
952 |
|
972 | |||
|
973 | def test_replace_binary_file_content_with_content_that_not_belong_to_original_type(self, backend, csrf_token): | |||
|
974 | repo = backend.create_repo() | |||
|
975 | backend.ensure_file(b"vcs/sheet.xlsx", content=b"PREVIOUS CONTENT'") | |||
|
976 | ||||
|
977 | response = self.app.post( | |||
|
978 | route_path('repo_files_replace_binary', | |||
|
979 | repo_name=repo.repo_name, | |||
|
980 | commit_id=backend.default_head_id, | |||
|
981 | f_path='vcs/sheet.xlsx'), | |||
|
982 | params={ | |||
|
983 | 'message': 'I committed', | |||
|
984 | 'csrf_token': csrf_token, | |||
|
985 | }, | |||
|
986 | upload_files=[('files_upload', 'vcs/sheet.docx', b'SOME CONTENT')], | |||
|
987 | status=200) | |||
|
988 | assert response.json['error'] == "file extension of uploaded file doesn't match an original file's extension" | |||
|
989 | ||||
|
990 | @pytest.mark.parametrize("replacement_files, expected_error", [ | |||
|
991 | ([], 'missing files'), | |||
|
992 | ( | |||
|
993 | [('files_upload', 'vcs/node1.docx', b'SOME CONTENT'), | |||
|
994 | ('files_upload', 'vcs/node2.docx', b'SOME CONTENT')], | |||
|
995 | 'too many files for replacement'), | |||
|
996 | ]) | |||
|
997 | def test_replace_binary_with_wrong_amount_of_content_sources(self, replacement_files, expected_error, backend, | |||
|
998 | csrf_token): | |||
|
999 | repo = backend.create_repo() | |||
|
1000 | backend.ensure_file(b"vcs/node.docx", content=b"PREVIOUS CONTENT'") | |||
|
1001 | ||||
|
1002 | response = self.app.post( | |||
|
1003 | route_path('repo_files_replace_binary', | |||
|
1004 | repo_name=repo.repo_name, | |||
|
1005 | commit_id=backend.default_head_id, | |||
|
1006 | f_path='vcs/node.docx'), | |||
|
1007 | params={ | |||
|
1008 | 'message': 'I committed', | |||
|
1009 | 'csrf_token': csrf_token, | |||
|
1010 | }, | |||
|
1011 | upload_files=replacement_files, | |||
|
1012 | status=200) | |||
|
1013 | assert response.json['error'] == expected_error | |||
|
1014 | ||||
953 | def test_delete_file_view(self, backend): |
|
1015 | def test_delete_file_view(self, backend): | |
954 | self.app.get( |
|
1016 | self.app.get( | |
955 | route_path('repo_files_remove_file', |
|
1017 | route_path('repo_files_remove_file', |
@@ -1341,6 +1341,9 b' class RepoFilesView(RepoAppView):' | |||||
1341 |
|
1341 | |||
1342 | self._ensure_not_locked() |
|
1342 | self._ensure_not_locked() | |
1343 |
|
1343 | |||
|
1344 | # Check if we need to use this page to upload binary | |||
|
1345 | upload_binary = str2bool(self.request.params.get('upload_binary', False)) | |||
|
1346 | ||||
1344 | c.commit = self._get_commit_or_redirect(commit_id, redirect_after=False) |
|
1347 | c.commit = self._get_commit_or_redirect(commit_id, redirect_after=False) | |
1345 | if c.commit is None: |
|
1348 | if c.commit is None: | |
1346 | c.commit = EmptyCommit(alias=self.rhodecode_vcs_repo.alias) |
|
1349 | c.commit = EmptyCommit(alias=self.rhodecode_vcs_repo.alias) | |
@@ -1357,8 +1360,10 b' class RepoFilesView(RepoAppView):' | |||||
1357 | self.forbid_non_head(is_head, f_path, commit_id=commit_id) |
|
1360 | self.forbid_non_head(is_head, f_path, commit_id=commit_id) | |
1358 | self.check_branch_permission(_branch_name, commit_id=commit_id) |
|
1361 | self.check_branch_permission(_branch_name, commit_id=commit_id) | |
1359 |
|
1362 | |||
1360 | c.default_message = (_('Added file via RhodeCode Enterprise')) |
|
1363 | c.default_message = (_('Added file via RhodeCode Enterprise')) \ | |
|
1364 | if not upload_binary else (_('Edited file {} via RhodeCode Enterprise').format(f_path)) | |||
1361 | c.f_path = f_path.lstrip('/') # ensure not relative path |
|
1365 | c.f_path = f_path.lstrip('/') # ensure not relative path | |
|
1366 | c.replace_binary = upload_binary | |||
1362 |
|
1367 | |||
1363 | return self._get_template_context(c) |
|
1368 | return self._get_template_context(c) | |
1364 |
|
1369 | |||
@@ -1584,3 +1589,120 b' class RepoFilesView(RepoAppView):' | |||||
1584 | 'error': None, |
|
1589 | 'error': None, | |
1585 | 'redirect_url': default_redirect_url |
|
1590 | 'redirect_url': default_redirect_url | |
1586 | } |
|
1591 | } | |
|
1592 | ||||
|
1593 | @LoginRequired() | |||
|
1594 | @HasRepoPermissionAnyDecorator('repository.write', 'repository.admin') | |||
|
1595 | @CSRFRequired() | |||
|
1596 | def repo_files_replace_file(self): | |||
|
1597 | _ = self.request.translate | |||
|
1598 | c = self.load_default_context() | |||
|
1599 | commit_id, f_path = self._get_commit_and_path() | |||
|
1600 | ||||
|
1601 | self._ensure_not_locked() | |||
|
1602 | ||||
|
1603 | c.commit = self._get_commit_or_redirect(commit_id, redirect_after=False) | |||
|
1604 | if c.commit is None: | |||
|
1605 | c.commit = EmptyCommit(alias=self.rhodecode_vcs_repo.alias) | |||
|
1606 | ||||
|
1607 | if self.rhodecode_vcs_repo.is_empty(): | |||
|
1608 | default_redirect_url = h.route_path( | |||
|
1609 | 'repo_summary', repo_name=self.db_repo_name) | |||
|
1610 | else: | |||
|
1611 | default_redirect_url = h.route_path( | |||
|
1612 | 'repo_commit', repo_name=self.db_repo_name, commit_id='tip') | |||
|
1613 | ||||
|
1614 | if self.rhodecode_vcs_repo.is_empty(): | |||
|
1615 | # for empty repository we cannot check for current branch, we rely on | |||
|
1616 | # c.commit.branch instead | |||
|
1617 | _branch_name, _sha_commit_id, is_head = c.commit.branch, '', True | |||
|
1618 | else: | |||
|
1619 | _branch_name, _sha_commit_id, is_head = \ | |||
|
1620 | self._is_valid_head(commit_id, self.rhodecode_vcs_repo, | |||
|
1621 | landing_ref=self.db_repo.landing_ref_name) | |||
|
1622 | ||||
|
1623 | error = self.forbid_non_head(is_head, f_path, json_mode=True) | |||
|
1624 | if error: | |||
|
1625 | return { | |||
|
1626 | 'error': error, | |||
|
1627 | 'redirect_url': default_redirect_url | |||
|
1628 | } | |||
|
1629 | error = self.check_branch_permission(_branch_name, json_mode=True) | |||
|
1630 | if error: | |||
|
1631 | return { | |||
|
1632 | 'error': error, | |||
|
1633 | 'redirect_url': default_redirect_url | |||
|
1634 | } | |||
|
1635 | ||||
|
1636 | c.default_message = (_('Edited file {} via RhodeCode Enterprise').format(f_path)) | |||
|
1637 | c.f_path = f_path | |||
|
1638 | ||||
|
1639 | r_post = self.request.POST | |||
|
1640 | ||||
|
1641 | message = c.default_message | |||
|
1642 | user_message = r_post.getall('message') | |||
|
1643 | if isinstance(user_message, list) and user_message: | |||
|
1644 | # we take the first from duplicated results if it's not empty | |||
|
1645 | message = user_message[0] if user_message[0] else message | |||
|
1646 | ||||
|
1647 | data_for_replacement = r_post.getall('files_upload') or [] | |||
|
1648 | if (objects_count := len(data_for_replacement)) > 1: | |||
|
1649 | return { | |||
|
1650 | 'error': 'too many files for replacement', | |||
|
1651 | 'redirect_url': default_redirect_url | |||
|
1652 | } | |||
|
1653 | elif not objects_count: | |||
|
1654 | return { | |||
|
1655 | 'error': 'missing files', | |||
|
1656 | 'redirect_url': default_redirect_url | |||
|
1657 | } | |||
|
1658 | ||||
|
1659 | content = data_for_replacement[0].file | |||
|
1660 | retrieved_filename = data_for_replacement[0].filename | |||
|
1661 | ||||
|
1662 | if retrieved_filename.split('.')[-1] != f_path.split('.')[-1]: | |||
|
1663 | return { | |||
|
1664 | 'error': 'file extension of uploaded file doesn\'t match an original file\'s extension', | |||
|
1665 | 'redirect_url': default_redirect_url | |||
|
1666 | } | |||
|
1667 | ||||
|
1668 | author = self._rhodecode_db_user.full_contact | |||
|
1669 | ||||
|
1670 | try: | |||
|
1671 | commit = ScmModel().update_binary_node( | |||
|
1672 | user=self._rhodecode_db_user.user_id, | |||
|
1673 | repo=self.db_repo, | |||
|
1674 | message=message, | |||
|
1675 | node={ | |||
|
1676 | 'content': content, | |||
|
1677 | 'file_path': f_path.encode(), | |||
|
1678 | }, | |||
|
1679 | parent_commit=c.commit, | |||
|
1680 | author=author, | |||
|
1681 | ) | |||
|
1682 | ||||
|
1683 | h.flash(_('Successfully committed 1 new file'), category='success') | |||
|
1684 | ||||
|
1685 | default_redirect_url = h.route_path( | |||
|
1686 | 'repo_commit', repo_name=self.db_repo_name, commit_id=commit.raw_id) | |||
|
1687 | ||||
|
1688 | except (NodeError, NodeAlreadyExistsError) as e: | |||
|
1689 | error = h.escape(e) | |||
|
1690 | h.flash(error, category='error') | |||
|
1691 | ||||
|
1692 | return { | |||
|
1693 | 'error': error, | |||
|
1694 | 'redirect_url': default_redirect_url | |||
|
1695 | } | |||
|
1696 | except Exception: | |||
|
1697 | log.exception('Error occurred during commit') | |||
|
1698 | error = _('Error occurred during commit') | |||
|
1699 | h.flash(error, category='error') | |||
|
1700 | return { | |||
|
1701 | 'error': error, | |||
|
1702 | 'redirect_url': default_redirect_url | |||
|
1703 | } | |||
|
1704 | ||||
|
1705 | return { | |||
|
1706 | 'error': None, | |||
|
1707 | 'redirect_url': default_redirect_url | |||
|
1708 | } |
@@ -255,6 +255,31 b' class ScmModel(BaseModel):' | |||||
255 | all_repos, repos_path=self.repos_path, order_by=sort_key) |
|
255 | all_repos, repos_path=self.repos_path, order_by=sort_key) | |
256 | return repo_iter |
|
256 | return repo_iter | |
257 |
|
257 | |||
|
258 | @staticmethod | |||
|
259 | def get_parent_commits(parent_commit, scm_instance): | |||
|
260 | if not parent_commit: | |||
|
261 | parent_commit = EmptyCommit(alias=scm_instance.alias) | |||
|
262 | ||||
|
263 | if isinstance(parent_commit, EmptyCommit): | |||
|
264 | # EmptyCommit means we're editing empty repository | |||
|
265 | parents = None | |||
|
266 | else: | |||
|
267 | parents = [parent_commit] | |||
|
268 | return parent_commit, parents | |||
|
269 | ||||
|
270 | def initialize_inmemory_vars(self, user, repo, message, author): | |||
|
271 | """ | |||
|
272 | Initialize node specific objects for further usage | |||
|
273 | """ | |||
|
274 | user = self._get_user(user) | |||
|
275 | scm_instance = repo.scm_instance(cache=False) | |||
|
276 | message = safe_str(message) | |||
|
277 | commiter = user.full_contact | |||
|
278 | author = safe_str(author) if author else commiter | |||
|
279 | imc = scm_instance.in_memory_commit | |||
|
280 | ||||
|
281 | return user, scm_instance, message, commiter, author, imc | |||
|
282 | ||||
258 | def get_repo_groups(self, all_groups=None): |
|
283 | def get_repo_groups(self, all_groups=None): | |
259 | if all_groups is None: |
|
284 | if all_groups is None: | |
260 | all_groups = RepoGroup.query()\ |
|
285 | all_groups = RepoGroup.query()\ | |
@@ -763,24 +788,10 b' class ScmModel(BaseModel):' | |||||
763 |
|
788 | |||
764 | :returns: new committed commit |
|
789 | :returns: new committed commit | |
765 | """ |
|
790 | """ | |
766 |
|
791 | user, scm_instance, message, commiter, author, imc = self.initialize_inmemory_vars( | ||
767 | user = self._get_user(user) |
|
792 | user, repo, message, author) | |
768 | scm_instance = repo.scm_instance(cache=False) |
|
|||
769 |
|
||||
770 | message = safe_str(message) |
|
|||
771 | commiter = user.full_contact |
|
|||
772 | author = safe_str(author) if author else commiter |
|
|||
773 |
|
793 | |||
774 | imc = scm_instance.in_memory_commit |
|
794 | parent_commit, parents = self.get_parent_commits(parent_commit, scm_instance) | |
775 |
|
||||
776 | if not parent_commit: |
|
|||
777 | parent_commit = EmptyCommit(alias=scm_instance.alias) |
|
|||
778 |
|
||||
779 | if isinstance(parent_commit, EmptyCommit): |
|
|||
780 | # EmptyCommit means we're editing empty repository |
|
|||
781 | parents = None |
|
|||
782 | else: |
|
|||
783 | parents = [parent_commit] |
|
|||
784 |
|
795 | |||
785 | upload_file_types = (io.BytesIO, io.BufferedRandom) |
|
796 | upload_file_types = (io.BytesIO, io.BufferedRandom) | |
786 | processed_nodes = [] |
|
797 | processed_nodes = [] | |
@@ -827,23 +838,10 b' class ScmModel(BaseModel):' | |||||
827 |
|
838 | |||
828 | def update_nodes(self, user, repo, message, nodes, parent_commit=None, |
|
839 | def update_nodes(self, user, repo, message, nodes, parent_commit=None, | |
829 | author=None, trigger_push_hook=True): |
|
840 | author=None, trigger_push_hook=True): | |
830 | user = self._get_user(user) |
|
841 | user, scm_instance, message, commiter, author, imc = self.initialize_inmemory_vars( | |
831 | scm_instance = repo.scm_instance(cache=False) |
|
842 | user, repo, message, author) | |
832 |
|
||||
833 | message = safe_str(message) |
|
|||
834 | commiter = user.full_contact |
|
|||
835 | author = safe_str(author) if author else commiter |
|
|||
836 |
|
||||
837 | imc = scm_instance.in_memory_commit |
|
|||
838 |
|
843 | |||
839 | if not parent_commit: |
|
844 | parent_commit, parents = self.get_parent_commits(parent_commit, scm_instance) | |
840 | parent_commit = EmptyCommit(alias=scm_instance.alias) |
|
|||
841 |
|
||||
842 | if isinstance(parent_commit, EmptyCommit): |
|
|||
843 | # EmptyCommit means we we're editing empty repository |
|
|||
844 | parents = None |
|
|||
845 | else: |
|
|||
846 | parents = [parent_commit] |
|
|||
847 |
|
845 | |||
848 | # add multiple nodes |
|
846 | # add multiple nodes | |
849 | for _filename, data in nodes.items(): |
|
847 | for _filename, data in nodes.items(): | |
@@ -890,6 +888,39 b' class ScmModel(BaseModel):' | |||||
890 |
|
888 | |||
891 | return tip |
|
889 | return tip | |
892 |
|
890 | |||
|
891 | def update_binary_node(self, user, repo, message, node, parent_commit=None, author=None): | |||
|
892 | user, scm_instance, message, commiter, author, imc = self.initialize_inmemory_vars( | |||
|
893 | user, repo, message, author) | |||
|
894 | ||||
|
895 | parent_commit, parents = self.get_parent_commits(parent_commit, scm_instance) | |||
|
896 | ||||
|
897 | file_path = node.get('file_path') | |||
|
898 | if isinstance(raw_content := node.get('content'), (io.BytesIO, io.BufferedRandom)): | |||
|
899 | content = raw_content.read() | |||
|
900 | else: | |||
|
901 | raise Exception("Wrong content was provided") | |||
|
902 | file_node = FileNode(file_path, content=content) | |||
|
903 | imc.change(file_node) | |||
|
904 | ||||
|
905 | try: | |||
|
906 | tip = imc.commit(message=message, | |||
|
907 | author=author, | |||
|
908 | parents=parents, | |||
|
909 | branch=parent_commit.branch) | |||
|
910 | except NodeNotChangedError: | |||
|
911 | raise | |||
|
912 | except Exception as e: | |||
|
913 | log.exception("Unexpected exception during call to imc.commit") | |||
|
914 | raise IMCCommitError(str(e)) | |||
|
915 | finally: | |||
|
916 | self.mark_for_invalidation(repo.repo_name) | |||
|
917 | ||||
|
918 | hooks_utils.trigger_post_push_hook( | |||
|
919 | username=user.username, action='push_local', hook_type='post_push', | |||
|
920 | repo_name=repo.repo_name, repo_type=scm_instance.alias, | |||
|
921 | commit_ids=[tip.raw_id]) | |||
|
922 | return tip | |||
|
923 | ||||
893 | def delete_nodes(self, user, repo, message, nodes, parent_commit=None, |
|
924 | def delete_nodes(self, user, repo, message, nodes, parent_commit=None, | |
894 | author=None, trigger_push_hook=True): |
|
925 | author=None, trigger_push_hook=True): | |
895 | """ |
|
926 | """ | |
@@ -908,8 +939,8 b' class ScmModel(BaseModel):' | |||||
908 | :returns: new commit after deletion |
|
939 | :returns: new commit after deletion | |
909 | """ |
|
940 | """ | |
910 |
|
941 | |||
911 | user = self._get_user(user) |
|
942 | user, scm_instance, message, commiter, author, imc = self.initialize_inmemory_vars( | |
912 | scm_instance = repo.scm_instance(cache=False) |
|
943 | user, repo, message, author) | |
913 |
|
944 | |||
914 | processed_nodes = [] |
|
945 | processed_nodes = [] | |
915 | for f_path in nodes: |
|
946 | for f_path in nodes: | |
@@ -919,20 +950,8 b' class ScmModel(BaseModel):' | |||||
919 | content = nodes[f_path].get('content') |
|
950 | content = nodes[f_path].get('content') | |
920 | processed_nodes.append((safe_bytes(f_path), content)) |
|
951 | processed_nodes.append((safe_bytes(f_path), content)) | |
921 |
|
952 | |||
922 | message = safe_str(message) |
|
953 | parent_commit, parents = self.get_parent_commits(parent_commit, scm_instance) | |
923 | commiter = user.full_contact |
|
|||
924 | author = safe_str(author) if author else commiter |
|
|||
925 |
|
||||
926 | imc = scm_instance.in_memory_commit |
|
|||
927 |
|
954 | |||
928 | if not parent_commit: |
|
|||
929 | parent_commit = EmptyCommit(alias=scm_instance.alias) |
|
|||
930 |
|
||||
931 | if isinstance(parent_commit, EmptyCommit): |
|
|||
932 | # EmptyCommit means we we're editing empty repository |
|
|||
933 | parents = None |
|
|||
934 | else: |
|
|||
935 | parents = [parent_commit] |
|
|||
936 | # add multiple nodes |
|
955 | # add multiple nodes | |
937 | for path, content in processed_nodes: |
|
956 | for path, content in processed_nodes: | |
938 | imc.remove(FileNode(path, content=content)) |
|
957 | imc.remove(FileNode(path, content=content)) |
@@ -36,7 +36,7 b'' | |||||
36 | %if c.on_branch_head and c.branch_or_raw_id: |
|
36 | %if c.on_branch_head and c.branch_or_raw_id: | |
37 | ## binary files are delete only |
|
37 | ## binary files are delete only | |
38 | % if c.file.is_binary: |
|
38 | % if c.file.is_binary: | |
39 | ${h.link_to(_('Edit'), '#Edit', class_="btn btn-default disabled tooltip", title=_('Editing binary files not allowed'))} |
|
39 | ${h.link_to(_('Replace'), h.route_path('repo_files_upload_file', repo_name=c.repo_name,commit_id=c.commit.raw_id,f_path=c.f_path, _query={'upload_binary': 'true'}), class_="btn btn-default active tooltip", title=_('You can replace content of your binary file'))} | |
40 | ${h.link_to(_('Delete'), h.route_path('repo_files_remove_file',repo_name=c.repo_name,commit_id=c.branch_or_raw_id,f_path=c.f_path, _query=query),class_="btn btn-danger")} |
|
40 | ${h.link_to(_('Delete'), h.route_path('repo_files_remove_file',repo_name=c.repo_name,commit_id=c.branch_or_raw_id,f_path=c.f_path, _query=query),class_="btn btn-danger")} | |
41 | % else: |
|
41 | % else: | |
42 | <a class="btn btn-default" href="${h.route_path('repo_files_edit_file',repo_name=c.repo_name,commit_id=c.branch_or_raw_id,f_path=c.f_path, _query=query)}"> |
|
42 | <a class="btn btn-default" href="${h.route_path('repo_files_edit_file',repo_name=c.repo_name,commit_id=c.branch_or_raw_id,f_path=c.f_path, _query=query)}"> |
@@ -49,7 +49,11 b'' | |||||
49 | </div> |
|
49 | </div> | |
50 |
|
50 | |||
51 | <div class="edit-file-title"> |
|
51 | <div class="edit-file-title"> | |
52 | <span class="title-heading">${_('Upload new file')} @ <code>${h.show_id(c.commit)}</code></span> |
|
52 | % if c.replace_binary: | |
|
53 | <span class="title-heading">${_('Replace content of')} <b>${c.f_path}</b> @ <code>${h.show_id(c.commit)}</code></span> | |||
|
54 | % else: | |||
|
55 | <span class="title-heading">${_('Upload new file')} @ <code>${h.show_id(c.commit)}</code></span> | |||
|
56 | % endif | |||
53 | % if c.commit.branch: |
|
57 | % if c.commit.branch: | |
54 | <span class="tag branchtag"> |
|
58 | <span class="tag branchtag"> | |
55 | <i class="icon-branch"></i> ${c.commit.branch} |
|
59 | <i class="icon-branch"></i> ${c.commit.branch} | |
@@ -57,24 +61,27 b'' | |||||
57 | % endif |
|
61 | % endif | |
58 | </div> |
|
62 | </div> | |
59 |
|
63 | |||
60 | <% form_url = h.route_path('repo_files_upload_file', repo_name=c.repo_name, commit_id=c.commit.raw_id, f_path=c.f_path) %> |
|
64 | % if not c.replace_binary: | |
61 | ##${h.secure_form(form_url, id='eform', enctype="multipart/form-data", request=request)} |
|
65 | <% form_url = h.route_path('repo_files_upload_file', repo_name=c.repo_name, commit_id=c.commit.raw_id, f_path=c.f_path) %> | |
62 | <div class="edit-file-fieldset"> |
|
66 | <div class="edit-file-fieldset"> | |
63 | <div class="path-items"> |
|
67 | <div class="path-items"> | |
64 | <ul class="tooltip" title="Repository path to store uploaded files. To change it, navigate to different path and click upload from there."> |
|
68 | <ul class="tooltip" title="Repository path to store uploaded files. To change it, navigate to different path and click upload from there."> | |
65 | <li class="breadcrumb-path"> |
|
69 | <li class="breadcrumb-path"> | |
66 | <div> |
|
70 | <div> | |
67 | <a href="${h.route_path('repo_files', repo_name=c.repo_name, commit_id=c.commit.raw_id, f_path='')}"><i class="icon-home"></i></a> / |
|
71 | <a href="${h.route_path('repo_files', repo_name=c.repo_name, commit_id=c.commit.raw_id, f_path='')}"><i class="icon-home"></i></a> / | |
68 | <a href="${h.route_path('repo_files', repo_name=c.repo_name, commit_id=c.commit.raw_id, f_path=c.f_path)}">${c.f_path}</a>${('/' if c.f_path else '')} |
|
72 | <a href="${h.route_path('repo_files', repo_name=c.repo_name, commit_id=c.commit.raw_id, f_path=c.f_path)}">${c.f_path}</a>${('/' if c.f_path else '')} | |
69 | </div> |
|
73 | </div> | |
70 | </li> |
|
74 | </li> | |
71 | <li class="location-path"> |
|
75 | <li class="location-path"> | |
72 |
|
76 | |||
73 | </li> |
|
77 | </li> | |
74 | </ul> |
|
78 | </ul> | |
|
79 | </div> | |||
|
80 | ||||
75 | </div> |
|
81 | </div> | |
76 |
|
82 | % else: | ||
77 | </div> |
|
83 | <% form_url = h.route_path('repo_files_replace_binary', repo_name=c.repo_name, commit_id=c.commit.raw_id, f_path=c.f_path) %> | |
|
84 | % endif | |||
78 |
|
85 | |||
79 | <div class="upload-form table"> |
|
86 | <div class="upload-form table"> | |
80 | <div> |
|
87 | <div> | |
@@ -83,7 +90,11 b'' | |||||
83 | <div class="dropzone-pure"> |
|
90 | <div class="dropzone-pure"> | |
84 | <div class="dz-message"> |
|
91 | <div class="dz-message"> | |
85 | <i class="icon-upload" style="font-size:36px"></i></br> |
|
92 | <i class="icon-upload" style="font-size:36px"></i></br> | |
86 | ${_("Drag'n Drop files here or")} <span class="link">${_('Choose your files')}</span>.<br> |
|
93 | % if not c.replace_binary: | |
|
94 | ${_("Drag'n Drop files here or")} <span class="link">${_('Choose your files')}</span>.<br> | |||
|
95 | % else: | |||
|
96 | ${_("Drag'n Drop file here or")} <span class="link">${_('Choose your file')}</span>.<br> | |||
|
97 | % endif | |||
87 | </div> |
|
98 | </div> | |
88 | </div> |
|
99 | </div> | |
89 |
|
100 |
@@ -75,6 +75,7 b' def get_url_defs():' | |||||
75 | "repo_files_add_file": "/{repo_name}/add_file/{commit_id}/{f_path}", |
|
75 | "repo_files_add_file": "/{repo_name}/add_file/{commit_id}/{f_path}", | |
76 | "repo_files_upload_file": "/{repo_name}/upload_file/{commit_id}/{f_path}", |
|
76 | "repo_files_upload_file": "/{repo_name}/upload_file/{commit_id}/{f_path}", | |
77 | "repo_files_create_file": "/{repo_name}/create_file/{commit_id}/{f_path}", |
|
77 | "repo_files_create_file": "/{repo_name}/create_file/{commit_id}/{f_path}", | |
|
78 | "repo_files_replace_binary": "/{repo_name}/replace_binary/{commit_id}/{f_path}", | |||
78 | "repo_nodetree_full": "/{repo_name}/nodetree_full/{commit_id}/{f_path}", |
|
79 | "repo_nodetree_full": "/{repo_name}/nodetree_full/{commit_id}/{f_path}", | |
79 | "repo_nodetree_full:default_path": "/{repo_name}/nodetree_full/{commit_id}/", |
|
80 | "repo_nodetree_full:default_path": "/{repo_name}/nodetree_full/{commit_id}/", | |
80 | "journal": ADMIN_PREFIX + "/journal", |
|
81 | "journal": ADMIN_PREFIX + "/journal", |
General Comments 0
You need to be logged in to leave comments.
Login now