diff --git a/pylons_app/templates/files/files_browser.html b/pylons_app/templates/files/files_browser.html
--- a/pylons_app/templates/files/files_browser.html
+++ b/pylons_app/templates/files/files_browser.html
@@ -23,31 +23,38 @@
+ %endif
+
%for cnt,node in enumerate(c.files_list,1):
${h.link_to(node.name,h.url('files_home',repo_name=c.repo_name,revision=c.cur_rev,f_path=node.path),class_=file_class(node))}
- %if node.is_file():
- ${h.format_byte_size(node.size,binary=True)}
- %endif
+ ${h.format_byte_size(node.size,binary=True)}
+
+
+ %if node.is_file():
+ ${node.mimetype}
+ %endif
%if node.is_file():
diff --git a/pylons_app/templates/files/files_source.html b/pylons_app/templates/files/files_source.html
--- a/pylons_app/templates/files/files_source.html
+++ b/pylons_app/templates/files/files_source.html
@@ -6,11 +6,15 @@
${_('Size')}
${h.format_byte_size(c.files_list.size,binary=True)}
+ ${_('Mimetype')}
+ ${c.files_list.mimetype}
${_('Options')}
${h.link_to(_('show annotation'),
- h.url('files_annotate_home',repo_name=c.repo_name,revision=c.cur_rev,f_path=c.f_path))}
+ h.url('files_annotate_home',repo_name=c.repo_name,revision=c.cur_rev,f_path=c.f_path))}
+ / ${h.link_to(_('show as raw'),
+ h.url('files_raw_home',repo_name=c.repo_name,revision=c.cur_rev,f_path=c.f_path))}
/ ${h.link_to(_('download as raw'),
- h.url('files_raw_home',repo_name=c.repo_name,revision=c.cur_rev,f_path=c.f_path))}
+ h.url('files_rawfile_home',repo_name=c.repo_name,revision=c.cur_rev,f_path=c.f_path))}
${_('History')}
@@ -32,7 +36,12 @@
"${c.files_list.last_changeset.message}"
- ${h.pygmentize(c.files_list,linenos=True,anchorlinenos=True,lineanchors='S',cssclass="code-highlight")}
+ % if c.files_list.size < c.file_size_limit:
+ ${h.pygmentize(c.files_list,linenos=True,anchorlinenos=True,lineanchors='S',cssclass="code-highlight")}
+ %else:
+ ${_('File is to big to display')} ${h.link_to(_('show as raw'),
+ h.url('files_raw_home',repo_name=c.repo_name,revision=c.cur_rev,f_path=c.f_path))}
+ %endif
diff --git a/pylons_app/templates/login.html b/pylons_app/templates/login.html
--- a/pylons_app/templates/login.html
+++ b/pylons_app/templates/login.html
@@ -60,7 +60,7 @@
- ${h.link_to(_('Forgot your password ?'),h.url('#'))}
+ ${h.link_to(_('Forgot your password ?'),h.url('reset_password'))}
%if h.HasPermissionAny('hg.admin', 'hg.register.auto_activate', 'hg.register.manual_activate')():
/
${h.link_to(_("Don't have an account ?"),h.url('register'))}
diff --git a/pylons_app/templates/password_reset.html b/pylons_app/templates/password_reset.html
new file mode 100644
--- /dev/null
+++ b/pylons_app/templates/password_reset.html
@@ -0,0 +1,54 @@
+## -*- coding: utf-8 -*-
+
+
+
+
${_('Reset You password to hg-app')}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
${_('Reset You password to hg-app')}
+
+
+
+
+ ${h.form(url('password_reset'))}
+
+ ${h.end_form()}
+
+
+
+
+
diff --git a/pylons_app/templates/search/search.html b/pylons_app/templates/search/search.html
--- a/pylons_app/templates/search/search.html
+++ b/pylons_app/templates/search/search.html
@@ -46,7 +46,7 @@
h.url('files_home',repo_name=sr['repository'],revision='tip',f_path=sr['f_path']))}
-
${h.literal(sr['content_short'])}
+
${h.literal(sr['content_short_hl'])}
@@ -59,11 +59,13 @@
%endif
- %endif
+ %endif
%endfor
-
-
-
+ %if c.cur_query:
+
+ %endif
%def>
diff --git a/pylons_app/templates/shortlog/shortlog_data.html b/pylons_app/templates/shortlog/shortlog_data.html
--- a/pylons_app/templates/shortlog/shortlog_data.html
+++ b/pylons_app/templates/shortlog/shortlog_data.html
@@ -13,7 +13,7 @@
- ${h.age(cs._ctx.date())}
+ ${h.age(cs._ctx.date())} - ${h.rfc822date_notz(cs._ctx.date())}
${h.person(cs.author)}
r${cs.revision}:${cs.raw_id}
diff --git a/pylons_app/templates/summary/summary.html b/pylons_app/templates/summary/summary.html
--- a/pylons_app/templates/summary/summary.html
+++ b/pylons_app/templates/summary/summary.html
@@ -76,7 +76,9 @@ E.onDOMReady(function(e){
${_('Last change')}:
- ${h.age(c.repo_info.last_change)} - ${h.rfc822date(c.repo_info.last_change)}
+ ${h.age(c.repo_info.last_change)} - ${h.rfc822date(c.repo_info.last_change)}
+ ${_('by')} ${h.get_changeset_safe(c.repo_info,'tip').author}
+
@@ -121,151 +123,356 @@ E.onDOMReady(function(e){
-
${_('Last month commit activity')}
+ ${_('Commit activity by day / author')}
-
+
+
+
+
+ YAHOO.util.Event.on(choiceContainer.getElementsByTagName("input"), "click", plotchoiced, [data, initial_ranges]);
+ }
+ SummaryPlot(${c.ts_min},${c.ts_max},${c.commit_data|n},${c.overview_data|n});
+
diff --git a/pylons_app/tests/__init__.py b/pylons_app/tests/__init__.py
--- a/pylons_app/tests/__init__.py
+++ b/pylons_app/tests/__init__.py
@@ -16,12 +16,18 @@ from routes.util import URLGenerator
from webtest import TestApp
import os
from pylons_app.model import meta
+import logging
+
+
+log = logging.getLogger(__name__)
+
import pylons.test
__all__ = ['environ', 'url', 'TestController']
# Invoke websetup with the current config file
-SetupCommand('setup-app').run([pylons.test.pylonsapp.config['__file__']])
+#SetupCommand('setup-app').run([config_file])
+
environ = {}
@@ -33,13 +39,13 @@ class TestController(TestCase):
self.app = TestApp(wsgiapp)
url._push_object(URLGenerator(config['routes.map'], environ))
self.sa = meta.Session
+
TestCase.__init__(self, *args, **kwargs)
-
- def log_user(self):
+ def log_user(self, username='test_admin', password='test'):
response = self.app.post(url(controller='login', action='index'),
- {'username':'test_admin',
- 'password':'test'})
+ {'username':username,
+ 'password':password})
assert response.status == '302 Found', 'Wrong response code from login got %s' % response.status
assert response.session['hg_app_user'].username == 'test_admin', 'wrong logged in user'
- return response.follow()
\ No newline at end of file
+ return response.follow()
diff --git a/pylons_app/tests/functional/test_admin.py b/pylons_app/tests/functional/test_admin.py
--- a/pylons_app/tests/functional/test_admin.py
+++ b/pylons_app/tests/functional/test_admin.py
@@ -3,5 +3,7 @@ from pylons_app.tests import *
class TestAdminController(TestController):
def test_index(self):
+ self.log_user()
response = self.app.get(url(controller='admin/admin', action='index'))
+ assert 'Admin dashboard - journal' in response.body,'No proper title in dashboard'
# Test response...
diff --git a/pylons_app/tests/functional/test_admin_settings.py b/pylons_app/tests/functional/test_admin_settings.py
--- a/pylons_app/tests/functional/test_admin_settings.py
+++ b/pylons_app/tests/functional/test_admin_settings.py
@@ -1,4 +1,5 @@
from pylons_app.tests import *
+from pylons_app.model.db import User
class TestSettingsController(TestController):
@@ -41,3 +42,75 @@ class TestSettingsController(TestControl
def test_edit_as_xml(self):
response = self.app.get(url('formatted_admin_edit_setting', setting_id=1, format='xml'))
+
+ def test_my_account(self):
+ self.log_user()
+ response = self.app.get(url('admin_settings_my_account'))
+ print response
+ assert 'value="test_admin' in response.body
+
+
+
+ def test_my_account_update(self):
+ self.log_user()
+ new_email = 'new@mail.pl'
+ response = self.app.post(url('admin_settings_my_account_update'), params=dict(
+ _method='put',
+ username='test_admin',
+ new_password='test',
+ password='',
+ name='NewName',
+ lastname='NewLastname',
+ email=new_email,))
+ response.follow()
+ print response
+
+ print 'x' * 100
+ print response.session
+ assert 'Your account was updated succesfully' in response.session['flash'][0][1], 'no flash message about success of change'
+ user = self.sa.query(User).filter(User.username == 'test_admin').one()
+ assert user.email == new_email , 'incorrect user email after update got %s vs %s' % (user.email, new_email)
+
+ def test_my_account_update_own_email_ok(self):
+ self.log_user()
+
+ new_email = 'new@mail.pl'
+ response = self.app.post(url('admin_settings_my_account_update'), params=dict(
+ _method='put',
+ username='test_admin',
+ new_password='test',
+ name='NewName',
+ lastname='NewLastname',
+ email=new_email,))
+ print response
+
+ def test_my_account_update_err_email_exists(self):
+ self.log_user()
+
+ new_email = 'test_regular@mail.com'#already exisitn email
+ response = self.app.post(url('admin_settings_my_account_update'), params=dict(
+ _method='put',
+ username='test_admin',
+ new_password='test',
+ name='NewName',
+ lastname='NewLastname',
+ email=new_email,))
+ print response
+
+ assert 'That e-mail address is already taken' in response.body, 'Missing error message about existing email'
+
+
+ def test_my_account_update_err(self):
+ self.log_user()
+
+ new_email = 'newmail.pl'
+ response = self.app.post(url('admin_settings_my_account_update'), params=dict(
+ _method='put',
+ username='test_regular2',
+ new_password='test',
+ name='NewName',
+ lastname='NewLastname',
+ email=new_email,))
+ print response
+ assert 'An email address must contain a single @' in response.body, 'Missing error message about wrong email'
+ assert 'This username already exists' in response.body, 'Missing error message about existing user'
diff --git a/pylons_app/tests/functional/test_branches.py b/pylons_app/tests/functional/test_branches.py
--- a/pylons_app/tests/functional/test_branches.py
+++ b/pylons_app/tests/functional/test_branches.py
@@ -3,5 +3,6 @@ from pylons_app.tests import *
class TestBranchesController(TestController):
def test_index(self):
+ self.log_user()
response = self.app.get(url(controller='branches', action='index',repo_name='vcs_test'))
# Test response...
diff --git a/pylons_app/tests/functional/test_changelog.py b/pylons_app/tests/functional/test_changelog.py
--- a/pylons_app/tests/functional/test_changelog.py
+++ b/pylons_app/tests/functional/test_changelog.py
@@ -3,5 +3,6 @@ from pylons_app.tests import *
class TestChangelogController(TestController):
def test_index(self):
+ self.log_user()
response = self.app.get(url(controller='changelog', action='index',repo_name='vcs_test'))
# Test response...
diff --git a/pylons_app/tests/functional/test_feed.py b/pylons_app/tests/functional/test_feed.py
--- a/pylons_app/tests/functional/test_feed.py
+++ b/pylons_app/tests/functional/test_feed.py
@@ -3,11 +3,13 @@ from pylons_app.tests import *
class TestFeedController(TestController):
def test_rss(self):
+ self.log_user()
response = self.app.get(url(controller='feed', action='rss',
repo_name='vcs_test'))
# Test response...
def test_atom(self):
+ self.log_user()
response = self.app.get(url(controller='feed', action='atom',
repo_name='vcs_test'))
# Test response...
\ No newline at end of file
diff --git a/pylons_app/tests/functional/test_files.py b/pylons_app/tests/functional/test_files.py
--- a/pylons_app/tests/functional/test_files.py
+++ b/pylons_app/tests/functional/test_files.py
@@ -3,6 +3,7 @@ from pylons_app.tests import *
class TestFilesController(TestController):
def test_index(self):
+ self.log_user()
response = self.app.get(url(controller='files', action='index',
repo_name='vcs_test',
revision='tip',
diff --git a/pylons_app/tests/functional/test_login.py b/pylons_app/tests/functional/test_login.py
--- a/pylons_app/tests/functional/test_login.py
+++ b/pylons_app/tests/functional/test_login.py
@@ -82,9 +82,9 @@ class TestLoginController(TestController
def test_register_ok(self):
- username = 'test_regular2'
+ username = 'test_regular4'
password = 'qweqwe'
- email = 'goodmail@mail.com'
+ email = 'marcin@test.com'
name = 'testname'
lastname = 'testlastname'
@@ -94,18 +94,46 @@ class TestLoginController(TestController
'email':email,
'name':name,
'lastname':lastname})
-
+ print response.body
assert response.status == '302 Found', 'Wrong response from register page got %s' % response.status
+ assert 'You have successfully registered into hg-app' in response.session['flash'][0], 'No flash message about user registration'
- ret = self.sa.query(User).filter(User.username == 'test_regular2').one()
+ ret = self.sa.query(User).filter(User.username == 'test_regular4').one()
assert ret.username == username , 'field mismatch %s %s' % (ret.username, username)
- assert check_password(password,ret.password) == True , 'password mismatch'
+ assert check_password(password, ret.password) == True , 'password mismatch'
assert ret.email == email , 'field mismatch %s %s' % (ret.email, email)
assert ret.name == name , 'field mismatch %s %s' % (ret.name, name)
assert ret.lastname == lastname , 'field mismatch %s %s' % (ret.lastname, lastname)
+ def test_forgot_password_wrong_mail(self):
+ response = self.app.post(url(controller='login', action='password_reset'),
+ {'email':'marcin@wrongmail.org', })
+
+ assert "That e-mail address doesn't exist" in response.body, 'Missing error message about wrong email'
+
+ def test_forgot_password(self):
+ response = self.app.get(url(controller='login', action='password_reset'))
+ assert response.status == '200 OK', 'Wrong response from login page got %s' % response.status
+
+ username = 'test_password_reset_1'
+ password = 'qweqwe'
+ email = 'marcin@python-works.com'
+ name = 'passwd'
+ lastname = 'reset'
+
+ response = self.app.post(url(controller='login', action='register'),
+ {'username':username,
+ 'password':password,
+ 'email':email,
+ 'name':name,
+ 'lastname':lastname})
+ #register new user for email test
+ response = self.app.post(url(controller='login', action='password_reset'),
+ {'email':email, })
+ print response.session['flash']
+ assert 'You have successfully registered into hg-app' in response.session['flash'][0], 'No flash message about user registration'
+ assert 'Your new password was sent' in response.session['flash'][1], 'No flash message about password reset'
-
diff --git a/pylons_app/tests/functional/test_search.py b/pylons_app/tests/functional/test_search.py
--- a/pylons_app/tests/functional/test_search.py
+++ b/pylons_app/tests/functional/test_search.py
@@ -9,7 +9,7 @@ class TestSearchController(TestControlle
self.log_user()
response = self.app.get(url(controller='search', action='index'))
print response.body
- assert 'class="small" id="q" name="q" type="text"' in response.body,'Search box content error'
+ assert 'class="small" id="q" name="q" type="text"' in response.body, 'Search box content error'
# Test response...
def test_empty_search(self):
@@ -18,12 +18,21 @@ class TestSearchController(TestControlle
raise SkipTest('skipped due to existing index')
else:
self.log_user()
- response = self.app.get(url(controller='search', action='index'),{'q':'vcs_test'})
- assert 'There is no index to search in. Please run whoosh indexer' in response.body,'No error message about empty index'
+ response = self.app.get(url(controller='search', action='index'), {'q':'vcs_test'})
+ assert 'There is no index to search in. Please run whoosh indexer' in response.body, 'No error message about empty index'
def test_normal_search(self):
self.log_user()
- response = self.app.get(url(controller='search', action='index'),{'q':'def+repo'})
+ response = self.app.get(url(controller='search', action='index'), {'q':'def repo'})
print response.body
- assert '9 results' in response.body,'no message about proper search results'
+ assert '10 results' in response.body, 'no message about proper search results'
+ assert 'Permission denied' not in response.body, 'Wrong permissions settings for that repo and user'
+
+ def test_repo_search(self):
+ self.log_user()
+ response = self.app.get(url(controller='search', action='index'), {'q':'repository:vcs_test def test'})
+ print response.body
+ assert '4 results' in response.body, 'no message about proper search results'
+ assert 'Permission denied' not in response.body, 'Wrong permissions settings for that repo and user'
+
diff --git a/pylons_app/tests/functional/test_settings.py b/pylons_app/tests/functional/test_settings.py
--- a/pylons_app/tests/functional/test_settings.py
+++ b/pylons_app/tests/functional/test_settings.py
@@ -3,6 +3,7 @@ from pylons_app.tests import *
class TestSettingsController(TestController):
def test_index(self):
+ self.log_user()
response = self.app.get(url(controller='settings', action='index',
repo_name='vcs_test'))
# Test response...
diff --git a/pylons_app/tests/functional/test_shortlog.py b/pylons_app/tests/functional/test_shortlog.py
--- a/pylons_app/tests/functional/test_shortlog.py
+++ b/pylons_app/tests/functional/test_shortlog.py
@@ -3,5 +3,6 @@ from pylons_app.tests import *
class TestShortlogController(TestController):
def test_index(self):
+ self.log_user()
response = self.app.get(url(controller='shortlog', action='index',repo_name='vcs_test'))
# Test response...
diff --git a/pylons_app/tests/functional/test_summary.py b/pylons_app/tests/functional/test_summary.py
--- a/pylons_app/tests/functional/test_summary.py
+++ b/pylons_app/tests/functional/test_summary.py
@@ -3,5 +3,6 @@ from pylons_app.tests import *
class TestSummaryController(TestController):
def test_index(self):
+ self.log_user()
response = self.app.get(url(controller='summary', action='index',repo_name='vcs_test'))
# Test response...
diff --git a/pylons_app/tests/functional/test_tags.py b/pylons_app/tests/functional/test_tags.py
--- a/pylons_app/tests/functional/test_tags.py
+++ b/pylons_app/tests/functional/test_tags.py
@@ -3,5 +3,6 @@ from pylons_app.tests import *
class TestTagsController(TestController):
def test_index(self):
+ self.log_user()
response = self.app.get(url(controller='tags', action='index',repo_name='vcs_test'))
# Test response...
diff --git a/pylons_app/websetup.py b/pylons_app/websetup.py
--- a/pylons_app/websetup.py
+++ b/pylons_app/websetup.py
@@ -1,40 +1,25 @@
"""Setup the pylons_app application"""
-from os.path import dirname as dn, join as jn
+from os.path import dirname as dn
from pylons_app.config.environment import load_environment
from pylons_app.lib.db_manage import DbManage
-import datetime
-from time import mktime
import logging
import os
import sys
-import tarfile
log = logging.getLogger(__name__)
ROOT = dn(dn(os.path.realpath(__file__)))
sys.path.append(ROOT)
+
def setup_app(command, conf, vars):
"""Place any commands to setup pylons_app here"""
log_sql = True
tests = False
-
- dbname = os.path.split(conf['sqlalchemy.db1.url'])[-1]
- filename = os.path.split(conf.filename)[-1]
+ REPO_TEST_PATH = None
- if filename == 'tests.ini':
- uniq_suffix = str(int(mktime(datetime.datetime.now().timetuple())))
- REPO_TEST_PATH = '/tmp/hg_app_test_%s' % uniq_suffix
-
- if not os.path.isdir(REPO_TEST_PATH):
- os.mkdir(REPO_TEST_PATH)
- cur_dir = dn(os.path.abspath(__file__))
- tar = tarfile.open(jn(cur_dir,'tests',"vcs_test.tar.gz"))
- tar.extractall(REPO_TEST_PATH)
- tar.close()
-
- tests = True
+ dbname = os.path.split(conf['sqlalchemy.db1.url'])[-1]
dbmanage = DbManage(log_sql, dbname, tests)
dbmanage.create_tables(override=True)
diff --git a/setup.cfg b/setup.cfg
--- a/setup.cfg
+++ b/setup.cfg
@@ -8,7 +8,7 @@ find_links = http://www.pylonshq.com/dow
[nosetests]
verbose=True
verbosity=2
-with-pylons=tests.ini
+with-pylons=test.ini
detailed-errors=1
# Babel configuration
diff --git a/setup.py b/setup.py
--- a/setup.py
+++ b/setup.py
@@ -7,7 +7,7 @@ except ImportError:
from setuptools import setup, find_packages
setup(
- name='HgApp-%s'%get_version(),
+ name='HgApp-%s' % get_version(),
version=get_version(),
description='Mercurial repository serving and browsing app',
keywords='mercurial web hgwebdir replacement serving hgweb',
@@ -20,12 +20,13 @@ setup(
"SQLAlchemy>=0.6",
"babel",
"Mako>=0.3.2",
- "vcs>=0.1.4",
+ "vcs>=0.1.5",
"pygments>=1.3.0",
"mercurial>=1.6",
"pysqlite",
- "whoosh==1.0.0b10",
+ "whoosh==1.0.0b17",
"py-bcrypt",
+ "celery",
],
setup_requires=["PasteScript>=1.6.3"],
packages=find_packages(exclude=['ez_setup']),
diff --git a/tests.ini b/test.ini
rename from tests.ini
rename to test.ini
--- a/tests.ini
+++ b/test.ini
@@ -1,28 +1,33 @@
################################################################################
################################################################################
-# pylons_app - Pylons environment configuration #
+# hg-app - Pylons environment configuration #
# #
# The %(here)s variable will be replaced with the parent directory of this file#
################################################################################
[DEFAULT]
debug = true
-############################################
-## Uncomment and replace with the address ##
-## which should receive any error reports ##
-############################################
+################################################################################
+## Uncomment and replace with the address which should receive ##
+## any error reports after application crash ##
+## Additionally those settings will be used by hg-app mailing system ##
+################################################################################
#email_to = admin@localhost
+#error_email_from = paste_error@localhost
+#app_email_from = hg-app-noreply@localhost
+#error_message =
+
#smtp_server = mail.server.com
-#error_email_from = paste_error@localhost
#smtp_username =
#smtp_password =
-#error_message = 'mercurial crash !'
+#smtp_port =
+#smtp_use_tls = false
[server:main]
##nr of threads to spawn
threadpool_workers = 5
-##max request before
+##max request before thread respawn
threadpool_max_requests = 2
##option to use threads of process
@@ -56,7 +61,7 @@ beaker.cache.super_short_term.expire=10
### BEAKER SESSION ####
####################################
## Type of storage used for the session, current types are
-## “dbm”, “file”, “memcached”, “database”, and “memory”.
+## "dbm", "file", "memcached", "database", and "memory".
## The storage uses the Container API
##that is also used by the cache system.
beaker.session.type = file