gists: migrated gists controller to pyramid view.

This commit is contained in:
Daniel Dourvaris 2017-07-07 12:05:30 +02:00
parent 979f4b8bec
commit 93b6dfba9a
18 changed files with 394 additions and 232 deletions

View file

@ -28,7 +28,7 @@ from rhodecode.api.tests.utils import (
@pytest.mark.usefixtures("testuser_api", "app") @pytest.mark.usefixtures("testuser_api", "app")
class TestApiGetGist(object): class TestApiGetGist(object):
def test_api_get_gist(self, gist_util, http_host_stub): def test_api_get_gist(self, gist_util, http_host_only_stub):
gist = gist_util.create_gist() gist = gist_util.create_gist()
gist_id = gist.gist_access_id gist_id = gist.gist_access_id
gist_created_on = gist.created_on gist_created_on = gist.created_on
@ -45,14 +45,14 @@ class TestApiGetGist(object):
'expires': -1.0, 'expires': -1.0,
'gist_id': int(gist_id), 'gist_id': int(gist_id),
'type': 'public', 'type': 'public',
'url': 'http://%s/_admin/gists/%s' % (http_host_stub, gist_id,), 'url': 'http://%s/_admin/gists/%s' % (http_host_only_stub, gist_id,),
'acl_level': Gist.ACL_LEVEL_PUBLIC, 'acl_level': Gist.ACL_LEVEL_PUBLIC,
'content': None, 'content': None,
} }
assert_ok(id_, expected, given=response.body) assert_ok(id_, expected, given=response.body)
def test_api_get_gist_with_content(self, gist_util, http_host_stub): def test_api_get_gist_with_content(self, gist_util, http_host_only_stub):
mapping = { mapping = {
u'filename1.txt': {'content': u'hello world'}, u'filename1.txt': {'content': u'hello world'},
u'filename1ą.txt': {'content': u'hello worldę'} u'filename1ą.txt': {'content': u'hello worldę'}
@ -73,7 +73,7 @@ class TestApiGetGist(object):
'expires': -1.0, 'expires': -1.0,
'gist_id': int(gist_id), 'gist_id': int(gist_id),
'type': 'public', 'type': 'public',
'url': 'http://%s/_admin/gists/%s' % (http_host_stub, gist_id,), 'url': 'http://%s/_admin/gists/%s' % (http_host_only_stub, gist_id,),
'acl_level': Gist.ACL_LEVEL_PUBLIC, 'acl_level': Gist.ACL_LEVEL_PUBLIC,
'content': { 'content': {
u'filename1.txt': u'hello world', u'filename1.txt': u'hello world',

View file

@ -0,0 +1,62 @@
# -*- coding: utf-8 -*-
# Copyright (C) 2016-2017 RhodeCode GmbH
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License, version 3
# (only), as published by the Free Software Foundation.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
# This program is dual-licensed. If you wish to learn more about the
# RhodeCode Enterprise Edition, including its added features, Support services,
# and proprietary license terms, please see https://rhodecode.com/licenses/
from rhodecode.apps._base import ADMIN_PREFIX
def admin_routes(config):
config.add_route(
name='gists_show', pattern='/gists')
config.add_route(
name='gists_new', pattern='/gists/new')
config.add_route(
name='gists_create', pattern='/gists/create')
config.add_route(
name='gist_show', pattern='/gists/{gist_id}')
config.add_route(
name='gist_delete', pattern='/gists/{gist_id}/delete')
config.add_route(
name='gist_edit', pattern='/gists/{gist_id}/edit')
config.add_route(
name='gist_edit_check_revision',
pattern='/gists/{gist_id}/edit/check_revision')
config.add_route(
name='gist_update', pattern='/gists/{gist_id}/update')
config.add_route(
name='gist_show_rev',
pattern='/gists/{gist_id}/{revision}')
config.add_route(
name='gist_show_formatted',
pattern='/gists/{gist_id}/{revision}/{format}')
config.add_route(
name='gist_show_formatted_path',
pattern='/gists/{gist_id}/{revision}/{format}/{f_path:.*}')
def includeme(config):
config.include(admin_routes, route_prefix=ADMIN_PREFIX)
# Scan module for configuration decorators.
config.scan()

View file

@ -0,0 +1,20 @@
# -*- coding: utf-8 -*-
# Copyright (C) 2016-2017 RhodeCode GmbH
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License, version 3
# (only), as published by the Free Software Foundation.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
# This program is dual-licensed. If you wish to learn more about the
# RhodeCode Enterprise Edition, including its added features, Support services,
# and proprietary license terms, please see https://rhodecode.com/licenses/

View file

@ -27,7 +27,31 @@ from rhodecode.model.gist import GistModel
from rhodecode.model.meta import Session from rhodecode.model.meta import Session
from rhodecode.tests import ( from rhodecode.tests import (
TEST_USER_ADMIN_LOGIN, TEST_USER_REGULAR_LOGIN, TEST_USER_REGULAR_PASS, TEST_USER_ADMIN_LOGIN, TEST_USER_REGULAR_LOGIN, TEST_USER_REGULAR_PASS,
TestController, assert_session_flash, url) TestController, assert_session_flash)
def route_path(name, params=None, **kwargs):
import urllib
from rhodecode.apps._base import ADMIN_PREFIX
base_url = {
'gists_show': ADMIN_PREFIX + '/gists',
'gists_new': ADMIN_PREFIX + '/gists/new',
'gists_create': ADMIN_PREFIX + '/gists/create',
'gist_show': ADMIN_PREFIX + '/gists/{gist_id}',
'gist_delete': ADMIN_PREFIX + '/gists/{gist_id}/delete',
'gist_edit': ADMIN_PREFIX + '/gists/{gist_id}/edit',
'gist_edit_check_revision': ADMIN_PREFIX + '/gists/{gist_id}/edit/check_revision',
'gist_update': ADMIN_PREFIX + '/gists/{gist_id}/update',
'gist_show_rev': ADMIN_PREFIX + '/gists/{gist_id}/{revision}',
'gist_show_formatted': ADMIN_PREFIX + '/gists/{gist_id}/{revision}/{format}',
'gist_show_formatted_path': ADMIN_PREFIX + '/gists/{gist_id}/{revision}/{format}/{f_path}',
}[name].format(**kwargs)
if params:
base_url = '{}?{}'.format(base_url, urllib.urlencode(params))
return base_url
class GistUtility(object): class GistUtility(object):
@ -70,7 +94,7 @@ class TestGistsController(TestController):
def test_index_empty(self, create_gist): def test_index_empty(self, create_gist):
self.log_user() self.log_user()
response = self.app.get(url('gists')) response = self.app.get(route_path('gists_show'))
response.mustcontain('data: [],') response.mustcontain('data: [],')
def test_index(self, create_gist): def test_index(self, create_gist):
@ -79,7 +103,7 @@ class TestGistsController(TestController):
g2 = create_gist('gist2', lifetime=1400) g2 = create_gist('gist2', lifetime=1400)
g3 = create_gist('gist3', description='gist3-desc') g3 = create_gist('gist3', description='gist3-desc')
g4 = create_gist('gist4', gist_type='private').gist_access_id g4 = create_gist('gist4', gist_type='private').gist_access_id
response = self.app.get(url('gists')) response = self.app.get(route_path('gists_show'))
response.mustcontain('gist: %s' % g1.gist_access_id) response.mustcontain('gist: %s' % g1.gist_access_id)
response.mustcontain('gist: %s' % g2.gist_access_id) response.mustcontain('gist: %s' % g2.gist_access_id)
@ -95,7 +119,7 @@ class TestGistsController(TestController):
def test_index_private_gists(self, create_gist): def test_index_private_gists(self, create_gist):
self.log_user() self.log_user()
gist = create_gist('gist5', gist_type='private') gist = create_gist('gist5', gist_type='private')
response = self.app.get(url('gists', private=1)) response = self.app.get(route_path('gists_show', params=dict(private=1)))
# and privates # and privates
response.mustcontain('gist: %s' % gist.gist_access_id) response.mustcontain('gist: %s' % gist.gist_access_id)
@ -107,7 +131,7 @@ class TestGistsController(TestController):
create_gist('gist3', description='gist3-desc') create_gist('gist3', description='gist3-desc')
create_gist('gist4', gist_type='private') create_gist('gist4', gist_type='private')
response = self.app.get(url('gists', all=1)) response = self.app.get(route_path('gists_show', params=dict(all=1)))
assert len(GistModel.get_all()) == 4 assert len(GistModel.get_all()) == 4
# and privates # and privates
@ -120,7 +144,7 @@ class TestGistsController(TestController):
create_gist('gist3', gist_type='private') create_gist('gist3', gist_type='private')
create_gist('gist4', gist_type='private') create_gist('gist4', gist_type='private')
response = self.app.get(url('gists', all=1)) response = self.app.get(route_path('gists_show', params=dict(all=1)))
assert len(GistModel.get_all()) == 3 assert len(GistModel.get_all()) == 3
# since we don't have access to private in this view, we # since we don't have access to private in this view, we
@ -131,7 +155,7 @@ class TestGistsController(TestController):
def test_create(self): def test_create(self):
self.log_user() self.log_user()
response = self.app.post( response = self.app.post(
url('gists'), route_path('gists_create'),
params={'lifetime': -1, params={'lifetime': -1,
'content': 'gist test', 'content': 'gist test',
'filename': 'foo', 'filename': 'foo',
@ -146,7 +170,7 @@ class TestGistsController(TestController):
def test_create_with_path_with_dirs(self): def test_create_with_path_with_dirs(self):
self.log_user() self.log_user()
response = self.app.post( response = self.app.post(
url('gists'), route_path('gists_create'),
params={'lifetime': -1, params={'lifetime': -1,
'content': 'gist test', 'content': 'gist test',
'filename': '/home/foo', 'filename': '/home/foo',
@ -163,12 +187,13 @@ class TestGistsController(TestController):
Session().add(gist) Session().add(gist)
Session().commit() Session().commit()
self.app.get(url('gist', gist_id=gist.gist_access_id), status=404) self.app.get(route_path('gist_show', gist_id=gist.gist_access_id),
status=404)
def test_create_private(self): def test_create_private(self):
self.log_user() self.log_user()
response = self.app.post( response = self.app.post(
url('gists'), route_path('gists_create'),
params={'lifetime': -1, params={'lifetime': -1,
'content': 'private gist test', 'content': 'private gist test',
'filename': 'private-foo', 'filename': 'private-foo',
@ -187,7 +212,7 @@ class TestGistsController(TestController):
def test_create_private_acl_private(self): def test_create_private_acl_private(self):
self.log_user() self.log_user()
response = self.app.post( response = self.app.post(
url('gists'), route_path('gists_create'),
params={'lifetime': -1, params={'lifetime': -1,
'content': 'private gist test', 'content': 'private gist test',
'filename': 'private-foo', 'filename': 'private-foo',
@ -206,7 +231,7 @@ class TestGistsController(TestController):
def test_create_with_description(self): def test_create_with_description(self):
self.log_user() self.log_user()
response = self.app.post( response = self.app.post(
url('gists'), route_path('gists_create'),
params={'lifetime': -1, params={'lifetime': -1,
'content': 'gist test', 'content': 'gist test',
'filename': 'foo-desc', 'filename': 'foo-desc',
@ -231,7 +256,8 @@ class TestGistsController(TestController):
'gist_acl_level': Gist.ACL_LEVEL_PUBLIC, 'gist_acl_level': Gist.ACL_LEVEL_PUBLIC,
'csrf_token': self.csrf_token 'csrf_token': self.csrf_token
} }
response = self.app.post(url('gists'), params=params, status=302) response = self.app.post(
route_path('gists_create'), params=params, status=302)
self.logout_user() self.logout_user()
response = response.follow() response = response.follow()
response.mustcontain('added file: foo-desc') response.mustcontain('added file: foo-desc')
@ -240,35 +266,36 @@ class TestGistsController(TestController):
def test_new(self): def test_new(self):
self.log_user() self.log_user()
self.app.get(url('new_gist')) self.app.get(route_path('gists_new'))
def test_delete(self, create_gist): def test_delete(self, create_gist):
self.log_user() self.log_user()
gist = create_gist('delete-me') gist = create_gist('delete-me')
response = self.app.post( response = self.app.post(
url('gist', gist_id=gist.gist_id), route_path('gist_delete', gist_id=gist.gist_id),
params={'_method': 'delete', 'csrf_token': self.csrf_token}) params={'csrf_token': self.csrf_token})
assert_session_flash(response, 'Deleted gist %s' % gist.gist_id) assert_session_flash(response, 'Deleted gist %s' % gist.gist_id)
def test_delete_normal_user_his_gist(self, create_gist): def test_delete_normal_user_his_gist(self, create_gist):
self.log_user(TEST_USER_REGULAR_LOGIN, TEST_USER_REGULAR_PASS) self.log_user(TEST_USER_REGULAR_LOGIN, TEST_USER_REGULAR_PASS)
gist = create_gist('delete-me', owner=TEST_USER_REGULAR_LOGIN) gist = create_gist('delete-me', owner=TEST_USER_REGULAR_LOGIN)
response = self.app.post( response = self.app.post(
url('gist', gist_id=gist.gist_id), route_path('gist_delete', gist_id=gist.gist_id),
params={'_method': 'delete', 'csrf_token': self.csrf_token}) params={'csrf_token': self.csrf_token})
assert_session_flash(response, 'Deleted gist %s' % gist.gist_id) assert_session_flash(response, 'Deleted gist %s' % gist.gist_id)
def test_delete_normal_user_not_his_own_gist(self, create_gist): def test_delete_normal_user_not_his_own_gist(self, create_gist):
self.log_user(TEST_USER_REGULAR_LOGIN, TEST_USER_REGULAR_PASS) self.log_user(TEST_USER_REGULAR_LOGIN, TEST_USER_REGULAR_PASS)
gist = create_gist('delete-me') gist = create_gist('delete-me-2')
self.app.post( self.app.post(
url('gist', gist_id=gist.gist_id), route_path('gist_delete', gist_id=gist.gist_id),
params={'_method': 'delete', 'csrf_token': self.csrf_token}, params={'csrf_token': self.csrf_token}, status=404)
status=403)
def test_show(self, create_gist): def test_show(self, create_gist):
gist = create_gist('gist-show-me') gist = create_gist('gist-show-me')
response = self.app.get(url('gist', gist_id=gist.gist_access_id)) response = self.app.get(route_path('gist_show', gist_id=gist.gist_access_id))
response.mustcontain('added file: gist-show-me<') response.mustcontain('added file: gist-show-me<')
@ -283,16 +310,19 @@ class TestGistsController(TestController):
with mock.patch( with mock.patch(
'rhodecode.lib.vcs.settings.ALIASES', ['git']): 'rhodecode.lib.vcs.settings.ALIASES', ['git']):
gist = create_gist('gist-show-me-again') gist = create_gist('gist-show-me-again')
self.app.get(url('gist', gist_id=gist.gist_access_id), status=200) self.app.get(
route_path('gist_show', gist_id=gist.gist_access_id), status=200)
def test_show_acl_private(self, create_gist): def test_show_acl_private(self, create_gist):
gist = create_gist('gist-show-me-only-when-im-logged-in', gist = create_gist('gist-show-me-only-when-im-logged-in',
acl_level=Gist.ACL_LEVEL_PRIVATE) acl_level=Gist.ACL_LEVEL_PRIVATE)
self.app.get(url('gist', gist_id=gist.gist_access_id), status=404) self.app.get(
route_path('gist_show', gist_id=gist.gist_access_id), status=404)
# now we log-in we should see thi gist # now we log-in we should see thi gist
self.log_user() self.log_user()
response = self.app.get(url('gist', gist_id=gist.gist_access_id)) response = self.app.get(
route_path('gist_show', gist_id=gist.gist_access_id))
response.mustcontain('added file: gist-show-me-only-when-im-logged-in') response.mustcontain('added file: gist-show-me-only-when-im-logged-in')
assert_response = response.assert_response() assert_response = response.assert_response()
@ -303,36 +333,42 @@ class TestGistsController(TestController):
def test_show_as_raw(self, create_gist): def test_show_as_raw(self, create_gist):
gist = create_gist('gist-show-me', content='GIST CONTENT') gist = create_gist('gist-show-me', content='GIST CONTENT')
response = self.app.get(url('formatted_gist', response = self.app.get(
gist_id=gist.gist_access_id, format='raw')) route_path('gist_show_formatted',
gist_id=gist.gist_access_id, revision='tip',
format='raw'))
assert response.body == 'GIST CONTENT' assert response.body == 'GIST CONTENT'
def test_show_as_raw_individual_file(self, create_gist): def test_show_as_raw_individual_file(self, create_gist):
gist = create_gist('gist-show-me-raw', content='GIST BODY') gist = create_gist('gist-show-me-raw', content='GIST BODY')
response = self.app.get(url('formatted_gist_file', response = self.app.get(
gist_id=gist.gist_access_id, format='raw', route_path('gist_show_formatted_path',
revision='tip', f_path='gist-show-me-raw')) gist_id=gist.gist_access_id, format='raw',
revision='tip', f_path='gist-show-me-raw'))
assert response.body == 'GIST BODY' assert response.body == 'GIST BODY'
def test_edit_page(self, create_gist): def test_edit_page(self, create_gist):
self.log_user() self.log_user()
gist = create_gist('gist-for-edit', content='GIST EDIT BODY') gist = create_gist('gist-for-edit', content='GIST EDIT BODY')
response = self.app.get(url('edit_gist', gist_id=gist.gist_access_id)) response = self.app.get(route_path('gist_edit', gist_id=gist.gist_access_id))
response.mustcontain('GIST EDIT BODY') response.mustcontain('GIST EDIT BODY')
def test_edit_page_non_logged_user(self, create_gist): def test_edit_page_non_logged_user(self, create_gist):
gist = create_gist('gist-for-edit', content='GIST EDIT BODY') gist = create_gist('gist-for-edit', content='GIST EDIT BODY')
self.app.get(url('edit_gist', gist_id=gist.gist_access_id), status=302) self.app.get(route_path('gist_edit', gist_id=gist.gist_access_id),
status=302)
def test_edit_normal_user_his_gist(self, create_gist): def test_edit_normal_user_his_gist(self, create_gist):
self.log_user(TEST_USER_REGULAR_LOGIN, TEST_USER_REGULAR_PASS) self.log_user(TEST_USER_REGULAR_LOGIN, TEST_USER_REGULAR_PASS)
gist = create_gist('gist-for-edit', owner=TEST_USER_REGULAR_LOGIN) gist = create_gist('gist-for-edit', owner=TEST_USER_REGULAR_LOGIN)
self.app.get(url('edit_gist', gist_id=gist.gist_access_id, status=200)) self.app.get(route_path('gist_edit', gist_id=gist.gist_access_id,
status=200))
def test_edit_normal_user_not_his_own_gist(self, create_gist): def test_edit_normal_user_not_his_own_gist(self, create_gist):
self.log_user(TEST_USER_REGULAR_LOGIN, TEST_USER_REGULAR_PASS) self.log_user(TEST_USER_REGULAR_LOGIN, TEST_USER_REGULAR_PASS)
gist = create_gist('delete-me') gist = create_gist('delete-me')
self.app.get(url('edit_gist', gist_id=gist.gist_access_id), status=403) self.app.get(route_path('gist_edit', gist_id=gist.gist_access_id),
status=404)
def test_user_first_name_is_escaped(self, user_util, create_gist): def test_user_first_name_is_escaped(self, user_util, create_gist):
xss_atack_string = '"><script>alert(\'First Name\')</script>' xss_atack_string = '"><script>alert(\'First Name\')</script>'
@ -341,7 +377,7 @@ class TestGistsController(TestController):
user = user_util.create_user( user = user_util.create_user(
firstname=xss_atack_string, password=password) firstname=xss_atack_string, password=password)
create_gist('gist', gist_type='public', owner=user.username) create_gist('gist', gist_type='public', owner=user.username)
response = self.app.get(url('gists')) response = self.app.get(route_path('gists_show'))
response.mustcontain(xss_escaped_string) response.mustcontain(xss_escaped_string)
def test_user_last_name_is_escaped(self, user_util, create_gist): def test_user_last_name_is_escaped(self, user_util, create_gist):
@ -351,5 +387,5 @@ class TestGistsController(TestController):
user = user_util.create_user( user = user_util.create_user(
lastname=xss_atack_string, password=password) lastname=xss_atack_string, password=password)
create_gist('gist', gist_type='public', owner=user.username) create_gist('gist', gist_type='public', owner=user.username)
response = self.app.get(url('gists')) response = self.app.get(route_path('gists_show'))
response.mustcontain(xss_escaped_string) response.mustcontain(xss_escaped_string)

View file

@ -18,35 +18,26 @@
# RhodeCode Enterprise Edition, including its added features, Support services, # RhodeCode Enterprise Edition, including its added features, Support services,
# and proprietary license terms, please see https://rhodecode.com/licenses/ # and proprietary license terms, please see https://rhodecode.com/licenses/
"""
gist controller for RhodeCode
"""
import time import time
import logging import logging
import formencode import formencode
import peppercorn import peppercorn
from pylons import request, response, tmpl_context as c, url from pyramid.httpexceptions import HTTPNotFound, HTTPForbidden, HTTPFound
from pylons.controllers.util import redirect from pyramid.view import view_config
from pylons.i18n.translation import _ from pyramid.renderers import render
from webob.exc import HTTPNotFound, HTTPForbidden from pyramid.response import Response
from sqlalchemy.sql.expression import or_
from rhodecode.apps._base import BaseAppView
from rhodecode.model.gist import GistModel
from rhodecode.model.meta import Session
from rhodecode.model.db import Gist, User
from rhodecode.lib import auth
from rhodecode.lib import helpers as h from rhodecode.lib import helpers as h
from rhodecode.lib.base import BaseController, render from rhodecode.lib.auth import LoginRequired, NotAnonymous, CSRFRequired
from rhodecode.lib.auth import LoginRequired, NotAnonymous
from rhodecode.lib.utils import jsonify
from rhodecode.lib.utils2 import time_to_datetime from rhodecode.lib.utils2 import time_to_datetime
from rhodecode.lib.ext_json import json from rhodecode.lib.ext_json import json
from rhodecode.lib.vcs.exceptions import VCSError, NodeNotChangedError from rhodecode.lib.vcs.exceptions import VCSError, NodeNotChangedError
from rhodecode.model.gist import GistModel
from rhodecode.model.meta import Session
from rhodecode.model.db import Gist, User, or_
from rhodecode.model import validation_schema from rhodecode.model import validation_schema
from rhodecode.model.validation_schema.schemas import gist_schema from rhodecode.model.validation_schema.schemas import gist_schema
@ -54,10 +45,13 @@ from rhodecode.model.validation_schema.schemas import gist_schema
log = logging.getLogger(__name__) log = logging.getLogger(__name__)
class GistsController(BaseController): class GistView(BaseAppView):
"""REST Controller styled on the Atom Publishing Protocol"""
def load_default_context(self):
_ = self.request.translate
c = self._get_local_tmpl_context()
c.user = c.auth_user.get_instance()
def __load_defaults(self, extra_values=None):
c.lifetime_values = [ c.lifetime_values = [
(-1, _('forever')), (-1, _('forever')),
(5, _('5 minutes')), (5, _('5 minutes')),
@ -65,22 +59,27 @@ class GistsController(BaseController):
(60 * 24, _('1 day')), (60 * 24, _('1 day')),
(60 * 24 * 30, _('1 month')), (60 * 24 * 30, _('1 month')),
] ]
if extra_values:
c.lifetime_values.append(extra_values)
c.lifetime_options = [(c.lifetime_values, _("Lifetime"))] c.lifetime_options = [(c.lifetime_values, _("Lifetime"))]
c.acl_options = [ c.acl_options = [
(Gist.ACL_LEVEL_PRIVATE, _("Requires registered account")), (Gist.ACL_LEVEL_PRIVATE, _("Requires registered account")),
(Gist.ACL_LEVEL_PUBLIC, _("Can be accessed by anonymous users")) (Gist.ACL_LEVEL_PUBLIC, _("Can be accessed by anonymous users"))
] ]
self._register_global_c(c)
return c
@LoginRequired() @LoginRequired()
def index(self): @view_config(
"""GET /admin/gists: All items in the collection""" route_name='gists_show', request_method='GET',
# url('gists') renderer='rhodecode:templates/admin/gists/index.mako')
not_default_user = c.rhodecode_user.username != User.DEFAULT_USER def gist_show_all(self):
c.show_private = request.GET.get('private') and not_default_user c = self.load_default_context()
c.show_public = request.GET.get('public') and not_default_user
c.show_all = request.GET.get('all') and c.rhodecode_user.admin not_default_user = self._rhodecode_user.username != User.DEFAULT_USER
c.show_private = self.request.GET.get('private') and not_default_user
c.show_public = self.request.GET.get('public') and not_default_user
c.show_all = self.request.GET.get('all') and self._rhodecode_user.admin
gists = _gists = Gist().query()\ gists = _gists = Gist().query()\
.filter(or_(Gist.gist_expires == -1, Gist.gist_expires >= time.time()))\ .filter(or_(Gist.gist_expires == -1, Gist.gist_expires >= time.time()))\
@ -90,18 +89,18 @@ class GistsController(BaseController):
# MY private # MY private
if c.show_private and not c.show_public: if c.show_private and not c.show_public:
gists = _gists.filter(Gist.gist_type == Gist.GIST_PRIVATE)\ gists = _gists.filter(Gist.gist_type == Gist.GIST_PRIVATE)\
.filter(Gist.gist_owner == c.rhodecode_user.user_id) .filter(Gist.gist_owner == self._rhodecode_user.user_id)
c.active = 'my_private' c.active = 'my_private'
# MY public # MY public
elif c.show_public and not c.show_private: elif c.show_public and not c.show_private:
gists = _gists.filter(Gist.gist_type == Gist.GIST_PUBLIC)\ gists = _gists.filter(Gist.gist_type == Gist.GIST_PUBLIC)\
.filter(Gist.gist_owner == c.rhodecode_user.user_id) .filter(Gist.gist_owner == self._rhodecode_user.user_id)
c.active = 'my_public' c.active = 'my_public'
# MY public+private # MY public+private
elif c.show_private and c.show_public: elif c.show_private and c.show_public:
gists = _gists.filter(or_(Gist.gist_type == Gist.GIST_PUBLIC, gists = _gists.filter(or_(Gist.gist_type == Gist.GIST_PUBLIC,
Gist.gist_type == Gist.GIST_PRIVATE))\ Gist.gist_type == Gist.GIST_PRIVATE))\
.filter(Gist.gist_owner == c.rhodecode_user.user_id) .filter(Gist.gist_owner == self._rhodecode_user.user_id)
c.active = 'my_all' c.active = 'my_all'
# Show all by super-admin # Show all by super-admin
elif c.show_all: elif c.show_all:
@ -130,17 +129,29 @@ class GistsController(BaseController):
'description': _render('gist_description', gist.gist_description) 'description': _render('gist_description', gist.gist_description)
}) })
c.data = json.dumps(data) c.data = json.dumps(data)
return render('admin/gists/index.mako')
return self._get_template_context(c)
@LoginRequired() @LoginRequired()
@NotAnonymous() @NotAnonymous()
@auth.CSRFRequired() @view_config(
def create(self): route_name='gists_new', request_method='GET',
"""POST /admin/gists: Create a new item""" renderer='rhodecode:templates/admin/gists/new.mako')
# url('gists') def gist_new(self):
self.__load_defaults() c = self.load_default_context()
return self._get_template_context(c)
data = dict(request.POST) @LoginRequired()
@NotAnonymous()
@CSRFRequired()
@view_config(
route_name='gists_create', request_method='POST',
renderer='rhodecode:templates/admin/gists/new.mako')
def gist_create(self):
_ = self.request.translate
c = self.load_default_context()
data = dict(self.request.POST)
data['filename'] = data.get('filename') or Gist.DEFAULT_FILENAME data['filename'] = data.get('filename') or Gist.DEFAULT_FILENAME
data['nodes'] = [{ data['nodes'] = [{
'filename': data['filename'], 'filename': data['filename'],
@ -166,7 +177,7 @@ class GistsController(BaseController):
gist = GistModel().create( gist = GistModel().create(
gist_id=schema_data['gistid'], # custom access id not real ID gist_id=schema_data['gistid'], # custom access id not real ID
description=schema_data['description'], description=schema_data['description'],
owner=c.rhodecode_user.user_id, owner=self._rhodecode_user.user_id,
gist_mapping=schema_data['nodes'], gist_mapping=schema_data['nodes'],
gist_type=schema_data['gist_type'], gist_type=schema_data['gist_type'],
lifetime=schema_data['lifetime'], lifetime=schema_data['lifetime'],
@ -185,75 +196,91 @@ class GistsController(BaseController):
errors['filename'] = errors['nodes.0.filename'] errors['filename'] = errors['nodes.0.filename']
del errors['nodes.0.filename'] del errors['nodes.0.filename']
return formencode.htmlfill.render( data = render('rhodecode:templates/admin/gists/new.mako',
render('admin/gists/new.mako'), self._get_template_context(c), self.request)
html = formencode.htmlfill.render(
data,
defaults=defaults, defaults=defaults,
errors=errors, errors=errors,
prefix_error=False, prefix_error=False,
encoding="UTF-8", encoding="UTF-8",
force_defaults=False force_defaults=False
) )
return Response(html)
except Exception: except Exception:
log.exception("Exception while trying to create a gist") log.exception("Exception while trying to create a gist")
h.flash(_('Error occurred during gist creation'), category='error') h.flash(_('Error occurred during gist creation'), category='error')
return redirect(url('new_gist')) raise HTTPFound(h.route_url('gists_new'))
return redirect(url('gist', gist_id=new_gist_id)) raise HTTPFound(h.route_url('gist_show', gist_id=new_gist_id))
@LoginRequired() @LoginRequired()
@NotAnonymous() @NotAnonymous()
def new(self): @CSRFRequired()
"""GET /admin/gists/new: Form to create a new item""" @view_config(
# url('new_gist') route_name='gist_delete', request_method='POST')
self.__load_defaults() def gist_delete(self):
return render('admin/gists/new.mako') _ = self.request.translate
gist_id = self.request.matchdict['gist_id']
@LoginRequired() c = self.load_default_context()
@NotAnonymous()
@auth.CSRFRequired()
def delete(self, gist_id):
"""DELETE /admin/gists/gist_id: Delete an existing item"""
# Forms posted to this method should contain a hidden field:
# <input type="hidden" name="_method" value="DELETE" />
# Or using helpers:
# h.form(url('gist', gist_id=ID),
# method='delete')
# url('gist', gist_id=ID)
c.gist = Gist.get_or_404(gist_id) c.gist = Gist.get_or_404(gist_id)
owner = c.gist.gist_owner == c.rhodecode_user.user_id owner = c.gist.gist_owner == self._rhodecode_user.user_id
if not (h.HasPermissionAny('hg.admin')() or owner): if not (h.HasPermissionAny('hg.admin')() or owner):
raise HTTPForbidden() log.warning('Deletion of Gist was forbidden '
'by unauthorized user: `%s`', self._rhodecode_user)
raise HTTPNotFound()
GistModel().delete(c.gist) GistModel().delete(c.gist)
Session().commit() Session().commit()
h.flash(_('Deleted gist %s') % c.gist.gist_access_id, category='success') h.flash(_('Deleted gist %s') % c.gist.gist_access_id, category='success')
return redirect(url('gists')) raise HTTPFound(h.route_url('gists_show'))
def _add_gist_to_context(self, gist_id): def _get_gist(self, gist_id):
c.gist = Gist.get_or_404(gist_id)
gist = Gist.get_or_404(gist_id)
# Check if this gist is expired # Check if this gist is expired
if c.gist.gist_expires != -1: if gist.gist_expires != -1:
if time.time() > c.gist.gist_expires: if time.time() > gist.gist_expires:
log.error( log.error(
'Gist expired at %s', time_to_datetime(c.gist.gist_expires)) 'Gist expired at %s', time_to_datetime(gist.gist_expires))
raise HTTPNotFound() raise HTTPNotFound()
# check if this gist requires a login # check if this gist requires a login
is_default_user = c.rhodecode_user.username == User.DEFAULT_USER is_default_user = self._rhodecode_user.username == User.DEFAULT_USER
if c.gist.acl_level == Gist.ACL_LEVEL_PRIVATE and is_default_user: if gist.acl_level == Gist.ACL_LEVEL_PRIVATE and is_default_user:
log.error("Anonymous user %s tried to access protected gist `%s`", log.error("Anonymous user %s tried to access protected gist `%s`",
c.rhodecode_user, gist_id) self._rhodecode_user, gist_id)
raise HTTPNotFound() raise HTTPNotFound()
return gist
@LoginRequired() @LoginRequired()
def show(self, gist_id, revision='tip', format='html', f_path=None): @view_config(
"""GET /admin/gists/gist_id: Show a specific item""" route_name='gist_show', request_method='GET',
# url('gist', gist_id=ID) renderer='rhodecode:templates/admin/gists/show.mako')
self._add_gist_to_context(gist_id) @view_config(
c.render = not request.GET.get('no-render', False) route_name='gist_show_rev', request_method='GET',
renderer='rhodecode:templates/admin/gists/show.mako')
@view_config(
route_name='gist_show_formatted', request_method='GET',
renderer=None)
@view_config(
route_name='gist_show_formatted_path', request_method='GET',
renderer=None)
def show(self):
gist_id = self.request.matchdict['gist_id']
# TODO(marcink): expose those via matching dict
revision = self.request.matchdict.get('revision', 'tip')
f_path = self.request.matchdict.get('f_path', None)
return_format = self.request.matchdict.get('format')
c = self.load_default_context()
c.gist = self._get_gist(gist_id)
c.render = not self.request.GET.get('no-render', False)
try: try:
c.file_last_commit, c.files = GistModel().get_gist_files( c.file_last_commit, c.files = GistModel().get_gist_files(
@ -261,25 +288,66 @@ class GistsController(BaseController):
except VCSError: except VCSError:
log.exception("Exception in gist show") log.exception("Exception in gist show")
raise HTTPNotFound() raise HTTPNotFound()
if format == 'raw':
if return_format == 'raw':
content = '\n\n'.join([f.content for f in c.files content = '\n\n'.join([f.content for f in c.files
if (f_path is None or f.path == f_path)]) if (f_path is None or f.path == f_path)])
response = Response(content)
response.content_type = 'text/plain' response.content_type = 'text/plain'
return content return response
return render('admin/gists/show.mako')
return self._get_template_context(c)
@LoginRequired() @LoginRequired()
@NotAnonymous() @NotAnonymous()
@auth.CSRFRequired() @view_config(
def edit(self, gist_id): route_name='gist_edit', request_method='GET',
self.__load_defaults() renderer='rhodecode:templates/admin/gists/edit.mako')
self._add_gist_to_context(gist_id) def gist_edit(self):
_ = self.request.translate
gist_id = self.request.matchdict['gist_id']
c = self.load_default_context()
c.gist = self._get_gist(gist_id)
owner = c.gist.gist_owner == c.rhodecode_user.user_id owner = c.gist.gist_owner == self._rhodecode_user.user_id
if not (h.HasPermissionAny('hg.admin')() or owner): if not (h.HasPermissionAny('hg.admin')() or owner):
raise HTTPForbidden() raise HTTPNotFound()
data = peppercorn.parse(request.POST.items()) try:
c.file_last_commit, c.files = GistModel().get_gist_files(gist_id)
except VCSError:
log.exception("Exception in gist edit")
raise HTTPNotFound()
if c.gist.gist_expires == -1:
expiry = _('never')
else:
# this cannot use timeago, since it's used in select2 as a value
expiry = h.age(h.time_to_datetime(c.gist.gist_expires))
c.lifetime_values.append(
(0, _('%(expiry)s - current value') % {'expiry': _(expiry)})
)
return self._get_template_context(c)
@LoginRequired()
@NotAnonymous()
@CSRFRequired()
@view_config(
route_name='gist_update', request_method='POST',
renderer='rhodecode:templates/admin/gists/edit.mako')
def gist_update(self):
_ = self.request.translate
gist_id = self.request.matchdict['gist_id']
c = self.load_default_context()
c.gist = self._get_gist(gist_id)
owner = c.gist.gist_owner == self._rhodecode_user.user_id
if not (h.HasPermissionAny('hg.admin')() or owner):
raise HTTPNotFound()
data = peppercorn.parse(self.request.POST.items())
schema = gist_schema.GistSchema() schema = gist_schema.GistSchema()
schema = schema.bind( schema = schema.bind(
@ -318,48 +386,23 @@ class GistsController(BaseController):
h.flash(_('Error occurred during update of gist %s') % gist_id, h.flash(_('Error occurred during update of gist %s') % gist_id,
category='error') category='error')
return redirect(url('gist', gist_id=gist_id)) raise HTTPFound(h.route_url('gist_show', gist_id=gist_id))
@LoginRequired() @LoginRequired()
@NotAnonymous() @NotAnonymous()
def edit_form(self, gist_id): @view_config(
translate = _ = c.pyramid_request.translate route_name='gist_edit_check_revision', request_method='GET',
renderer='json_ext')
def gist_edit_check_revision(self):
_ = self.request.translate
gist_id = self.request.matchdict['gist_id']
c = self.load_default_context()
c.gist = self._get_gist(gist_id)
"""GET /admin/gists/gist_id/edit: Form to edit an existing item"""
# url('edit_gist', gist_id=ID)
self._add_gist_to_context(gist_id)
owner = c.gist.gist_owner == c.rhodecode_user.user_id
if not (h.HasPermissionAny('hg.admin')() or owner):
raise HTTPForbidden()
try:
c.file_last_commit, c.files = GistModel().get_gist_files(gist_id)
except VCSError:
log.exception("Exception in gist edit")
raise HTTPNotFound()
if c.gist.gist_expires == -1:
expiry = _('never')
else:
# this cannot use timeago, since it's used in select2 as a value
expiry = h.age(h.time_to_datetime(c.gist.gist_expires))
expiry = translate(expiry)
self.__load_defaults(
extra_values=(0, _('%(expiry)s - current value') % {'expiry': expiry}))
return render('admin/gists/edit.mako')
@LoginRequired()
@NotAnonymous()
@jsonify
def check_revision(self, gist_id):
c.gist = Gist.get_or_404(gist_id)
last_rev = c.gist.scm_instance().get_commit() last_rev = c.gist.scm_instance().get_commit()
success = True success = True
revision = request.GET.get('revision') revision = self.request.GET.get('revision')
##TODO: maybe move this to model ?
if revision != last_rev.raw_id: if revision != last_rev.raw_id:
log.error('Last revision %s is different then submitted %s' log.error('Last revision %s is different then submitted %s'
% (revision, last_rev)) % (revision, last_rev))

View file

@ -303,6 +303,7 @@ def includeme(config):
config.include('rhodecode.apps.user_profile') config.include('rhodecode.apps.user_profile')
config.include('rhodecode.apps.my_account') config.include('rhodecode.apps.my_account')
config.include('rhodecode.apps.svn_support') config.include('rhodecode.apps.svn_support')
config.include('rhodecode.apps.gist')
config.include('rhodecode.tweens') config.include('rhodecode.tweens')
config.include('rhodecode.api') config.include('rhodecode.api')

View file

@ -489,39 +489,6 @@ def make_map(config):
m.connect('notification', '/notifications/{notification_id}', m.connect('notification', '/notifications/{notification_id}',
action='show', conditions={'method': ['GET']}) action='show', conditions={'method': ['GET']})
# ADMIN GIST
with rmap.submapper(path_prefix=ADMIN_PREFIX,
controller='admin/gists') as m:
m.connect('gists', '/gists',
action='create', conditions={'method': ['POST']})
m.connect('gists', '/gists', jsroute=True,
action='index', conditions={'method': ['GET']})
m.connect('new_gist', '/gists/new', jsroute=True,
action='new', conditions={'method': ['GET']})
m.connect('/gists/{gist_id}',
action='delete', conditions={'method': ['DELETE']})
m.connect('edit_gist', '/gists/{gist_id}/edit',
action='edit_form', conditions={'method': ['GET']})
m.connect('edit_gist', '/gists/{gist_id}/edit',
action='edit', conditions={'method': ['POST']})
m.connect(
'edit_gist_check_revision', '/gists/{gist_id}/edit/check_revision',
action='check_revision', conditions={'method': ['GET']})
m.connect('gist', '/gists/{gist_id}',
action='show', conditions={'method': ['GET']})
m.connect('gist_rev', '/gists/{gist_id}/{revision}',
revision='tip',
action='show', conditions={'method': ['GET']})
m.connect('formatted_gist', '/gists/{gist_id}/{revision}/{format}',
revision='tip',
action='show', conditions={'method': ['GET']})
m.connect('formatted_gist_file', '/gists/{gist_id}/{revision}/{format}/{f_path}',
revision='tip',
action='show', conditions={'method': ['GET']},
requirements=URL_NAME_REQUIREMENTS)
# USER JOURNAL # USER JOURNAL
rmap.connect('journal', '%s/journal' % (ADMIN_PREFIX,), rmap.connect('journal', '%s/journal' % (ADMIN_PREFIX,),
controller='journal', action='index') controller='journal', action='index')

View file

@ -67,15 +67,25 @@ class CSRFDetector(object):
'/error/document', '/error/document',
)) ))
_SKIP_PATTERN = frozenset((
'/_admin/gists/',
))
def __init__(self, app): def __init__(self, app):
self._app = app self._app = app
def __call__(self, environ, start_response): def __call__(self, environ, start_response):
if environ['REQUEST_METHOD'].upper() not in ('GET', 'POST'): if environ['REQUEST_METHOD'].upper() not in ('GET', 'POST'):
raise Exception(self._PUT_DELETE_MESSAGE) raise Exception(self._PUT_DELETE_MESSAGE)
token_expected = environ['PATH_INFO'] not in self._PATHS_WITHOUT_TOKEN
allowed = True
for pattern in self._SKIP_PATTERN:
if environ['PATH_INFO'].startswith(pattern):
allowed = False
break
if (environ['REQUEST_METHOD'] == 'POST' and if (environ['REQUEST_METHOD'] == 'POST' and
environ['PATH_INFO'] not in self._PATHS_WITHOUT_TOKEN and token_expected and allowed and
routes.middleware.is_form_post(environ)): routes.middleware.is_form_post(environ)):
body = environ['wsgi.input'] body = environ['wsgi.input']
if body.seekable(): if body.seekable():

View file

@ -3788,14 +3788,8 @@ class Gist(Base, BaseModel):
return cls.query().filter(cls.gist_access_id == gist_access_id).scalar() return cls.query().filter(cls.gist_access_id == gist_access_id).scalar()
def gist_url(self): def gist_url(self):
import rhodecode from rhodecode.model.gist import GistModel
from pylons import url return GistModel().get_url(self)
alias_url = rhodecode.CONFIG.get('gist_alias_url')
if alias_url:
return alias_url.replace('{gistid}', self.gist_access_id)
return url('gist', gist_id=self.gist_access_id, qualified=True)
@classmethod @classmethod
def base_path(cls): def base_path(cls):

View file

@ -28,6 +28,8 @@ import logging
import traceback import traceback
import shutil import shutil
from pyramid.threadlocal import get_current_request
from rhodecode.lib.utils2 import ( from rhodecode.lib.utils2 import (
safe_unicode, unique_id, safe_int, time_to_datetime, AttributeDict) safe_unicode, unique_id, safe_int, time_to_datetime, AttributeDict)
from rhodecode.lib.ext_json import json from rhodecode.lib.ext_json import json
@ -233,3 +235,16 @@ class GistModel(BaseModel):
) )
return gist return gist
def get_url(self, gist, request=None):
import rhodecode
if not request:
request = get_current_request()
alias_url = rhodecode.CONFIG.get('gist_alias_url')
if alias_url:
return alias_url.replace('{gistid}', gist.gist_access_id)
return request.route_url('gist_show', gist_id=gist.gist_access_id)

View file

@ -46,13 +46,13 @@ function setRCMouseBindings(repoName, repoLandingRev) {
window.location = pyroutes.url('home'); window.location = pyroutes.url('home');
}); });
Mousetrap.bind(['g g'], function(e) { Mousetrap.bind(['g g'], function(e) {
window.location = pyroutes.url('gists', {'private': 1}); window.location = pyroutes.url('gists_show', {'private': 1});
}); });
Mousetrap.bind(['g G'], function(e) { Mousetrap.bind(['g G'], function(e) {
window.location = pyroutes.url('gists', {'public': 1}); window.location = pyroutes.url('gists_show', {'public': 1});
}); });
Mousetrap.bind(['n g'], function(e) { Mousetrap.bind(['n g'], function(e) {
window.location = pyroutes.url('new_gist'); window.location = pyroutes.url('gists_new');
}); });
Mousetrap.bind(['n r'], function(e) { Mousetrap.bind(['n r'], function(e) {
window.location = pyroutes.url('new_repo'); window.location = pyroutes.url('new_repo');

View file

@ -15,8 +15,6 @@ function registerRCRoutes() {
pyroutes.register('new_repo', '/_admin/create_repository', []); pyroutes.register('new_repo', '/_admin/create_repository', []);
pyroutes.register('edit_user', '/_admin/users/%(user_id)s/edit', ['user_id']); pyroutes.register('edit_user', '/_admin/users/%(user_id)s/edit', ['user_id']);
pyroutes.register('edit_user_group_members', '/_admin/user_groups/%(user_group_id)s/edit/members', ['user_group_id']); pyroutes.register('edit_user_group_members', '/_admin/user_groups/%(user_group_id)s/edit/members', ['user_group_id']);
pyroutes.register('gists', '/_admin/gists', []);
pyroutes.register('new_gist', '/_admin/gists/new', []);
pyroutes.register('toggle_following', '/_admin/toggle_following', []); pyroutes.register('toggle_following', '/_admin/toggle_following', []);
pyroutes.register('changeset_home', '/%(repo_name)s/changeset/%(revision)s', ['repo_name', 'revision']); pyroutes.register('changeset_home', '/%(repo_name)s/changeset/%(revision)s', ['repo_name', 'revision']);
pyroutes.register('changeset_comment', '/%(repo_name)s/changeset/%(revision)s/comment', ['repo_name', 'revision']); pyroutes.register('changeset_comment', '/%(repo_name)s/changeset/%(revision)s/comment', ['repo_name', 'revision']);
@ -152,5 +150,16 @@ function registerRCRoutes() {
pyroutes.register('my_account_notifications', '/_admin/my_account/notifications', []); pyroutes.register('my_account_notifications', '/_admin/my_account/notifications', []);
pyroutes.register('my_account_notifications_toggle_visibility', '/_admin/my_account/toggle_visibility', []); pyroutes.register('my_account_notifications_toggle_visibility', '/_admin/my_account/toggle_visibility', []);
pyroutes.register('my_account_notifications_test_channelstream', '/_admin/my_account/test_channelstream', []); pyroutes.register('my_account_notifications_test_channelstream', '/_admin/my_account/test_channelstream', []);
pyroutes.register('gists_show', '/_admin/gists', []);
pyroutes.register('gists_new', '/_admin/gists/new', []);
pyroutes.register('gists_create', '/_admin/gists/create', []);
pyroutes.register('gist_show', '/_admin/gists/%(gist_id)s', ['gist_id']);
pyroutes.register('gist_delete', '/_admin/gists/%(gist_id)s/delete', ['gist_id']);
pyroutes.register('gist_edit', '/_admin/gists/%(gist_id)s/edit', ['gist_id']);
pyroutes.register('gist_edit_check_revision', '/_admin/gists/%(gist_id)s/edit/check_revision', ['gist_id']);
pyroutes.register('gist_update', '/_admin/gists/%(gist_id)s/update', ['gist_id']);
pyroutes.register('gist_show_rev', '/_admin/gists/%(gist_id)s/%(revision)s', ['gist_id', 'revision']);
pyroutes.register('gist_show_formatted', '/_admin/gists/%(gist_id)s/%(revision)s/%(format)s', ['gist_id', 'revision', 'format']);
pyroutes.register('gist_show_formatted_path', '/_admin/gists/%(gist_id)s/%(revision)s/%(format)s/%(f_path)s', ['gist_id', 'revision', 'format', 'f_path']);
pyroutes.register('apiv2', '/_admin/api', []); pyroutes.register('apiv2', '/_admin/api', []);
} }

View file

@ -26,7 +26,7 @@
<div class="table"> <div class="table">
<div id="files_data"> <div id="files_data">
${h.secure_form(h.url('edit_gist', gist_id=c.gist.gist_access_id), method='post', id='eform')} ${h.secure_form(h.route_path('gist_update', gist_id=c.gist.gist_access_id), id='eform', method='POST')}
<div> <div>
<input type="hidden" value="${c.file_last_commit.raw_id}" name="parent_hash"> <input type="hidden" value="${c.file_last_commit.raw_id}" name="parent_hash">
<textarea id="description" name="description" <textarea id="description" name="description"
@ -99,7 +99,7 @@
<div class="pull-right"> <div class="pull-right">
${h.submit('update',_('Update Gist'),class_="btn btn-success")} ${h.submit('update',_('Update Gist'),class_="btn btn-success")}
<a class="btn" href="${h.url('gist', gist_id=c.gist.gist_access_id)}">${_('Cancel')}</a> <a class="btn" href="${h.route_path('gist_show', gist_id=c.gist.gist_access_id)}">${_('Cancel')}</a>
</div> </div>
${h.end_form()} ${h.end_form()}
</div> </div>
@ -109,9 +109,12 @@
<script> <script>
$('#update').on('click', function(e){ $('#update').on('click', function(e){
e.preventDefault(); e.preventDefault();
$(this).val('Updating...');
$(this).attr('disabled', 'disabled');
// check for newer version. // check for newer version.
$.ajax({ $.ajax({
url: "${h.url('edit_gist_check_revision', gist_id=c.gist.gist_access_id)}", url: "${h.route_path('gist_edit_check_revision', gist_id=c.gist.gist_access_id)}",
data: { data: {
'revision': '${c.file_last_commit.raw_id}' 'revision': '${c.file_last_commit.raw_id}'
}, },
@ -120,7 +123,7 @@
success: function(data) { success: function(data) {
if(data.success === false){ if(data.success === false){
message = '${h.literal(_('Gist was updated since you started editing. Copy your changes and click %(here)s to reload the new version.') message = '${h.literal(_('Gist was updated since you started editing. Copy your changes and click %(here)s to reload the new version.')
% {'here': h.link_to('here',h.url('edit_gist', gist_id=c.gist.gist_access_id))})}' % {'here': h.link_to('here', h.route_path('gist_edit', gist_id=c.gist.gist_access_id))})}'
alertMessage = [{"message": { alertMessage = [{"message": {
"message": message, "force": "true", "level": "warning"}}]; "message": message, "force": "true", "level": "warning"}}];
$.Topic('/notifications').publish(alertMessage[0]); $.Topic('/notifications').publish(alertMessage[0]);

View file

@ -41,7 +41,7 @@
%if c.rhodecode_user.username != h.DEFAULT_USER: %if c.rhodecode_user.username != h.DEFAULT_USER:
<ul class="links block-right"> <ul class="links block-right">
<li> <li>
<a href="${h.url('new_gist')}" class="btn btn-primary">${_(u'Create New Gist')}</a> <a href="${h.route_path('gists_new')}" class="btn btn-primary">${_(u'Create New Gist')}</a>
</li> </li>
</ul> </ul>
%endif %endif
@ -53,13 +53,13 @@
<div class="sidebar"> <div class="sidebar">
<ul class="nav nav-pills nav-stacked"> <ul class="nav nav-pills nav-stacked">
% if h.HasPermissionAll('hg.admin')('access admin gists page'): % if h.HasPermissionAll('hg.admin')('access admin gists page'):
<li class="${'active' if c.active=='all' else ''}"><a href="${h.url('gists', all=1)}">${_('All gists')}</a></li> <li class="${'active' if c.active=='all' else ''}"><a href="${h.route_path('gists_show', _query={'all': 1})}">${_('All gists')}</a></li>
%endif %endif
<li class="${'active' if c.active=='public' else ''}"><a href="${h.url('gists')}">${_('All public')}</a></li> <li class="${'active' if c.active=='public' else ''}"><a href="${h.route_path('gists_show')}">${_('All public')}</a></li>
%if c.rhodecode_user.username != h.DEFAULT_USER: %if c.rhodecode_user.username != h.DEFAULT_USER:
<li class="${'active' if c.active=='my_all' else ''}"><a href="${h.url('gists', public=1, private=1)}">${_('My gists')}</a></li> <li class="${'active' if c.active=='my_all' else ''}"><a href="${h.route_path('gists_show', _query={'public':1, 'private': 1})}">${_('My gists')}</a></li>
<li class="${'active' if c.active=='my_private' else ''}"><a href="${h.url('gists', private=1)}">${_('My private')}</a></li> <li class="${'active' if c.active=='my_private' else ''}"><a href="${h.route_path('gists_show', _query={'private': 1})}">${_('My private')}</a></li>
<li class="${'active' if c.active=='my_public' else ''}"><a href="${h.url('gists', public=1)}">${_('My public')}</a></li> <li class="${'active' if c.active=='my_public' else ''}"><a href="${h.route_path('gists_show', _query={'public': 1})}">${_('My public')}</a></li>
%endif %endif
</ul> </ul>
</div> </div>

View file

@ -25,7 +25,7 @@
<div class="table"> <div class="table">
<div id="files_data"> <div id="files_data">
${h.secure_form(h.url('gists'), method='post',id='eform')} ${h.secure_form(h.route_path('gists_create'), id='eform', method='POST')}
<div> <div>
<textarea id="description" name="description" placeholder="${_('Gist description ...')}"></textarea> <textarea id="description" name="description" placeholder="${_('Gist description ...')}"></textarea>

View file

@ -18,7 +18,6 @@
<%def name="breadcrumbs_links()"> <%def name="breadcrumbs_links()">
${_('Gist')} &middot; ${c.gist.gist_access_id} ${_('Gist')} &middot; ${c.gist.gist_access_id}
/ ${_('URL')}: ${c.gist.gist_url()}
</%def> </%def>
<%def name="menu_bar_nav()"> <%def name="menu_bar_nav()">
@ -33,11 +32,12 @@
%if c.rhodecode_user.username != h.DEFAULT_USER: %if c.rhodecode_user.username != h.DEFAULT_USER:
<ul class="links"> <ul class="links">
<li> <li>
<a href="${h.url('new_gist')}" class="btn btn-primary">${_(u'Create New Gist')}</a> <a href="${h.route_path('gists_new')}" class="btn btn-primary">${_(u'Create New Gist')}</a>
</li> </li>
</ul> </ul>
%endif %endif
</div> </div>
<code>${c.gist.gist_url()}</code>
<div class="table"> <div class="table">
<div id="files_data"> <div id="files_data">
<div id="codeblock" class="codeblock"> <div id="codeblock" class="codeblock">
@ -45,7 +45,7 @@
<div class="stats"> <div class="stats">
%if h.HasPermissionAny('hg.admin')() or c.gist.gist_owner == c.rhodecode_user.user_id: %if h.HasPermissionAny('hg.admin')() or c.gist.gist_owner == c.rhodecode_user.user_id:
<div class="remove_gist"> <div class="remove_gist">
${h.secure_form(url('gist', gist_id=c.gist.gist_access_id),method='delete')} ${h.secure_form(h.route_path('gist_delete', gist_id=c.gist.gist_access_id), method='POST')}
${h.submit('remove_gist', _('Delete'),class_="btn btn-mini btn-danger",onclick="return confirm('"+_('Confirm to delete this Gist')+"');")} ${h.submit('remove_gist', _('Delete'),class_="btn btn-mini btn-danger",onclick="return confirm('"+_('Confirm to delete this Gist')+"');")}
${h.end_form()} ${h.end_form()}
</div> </div>
@ -53,9 +53,9 @@
<div class="buttons"> <div class="buttons">
## only owner should see that ## only owner should see that
%if h.HasPermissionAny('hg.admin')() or c.gist.gist_owner == c.rhodecode_user.user_id: %if h.HasPermissionAny('hg.admin')() or c.gist.gist_owner == c.rhodecode_user.user_id:
${h.link_to(_('Edit'),h.url('edit_gist', gist_id=c.gist.gist_access_id),class_="btn btn-mini")} ${h.link_to(_('Edit'), h.route_path('gist_edit', gist_id=c.gist.gist_access_id), class_="btn btn-mini")}
%endif %endif
${h.link_to(_('Show as Raw'),h.url('formatted_gist', gist_id=c.gist.gist_access_id, format='raw'),class_="btn btn-mini")} ${h.link_to(_('Show as Raw'), h.route_path('gist_show_formatted', gist_id=c.gist.gist_access_id, revision='tip', format='raw'), class_="btn btn-mini")}
</div> </div>
<div class="left" > <div class="left" >
%if c.gist.gist_type != 'public': %if c.gist.gist_type != 'public':
@ -78,19 +78,21 @@
</div> </div>
</div> </div>
<div class="commit">${h.urlify_commit_message(c.file_last_commit.message,c.repo_name)}</div> <div class="commit">${h.urlify_commit_message(c.file_last_commit.message, None)}</div>
</div> </div>
## iterate over the files ## iterate over the files
% for file in c.files: % for file in c.files:
<% renderer = c.render and h.renderer_from_filename(file.path, exclude=['.txt', '.TXT'])%> <% renderer = c.render and h.renderer_from_filename(file.path, exclude=['.txt', '.TXT'])%>
<!-- <div id="${h.FID('G', file.path)}" class="stats" > <!--
<div id="${h.FID('G', file.path)}" class="stats" >
<a href="${c.gist.gist_url()}">¶</a> <a href="${c.gist.gist_url()}">¶</a>
<b >${file.path}</b> <b >${file.path}</b>
<div> <div>
${h.link_to(_('Show as raw'),h.url('formatted_gist_file', gist_id=c.gist.gist_access_id, format='raw', revision=file.commit.raw_id, f_path=file.path),class_="btn btn-mini")} ${h.link_to(_('Show as raw'), h.route_path('gist_show_formatted_path', gist_id=c.gist.gist_access_id, revision=file.commit.raw_id, format='raw', f_path=file.path), class_="btn btn-mini")}
</div> </div>
</div> --> </div>
-->
<div class="code-body textarea text-area editor"> <div class="code-body textarea text-area editor">
%if renderer: %if renderer:
${h.render(file.content, renderer=renderer)} ${h.render(file.content, renderer=renderer)}

View file

@ -397,7 +397,7 @@
</li> </li>
%endif %endif
<li class="${is_active('gists')}"> <li class="${is_active('gists')}">
<a class="menulink childs" title="${_('Show Gists')}" href="${h.url('gists')}"> <a class="menulink childs" title="${_('Show Gists')}" href="${h.route_path('gists_show')}">
<div class="menulabel">${_('Gists')}</div> <div class="menulabel">${_('Gists')}</div>
</a> </a>
</li> </li>

View file

@ -241,7 +241,7 @@
<%def name="gist_access_id(gist_access_id, full_contact)"> <%def name="gist_access_id(gist_access_id, full_contact)">
<div> <div>
<b> <b>
<a href="${h.url('gist',gist_id=gist_access_id)}">gist: ${gist_access_id}</a> <a href="${h.route_path('gist_show', gist_id=gist_access_id)}">gist: ${gist_access_id}</a>
</b> </b>
</div> </div>
</%def> </%def>