Show More
@@ -40,7 +40,7 b' from rhodecode.lib.exceptions import (' | |||
|
40 | 40 | UserCreationError, UserOwnsReposException, UserOwnsRepoGroupsException, |
|
41 | 41 | UserOwnsUserGroupsException, UserOwnsPullRequestsException, |
|
42 | 42 | UserOwnsArtifactsException, DefaultUserException) |
|
43 |
from rhodecode.lib |
|
|
43 | from rhodecode.lib import ext_json | |
|
44 | 44 | from rhodecode.lib.auth import ( |
|
45 | 45 | LoginRequired, HasPermissionAllDecorator, CSRFRequired) |
|
46 | 46 | from rhodecode.lib import helpers as h |
@@ -1169,7 +1169,7 b' class UsersView(UserAppView):' | |||
|
1169 | 1169 | |
|
1170 | 1170 | groups = [UserGroupModel.get_user_groups_as_dict(group.users_group) |
|
1171 | 1171 | for group in c.user.group_member] |
|
1172 |
c.groups = json. |
|
|
1172 | c.groups = ext_json.str_json(groups) | |
|
1173 | 1173 | c.active = 'groups' |
|
1174 | 1174 | |
|
1175 | 1175 | return self._get_template_context(c) |
@@ -1258,9 +1258,8 b' class UsersView(UserAppView):' | |||
|
1258 | 1258 | for entry in user_log: |
|
1259 | 1259 | audit_log_data[entry.user_log_id] = entry.get_dict() |
|
1260 | 1260 | |
|
1261 |
response = Response(json. |
|
|
1262 | response.content_disposition = str( | |
|
1263 | 'attachment; filename=%s' % 'user_{}_audit_logs.json'.format(c.user.user_id)) | |
|
1261 | response = Response(ext_json.formatted_str_json(audit_log_data)) | |
|
1262 | response.content_disposition = f'attachment; filename=user_{c.user.user_id}_audit_logs.json' | |
|
1264 | 1263 | response.content_type = 'application/json' |
|
1265 | 1264 | |
|
1266 | 1265 | return response |
@@ -25,6 +25,7 b' from pyramid.settings import asbool' | |||
|
25 | 25 | |
|
26 | 26 | from rhodecode.apps._base import ADMIN_PREFIX |
|
27 | 27 | from rhodecode.lib.ext_json import json |
|
28 | from rhodecode.lib.str_utils import safe_str | |
|
28 | 29 | |
|
29 | 30 | |
|
30 | 31 | def url_gen(request): |
@@ -38,7 +39,7 b' def url_gen(request):' | |||
|
38 | 39 | 'longpoll': longpoll_url or proxy_url, |
|
39 | 40 | 'ws': ws_url or proxy_url.replace('http', 'ws') |
|
40 | 41 | } |
|
41 | return json.dumps(urls) | |
|
42 | return safe_str(json.dumps(urls)) | |
|
42 | 43 | |
|
43 | 44 | |
|
44 | 45 | PLUGIN_DEFINITION = { |
@@ -30,7 +30,7 b' from pyramid.renderers import render' | |||
|
30 | 30 | from pyramid.response import Response |
|
31 | 31 | |
|
32 | 32 | from rhodecode.apps._base import BaseAppView |
|
33 | from rhodecode.lib import helpers as h | |
|
33 | from rhodecode.lib import helpers as h, ext_json | |
|
34 | 34 | from rhodecode.lib.auth import LoginRequired, NotAnonymous, CSRFRequired |
|
35 | 35 | from rhodecode.lib.utils2 import time_to_datetime |
|
36 | 36 | from rhodecode.lib.ext_json import json |
@@ -124,7 +124,7 b' class GistView(BaseAppView):' | |||
|
124 | 124 | 'expires': _render('gist_expires', gist.gist_expires), |
|
125 | 125 | 'description': _render('gist_description', gist.gist_description) |
|
126 | 126 | }) |
|
127 |
c.data = json. |
|
|
127 | c.data = ext_json.str_json(data) | |
|
128 | 128 | |
|
129 | 129 | return self._get_template_context(c) |
|
130 | 130 |
@@ -31,7 +31,7 b' from rhodecode.apps._base import BaseApp' | |||
|
31 | 31 | from rhodecode import forms |
|
32 | 32 | from rhodecode.lib import helpers as h |
|
33 | 33 | from rhodecode.lib import audit_logger |
|
34 |
from rhodecode.lib |
|
|
34 | from rhodecode.lib import ext_json | |
|
35 | 35 | from rhodecode.lib.auth import ( |
|
36 | 36 | LoginRequired, NotAnonymous, CSRFRequired, |
|
37 | 37 | HasRepoPermissionAny, HasRepoGroupPermissionAny, AuthUser) |
@@ -457,7 +457,7 b' class MyAccountView(BaseAppView, DataGri' | |||
|
457 | 457 | repos_data.append(row) |
|
458 | 458 | |
|
459 | 459 | # json used to render the grid |
|
460 |
return json. |
|
|
460 | return ext_json.str_json(repos_data) | |
|
461 | 461 | |
|
462 | 462 | @LoginRequired() |
|
463 | 463 | @NotAnonymous() |
@@ -779,5 +779,5 b' class MyAccountView(BaseAppView, DataGri' | |||
|
779 | 779 | c.active = 'user_group_membership' |
|
780 | 780 | groups = [UserGroupModel.get_user_groups_as_dict(group.users_group) |
|
781 | 781 | for group in self._rhodecode_db_user.group_member] |
|
782 |
c.user_groups = json. |
|
|
782 | c.user_groups = ext_json.str_json(groups) | |
|
783 | 783 | return self._get_template_context(c) |
@@ -21,9 +21,8 b' import logging' | |||
|
21 | 21 | |
|
22 | 22 | from pyramid.httpexceptions import HTTPNotFound |
|
23 | 23 | |
|
24 | ||
|
25 | 24 | from rhodecode.apps._base import BaseReferencesView |
|
26 |
from rhodecode.lib |
|
|
25 | from rhodecode.lib import ext_json | |
|
27 | 26 | from rhodecode.lib import helpers as h |
|
28 | 27 | from rhodecode.lib.auth import (LoginRequired, HasRepoPermissionAnyDecorator) |
|
29 | 28 | |
@@ -47,5 +46,5 b' class RepoBookmarksView(BaseReferencesVi' | |||
|
47 | 46 | ref_items=ref_items, partials_template='bookmarks/bookmarks_data.mako') |
|
48 | 47 | |
|
49 | 48 | c.has_references = bool(data) |
|
50 |
c.data = json. |
|
|
49 | c.data = ext_json.str_json(data) | |
|
51 | 50 | return self._get_template_context(c) |
@@ -22,7 +22,7 b' import logging' | |||
|
22 | 22 | |
|
23 | 23 | |
|
24 | 24 | from rhodecode.apps._base import BaseReferencesView |
|
25 |
from rhodecode.lib |
|
|
25 | from rhodecode.lib import ext_json | |
|
26 | 26 | from rhodecode.lib.auth import (LoginRequired, HasRepoPermissionAnyDecorator) |
|
27 | 27 | |
|
28 | 28 | |
@@ -42,5 +42,5 b' class RepoBranchesView(BaseReferencesVie' | |||
|
42 | 42 | ref_items=ref_items, partials_template='branches/branches_data.mako') |
|
43 | 43 | |
|
44 | 44 | c.has_references = bool(data) |
|
45 |
c.data = json. |
|
|
45 | c.data = ext_json.str_json(data) | |
|
46 | 46 | return self._get_template_context(c) |
@@ -28,6 +28,7 b' from pyramid.response import Response' | |||
|
28 | 28 | |
|
29 | 29 | from rhodecode.apps._base import RepoAppView |
|
30 | 30 | import rhodecode.lib.helpers as h |
|
31 | from rhodecode.lib import ext_json | |
|
31 | 32 | from rhodecode.lib.auth import ( |
|
32 | 33 | LoginRequired, HasRepoPermissionAnyDecorator) |
|
33 | 34 | |
@@ -106,7 +107,7 b' class RepoChangelogView(RepoAppView):' | |||
|
106 | 107 | |
|
107 | 108 | data = [[commit_id, vtx, edges, branch] |
|
108 | 109 | for commit_id, vtx, edges, branch in _colored(dag)] |
|
109 |
return json. |
|
|
110 | return ext_json.str_json(data), ext_json.str_json(current) | |
|
110 | 111 | |
|
111 | 112 | def _check_if_valid_branch(self, branch_name, repo_name, f_path): |
|
112 | 113 | if branch_name not in self.rhodecode_vcs_repo.branches_all: |
@@ -33,7 +33,7 b' from rhodecode.apps.file_store.exception' | |||
|
33 | 33 | from rhodecode.lib import diffs, codeblocks, channelstream |
|
34 | 34 | from rhodecode.lib.auth import ( |
|
35 | 35 | LoginRequired, HasRepoPermissionAnyDecorator, NotAnonymous, CSRFRequired) |
|
36 |
from rhodecode.lib |
|
|
36 | from rhodecode.lib import ext_json | |
|
37 | 37 | from collections import OrderedDict |
|
38 | 38 | from rhodecode.lib.diffs import ( |
|
39 | 39 | cache_diff, load_cached_diff, diff_cache_exist, get_diff_context, |
@@ -218,7 +218,7 b' class RepoCommitsView(RepoAppView):' | |||
|
218 | 218 | member_reviewer['allowed_to_update'] = False |
|
219 | 219 | c.commit_set_reviewers_data_json['reviewers'].append(member_reviewer) |
|
220 | 220 | |
|
221 |
c.commit_set_reviewers_data_json = json. |
|
|
221 | c.commit_set_reviewers_data_json = ext_json.str_json(c.commit_set_reviewers_data_json) | |
|
222 | 222 | |
|
223 | 223 | # NOTE(marcink): this uses the same voting logic as in pull-requests |
|
224 | 224 | c.commit_review_status = ChangesetStatusModel().calculate_status(review_statuses) |
@@ -35,7 +35,7 b' from rhodecode.lib import helpers as h, ' | |||
|
35 | 35 | from rhodecode.lib.base import vcs_operation_context |
|
36 | 36 | from rhodecode.lib.diffs import load_cached_diff, cache_diff, diff_cache_exist |
|
37 | 37 | from rhodecode.lib.exceptions import CommentVersionMismatch |
|
38 |
from rhodecode.lib |
|
|
38 | from rhodecode.lib import ext_json | |
|
39 | 39 | from rhodecode.lib.auth import ( |
|
40 | 40 | LoginRequired, HasRepoPermissionAny, HasRepoPermissionAnyDecorator, |
|
41 | 41 | NotAnonymous, CSRFRequired) |
@@ -468,7 +468,7 b' class RepoPullRequestsView(RepoAppView, ' | |||
|
468 | 468 | c.observers_count = pull_request.observers_count |
|
469 | 469 | |
|
470 | 470 | # reviewers and statuses |
|
471 |
c.pull_request_default_reviewers_data_json = json. |
|
|
471 | c.pull_request_default_reviewers_data_json = ext_json.str_json(pull_request.reviewer_data) | |
|
472 | 472 | c.pull_request_set_reviewers_data_json = collections.OrderedDict({'reviewers': []}) |
|
473 | 473 | c.pull_request_set_observers_data_json = collections.OrderedDict({'observers': []}) |
|
474 | 474 | |
@@ -485,7 +485,7 b' class RepoPullRequestsView(RepoAppView, ' | |||
|
485 | 485 | member_reviewer['allowed_to_update'] = c.allowed_to_update |
|
486 | 486 | c.pull_request_set_reviewers_data_json['reviewers'].append(member_reviewer) |
|
487 | 487 | |
|
488 |
c.pull_request_set_reviewers_data_json = json. |
|
|
488 | c.pull_request_set_reviewers_data_json = ext_json.str_json(c.pull_request_set_reviewers_data_json) | |
|
489 | 489 | |
|
490 | 490 | for observer_obj, member in pull_request_at_ver.observers(): |
|
491 | 491 | member_observer = h.reviewer_as_json( |
@@ -496,7 +496,7 b' class RepoPullRequestsView(RepoAppView, ' | |||
|
496 | 496 | member_observer['allowed_to_update'] = c.allowed_to_update |
|
497 | 497 | c.pull_request_set_observers_data_json['observers'].append(member_observer) |
|
498 | 498 | |
|
499 |
c.pull_request_set_observers_data_json = json. |
|
|
499 | c.pull_request_set_observers_data_json = ext_json.str_json(c.pull_request_set_observers_data_json) | |
|
500 | 500 | |
|
501 | 501 | general_comments, inline_comments = \ |
|
502 | 502 | self.register_comments_vars(c, pull_request_latest, versions) |
@@ -889,9 +889,9 b' class RepoPullRequestsView(RepoAppView, ' | |||
|
889 | 889 | |
|
890 | 890 | c.default_repo_data = { |
|
891 | 891 | 'source_repo_name': source_repo.repo_name, |
|
892 |
'source_refs_json': json. |
|
|
892 | 'source_refs_json': ext_json.str_json(source_repo_data), | |
|
893 | 893 | 'target_repo_name': default_target_repo.repo_name, |
|
894 |
'target_refs_json': json. |
|
|
894 | 'target_refs_json': ext_json.str_json(target_repo_data), | |
|
895 | 895 | } |
|
896 | 896 | c.default_source_ref = selected_source_ref |
|
897 | 897 |
@@ -21,7 +21,7 b'' | |||
|
21 | 21 | import logging |
|
22 | 22 | |
|
23 | 23 | from rhodecode.apps._base import BaseReferencesView |
|
24 |
from rhodecode.lib |
|
|
24 | from rhodecode.lib import ext_json | |
|
25 | 25 | from rhodecode.lib.auth import (LoginRequired, HasRepoPermissionAnyDecorator) |
|
26 | 26 | |
|
27 | 27 | log = logging.getLogger(__name__) |
@@ -40,5 +40,5 b' class RepoTagsView(BaseReferencesView):' | |||
|
40 | 40 | ref_items=ref_items, partials_template='tags/tags_data.mako') |
|
41 | 41 | |
|
42 | 42 | c.has_references = bool(data) |
|
43 |
c.data = json. |
|
|
43 | c.data = ext_json.str_json(data) | |
|
44 | 44 | return self._get_template_context(c) |
@@ -18,7 +18,7 b'' | |||
|
18 | 18 | # RhodeCode Enterprise Edition, including its added features, Support services, |
|
19 | 19 | # and proprietary license terms, please see https://rhodecode.com/licenses/ |
|
20 | 20 | |
|
21 |
from rhodecode.lib |
|
|
21 | from rhodecode.lib import ext_json | |
|
22 | 22 | |
|
23 | 23 | |
|
24 | 24 | def pyramid_ext_json(info): |
@@ -34,7 +34,8 b' def pyramid_ext_json(info):' | |||
|
34 | 34 | if ct == response.default_content_type: |
|
35 | 35 | response.content_type = 'application/json' |
|
36 | 36 | indent = getattr(request, 'ext_json_indent', None) |
|
37 | ||
|
38 |
return json. |
|
|
37 | if indent: | |
|
38 | return ext_json.formatted_json(value) | |
|
39 | return ext_json.json.dumps(value) | |
|
39 | 40 | |
|
40 | 41 | return _render |
@@ -78,7 +78,9 b' from webhelpers2.number import format_by' | |||
|
78 | 78 | |
|
79 | 79 | from rhodecode.lib.action_parser import action_parser |
|
80 | 80 | from rhodecode.lib.pagination import Page, RepoPage, SqlPage |
|
81 | from rhodecode.lib import ext_json | |
|
81 | 82 | from rhodecode.lib.ext_json import json |
|
83 | from rhodecode.lib.str_utils import safe_bytes | |
|
82 | 84 | from rhodecode.lib.utils import repo_name_slug, get_custom_lexer |
|
83 | 85 | from rhodecode.lib.utils2 import ( |
|
84 | 86 | str2bool, safe_unicode, safe_str, |
@@ -135,6 +137,14 b' def html_escape(text, html_escape_table=' | |||
|
135 | 137 | return text.translate(html_escape_table) |
|
136 | 138 | |
|
137 | 139 | |
|
140 | def str_json(*args, **kwargs): | |
|
141 | return ext_json.str_json(*args, **kwargs) | |
|
142 | ||
|
143 | ||
|
144 | def formatted_str_json(*args, **kwargs): | |
|
145 | return ext_json.formatted_str_json(*args, **kwargs) | |
|
146 | ||
|
147 | ||
|
138 | 148 | def chop_at_smart(s, sub, inclusive=False, suffix_if_chopped=None): |
|
139 | 149 | """ |
|
140 | 150 | Truncate string ``s`` at the first occurrence of ``sub``. |
@@ -346,7 +356,7 b' def files_url_data(request):' | |||
|
346 | 356 | if 'commit_id' not in matchdict: |
|
347 | 357 | matchdict['commit_id'] = 'tip' |
|
348 | 358 | |
|
349 |
return json. |
|
|
359 | return ext_json.str_json(matchdict) | |
|
350 | 360 | |
|
351 | 361 | |
|
352 | 362 | def repo_files_by_ref_url(db_repo_name, db_repo_type, f_path, ref_name, commit_id, query=None, ): |
@@ -768,7 +778,7 b' class Flash(object):' | |||
|
768 | 778 | 'subdata': message.sub_data |
|
769 | 779 | } |
|
770 | 780 | }) |
|
771 | return json.dumps(payloads) | |
|
781 | return safe_str(json.dumps(payloads)) | |
|
772 | 782 | |
|
773 | 783 | def __call__(self, message, category=None, ignore_duplicate=True, |
|
774 | 784 | session=None, request=None): |
@@ -251,7 +251,7 b' def write_usage_data(event):' | |||
|
251 | 251 | ) |
|
252 | 252 | |
|
253 | 253 | with open(dest_file, 'wb') as f: |
|
254 |
f.write(ext_json.json |
|
|
254 | f.write(ext_json.formatted_json(metadata)) | |
|
255 | 255 | |
|
256 | 256 | try: |
|
257 | 257 | log.debug('Writing usage file at: %s', usage_metadata_destination) |
@@ -83,7 +83,7 b'' | |||
|
83 | 83 | <td class="td-journalaction"> |
|
84 | 84 | % if c.audit_log_entry.version == c.audit_log_entry.VERSION_2: |
|
85 | 85 | <div> |
|
86 |
<pre>${h.json |
|
|
86 | <pre>${h.formatted_str_json(c.audit_log_entry.action_data)}</pre> | |
|
87 | 87 | </div> |
|
88 | 88 | % else: |
|
89 | 89 | <pre title="${_('data not available for v1 entries type')}">-</pre> |
@@ -41,7 +41,7 b'' | |||
|
41 | 41 | % if l.version == l.VERSION_2: |
|
42 | 42 | <a href="#" onclick="$('#entry-'+${l.user_log_id}).toggle();return false">${_('toggle')}</a> |
|
43 | 43 | <div id="entry-${l.user_log_id}" style="display: none"> |
|
44 |
<pre>${h.json |
|
|
44 | <pre>${h.formatted_str_json(l.action_data)}</pre> | |
|
45 | 45 | </div> |
|
46 | 46 | % else: |
|
47 | 47 | <pre title="${_('data not available for v1 entries type')}">-</pre> |
@@ -92,7 +92,7 b' examples = [' | |||
|
92 | 92 | % if loop.index == 0: |
|
93 | 93 | <a href="#showMore" onclick="$('.it-examples').toggle(); return false">${_('show examples')}</a> |
|
94 | 94 | % else: |
|
95 |
<a href="#copyToInput" onclick="copyToInput(this, '${h.json |
|
|
95 | <a href="#copyToInput" onclick="copyToInput(this, '${h.str_json(name)}', '${h.str_json(pat)}', '${h.str_json(url)}', '${h.str_json(pref)}'); return false">copy to input</a> | |
|
96 | 96 | % endif |
|
97 | 97 | </td> |
|
98 | 98 | </tr> |
@@ -80,7 +80,7 b" c.template_context['attachment_store'] =" | |||
|
80 | 80 | <script src="${h.asset('js/rhodecode/i18n/%s.js' % c.language, ver=c.rhodecode_version_hash)}"></script> |
|
81 | 81 | <script type="text/javascript"> |
|
82 | 82 | // register templateContext to pass template variables to JS |
|
83 |
var templateContext = ${h.json |
|
|
83 | var templateContext = ${h.str_json(c.template_context)|n}; | |
|
84 | 84 | |
|
85 | 85 | var APPLICATION_URL = "${h.route_path('home').rstrip('/')}"; |
|
86 | 86 | var APPLICATION_PLUGINS = []; |
@@ -109,7 +109,7 b'' | |||
|
109 | 109 | % endif |
|
110 | 110 | |
|
111 | 111 | <% |
|
112 |
data = h.json |
|
|
112 | data = h.str_json({ | |
|
113 | 113 | 'comment_id': comment_obj.comment_id, |
|
114 | 114 | 'version_info': version_info, |
|
115 | 115 | 'file_name': comment_obj.f_path, |
@@ -37,12 +37,12 b'' | |||
|
37 | 37 | line="${comment.line_no}" |
|
38 | 38 | data-comment-id="${comment.comment_id}" |
|
39 | 39 | data-comment-type="${comment.comment_type}" |
|
40 |
data-comment-draft=${h.json |
|
|
40 | data-comment-draft=${h.str_json(comment.draft)} | |
|
41 | 41 | data-comment-renderer="${comment.renderer}" |
|
42 | 42 | data-comment-text="${comment.text | html_filters.base64,n}" |
|
43 | 43 | data-comment-f-path="${comment.f_path}" |
|
44 | 44 | data-comment-line-no="${comment.line_no}" |
|
45 |
data-comment-inline=${h.json |
|
|
45 | data-comment-inline=${h.str_json(inline)} | |
|
46 | 46 | style="${'display: none;' if outdated_at_ver else ''}"> |
|
47 | 47 | |
|
48 | 48 | <div class="meta"> |
@@ -81,7 +81,7 b'' | |||
|
81 | 81 | % if comment.resolved_comment: |
|
82 | 82 | <div class="tooltip" title="${_('This comment resolves TODO #{}').format(comment.resolved_comment.comment_id)}"> |
|
83 | 83 | fix |
|
84 |
<a href="#comment-${comment.resolved_comment.comment_id}" onclick="Rhodecode.comments.scrollToComment($('#comment-${comment.resolved_comment.comment_id}'), 0, ${h.json |
|
|
84 | <a href="#comment-${comment.resolved_comment.comment_id}" onclick="Rhodecode.comments.scrollToComment($('#comment-${comment.resolved_comment.comment_id}'), 0, ${h.str_json(comment.resolved_comment.outdated)})"> | |
|
85 | 85 | <span style="text-decoration: line-through">#${comment.resolved_comment.comment_id}</span> |
|
86 | 86 | </a> |
|
87 | 87 | </div> |
@@ -547,7 +547,7 b'' | |||
|
547 | 547 | <i class="icon-info-circled tooltip-hovercard" |
|
548 | 548 | data-hovercard-alt="ALT" |
|
549 | 549 | data-hovercard-url="javascript:commentHelp('${c.visual.default_renderer.upper()}')" |
|
550 |
data-comment-json-b64='${h.b64(h.json |
|
|
550 | data-comment-json-b64='${h.b64(h.str_json({}))}'></i> | |
|
551 | 551 | </span> |
|
552 | 552 | </div> |
|
553 | 553 | </div> |
@@ -1114,7 +1114,7 b' def get_comments_for(diff_type, comments' | |||
|
1114 | 1114 | % for filediff in diffset.files: |
|
1115 | 1115 | {id:"a_${h.FID(filediff.raw_id, filediff.patch['filename'])}", |
|
1116 | 1116 | text:"${filediff.patch['filename']}", |
|
1117 |
ops:${h.json |
|
|
1117 | ops:${h.str_json(filediff.patch['stats'])|n}}${('' if loop.last else ',')} | |
|
1118 | 1118 | % endfor |
|
1119 | 1119 | ] |
|
1120 | 1120 | }; |
@@ -22,7 +22,7 b'' | |||
|
22 | 22 | <span style="white-space: pre-line">- ${pr_check_details['message']}</span> |
|
23 | 23 | % if pr_check_key == 'todo': |
|
24 | 24 | % for co in pr_check_details['details']: |
|
25 |
<a class="permalink" href="#comment-${co.comment_id}" onclick="Rhodecode.comments.scrollToComment($('#comment-${co.comment_id}'), 0, ${h.json |
|
|
25 | <a class="permalink" href="#comment-${co.comment_id}" onclick="Rhodecode.comments.scrollToComment($('#comment-${co.comment_id}'), 0, ${h.str_json(co.outdated)})"> #${co.comment_id}</a>${'' if loop.last else ','} | |
|
26 | 26 | % endfor |
|
27 | 27 | % endif |
|
28 | 28 | </div> |
General Comments 0
You need to be logged in to leave comments.
Login now