##// END OF EJS Templates
Merge with 6aa7db1c083a1384ebff5c2bb3c943a035bb310d - celery branch
marcink -
r499:ca41d544 merge rhodecode-0.0.0.8.3 default
parent child Browse files
Show More
@@ -0,0 +1,74 b''
1 # List of modules to import when celery starts.
2 import sys
3 import os
4 import ConfigParser
5 root = os.getcwd()
6
7 PYLONS_CONFIG_NAME = 'development.ini'
8
9 sys.path.append(root)
10 config = ConfigParser.ConfigParser({'here':root})
11 config.read('%s/%s' % (root, PYLONS_CONFIG_NAME))
12 PYLONS_CONFIG = config
13
14 CELERY_IMPORTS = ("pylons_app.lib.celerylib.tasks",)
15
16 ## Result store settings.
17 CELERY_RESULT_BACKEND = "database"
18 CELERY_RESULT_DBURI = dict(config.items('app:main'))['sqlalchemy.db1.url']
19 CELERY_RESULT_SERIALIZER = 'json'
20
21
22 BROKER_CONNECTION_MAX_RETRIES = 30
23
24 ## Broker settings.
25 BROKER_HOST = "localhost"
26 BROKER_PORT = 5672
27 BROKER_VHOST = "rabbitmqhost"
28 BROKER_USER = "rabbitmq"
29 BROKER_PASSWORD = "qweqwe"
30
31 ## Worker settings
32 ## If you're doing mostly I/O you can have more processes,
33 ## but if mostly spending CPU, try to keep it close to the
34 ## number of CPUs on your machine. If not set, the number of CPUs/cores
35 ## available will be used.
36 CELERYD_CONCURRENCY = 2
37 # CELERYD_LOG_FILE = "celeryd.log"
38 CELERYD_LOG_LEVEL = "DEBUG"
39 CELERYD_MAX_TASKS_PER_CHILD = 1
40
41 #Tasks will never be sent to the queue, but executed locally instead.
42 CELERY_ALWAYS_EAGER = False
43
44 #===============================================================================
45 # EMAIL SETTINGS
46 #===============================================================================
47 pylons_email_config = dict(config.items('DEFAULT'))
48
49 CELERY_SEND_TASK_ERROR_EMAILS = True
50
51 #List of (name, email_address) tuples for the admins that should receive error e-mails.
52 ADMINS = [('Administrator', pylons_email_config.get('email_to'))]
53
54 #The e-mail address this worker sends e-mails from. Default is "celery@localhost".
55 SERVER_EMAIL = pylons_email_config.get('error_email_from')
56
57 #The mail server to use. Default is "localhost".
58 MAIL_HOST = pylons_email_config.get('smtp_server')
59
60 #Username (if required) to log on to the mail server with.
61 MAIL_HOST_USER = pylons_email_config.get('smtp_username')
62
63 #Password (if required) to log on to the mail server with.
64 MAIL_HOST_PASSWORD = pylons_email_config.get('smtp_password')
65
66 MAIL_PORT = pylons_email_config.get('smtp_port')
67
68
69 #===============================================================================
70 # INSTRUCTIONS FOR RABBITMQ
71 #===============================================================================
72 # rabbitmqctl add_user rabbitmq qweqwe
73 # rabbitmqctl add_vhost rabbitmqhost
74 # rabbitmqctl set_permissions -p rabbitmqhost rabbitmq ".*" ".*" ".*"
@@ -0,0 +1,66 b''
1 from pylons_app.lib.pidlock import DaemonLock, LockHeld
2 from vcs.utils.lazy import LazyProperty
3 from decorator import decorator
4 import logging
5 import os
6 import sys
7 import traceback
8 from hashlib import md5
9 log = logging.getLogger(__name__)
10
11 class ResultWrapper(object):
12 def __init__(self, task):
13 self.task = task
14
15 @LazyProperty
16 def result(self):
17 return self.task
18
19 def run_task(task, *args, **kwargs):
20 try:
21 t = task.delay(*args, **kwargs)
22 log.info('running task %s', t.task_id)
23 return t
24 except Exception, e:
25 print e
26 if e.errno == 111:
27 log.debug('Unnable to connect. Sync execution')
28 else:
29 log.error(traceback.format_exc())
30 #pure sync version
31 return ResultWrapper(task(*args, **kwargs))
32
33
34 class LockTask(object):
35 """LockTask decorator"""
36
37 def __init__(self, func):
38 self.func = func
39
40 def __call__(self, func):
41 return decorator(self.__wrapper, func)
42
43 def __wrapper(self, func, *fargs, **fkwargs):
44 params = []
45 params.extend(fargs)
46 params.extend(fkwargs.values())
47 lockkey = 'task_%s' % \
48 md5(str(self.func) + '-' + '-'.join(map(str, params))).hexdigest()
49 log.info('running task with lockkey %s', lockkey)
50 try:
51 l = DaemonLock(lockkey)
52 return func(*fargs, **fkwargs)
53 l.release()
54 except LockHeld:
55 log.info('LockHeld')
56 return 'Task with key %s already running' % lockkey
57
58
59
60
61
62
63
64
65
66
@@ -0,0 +1,270 b''
1 from celery.decorators import task
2 from celery.task.sets import subtask
3 from celeryconfig import PYLONS_CONFIG as config
4 from pylons.i18n.translation import _
5 from pylons_app.lib.celerylib import run_task, LockTask
6 from pylons_app.lib.helpers import person
7 from pylons_app.lib.smtp_mailer import SmtpMailer
8 from pylons_app.lib.utils import OrderedDict
9 from operator import itemgetter
10 from vcs.backends.hg import MercurialRepository
11 from time import mktime
12 import traceback
13 import json
14
15 __all__ = ['whoosh_index', 'get_commits_stats',
16 'reset_user_password', 'send_email']
17
18 def get_session():
19 from sqlalchemy import engine_from_config
20 from sqlalchemy.orm import sessionmaker, scoped_session
21 engine = engine_from_config(dict(config.items('app:main')), 'sqlalchemy.db1.')
22 sa = scoped_session(sessionmaker(bind=engine))
23 return sa
24
25 def get_hg_settings():
26 from pylons_app.model.db import HgAppSettings
27 try:
28 sa = get_session()
29 ret = sa.query(HgAppSettings).all()
30 finally:
31 sa.remove()
32
33 if not ret:
34 raise Exception('Could not get application settings !')
35 settings = {}
36 for each in ret:
37 settings['hg_app_' + each.app_settings_name] = each.app_settings_value
38
39 return settings
40
41 def get_hg_ui_settings():
42 from pylons_app.model.db import HgAppUi
43 try:
44 sa = get_session()
45 ret = sa.query(HgAppUi).all()
46 finally:
47 sa.remove()
48
49 if not ret:
50 raise Exception('Could not get application ui settings !')
51 settings = {}
52 for each in ret:
53 k = each.ui_key
54 v = each.ui_value
55 if k == '/':
56 k = 'root_path'
57
58 if k.find('.') != -1:
59 k = k.replace('.', '_')
60
61 if each.ui_section == 'hooks':
62 v = each.ui_active
63
64 settings[each.ui_section + '_' + k] = v
65
66 return settings
67
68 @task
69 def whoosh_index(repo_location, full_index):
70 log = whoosh_index.get_logger()
71 from pylons_app.lib.pidlock import DaemonLock
72 from pylons_app.lib.indexers.daemon import WhooshIndexingDaemon, LockHeld
73 try:
74 l = DaemonLock()
75 WhooshIndexingDaemon(repo_location=repo_location)\
76 .run(full_index=full_index)
77 l.release()
78 return 'Done'
79 except LockHeld:
80 log.info('LockHeld')
81 return 'LockHeld'
82
83
84 @task
85 @LockTask('get_commits_stats')
86 def get_commits_stats(repo_name, ts_min_y, ts_max_y):
87 author_key_cleaner = lambda k: person(k).replace('"', "") #for js data compatibilty
88
89 from pylons_app.model.db import Statistics, Repository
90 log = get_commits_stats.get_logger()
91 commits_by_day_author_aggregate = {}
92 commits_by_day_aggregate = {}
93 repos_path = get_hg_ui_settings()['paths_root_path'].replace('*', '')
94 repo = MercurialRepository(repos_path + repo_name)
95
96 skip_date_limit = True
97 parse_limit = 350 #limit for single task changeset parsing
98 last_rev = 0
99 last_cs = None
100 timegetter = itemgetter('time')
101
102 sa = get_session()
103
104 dbrepo = sa.query(Repository)\
105 .filter(Repository.repo_name == repo_name).scalar()
106 cur_stats = sa.query(Statistics)\
107 .filter(Statistics.repository == dbrepo).scalar()
108 if cur_stats:
109 last_rev = cur_stats.stat_on_revision
110
111 if last_rev == repo.revisions[-1]:
112 #pass silently without any work
113 return True
114
115 if cur_stats:
116 commits_by_day_aggregate = OrderedDict(
117 json.loads(
118 cur_stats.commit_activity_combined))
119 commits_by_day_author_aggregate = json.loads(cur_stats.commit_activity)
120
121 for cnt, rev in enumerate(repo.revisions[last_rev:]):
122 last_cs = cs = repo.get_changeset(rev)
123 k = '%s-%s-%s' % (cs.date.timetuple()[0], cs.date.timetuple()[1],
124 cs.date.timetuple()[2])
125 timetupple = [int(x) for x in k.split('-')]
126 timetupple.extend([0 for _ in xrange(6)])
127 k = mktime(timetupple)
128 if commits_by_day_author_aggregate.has_key(author_key_cleaner(cs.author)):
129 try:
130 l = [timegetter(x) for x in commits_by_day_author_aggregate\
131 [author_key_cleaner(cs.author)]['data']]
132 time_pos = l.index(k)
133 except ValueError:
134 time_pos = False
135
136 if time_pos >= 0 and time_pos is not False:
137
138 datadict = commits_by_day_author_aggregate\
139 [author_key_cleaner(cs.author)]['data'][time_pos]
140
141 datadict["commits"] += 1
142 datadict["added"] += len(cs.added)
143 datadict["changed"] += len(cs.changed)
144 datadict["removed"] += len(cs.removed)
145 #print datadict
146
147 else:
148 #print 'ELSE !!!!'
149 if k >= ts_min_y and k <= ts_max_y or skip_date_limit:
150
151 datadict = {"time":k,
152 "commits":1,
153 "added":len(cs.added),
154 "changed":len(cs.changed),
155 "removed":len(cs.removed),
156 }
157 commits_by_day_author_aggregate\
158 [author_key_cleaner(cs.author)]['data'].append(datadict)
159
160 else:
161 #print k, 'nokey ADDING'
162 if k >= ts_min_y and k <= ts_max_y or skip_date_limit:
163 commits_by_day_author_aggregate[author_key_cleaner(cs.author)] = {
164 "label":author_key_cleaner(cs.author),
165 "data":[{"time":k,
166 "commits":1,
167 "added":len(cs.added),
168 "changed":len(cs.changed),
169 "removed":len(cs.removed),
170 }],
171 "schema":["commits"],
172 }
173
174 # #gather all data by day
175 if commits_by_day_aggregate.has_key(k):
176 commits_by_day_aggregate[k] += 1
177 else:
178 commits_by_day_aggregate[k] = 1
179
180 if cnt >= parse_limit:
181 #don't fetch to much data since we can freeze application
182 break
183
184 overview_data = []
185 for k, v in commits_by_day_aggregate.items():
186 overview_data.append([k, v])
187 overview_data = sorted(overview_data, key=itemgetter(0))
188
189 if not commits_by_day_author_aggregate:
190 commits_by_day_author_aggregate[author_key_cleaner(repo.contact)] = {
191 "label":author_key_cleaner(repo.contact),
192 "data":[0, 1],
193 "schema":["commits"],
194 }
195
196 stats = cur_stats if cur_stats else Statistics()
197 stats.commit_activity = json.dumps(commits_by_day_author_aggregate)
198 stats.commit_activity_combined = json.dumps(overview_data)
199 stats.repository = dbrepo
200 stats.stat_on_revision = last_cs.revision
201 stats.languages = json.dumps({'_TOTAL_':0, '':0})
202
203 try:
204 sa.add(stats)
205 sa.commit()
206 except:
207 log.error(traceback.format_exc())
208 sa.rollback()
209 return False
210
211 run_task(get_commits_stats, repo_name, ts_min_y, ts_max_y)
212
213 return True
214
215 @task
216 def reset_user_password(user_email):
217 log = reset_user_password.get_logger()
218 from pylons_app.lib import auth
219 from pylons_app.model.db import User
220
221 try:
222 try:
223 sa = get_session()
224 user = sa.query(User).filter(User.email == user_email).scalar()
225 new_passwd = auth.PasswordGenerator().gen_password(8,
226 auth.PasswordGenerator.ALPHABETS_BIG_SMALL)
227 if user:
228 user.password = auth.get_crypt_password(new_passwd)
229 sa.add(user)
230 sa.commit()
231 log.info('change password for %s', user_email)
232 if new_passwd is None:
233 raise Exception('unable to generate new password')
234
235 except:
236 log.error(traceback.format_exc())
237 sa.rollback()
238
239 run_task(send_email, user_email,
240 "Your new hg-app password",
241 'Your new hg-app password:%s' % (new_passwd))
242 log.info('send new password mail to %s', user_email)
243
244
245 except:
246 log.error('Failed to update user password')
247 log.error(traceback.format_exc())
248 return True
249
250 @task
251 def send_email(recipients, subject, body):
252 log = send_email.get_logger()
253 email_config = dict(config.items('DEFAULT'))
254 mail_from = email_config.get('app_email_from')
255 user = email_config.get('smtp_username')
256 passwd = email_config.get('smtp_password')
257 mail_server = email_config.get('smtp_server')
258 mail_port = email_config.get('smtp_port')
259 tls = email_config.get('smtp_use_tls')
260 ssl = False
261
262 try:
263 m = SmtpMailer(mail_from, user, passwd, mail_server,
264 mail_port, ssl, tls)
265 m.send(recipients, subject, body)
266 except:
267 log.error('Mail sending failed')
268 log.error(traceback.format_exc())
269 return False
270 return True
@@ -0,0 +1,118 b''
1 import logging
2 import smtplib
3 import mimetypes
4 from email.mime.multipart import MIMEMultipart
5 from email.mime.image import MIMEImage
6 from email.mime.audio import MIMEAudio
7 from email.mime.base import MIMEBase
8 from email.mime.text import MIMEText
9 from email.utils import formatdate
10 from email import encoders
11
12 class SmtpMailer(object):
13 """simple smtp mailer class
14
15 mailer = SmtpMailer(mail_from, user, passwd, mail_server, mail_port, ssl, tls)
16 mailer.send(recipients, subject, body, attachment_files)
17
18 :param recipients might be a list of string or single string
19 :param attachment_files is a dict of {filename:location}
20 it tries to guess the mimetype and attach the file
21 """
22
23 def __init__(self, mail_from, user, passwd, mail_server,
24 mail_port=None, ssl=False, tls=False):
25
26 self.mail_from = mail_from
27 self.mail_server = mail_server
28 self.mail_port = mail_port
29 self.user = user
30 self.passwd = passwd
31 self.ssl = ssl
32 self.tls = tls
33 self.debug = False
34
35 def send(self, recipients=[], subject='', body='', attachment_files={}):
36
37 if isinstance(recipients, basestring):
38 recipients = [recipients]
39 if self.ssl:
40 smtp_serv = smtplib.SMTP_SSL(self.mail_server, self.mail_port)
41 else:
42 smtp_serv = smtplib.SMTP(self.mail_server, self.mail_port)
43
44 if self.tls:
45 smtp_serv.starttls()
46
47 if self.debug:
48 smtp_serv.set_debuglevel(1)
49
50 smtp_serv.ehlo("mailer")
51
52 #if server requires authorization you must provide login and password
53 smtp_serv.login(self.user, self.passwd)
54
55 date_ = formatdate(localtime=True)
56 msg = MIMEMultipart()
57 msg['From'] = self.mail_from
58 msg['To'] = ','.join(recipients)
59 msg['Date'] = date_
60 msg['Subject'] = subject
61 msg.preamble = 'You will not see this in a MIME-aware mail reader.\n'
62
63 msg.attach(MIMEText(body))
64
65 if attachment_files:
66 self.__atach_files(msg, attachment_files)
67
68 smtp_serv.sendmail(self.mail_from, recipients, msg.as_string())
69 logging.info('MAIL SEND TO: %s' % recipients)
70 smtp_serv.quit()
71
72
73 def __atach_files(self, msg, attachment_files):
74 if isinstance(attachment_files, dict):
75 for f_name, msg_file in attachment_files.items():
76 ctype, encoding = mimetypes.guess_type(f_name)
77 logging.info("guessing file %s type based on %s" , ctype, f_name)
78 if ctype is None or encoding is not None:
79 # No guess could be made, or the file is encoded (compressed), so
80 # use a generic bag-of-bits type.
81 ctype = 'application/octet-stream'
82 maintype, subtype = ctype.split('/', 1)
83 if maintype == 'text':
84 # Note: we should handle calculating the charset
85 file_part = MIMEText(self.get_content(msg_file),
86 _subtype=subtype)
87 elif maintype == 'image':
88 file_part = MIMEImage(self.get_content(msg_file),
89 _subtype=subtype)
90 elif maintype == 'audio':
91 file_part = MIMEAudio(self.get_content(msg_file),
92 _subtype=subtype)
93 else:
94 file_part = MIMEBase(maintype, subtype)
95 file_part.set_payload(self.get_content(msg_file))
96 # Encode the payload using Base64
97 encoders.encode_base64(msg)
98 # Set the filename parameter
99 file_part.add_header('Content-Disposition', 'attachment',
100 filename=f_name)
101 file_part.add_header('Content-Type', ctype, name=f_name)
102 msg.attach(file_part)
103 else:
104 raise Exception('Attachment files should be'
105 'a dict in format {"filename":"filepath"}')
106
107 def get_content(self, msg_file):
108 '''
109 Get content based on type, if content is a string do open first
110 else just read because it's a probably open file object
111 @param msg_file:
112 '''
113 if isinstance(msg_file, str):
114 return open(msg_file, "rb").read()
115 else:
116 #just for safe seek to 0
117 msg_file.seek(0)
118 return msg_file.read()
@@ -0,0 +1,267 b''
1 """caching_query.py
2
3 Represent persistence structures which allow the usage of
4 Beaker caching with SQLAlchemy.
5
6 The three new concepts introduced here are:
7
8 * CachingQuery - a Query subclass that caches and
9 retrieves results in/from Beaker.
10 * FromCache - a query option that establishes caching
11 parameters on a Query
12 * RelationshipCache - a variant of FromCache which is specific
13 to a query invoked during a lazy load.
14 * _params_from_query - extracts value parameters from
15 a Query.
16
17 The rest of what's here are standard SQLAlchemy and
18 Beaker constructs.
19
20 """
21 from sqlalchemy.orm.interfaces import MapperOption
22 from sqlalchemy.orm.query import Query
23 from sqlalchemy.sql import visitors
24
25 class CachingQuery(Query):
26 """A Query subclass which optionally loads full results from a Beaker
27 cache region.
28
29 The CachingQuery stores additional state that allows it to consult
30 a Beaker cache before accessing the database:
31
32 * A "region", which is a cache region argument passed to a
33 Beaker CacheManager, specifies a particular cache configuration
34 (including backend implementation, expiration times, etc.)
35 * A "namespace", which is a qualifying name that identifies a
36 group of keys within the cache. A query that filters on a name
37 might use the name "by_name", a query that filters on a date range
38 to a joined table might use the name "related_date_range".
39
40 When the above state is present, a Beaker cache is retrieved.
41
42 The "namespace" name is first concatenated with
43 a string composed of the individual entities and columns the Query
44 requests, i.e. such as ``Query(User.id, User.name)``.
45
46 The Beaker cache is then loaded from the cache manager based
47 on the region and composed namespace. The key within the cache
48 itself is then constructed against the bind parameters specified
49 by this query, which are usually literals defined in the
50 WHERE clause.
51
52 The FromCache and RelationshipCache mapper options below represent
53 the "public" method of configuring this state upon the CachingQuery.
54
55 """
56
57 def __init__(self, manager, *args, **kw):
58 self.cache_manager = manager
59 Query.__init__(self, *args, **kw)
60
61 def __iter__(self):
62 """override __iter__ to pull results from Beaker
63 if particular attributes have been configured.
64
65 Note that this approach does *not* detach the loaded objects from
66 the current session. If the cache backend is an in-process cache
67 (like "memory") and lives beyond the scope of the current session's
68 transaction, those objects may be expired. The method here can be
69 modified to first expunge() each loaded item from the current
70 session before returning the list of items, so that the items
71 in the cache are not the same ones in the current Session.
72
73 """
74 if hasattr(self, '_cache_parameters'):
75 return self.get_value(createfunc=lambda: list(Query.__iter__(self)))
76 else:
77 return Query.__iter__(self)
78
79 def invalidate(self):
80 """Invalidate the value represented by this Query."""
81
82 cache, cache_key = _get_cache_parameters(self)
83 cache.remove(cache_key)
84
85 def get_value(self, merge=True, createfunc=None):
86 """Return the value from the cache for this query.
87
88 Raise KeyError if no value present and no
89 createfunc specified.
90
91 """
92 cache, cache_key = _get_cache_parameters(self)
93 ret = cache.get_value(cache_key, createfunc=createfunc)
94 if merge:
95 ret = self.merge_result(ret, load=False)
96 return ret
97
98 def set_value(self, value):
99 """Set the value in the cache for this query."""
100
101 cache, cache_key = _get_cache_parameters(self)
102 cache.put(cache_key, value)
103
104 def query_callable(manager):
105 def query(*arg, **kw):
106 return CachingQuery(manager, *arg, **kw)
107 return query
108
109 def _get_cache_parameters(query):
110 """For a query with cache_region and cache_namespace configured,
111 return the correspoinding Cache instance and cache key, based
112 on this query's current criterion and parameter values.
113
114 """
115 if not hasattr(query, '_cache_parameters'):
116 raise ValueError("This Query does not have caching parameters configured.")
117
118 region, namespace, cache_key = query._cache_parameters
119
120 namespace = _namespace_from_query(namespace, query)
121
122 if cache_key is None:
123 # cache key - the value arguments from this query's parameters.
124 args = _params_from_query(query)
125 cache_key = " ".join([str(x) for x in args])
126
127 # get cache
128 cache = query.cache_manager.get_cache_region(namespace, region)
129
130 # optional - hash the cache_key too for consistent length
131 # import uuid
132 # cache_key= str(uuid.uuid5(uuid.NAMESPACE_DNS, cache_key))
133
134 return cache, cache_key
135
136 def _namespace_from_query(namespace, query):
137 # cache namespace - the token handed in by the
138 # option + class we're querying against
139 namespace = " ".join([namespace] + [str(x) for x in query._entities])
140
141 # memcached wants this
142 namespace = namespace.replace(' ', '_')
143
144 return namespace
145
146 def _set_cache_parameters(query, region, namespace, cache_key):
147
148 if hasattr(query, '_cache_parameters'):
149 region, namespace, cache_key = query._cache_parameters
150 raise ValueError("This query is already configured "
151 "for region %r namespace %r" %
152 (region, namespace)
153 )
154 query._cache_parameters = region, namespace, cache_key
155
156 class FromCache(MapperOption):
157 """Specifies that a Query should load results from a cache."""
158
159 propagate_to_loaders = False
160
161 def __init__(self, region, namespace, cache_key=None):
162 """Construct a new FromCache.
163
164 :param region: the cache region. Should be a
165 region configured in the Beaker CacheManager.
166
167 :param namespace: the cache namespace. Should
168 be a name uniquely describing the target Query's
169 lexical structure.
170
171 :param cache_key: optional. A string cache key
172 that will serve as the key to the query. Use this
173 if your query has a huge amount of parameters (such
174 as when using in_()) which correspond more simply to
175 some other identifier.
176
177 """
178 self.region = region
179 self.namespace = namespace
180 self.cache_key = cache_key
181
182 def process_query(self, query):
183 """Process a Query during normal loading operation."""
184
185 _set_cache_parameters(query, self.region, self.namespace, self.cache_key)
186
187 class RelationshipCache(MapperOption):
188 """Specifies that a Query as called within a "lazy load"
189 should load results from a cache."""
190
191 propagate_to_loaders = True
192
193 def __init__(self, region, namespace, attribute):
194 """Construct a new RelationshipCache.
195
196 :param region: the cache region. Should be a
197 region configured in the Beaker CacheManager.
198
199 :param namespace: the cache namespace. Should
200 be a name uniquely describing the target Query's
201 lexical structure.
202
203 :param attribute: A Class.attribute which
204 indicates a particular class relationship() whose
205 lazy loader should be pulled from the cache.
206
207 """
208 self.region = region
209 self.namespace = namespace
210 self._relationship_options = {
211 (attribute.property.parent.class_, attribute.property.key) : self
212 }
213
214 def process_query_conditionally(self, query):
215 """Process a Query that is used within a lazy loader.
216
217 (the process_query_conditionally() method is a SQLAlchemy
218 hook invoked only within lazyload.)
219
220 """
221 if query._current_path:
222 mapper, key = query._current_path[-2:]
223
224 for cls in mapper.class_.__mro__:
225 if (cls, key) in self._relationship_options:
226 relationship_option = self._relationship_options[(cls, key)]
227 _set_cache_parameters(
228 query,
229 relationship_option.region,
230 relationship_option.namespace,
231 None)
232
233 def and_(self, option):
234 """Chain another RelationshipCache option to this one.
235
236 While many RelationshipCache objects can be specified on a single
237 Query separately, chaining them together allows for a more efficient
238 lookup during load.
239
240 """
241 self._relationship_options.update(option._relationship_options)
242 return self
243
244
245 def _params_from_query(query):
246 """Pull the bind parameter values from a query.
247
248 This takes into account any scalar attribute bindparam set up.
249
250 E.g. params_from_query(query.filter(Cls.foo==5).filter(Cls.bar==7)))
251 would return [5, 7].
252
253 """
254 v = []
255 def visit_bindparam(bind):
256 value = query._params.get(bind.key, bind.value)
257
258 # lazyloader may dig a callable in here, intended
259 # to late-evaluate params after autoflush is called.
260 # convert to a scalar value.
261 if callable(value):
262 value = value()
263
264 v.append(value)
265 if query._criterion is not None:
266 visitors.traverse(query._criterion, {}, {'bindparam':visit_bindparam})
267 return v
@@ -0,0 +1,54 b''
1 ## -*- coding: utf-8 -*-
2 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
3 <html xmlns="http://www.w3.org/1999/xhtml" id="mainhtml">
4 <head>
5 <title>${_('Reset You password to hg-app')}</title>
6 <meta http-equiv="Content-Type" content="text/html;charset=utf-8" />
7 <link rel="icon" href="/images/hgicon.png" type="image/png" />
8 <meta name="robots" content="index, nofollow"/>
9
10 <!-- stylesheets -->
11 <link rel="stylesheet" type="text/css" href="/css/reset.css" />
12 <link rel="stylesheet" type="text/css" href="/css/style.css" media="screen" />
13 <link id="color" rel="stylesheet" type="text/css" href="/css/colors/blue.css" />
14
15 <!-- scripts -->
16
17 </head>
18 <body>
19 <div id="register">
20
21 <div class="title">
22 <h5>${_('Reset You password to hg-app')}</h5>
23 <div class="corner tl"></div>
24 <div class="corner tr"></div>
25 </div>
26 <div class="inner">
27 ${h.form(url('password_reset'))}
28 <div class="form">
29 <!-- fields -->
30 <div class="fields">
31
32 <div class="field">
33 <div class="label">
34 <label for="email">${_('Email address')}:</label>
35 </div>
36 <div class="input">
37 ${h.text('email')}
38 </div>
39 </div>
40
41 <div class="buttons">
42 <div class="nohighlight">
43 ${h.submit('send','Reset my password',class_="ui-button ui-widget ui-state-default ui-corner-all")}
44 <div class="activation_msg">${_('Your new password will be send to matching email address')}</div>
45 </div>
46 </div>
47 </div>
48 </div>
49 ${h.end_form()}
50 </div>
51 </div>
52 </body>
53 </html>
54
@@ -1,61 +1,69 b''
1 -------------------------------------
1 -------------------------------------
2 Pylons based replacement for hgwebdir
2 Pylons based replacement for hgwebdir
3 -------------------------------------
3 -------------------------------------
4
4
5 Fully customizable, with authentication, permissions. Based on vcs library.
5 Fully customizable, with authentication, permissions. Based on vcs library.
6
6
7 **Overview**
7 **Overview**
8
8
9 - has it's own middleware to handle mercurial protocol request each request can
9 - has it's own middleware to handle mercurial protocol request each request can
10 be logged and authenticated + threaded performance unlikely to hgweb
10 be logged and authenticated + threaded performance unlikely to hgweb
11 - full permissions per project read/write/admin access even on mercurial request
11 - full permissions per project read/write/admin access even on mercurial request
12 - mako templates let's you cusmotize look and feel of application.
12 - mako templates let's you cusmotize look and feel of application.
13 - diffs annotations and source code all colored by pygments.
13 - diffs annotations and source code all colored by pygments.
14 - mercurial branch graph and yui-flot powered graphs
14 - mercurial branch graph and yui-flot powered graphs with zooming
15 - admin interface for performing user/permission managments as well as repository
15 - admin interface for performing user/permission managments as well as repository
16 managment.
16 managment.
17 - full text search of source codes with indexing daemons using whoosh
18 (no external search servers required all in one application)
19 - async tasks for speed and performance using celery (works without them too)
17 - Additional settings for mercurial web, (hooks editable from admin
20 - Additional settings for mercurial web, (hooks editable from admin
18 panel !) also manage paths, archive, remote messages
21 panel !) also manage paths, archive, remote messages
19 - backup scripts can do backup of whole app and send it over scp to desired location
22 - backup scripts can do backup of whole app and send it over scp to desired location
20 - setup project descriptions and info inside built in db for easy, non
23 - setup project descriptions and info inside built in db for easy, non
21 file-system operations
24 file-system operations
22 - added cache with invalidation on push/repo managment for high performance and
25 - added cache with invalidation on push/repo managment for high performance and
23 always upto date data.
26 always upto date data.
24 - rss / atom feeds, gravatar support
27 - rss / atom feeds, gravatar support
25 - based on pylons 1.0 / sqlalchemy 0.6
28 - based on pylons 1.0 / sqlalchemy 0.6
26
29
27 **Incoming**
30 **Incoming**
28
31
29 - code review based on hg-review (when it's stable)
32 - code review based on hg-review (when it's stable)
30 - git support (when vcs can handle it)
33 - git support (when vcs can handle it - almost there !)
31 - full text search of source codes with indexing daemons using whoosh
34 - commit based wikis
32 (no external search servers required all in one application)
35 - in server forks
33 - manage hg ui() per repo, add hooks settings, per repo, and not globally
36 - clonning from remote repositories into hg-app
34 - other cools stuff that i can figure out
37 - other cools stuff that i can figure out (or You can help me figure out)
35
38
36 .. note::
39 .. note::
37 This software is still in beta mode.
40 This software is still in beta mode.
38 I don't guarantee that it'll work correctly.
41 I don't guarantee that it'll work correctly.
39
42
40
43
41 -------------
44 -------------
42 Installation
45 Installation
43 -------------
46 -------------
44 .. note::
47 .. note::
45 I recomend to install tip version of vcs while the app is in beta mode.
48 I recomend to install tip version of vcs while the app is in beta mode.
46
49
47
50
48 - create new virtualenv and activate it - highly recommend that you use separate
51 - create new virtualenv and activate it - highly recommend that you use separate
49 virtual-env for whole application
52 virtual-env for whole application
50 - download hg app from default (not demo) branch from bitbucket and run
53 - download hg app from default branch from bitbucket and run
51 'python setup.py install' this will install all required dependencies needed
54 'python setup.py install' this will install all required dependencies needed
52 - run paster setup-app production.ini it should create all needed tables
55 - run paster setup-app production.ini it should create all needed tables
53 and an admin account.
56 and an admin account make sure You specify correct path to repositories.
54 - remember that the given path for mercurial repositories must be write
57 - remember that the given path for mercurial repositories must be write
55 accessible for the application
58 accessible for the application
56 - run paster serve development.ini - or you can use manage-hg_app script.
59 - run paster serve development.ini - or you can use manage-hg_app script.
57 the app should be available at the 127.0.0.1:5000
60 the app should be available at the 127.0.0.1:5000
58 - use admin account you created to login.
61 - use admin account you created to login.
59 - default permissions on each repository is read, and owner is admin. So remember
62 - default permissions on each repository is read, and owner is admin. So remember
60 to update these.
63 to update these.
64 - in order to use full power of async tasks, You must install message broker
65 preferrably rabbitmq and start celeryd daemon. The app should gain some speed
66 than. For installation instructions
67 You can visit: http://ask.github.com/celery/getting-started/index.html. All
68 needed configs are inside hg-app ie. celeryconfig.py
61 No newline at end of file
69
@@ -1,155 +1,160 b''
1 ################################################################################
1 ################################################################################
2 ################################################################################
2 ################################################################################
3 # pylons_app - Pylons environment configuration #
3 # hg-app - Pylons environment configuration #
4 # #
4 # #
5 # The %(here)s variable will be replaced with the parent directory of this file#
5 # The %(here)s variable will be replaced with the parent directory of this file#
6 ################################################################################
6 ################################################################################
7
7
8 [DEFAULT]
8 [DEFAULT]
9 debug = true
9 debug = true
10 ############################################
10 ################################################################################
11 ## Uncomment and replace with the address ##
11 ## Uncomment and replace with the address which should receive ##
12 ## which should receive any error reports ##
12 ## any error reports after application crash ##
13 ############################################
13 ## Additionally those settings will be used by hg-app mailing system ##
14 ################################################################################
14 #email_to = admin@localhost
15 #email_to = admin@localhost
16 #error_email_from = paste_error@localhost
17 #app_email_from = hg-app-noreply@localhost
18 #error_message =
19
15 #smtp_server = mail.server.com
20 #smtp_server = mail.server.com
16 #error_email_from = paste_error@localhost
17 #smtp_username =
21 #smtp_username =
18 #smtp_password =
22 #smtp_password =
19 #error_message = 'mercurial crash !'
23 #smtp_port =
24 #smtp_use_tls =
20
25
21 [server:main]
26 [server:main]
22 ##nr of threads to spawn
27 ##nr of threads to spawn
23 threadpool_workers = 5
28 threadpool_workers = 5
24
29
25 ##max request before
30 ##max request before
26 threadpool_max_requests = 2
31 threadpool_max_requests = 6
27
32
28 ##option to use threads of process
33 ##option to use threads of process
29 use_threadpool = true
34 use_threadpool = false
30
35
31 use = egg:Paste#http
36 use = egg:Paste#http
32 host = 127.0.0.1
37 host = 127.0.0.1
33 port = 5000
38 port = 5000
34
39
35 [app:main]
40 [app:main]
36 use = egg:pylons_app
41 use = egg:pylons_app
37 full_stack = true
42 full_stack = true
38 static_files = true
43 static_files = true
39 lang=en
44 lang=en
40 cache_dir = %(here)s/data
45 cache_dir = %(here)s/data
41
46
42 ####################################
47 ####################################
43 ### BEAKER CACHE ####
48 ### BEAKER CACHE ####
44 ####################################
49 ####################################
45 beaker.cache.data_dir=/%(here)s/data/cache/data
50 beaker.cache.data_dir=/%(here)s/data/cache/data
46 beaker.cache.lock_dir=/%(here)s/data/cache/lock
51 beaker.cache.lock_dir=/%(here)s/data/cache/lock
47 beaker.cache.regions=super_short_term,short_term,long_term
52 beaker.cache.regions=super_short_term,short_term,long_term
48 beaker.cache.long_term.type=memory
53 beaker.cache.long_term.type=memory
49 beaker.cache.long_term.expire=36000
54 beaker.cache.long_term.expire=36000
50 beaker.cache.short_term.type=memory
55 beaker.cache.short_term.type=memory
51 beaker.cache.short_term.expire=60
56 beaker.cache.short_term.expire=60
52 beaker.cache.super_short_term.type=memory
57 beaker.cache.super_short_term.type=memory
53 beaker.cache.super_short_term.expire=10
58 beaker.cache.super_short_term.expire=10
54
59
55 ####################################
60 ####################################
56 ### BEAKER SESSION ####
61 ### BEAKER SESSION ####
57 ####################################
62 ####################################
58 ## Type of storage used for the session, current types are
63 ## Type of storage used for the session, current types are
59 ## “dbm”, “file”, “memcached”, “database”, and “memory”.
64 ## "dbm", "file", "memcached", "database", and "memory".
60 ## The storage uses the Container API
65 ## The storage uses the Container API
61 ##that is also used by the cache system.
66 ##that is also used by the cache system.
62 beaker.session.type = file
67 beaker.session.type = file
63
68
64 beaker.session.key = hg-app
69 beaker.session.key = hg-app
65 beaker.session.secret = g654dcno0-9873jhgfreyu
70 beaker.session.secret = g654dcno0-9873jhgfreyu
66 beaker.session.timeout = 36000
71 beaker.session.timeout = 36000
67
72
68 ##auto save the session to not to use .save()
73 ##auto save the session to not to use .save()
69 beaker.session.auto = False
74 beaker.session.auto = False
70
75
71 ##true exire at browser close
76 ##true exire at browser close
72 #beaker.session.cookie_expires = 3600
77 #beaker.session.cookie_expires = 3600
73
78
74
79
75 ################################################################################
80 ################################################################################
76 ## WARNING: *THE LINE BELOW MUST BE UNCOMMENTED ON A PRODUCTION ENVIRONMENT* ##
81 ## WARNING: *THE LINE BELOW MUST BE UNCOMMENTED ON A PRODUCTION ENVIRONMENT* ##
77 ## Debug mode will enable the interactive debugging tool, allowing ANYONE to ##
82 ## Debug mode will enable the interactive debugging tool, allowing ANYONE to ##
78 ## execute malicious code after an exception is raised. ##
83 ## execute malicious code after an exception is raised. ##
79 ################################################################################
84 ################################################################################
80 #set debug = false
85 #set debug = false
81
86
82 ##################################
87 ##################################
83 ### LOGVIEW CONFIG ###
88 ### LOGVIEW CONFIG ###
84 ##################################
89 ##################################
85 logview.sqlalchemy = #faa
90 logview.sqlalchemy = #faa
86 logview.pylons.templating = #bfb
91 logview.pylons.templating = #bfb
87 logview.pylons.util = #eee
92 logview.pylons.util = #eee
88
93
89 #########################################################
94 #########################################################
90 ### DB CONFIGS - EACH DB WILL HAVE IT'S OWN CONFIG ###
95 ### DB CONFIGS - EACH DB WILL HAVE IT'S OWN CONFIG ###
91 #########################################################
96 #########################################################
92 sqlalchemy.db1.url = sqlite:///%(here)s/hg_app.db
97 sqlalchemy.db1.url = sqlite:///%(here)s/hg_app.db
93 #sqlalchemy.db1.echo = False
98 #sqlalchemy.db1.echo = False
94 #sqlalchemy.db1.pool_recycle = 3600
99 #sqlalchemy.db1.pool_recycle = 3600
95 sqlalchemy.convert_unicode = true
100 sqlalchemy.convert_unicode = true
96
101
97 ################################
102 ################################
98 ### LOGGING CONFIGURATION ####
103 ### LOGGING CONFIGURATION ####
99 ################################
104 ################################
100 [loggers]
105 [loggers]
101 keys = root, routes, pylons_app, sqlalchemy
106 keys = root, routes, pylons_app, sqlalchemy
102
107
103 [handlers]
108 [handlers]
104 keys = console
109 keys = console
105
110
106 [formatters]
111 [formatters]
107 keys = generic,color_formatter
112 keys = generic,color_formatter
108
113
109 #############
114 #############
110 ## LOGGERS ##
115 ## LOGGERS ##
111 #############
116 #############
112 [logger_root]
117 [logger_root]
113 level = NOTSET
118 level = NOTSET
114 handlers = console
119 handlers = console
115
120
116 [logger_routes]
121 [logger_routes]
117 level = DEBUG
122 level = DEBUG
118 handlers = console
123 handlers = console
119 qualname = routes.middleware
124 qualname = routes.middleware
120 # "level = DEBUG" logs the route matched and routing variables.
125 # "level = DEBUG" logs the route matched and routing variables.
121
126
122 [logger_pylons_app]
127 [logger_pylons_app]
123 level = DEBUG
128 level = DEBUG
124 handlers = console
129 handlers = console
125 qualname = pylons_app
130 qualname = pylons_app
126 propagate = 0
131 propagate = 0
127
132
128 [logger_sqlalchemy]
133 [logger_sqlalchemy]
129 level = ERROR
134 level = ERROR
130 handlers = console
135 handlers = console
131 qualname = sqlalchemy.engine
136 qualname = sqlalchemy.engine
132 propagate = 0
137 propagate = 0
133
138
134 ##############
139 ##############
135 ## HANDLERS ##
140 ## HANDLERS ##
136 ##############
141 ##############
137
142
138 [handler_console]
143 [handler_console]
139 class = StreamHandler
144 class = StreamHandler
140 args = (sys.stderr,)
145 args = (sys.stderr,)
141 level = NOTSET
146 level = NOTSET
142 formatter = color_formatter
147 formatter = color_formatter
143
148
144 ################
149 ################
145 ## FORMATTERS ##
150 ## FORMATTERS ##
146 ################
151 ################
147
152
148 [formatter_generic]
153 [formatter_generic]
149 format = %(asctime)s.%(msecs)03d %(levelname)-5.5s [%(name)s] %(message)s
154 format = %(asctime)s.%(msecs)03d %(levelname)-5.5s [%(name)s] %(message)s
150 datefmt = %Y-%m-%d %H:%M:%S
155 datefmt = %Y-%m-%d %H:%M:%S
151
156
152 [formatter_color_formatter]
157 [formatter_color_formatter]
153 class=pylons_app.lib.colored_formatter.ColorFormatter
158 class=pylons_app.lib.colored_formatter.ColorFormatter
154 format= %(asctime)s.%(msecs)03d %(levelname)-5.5s [%(name)s] %(message)s
159 format= %(asctime)s.%(msecs)03d %(levelname)-5.5s [%(name)s] %(message)s
155 datefmt = %Y-%m-%d %H:%M:%S No newline at end of file
160 datefmt = %Y-%m-%d %H:%M:%S
@@ -1,155 +1,160 b''
1 ################################################################################
1 ################################################################################
2 ################################################################################
2 ################################################################################
3 # pylons_app - Pylons environment configuration #
3 # hg-app - Pylons environment configuration #
4 # #
4 # #
5 # The %(here)s variable will be replaced with the parent directory of this file#
5 # The %(here)s variable will be replaced with the parent directory of this file#
6 ################################################################################
6 ################################################################################
7
7
8 [DEFAULT]
8 [DEFAULT]
9 debug = true
9 debug = true
10 ############################################
10 ################################################################################
11 ## Uncomment and replace with the address ##
11 ## Uncomment and replace with the address which should receive ##
12 ## which should receive any error reports ##
12 ## any error reports after application crash ##
13 ############################################
13 ## Additionally those settings will be used by hg-app mailing system ##
14 ################################################################################
14 #email_to = admin@localhost
15 #email_to = admin@localhost
16 #error_email_from = paste_error@localhost
17 #app_email_from = hg-app-noreply@localhost
18 #error_message =
19
15 #smtp_server = mail.server.com
20 #smtp_server = mail.server.com
16 #error_email_from = paste_error@localhost
17 #smtp_username =
21 #smtp_username =
18 #smtp_password =
22 #smtp_password =
19 #error_message = 'mercurial crash !'
23 #smtp_port =
24 #smtp_use_tls = false
20
25
21 [server:main]
26 [server:main]
22 ##nr of threads to spawn
27 ##nr of threads to spawn
23 threadpool_workers = 5
28 threadpool_workers = 5
24
29
25 ##max request before
30 ##max request before thread respawn
26 threadpool_max_requests = 2
31 threadpool_max_requests = 2
27
32
28 ##option to use threads of process
33 ##option to use threads of process
29 use_threadpool = true
34 use_threadpool = true
30
35
31 use = egg:Paste#http
36 use = egg:Paste#http
32 host = 127.0.0.1
37 host = 127.0.0.1
33 port = 8001
38 port = 8001
34
39
35 [app:main]
40 [app:main]
36 use = egg:pylons_app
41 use = egg:pylons_app
37 full_stack = true
42 full_stack = true
38 static_files = false
43 static_files = false
39 lang=en
44 lang=en
40 cache_dir = %(here)s/data
45 cache_dir = %(here)s/data
41
46
42 ####################################
47 ####################################
43 ### BEAKER CACHE ####
48 ### BEAKER CACHE ####
44 ####################################
49 ####################################
45 beaker.cache.data_dir=/%(here)s/data/cache/data
50 beaker.cache.data_dir=/%(here)s/data/cache/data
46 beaker.cache.lock_dir=/%(here)s/data/cache/lock
51 beaker.cache.lock_dir=/%(here)s/data/cache/lock
47 beaker.cache.regions=super_short_term,short_term,long_term
52 beaker.cache.regions=super_short_term,short_term,long_term
48 beaker.cache.long_term.type=memory
53 beaker.cache.long_term.type=memory
49 beaker.cache.long_term.expire=36000
54 beaker.cache.long_term.expire=36000
50 beaker.cache.short_term.type=memory
55 beaker.cache.short_term.type=memory
51 beaker.cache.short_term.expire=60
56 beaker.cache.short_term.expire=60
52 beaker.cache.super_short_term.type=memory
57 beaker.cache.super_short_term.type=memory
53 beaker.cache.super_short_term.expire=10
58 beaker.cache.super_short_term.expire=10
54
59
55 ####################################
60 ####################################
56 ### BEAKER SESSION ####
61 ### BEAKER SESSION ####
57 ####################################
62 ####################################
58 ## Type of storage used for the session, current types are
63 ## Type of storage used for the session, current types are
59 ## dbm, file, memcached, database, and memory.
64 ## dbm, file, memcached, database, and memory.
60 ## The storage uses the Container API
65 ## The storage uses the Container API
61 ##that is also used by the cache system.
66 ##that is also used by the cache system.
62 beaker.session.type = file
67 beaker.session.type = file
63
68
64 beaker.session.key = hg-app
69 beaker.session.key = hg-app
65 beaker.session.secret = g654dcno0-9873jhgfreyu
70 beaker.session.secret = g654dcno0-9873jhgfreyu
66 beaker.session.timeout = 36000
71 beaker.session.timeout = 36000
67
72
68 ##auto save the session to not to use .save()
73 ##auto save the session to not to use .save()
69 beaker.session.auto = False
74 beaker.session.auto = False
70
75
71 ##true exire at browser close
76 ##true exire at browser close
72 #beaker.session.cookie_expires = 3600
77 #beaker.session.cookie_expires = 3600
73
78
74
79
75 ################################################################################
80 ################################################################################
76 ## WARNING: *THE LINE BELOW MUST BE UNCOMMENTED ON A PRODUCTION ENVIRONMENT* ##
81 ## WARNING: *THE LINE BELOW MUST BE UNCOMMENTED ON A PRODUCTION ENVIRONMENT* ##
77 ## Debug mode will enable the interactive debugging tool, allowing ANYONE to ##
82 ## Debug mode will enable the interactive debugging tool, allowing ANYONE to ##
78 ## execute malicious code after an exception is raised. ##
83 ## execute malicious code after an exception is raised. ##
79 ################################################################################
84 ################################################################################
80 set debug = false
85 set debug = false
81
86
82 ##################################
87 ##################################
83 ### LOGVIEW CONFIG ###
88 ### LOGVIEW CONFIG ###
84 ##################################
89 ##################################
85 logview.sqlalchemy = #faa
90 logview.sqlalchemy = #faa
86 logview.pylons.templating = #bfb
91 logview.pylons.templating = #bfb
87 logview.pylons.util = #eee
92 logview.pylons.util = #eee
88
93
89 #########################################################
94 #########################################################
90 ### DB CONFIGS - EACH DB WILL HAVE IT'S OWN CONFIG ###
95 ### DB CONFIGS - EACH DB WILL HAVE IT'S OWN CONFIG ###
91 #########################################################
96 #########################################################
92 sqlalchemy.db1.url = sqlite:///%(here)s/hg_app.db
97 sqlalchemy.db1.url = sqlite:///%(here)s/hg_app.db
93 #sqlalchemy.db1.echo = False
98 #sqlalchemy.db1.echo = False
94 #sqlalchemy.db1.pool_recycle = 3600
99 #sqlalchemy.db1.pool_recycle = 3600
95 sqlalchemy.convert_unicode = true
100 sqlalchemy.convert_unicode = true
96
101
97 ################################
102 ################################
98 ### LOGGING CONFIGURATION ####
103 ### LOGGING CONFIGURATION ####
99 ################################
104 ################################
100 [loggers]
105 [loggers]
101 keys = root, routes, pylons_app, sqlalchemy
106 keys = root, routes, pylons_app, sqlalchemy
102
107
103 [handlers]
108 [handlers]
104 keys = console
109 keys = console
105
110
106 [formatters]
111 [formatters]
107 keys = generic,color_formatter
112 keys = generic,color_formatter
108
113
109 #############
114 #############
110 ## LOGGERS ##
115 ## LOGGERS ##
111 #############
116 #############
112 [logger_root]
117 [logger_root]
113 level = INFO
118 level = INFO
114 handlers = console
119 handlers = console
115
120
116 [logger_routes]
121 [logger_routes]
117 level = INFO
122 level = INFO
118 handlers = console
123 handlers = console
119 qualname = routes.middleware
124 qualname = routes.middleware
120 # "level = DEBUG" logs the route matched and routing variables.
125 # "level = DEBUG" logs the route matched and routing variables.
121
126
122 [logger_pylons_app]
127 [logger_pylons_app]
123 level = DEBUG
128 level = DEBUG
124 handlers = console
129 handlers = console
125 qualname = pylons_app
130 qualname = pylons_app
126 propagate = 0
131 propagate = 0
127
132
128 [logger_sqlalchemy]
133 [logger_sqlalchemy]
129 level = ERROR
134 level = ERROR
130 handlers = console
135 handlers = console
131 qualname = sqlalchemy.engine
136 qualname = sqlalchemy.engine
132 propagate = 0
137 propagate = 0
133
138
134 ##############
139 ##############
135 ## HANDLERS ##
140 ## HANDLERS ##
136 ##############
141 ##############
137
142
138 [handler_console]
143 [handler_console]
139 class = StreamHandler
144 class = StreamHandler
140 args = (sys.stderr,)
145 args = (sys.stderr,)
141 level = NOTSET
146 level = NOTSET
142 formatter = color_formatter
147 formatter = color_formatter
143
148
144 ################
149 ################
145 ## FORMATTERS ##
150 ## FORMATTERS ##
146 ################
151 ################
147
152
148 [formatter_generic]
153 [formatter_generic]
149 format = %(asctime)s.%(msecs)03d %(levelname)-5.5s [%(name)s] %(message)s
154 format = %(asctime)s.%(msecs)03d %(levelname)-5.5s [%(name)s] %(message)s
150 datefmt = %Y-%m-%d %H:%M:%S
155 datefmt = %Y-%m-%d %H:%M:%S
151
156
152 [formatter_color_formatter]
157 [formatter_color_formatter]
153 class=pylons_app.lib.colored_formatter.ColorFormatter
158 class=pylons_app.lib.colored_formatter.ColorFormatter
154 format= %(asctime)s.%(msecs)03d %(levelname)-5.5s [%(name)s] %(message)s
159 format= %(asctime)s.%(msecs)03d %(levelname)-5.5s [%(name)s] %(message)s
155 datefmt = %Y-%m-%d %H:%M:%S No newline at end of file
160 datefmt = %Y-%m-%d %H:%M:%S
@@ -1,34 +1,35 b''
1 #!/usr/bin/env python
1 #!/usr/bin/env python
2 # encoding: utf-8
2 # encoding: utf-8
3 # Hg app, a web based mercurial repository managment based on pylons
3 # Hg app, a web based mercurial repository managment based on pylons
4 # Copyright (C) 2009-2010 Marcin Kuzminski <marcin@python-works.com>
4 # Copyright (C) 2009-2010 Marcin Kuzminski <marcin@python-works.com>
5 #
5 #
6 # This program is free software; you can redistribute it and/or
6 # This program is free software; you can redistribute it and/or
7 # modify it under the terms of the GNU General Public License
7 # modify it under the terms of the GNU General Public License
8 # as published by the Free Software Foundation; version 2
8 # as published by the Free Software Foundation; version 2
9 # of the License or (at your opinion) any later version of the license.
9 # of the License or (at your opinion) any later version of the license.
10 #
10 #
11 # This program is distributed in the hope that it will be useful,
11 # This program is distributed in the hope that it will be useful,
12 # but WITHOUT ANY WARRANTY; without even the implied warranty of
12 # but WITHOUT ANY WARRANTY; without even the implied warranty of
13 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 # GNU General Public License for more details.
14 # GNU General Public License for more details.
15 #
15 #
16 # You should have received a copy of the GNU General Public License
16 # You should have received a copy of the GNU General Public License
17 # along with this program; if not, write to the Free Software
17 # along with this program; if not, write to the Free Software
18 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
18 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
19 # MA 02110-1301, USA.
19 # MA 02110-1301, USA.
20 """
20 """
21 Created on April 9, 2010
21 Created on April 9, 2010
22 Hg app, a web based mercurial repository managment based on pylons
22 Hg app, a web based mercurial repository managment based on pylons
23 versioning implementation: http://semver.org/
23 @author: marcink
24 @author: marcink
24 """
25 """
25
26
26 VERSION = (0, 8, 2, 'beta')
27 VERSION = (0, 8, 3, 'beta')
27
28
28 __version__ = '.'.join((str(each) for each in VERSION[:4]))
29 __version__ = '.'.join((str(each) for each in VERSION[:4]))
29
30
30 def get_version():
31 def get_version():
31 """
32 """
32 Returns shorter version (digit parts only) as string.
33 Returns shorter version (digit parts only) as string.
33 """
34 """
34 return '.'.join((str(each) for each in VERSION[:3]))
35 return '.'.join((str(each) for each in VERSION[:3]))
@@ -1,156 +1,161 b''
1 ################################################################################
1 ################################################################################
2 ################################################################################
2 ################################################################################
3 # hg-app - Pylons environment configuration #
3 # hg-app - Pylons environment configuration #
4 # #
4 # #
5 # The %(here)s variable will be replaced with the parent directory of this file#
5 # The %(here)s variable will be replaced with the parent directory of this file#
6 ################################################################################
6 ################################################################################
7
7
8 [DEFAULT]
8 [DEFAULT]
9 debug = true
9 debug = true
10 ############################################
10 ################################################################################
11 ## Uncomment and replace with the address ##
11 ## Uncomment and replace with the address which should receive ##
12 ## which should receive any error reports ##
12 ## any error reports after application crash ##
13 ############################################
13 ## Additionally those settings will be used by hg-app mailing system ##
14 ################################################################################
14 #email_to = admin@localhost
15 #email_to = admin@localhost
16 #error_email_from = paste_error@localhost
17 #app_email_from = hg-app-noreply@localhost
18 #error_message =
19
15 #smtp_server = mail.server.com
20 #smtp_server = mail.server.com
16 #error_email_from = paste_error@localhost
17 #smtp_username =
21 #smtp_username =
18 #smtp_password =
22 #smtp_password =
19 #error_message = 'hp-app crash !'
23 #smtp_port =
24 #smtp_use_tls = false
20
25
21 [server:main]
26 [server:main]
22 ##nr of threads to spawn
27 ##nr of threads to spawn
23 threadpool_workers = 5
28 threadpool_workers = 5
24
29
25 ##max request before thread respawn
30 ##max request before thread respawn
26 threadpool_max_requests = 2
31 threadpool_max_requests = 2
27
32
28 ##option to use threads of process
33 ##option to use threads of process
29 use_threadpool = true
34 use_threadpool = true
30
35
31 use = egg:Paste#http
36 use = egg:Paste#http
32 host = 127.0.0.1
37 host = 127.0.0.1
33 port = 8001
38 port = 8001
34
39
35 [app:main]
40 [app:main]
36 use = egg:pylons_app
41 use = egg:pylons_app
37 full_stack = true
42 full_stack = true
38 static_files = false
43 static_files = false
39 lang=en
44 lang=en
40 cache_dir = %(here)s/data
45 cache_dir = %(here)s/data
41 app_instance_uuid = ${app_instance_uuid}
46 app_instance_uuid = ${app_instance_uuid}
42
47
43 ####################################
48 ####################################
44 ### BEAKER CACHE ####
49 ### BEAKER CACHE ####
45 ####################################
50 ####################################
46 beaker.cache.data_dir=/%(here)s/data/cache/data
51 beaker.cache.data_dir=/%(here)s/data/cache/data
47 beaker.cache.lock_dir=/%(here)s/data/cache/lock
52 beaker.cache.lock_dir=/%(here)s/data/cache/lock
48 beaker.cache.regions=super_short_term,short_term,long_term
53 beaker.cache.regions=super_short_term,short_term,long_term
49 beaker.cache.long_term.type=memory
54 beaker.cache.long_term.type=memory
50 beaker.cache.long_term.expire=36000
55 beaker.cache.long_term.expire=36000
51 beaker.cache.short_term.type=memory
56 beaker.cache.short_term.type=memory
52 beaker.cache.short_term.expire=60
57 beaker.cache.short_term.expire=60
53 beaker.cache.super_short_term.type=memory
58 beaker.cache.super_short_term.type=memory
54 beaker.cache.super_short_term.expire=10
59 beaker.cache.super_short_term.expire=10
55
60
56 ####################################
61 ####################################
57 ### BEAKER SESSION ####
62 ### BEAKER SESSION ####
58 ####################################
63 ####################################
59 ## Type of storage used for the session, current types are
64 ## Type of storage used for the session, current types are
60 ## dbm, file, memcached, database, and memory.
65 ## dbm, file, memcached, database, and memory.
61 ## The storage uses the Container API
66 ## The storage uses the Container API
62 ##that is also used by the cache system.
67 ##that is also used by the cache system.
63 beaker.session.type = file
68 beaker.session.type = file
64
69
65 beaker.session.key = hg-app
70 beaker.session.key = hg-app
66 beaker.session.secret = ${app_instance_secret}
71 beaker.session.secret = ${app_instance_secret}
67 beaker.session.timeout = 36000
72 beaker.session.timeout = 36000
68
73
69 ##auto save the session to not to use .save()
74 ##auto save the session to not to use .save()
70 beaker.session.auto = False
75 beaker.session.auto = False
71
76
72 ##true exire at browser close
77 ##true exire at browser close
73 #beaker.session.cookie_expires = 3600
78 #beaker.session.cookie_expires = 3600
74
79
75
80
76 ################################################################################
81 ################################################################################
77 ## WARNING: *THE LINE BELOW MUST BE UNCOMMENTED ON A PRODUCTION ENVIRONMENT* ##
82 ## WARNING: *THE LINE BELOW MUST BE UNCOMMENTED ON A PRODUCTION ENVIRONMENT* ##
78 ## Debug mode will enable the interactive debugging tool, allowing ANYONE to ##
83 ## Debug mode will enable the interactive debugging tool, allowing ANYONE to ##
79 ## execute malicious code after an exception is raised. ##
84 ## execute malicious code after an exception is raised. ##
80 ################################################################################
85 ################################################################################
81 set debug = false
86 set debug = false
82
87
83 ##################################
88 ##################################
84 ### LOGVIEW CONFIG ###
89 ### LOGVIEW CONFIG ###
85 ##################################
90 ##################################
86 logview.sqlalchemy = #faa
91 logview.sqlalchemy = #faa
87 logview.pylons.templating = #bfb
92 logview.pylons.templating = #bfb
88 logview.pylons.util = #eee
93 logview.pylons.util = #eee
89
94
90 #########################################################
95 #########################################################
91 ### DB CONFIGS - EACH DB WILL HAVE IT'S OWN CONFIG ###
96 ### DB CONFIGS - EACH DB WILL HAVE IT'S OWN CONFIG ###
92 #########################################################
97 #########################################################
93 sqlalchemy.db1.url = sqlite:///%(here)s/hg_app.db
98 sqlalchemy.db1.url = sqlite:///%(here)s/hg_app.db
94 #sqlalchemy.db1.echo = False
99 #sqlalchemy.db1.echo = False
95 #sqlalchemy.db1.pool_recycle = 3600
100 #sqlalchemy.db1.pool_recycle = 3600
96 sqlalchemy.convert_unicode = true
101 sqlalchemy.convert_unicode = true
97
102
98 ################################
103 ################################
99 ### LOGGING CONFIGURATION ####
104 ### LOGGING CONFIGURATION ####
100 ################################
105 ################################
101 [loggers]
106 [loggers]
102 keys = root, routes, pylons_app, sqlalchemy
107 keys = root, routes, pylons_app, sqlalchemy
103
108
104 [handlers]
109 [handlers]
105 keys = console
110 keys = console
106
111
107 [formatters]
112 [formatters]
108 keys = generic,color_formatter
113 keys = generic,color_formatter
109
114
110 #############
115 #############
111 ## LOGGERS ##
116 ## LOGGERS ##
112 #############
117 #############
113 [logger_root]
118 [logger_root]
114 level = INFO
119 level = INFO
115 handlers = console
120 handlers = console
116
121
117 [logger_routes]
122 [logger_routes]
118 level = INFO
123 level = INFO
119 handlers = console
124 handlers = console
120 qualname = routes.middleware
125 qualname = routes.middleware
121 # "level = DEBUG" logs the route matched and routing variables.
126 # "level = DEBUG" logs the route matched and routing variables.
122
127
123 [logger_pylons_app]
128 [logger_pylons_app]
124 level = DEBUG
129 level = DEBUG
125 handlers = console
130 handlers = console
126 qualname = pylons_app
131 qualname = pylons_app
127 propagate = 0
132 propagate = 0
128
133
129 [logger_sqlalchemy]
134 [logger_sqlalchemy]
130 level = ERROR
135 level = ERROR
131 handlers = console
136 handlers = console
132 qualname = sqlalchemy.engine
137 qualname = sqlalchemy.engine
133 propagate = 0
138 propagate = 0
134
139
135 ##############
140 ##############
136 ## HANDLERS ##
141 ## HANDLERS ##
137 ##############
142 ##############
138
143
139 [handler_console]
144 [handler_console]
140 class = StreamHandler
145 class = StreamHandler
141 args = (sys.stderr,)
146 args = (sys.stderr,)
142 level = NOTSET
147 level = NOTSET
143 formatter = color_formatter
148 formatter = color_formatter
144
149
145 ################
150 ################
146 ## FORMATTERS ##
151 ## FORMATTERS ##
147 ################
152 ################
148
153
149 [formatter_generic]
154 [formatter_generic]
150 format = %(asctime)s.%(msecs)03d %(levelname)-5.5s [%(name)s] %(message)s
155 format = %(asctime)s.%(msecs)03d %(levelname)-5.5s [%(name)s] %(message)s
151 datefmt = %Y-%m-%d %H:%M:%S
156 datefmt = %Y-%m-%d %H:%M:%S
152
157
153 [formatter_color_formatter]
158 [formatter_color_formatter]
154 class=pylons_app.lib.colored_formatter.ColorFormatter
159 class=pylons_app.lib.colored_formatter.ColorFormatter
155 format= %(asctime)s.%(msecs)03d %(levelname)-5.5s [%(name)s] %(message)s
160 format= %(asctime)s.%(msecs)03d %(levelname)-5.5s [%(name)s] %(message)s
156 datefmt = %Y-%m-%d %H:%M:%S No newline at end of file
161 datefmt = %Y-%m-%d %H:%M:%S
@@ -1,74 +1,79 b''
1 """Pylons environment configuration"""
1 """Pylons environment configuration"""
2 from mako.lookup import TemplateLookup
2 from mako.lookup import TemplateLookup
3 from pylons.configuration import PylonsConfig
3 from pylons.configuration import PylonsConfig
4 from pylons.error import handle_mako_error
4 from pylons.error import handle_mako_error
5 from pylons_app.config.routing import make_map
5 from pylons_app.config.routing import make_map
6 from pylons_app.lib.auth import set_available_permissions, set_base_path
6 from pylons_app.lib.auth import set_available_permissions, set_base_path
7 from pylons_app.lib.utils import repo2db_mapper, make_ui, set_hg_app_config
7 from pylons_app.lib.utils import repo2db_mapper, make_ui, set_hg_app_config
8 from pylons_app.model import init_model
8 from pylons_app.model import init_model
9 from pylons_app.model.hg_model import _get_repos_cached_initial
9 from pylons_app.model.hg_model import _get_repos_cached_initial
10 from sqlalchemy import engine_from_config
10 from sqlalchemy import engine_from_config
11 import logging
11 import logging
12 import os
12 import os
13 import pylons_app.lib.app_globals as app_globals
13 import pylons_app.lib.app_globals as app_globals
14 import pylons_app.lib.helpers
14 import pylons_app.lib.helpers
15
15
16 log = logging.getLogger(__name__)
16 log = logging.getLogger(__name__)
17
17
18 def load_environment(global_conf, app_conf, initial=False):
18 def load_environment(global_conf, app_conf, initial=False):
19 """Configure the Pylons environment via the ``pylons.config``
19 """Configure the Pylons environment via the ``pylons.config``
20 object
20 object
21 """
21 """
22 config = PylonsConfig()
22 config = PylonsConfig()
23
23
24 # Pylons paths
24 # Pylons paths
25 root = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
25 root = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
26 paths = dict(root=root,
26 paths = dict(root=root,
27 controllers=os.path.join(root, 'controllers'),
27 controllers=os.path.join(root, 'controllers'),
28 static_files=os.path.join(root, 'public'),
28 static_files=os.path.join(root, 'public'),
29 templates=[os.path.join(root, 'templates')])
29 templates=[os.path.join(root, 'templates')])
30
30
31 # Initialize config with the basic options
31 # Initialize config with the basic options
32 config.init_app(global_conf, app_conf, package='pylons_app', paths=paths)
32 config.init_app(global_conf, app_conf, package='pylons_app', paths=paths)
33
33
34 config['routes.map'] = make_map(config)
34 config['routes.map'] = make_map(config)
35 config['pylons.app_globals'] = app_globals.Globals(config)
35 config['pylons.app_globals'] = app_globals.Globals(config)
36 config['pylons.h'] = pylons_app.lib.helpers
36 config['pylons.h'] = pylons_app.lib.helpers
37
37
38 # Setup cache object as early as possible
38 # Setup cache object as early as possible
39 import pylons
39 import pylons
40 pylons.cache._push_object(config['pylons.app_globals'].cache)
40 pylons.cache._push_object(config['pylons.app_globals'].cache)
41
41
42 # Create the Mako TemplateLookup, with the default auto-escaping
42 # Create the Mako TemplateLookup, with the default auto-escaping
43 config['pylons.app_globals'].mako_lookup = TemplateLookup(
43 config['pylons.app_globals'].mako_lookup = TemplateLookup(
44 directories=paths['templates'],
44 directories=paths['templates'],
45 error_handler=handle_mako_error,
45 error_handler=handle_mako_error,
46 module_directory=os.path.join(app_conf['cache_dir'], 'templates'),
46 module_directory=os.path.join(app_conf['cache_dir'], 'templates'),
47 input_encoding='utf-8', default_filters=['escape'],
47 input_encoding='utf-8', default_filters=['escape'],
48 imports=['from webhelpers.html import escape'])
48 imports=['from webhelpers.html import escape'])
49
49
50 #sets the c attribute access when don't existing attribute are accessed
50 #sets the c attribute access when don't existing attribute are accessed
51 config['pylons.strict_tmpl_context'] = True
51 config['pylons.strict_tmpl_context'] = True
52 test = os.path.split(config['__file__'])[-1] == 'tests.ini'
52 test = os.path.split(config['__file__'])[-1] == 'test.ini'
53 if test:
54 from pylons_app.lib.utils import create_test_env, create_test_index
55 create_test_env('/tmp', config)
56 create_test_index('/tmp/*', True)
57
53 #MULTIPLE DB configs
58 #MULTIPLE DB configs
54 # Setup the SQLAlchemy database engine
59 # Setup the SQLAlchemy database engine
55 if config['debug'] and not test:
60 if config['debug'] and not test:
56 #use query time debugging.
61 #use query time debugging.
57 from pylons_app.lib.timerproxy import TimerProxy
62 from pylons_app.lib.timerproxy import TimerProxy
58 sa_engine_db1 = engine_from_config(config, 'sqlalchemy.db1.',
63 sa_engine_db1 = engine_from_config(config, 'sqlalchemy.db1.',
59 proxy=TimerProxy())
64 proxy=TimerProxy())
60 else:
65 else:
61 sa_engine_db1 = engine_from_config(config, 'sqlalchemy.db1.')
66 sa_engine_db1 = engine_from_config(config, 'sqlalchemy.db1.')
62
67
63 init_model(sa_engine_db1)
68 init_model(sa_engine_db1)
64 #init baseui
69 #init baseui
65 config['pylons.app_globals'].baseui = make_ui('db')
70 config['pylons.app_globals'].baseui = make_ui('db')
66
71
67 repo2db_mapper(_get_repos_cached_initial(config['pylons.app_globals'], initial))
72 repo2db_mapper(_get_repos_cached_initial(config['pylons.app_globals'], initial))
68 set_available_permissions(config)
73 set_available_permissions(config)
69 set_base_path(config)
74 set_base_path(config)
70 set_hg_app_config(config)
75 set_hg_app_config(config)
71 # CONFIGURATION OPTIONS HERE (note: all config options will override
76 # CONFIGURATION OPTIONS HERE (note: all config options will override
72 # any Pylons config options)
77 # any Pylons config options)
73
78
74 return config
79 return config
@@ -1,167 +1,171 b''
1 """Routes configuration
1 """Routes configuration
2
2
3 The more specific and detailed routes should be defined first so they
3 The more specific and detailed routes should be defined first so they
4 may take precedent over the more generic routes. For more information
4 may take precedent over the more generic routes. For more information
5 refer to the routes manual at http://routes.groovie.org/docs/
5 refer to the routes manual at http://routes.groovie.org/docs/
6 """
6 """
7 from __future__ import with_statement
7 from __future__ import with_statement
8 from routes import Mapper
8 from routes import Mapper
9 from pylons_app.lib.utils import check_repo_fast as cr
9 from pylons_app.lib.utils import check_repo_fast as cr
10
10
11 def make_map(config):
11 def make_map(config):
12 """Create, configure and return the routes Mapper"""
12 """Create, configure and return the routes Mapper"""
13 map = Mapper(directory=config['pylons.paths']['controllers'],
13 map = Mapper(directory=config['pylons.paths']['controllers'],
14 always_scan=config['debug'])
14 always_scan=config['debug'])
15 map.minimization = False
15 map.minimization = False
16 map.explicit = False
16 map.explicit = False
17
17
18 # The ErrorController route (handles 404/500 error pages); it should
18 # The ErrorController route (handles 404/500 error pages); it should
19 # likely stay at the top, ensuring it can always be resolved
19 # likely stay at the top, ensuring it can always be resolved
20 map.connect('/error/{action}', controller='error')
20 map.connect('/error/{action}', controller='error')
21 map.connect('/error/{action}/{id}', controller='error')
21 map.connect('/error/{action}/{id}', controller='error')
22
22
23 # CUSTOM ROUTES HERE
23 # CUSTOM ROUTES HERE
24 map.connect('hg_home', '/', controller='hg', action='index')
24 map.connect('hg_home', '/', controller='hg', action='index')
25
25
26 def check_repo(environ, match_dict):
26 def check_repo(environ, match_dict):
27 """
27 """
28 check for valid repository for proper 404 handling
28 check for valid repository for proper 404 handling
29 @param environ:
29 @param environ:
30 @param match_dict:
30 @param match_dict:
31 """
31 """
32 repo_name = match_dict.get('repo_name')
32 repo_name = match_dict.get('repo_name')
33 return not cr(repo_name, config['base_path'])
33 return not cr(repo_name, config['base_path'])
34
34
35 #REST REPO MAP
35 #REST REPO MAP
36 with map.submapper(path_prefix='/_admin', controller='admin/repos') as m:
36 with map.submapper(path_prefix='/_admin', controller='admin/repos') as m:
37 m.connect("repos", "/repos",
37 m.connect("repos", "/repos",
38 action="create", conditions=dict(method=["POST"]))
38 action="create", conditions=dict(method=["POST"]))
39 m.connect("repos", "/repos",
39 m.connect("repos", "/repos",
40 action="index", conditions=dict(method=["GET"]))
40 action="index", conditions=dict(method=["GET"]))
41 m.connect("formatted_repos", "/repos.{format}",
41 m.connect("formatted_repos", "/repos.{format}",
42 action="index",
42 action="index",
43 conditions=dict(method=["GET"]))
43 conditions=dict(method=["GET"]))
44 m.connect("new_repo", "/repos/new",
44 m.connect("new_repo", "/repos/new",
45 action="new", conditions=dict(method=["GET"]))
45 action="new", conditions=dict(method=["GET"]))
46 m.connect("formatted_new_repo", "/repos/new.{format}",
46 m.connect("formatted_new_repo", "/repos/new.{format}",
47 action="new", conditions=dict(method=["GET"]))
47 action="new", conditions=dict(method=["GET"]))
48 m.connect("/repos/{repo_name:.*}",
48 m.connect("/repos/{repo_name:.*}",
49 action="update", conditions=dict(method=["PUT"],
49 action="update", conditions=dict(method=["PUT"],
50 function=check_repo))
50 function=check_repo))
51 m.connect("/repos/{repo_name:.*}",
51 m.connect("/repos/{repo_name:.*}",
52 action="delete", conditions=dict(method=["DELETE"],
52 action="delete", conditions=dict(method=["DELETE"],
53 function=check_repo))
53 function=check_repo))
54 m.connect("edit_repo", "/repos/{repo_name:.*}/edit",
54 m.connect("edit_repo", "/repos/{repo_name:.*}/edit",
55 action="edit", conditions=dict(method=["GET"],
55 action="edit", conditions=dict(method=["GET"],
56 function=check_repo))
56 function=check_repo))
57 m.connect("formatted_edit_repo", "/repos/{repo_name:.*}.{format}/edit",
57 m.connect("formatted_edit_repo", "/repos/{repo_name:.*}.{format}/edit",
58 action="edit", conditions=dict(method=["GET"],
58 action="edit", conditions=dict(method=["GET"],
59 function=check_repo))
59 function=check_repo))
60 m.connect("repo", "/repos/{repo_name:.*}",
60 m.connect("repo", "/repos/{repo_name:.*}",
61 action="show", conditions=dict(method=["GET"],
61 action="show", conditions=dict(method=["GET"],
62 function=check_repo))
62 function=check_repo))
63 m.connect("formatted_repo", "/repos/{repo_name:.*}.{format}",
63 m.connect("formatted_repo", "/repos/{repo_name:.*}.{format}",
64 action="show", conditions=dict(method=["GET"],
64 action="show", conditions=dict(method=["GET"],
65 function=check_repo))
65 function=check_repo))
66 #ajax delete repo perm user
66 #ajax delete repo perm user
67 m.connect('delete_repo_user', "/repos_delete_user/{repo_name:.*}",
67 m.connect('delete_repo_user', "/repos_delete_user/{repo_name:.*}",
68 action="delete_perm_user", conditions=dict(method=["DELETE"],
68 action="delete_perm_user", conditions=dict(method=["DELETE"],
69 function=check_repo))
69 function=check_repo))
70
70
71 map.resource('user', 'users', controller='admin/users', path_prefix='/_admin')
71 map.resource('user', 'users', controller='admin/users', path_prefix='/_admin')
72 map.resource('permission', 'permissions', controller='admin/permissions', path_prefix='/_admin')
72 map.resource('permission', 'permissions', controller='admin/permissions', path_prefix='/_admin')
73
73
74 #REST SETTINGS MAP
74 #REST SETTINGS MAP
75 with map.submapper(path_prefix='/_admin', controller='admin/settings') as m:
75 with map.submapper(path_prefix='/_admin', controller='admin/settings') as m:
76 m.connect("admin_settings", "/settings",
76 m.connect("admin_settings", "/settings",
77 action="create", conditions=dict(method=["POST"]))
77 action="create", conditions=dict(method=["POST"]))
78 m.connect("admin_settings", "/settings",
78 m.connect("admin_settings", "/settings",
79 action="index", conditions=dict(method=["GET"]))
79 action="index", conditions=dict(method=["GET"]))
80 m.connect("formatted_admin_settings", "/settings.{format}",
80 m.connect("formatted_admin_settings", "/settings.{format}",
81 action="index", conditions=dict(method=["GET"]))
81 action="index", conditions=dict(method=["GET"]))
82 m.connect("admin_new_setting", "/settings/new",
82 m.connect("admin_new_setting", "/settings/new",
83 action="new", conditions=dict(method=["GET"]))
83 action="new", conditions=dict(method=["GET"]))
84 m.connect("formatted_admin_new_setting", "/settings/new.{format}",
84 m.connect("formatted_admin_new_setting", "/settings/new.{format}",
85 action="new", conditions=dict(method=["GET"]))
85 action="new", conditions=dict(method=["GET"]))
86 m.connect("/settings/{setting_id}",
86 m.connect("/settings/{setting_id}",
87 action="update", conditions=dict(method=["PUT"]))
87 action="update", conditions=dict(method=["PUT"]))
88 m.connect("/settings/{setting_id}",
88 m.connect("/settings/{setting_id}",
89 action="delete", conditions=dict(method=["DELETE"]))
89 action="delete", conditions=dict(method=["DELETE"]))
90 m.connect("admin_edit_setting", "/settings/{setting_id}/edit",
90 m.connect("admin_edit_setting", "/settings/{setting_id}/edit",
91 action="edit", conditions=dict(method=["GET"]))
91 action="edit", conditions=dict(method=["GET"]))
92 m.connect("formatted_admin_edit_setting", "/settings/{setting_id}.{format}/edit",
92 m.connect("formatted_admin_edit_setting", "/settings/{setting_id}.{format}/edit",
93 action="edit", conditions=dict(method=["GET"]))
93 action="edit", conditions=dict(method=["GET"]))
94 m.connect("admin_setting", "/settings/{setting_id}",
94 m.connect("admin_setting", "/settings/{setting_id}",
95 action="show", conditions=dict(method=["GET"]))
95 action="show", conditions=dict(method=["GET"]))
96 m.connect("formatted_admin_setting", "/settings/{setting_id}.{format}",
96 m.connect("formatted_admin_setting", "/settings/{setting_id}.{format}",
97 action="show", conditions=dict(method=["GET"]))
97 action="show", conditions=dict(method=["GET"]))
98 m.connect("admin_settings_my_account", "/my_account",
98 m.connect("admin_settings_my_account", "/my_account",
99 action="my_account", conditions=dict(method=["GET"]))
99 action="my_account", conditions=dict(method=["GET"]))
100 m.connect("admin_settings_my_account_update", "/my_account_update",
100 m.connect("admin_settings_my_account_update", "/my_account_update",
101 action="my_account_update", conditions=dict(method=["PUT"]))
101 action="my_account_update", conditions=dict(method=["PUT"]))
102 m.connect("admin_settings_create_repository", "/create_repository",
102 m.connect("admin_settings_create_repository", "/create_repository",
103 action="create_repository", conditions=dict(method=["GET"]))
103 action="create_repository", conditions=dict(method=["GET"]))
104
104
105 #ADMIN
105 #ADMIN
106 with map.submapper(path_prefix='/_admin', controller='admin/admin') as m:
106 with map.submapper(path_prefix='/_admin', controller='admin/admin') as m:
107 m.connect('admin_home', '', action='index')#main page
107 m.connect('admin_home', '', action='index')#main page
108 m.connect('admin_add_repo', '/add_repo/{new_repo:[a-z0-9\. _-]*}',
108 m.connect('admin_add_repo', '/add_repo/{new_repo:[a-z0-9\. _-]*}',
109 action='add_repo')
109 action='add_repo')
110 #SEARCH
110 #SEARCH
111 map.connect('search', '/_admin/search', controller='search')
111 map.connect('search', '/_admin/search', controller='search')
112
112
113 #LOGIN/LOGOUT
113 #LOGIN/LOGOUT/REGISTER/SIGN IN
114 map.connect('login_home', '/_admin/login', controller='login')
114 map.connect('login_home', '/_admin/login', controller='login')
115 map.connect('logout_home', '/_admin/logout', controller='login', action='logout')
115 map.connect('logout_home', '/_admin/logout', controller='login', action='logout')
116 map.connect('register', '/_admin/register', controller='login', action='register')
116 map.connect('register', '/_admin/register', controller='login', action='register')
117 map.connect('reset_password', '/_admin/password_reset', controller='login', action='password_reset')
117
118
118 #FEEDS
119 #FEEDS
119 map.connect('rss_feed_home', '/{repo_name:.*}/feed/rss',
120 map.connect('rss_feed_home', '/{repo_name:.*}/feed/rss',
120 controller='feed', action='rss',
121 controller='feed', action='rss',
121 conditions=dict(function=check_repo))
122 conditions=dict(function=check_repo))
122 map.connect('atom_feed_home', '/{repo_name:.*}/feed/atom',
123 map.connect('atom_feed_home', '/{repo_name:.*}/feed/atom',
123 controller='feed', action='atom',
124 controller='feed', action='atom',
124 conditions=dict(function=check_repo))
125 conditions=dict(function=check_repo))
125
126
126
127
127 #OTHERS
128 #OTHERS
128 map.connect('changeset_home', '/{repo_name:.*}/changeset/{revision}',
129 map.connect('changeset_home', '/{repo_name:.*}/changeset/{revision}',
129 controller='changeset', revision='tip',
130 controller='changeset', revision='tip',
130 conditions=dict(function=check_repo))
131 conditions=dict(function=check_repo))
131 map.connect('raw_changeset_home', '/{repo_name:.*}/raw-changeset/{revision}',
132 map.connect('raw_changeset_home', '/{repo_name:.*}/raw-changeset/{revision}',
132 controller='changeset',action='raw_changeset', revision='tip',
133 controller='changeset', action='raw_changeset', revision='tip',
133 conditions=dict(function=check_repo))
134 conditions=dict(function=check_repo))
134 map.connect('summary_home', '/{repo_name:.*}/summary',
135 map.connect('summary_home', '/{repo_name:.*}/summary',
135 controller='summary', conditions=dict(function=check_repo))
136 controller='summary', conditions=dict(function=check_repo))
136 map.connect('shortlog_home', '/{repo_name:.*}/shortlog',
137 map.connect('shortlog_home', '/{repo_name:.*}/shortlog',
137 controller='shortlog', conditions=dict(function=check_repo))
138 controller='shortlog', conditions=dict(function=check_repo))
138 map.connect('branches_home', '/{repo_name:.*}/branches',
139 map.connect('branches_home', '/{repo_name:.*}/branches',
139 controller='branches', conditions=dict(function=check_repo))
140 controller='branches', conditions=dict(function=check_repo))
140 map.connect('tags_home', '/{repo_name:.*}/tags',
141 map.connect('tags_home', '/{repo_name:.*}/tags',
141 controller='tags', conditions=dict(function=check_repo))
142 controller='tags', conditions=dict(function=check_repo))
142 map.connect('changelog_home', '/{repo_name:.*}/changelog',
143 map.connect('changelog_home', '/{repo_name:.*}/changelog',
143 controller='changelog', conditions=dict(function=check_repo))
144 controller='changelog', conditions=dict(function=check_repo))
144 map.connect('files_home', '/{repo_name:.*}/files/{revision}/{f_path:.*}',
145 map.connect('files_home', '/{repo_name:.*}/files/{revision}/{f_path:.*}',
145 controller='files', revision='tip', f_path='',
146 controller='files', revision='tip', f_path='',
146 conditions=dict(function=check_repo))
147 conditions=dict(function=check_repo))
147 map.connect('files_diff_home', '/{repo_name:.*}/diff/{f_path:.*}',
148 map.connect('files_diff_home', '/{repo_name:.*}/diff/{f_path:.*}',
148 controller='files', action='diff', revision='tip', f_path='',
149 controller='files', action='diff', revision='tip', f_path='',
149 conditions=dict(function=check_repo))
150 conditions=dict(function=check_repo))
150 map.connect('files_raw_home', '/{repo_name:.*}/rawfile/{revision}/{f_path:.*}',
151 map.connect('files_rawfile_home', '/{repo_name:.*}/rawfile/{revision}/{f_path:.*}',
151 controller='files', action='rawfile', revision='tip', f_path='',
152 controller='files', action='rawfile', revision='tip', f_path='',
152 conditions=dict(function=check_repo))
153 conditions=dict(function=check_repo))
154 map.connect('files_raw_home', '/{repo_name:.*}/raw/{revision}/{f_path:.*}',
155 controller='files', action='raw', revision='tip', f_path='',
156 conditions=dict(function=check_repo))
153 map.connect('files_annotate_home', '/{repo_name:.*}/annotate/{revision}/{f_path:.*}',
157 map.connect('files_annotate_home', '/{repo_name:.*}/annotate/{revision}/{f_path:.*}',
154 controller='files', action='annotate', revision='tip', f_path='',
158 controller='files', action='annotate', revision='tip', f_path='',
155 conditions=dict(function=check_repo))
159 conditions=dict(function=check_repo))
156 map.connect('files_archive_home', '/{repo_name:.*}/archive/{revision}/{fileformat}',
160 map.connect('files_archive_home', '/{repo_name:.*}/archive/{revision}/{fileformat}',
157 controller='files', action='archivefile', revision='tip',
161 controller='files', action='archivefile', revision='tip',
158 conditions=dict(function=check_repo))
162 conditions=dict(function=check_repo))
159 map.connect('repo_settings_update', '/{repo_name:.*}/settings',
163 map.connect('repo_settings_update', '/{repo_name:.*}/settings',
160 controller='settings', action="update",
164 controller='settings', action="update",
161 conditions=dict(method=["PUT"], function=check_repo))
165 conditions=dict(method=["PUT"], function=check_repo))
162 map.connect('repo_settings_home', '/{repo_name:.*}/settings',
166 map.connect('repo_settings_home', '/{repo_name:.*}/settings',
163 controller='settings', action='index',
167 controller='settings', action='index',
164 conditions=dict(function=check_repo))
168 conditions=dict(function=check_repo))
165
169
166
170
167 return map
171 return map
@@ -1,286 +1,298 b''
1 #!/usr/bin/env python
1 #!/usr/bin/env python
2 # encoding: utf-8
2 # encoding: utf-8
3 # settings controller for pylons
3 # settings controller for pylons
4 # Copyright (C) 2009-2010 Marcin Kuzminski <marcin@python-works.com>
4 # Copyright (C) 2009-2010 Marcin Kuzminski <marcin@python-works.com>
5 #
5 #
6 # This program is free software; you can redistribute it and/or
6 # This program is free software; you can redistribute it and/or
7 # modify it under the terms of the GNU General Public License
7 # modify it under the terms of the GNU General Public License
8 # as published by the Free Software Foundation; version 2
8 # as published by the Free Software Foundation; version 2
9 # of the License or (at your opinion) any later version of the license.
9 # of the License or (at your opinion) any later version of the license.
10 #
10 #
11 # This program is distributed in the hope that it will be useful,
11 # This program is distributed in the hope that it will be useful,
12 # but WITHOUT ANY WARRANTY; without even the implied warranty of
12 # but WITHOUT ANY WARRANTY; without even the implied warranty of
13 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 # GNU General Public License for more details.
14 # GNU General Public License for more details.
15 #
15 #
16 # You should have received a copy of the GNU General Public License
16 # You should have received a copy of the GNU General Public License
17 # along with this program; if not, write to the Free Software
17 # along with this program; if not, write to the Free Software
18 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
18 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
19 # MA 02110-1301, USA.
19 # MA 02110-1301, USA.
20 """
20 """
21 Created on July 14, 2010
21 Created on July 14, 2010
22 settings controller for pylons
22 settings controller for pylons
23 @author: marcink
23 @author: marcink
24 """
24 """
25 from formencode import htmlfill
25 from formencode import htmlfill
26 from pylons import request, session, tmpl_context as c, url, app_globals as g, \
26 from pylons import request, session, tmpl_context as c, url, app_globals as g, \
27 config
27 config
28 from pylons.controllers.util import abort, redirect
28 from pylons.controllers.util import abort, redirect
29 from pylons.i18n.translation import _
29 from pylons.i18n.translation import _
30 from pylons_app.lib import helpers as h
30 from pylons_app.lib import helpers as h
31 from pylons_app.lib.auth import LoginRequired, HasPermissionAllDecorator, \
31 from pylons_app.lib.auth import LoginRequired, HasPermissionAllDecorator, \
32 HasPermissionAnyDecorator
32 HasPermissionAnyDecorator
33 from pylons_app.lib.base import BaseController, render
33 from pylons_app.lib.base import BaseController, render
34 from pylons_app.lib.utils import repo2db_mapper, invalidate_cache, \
34 from pylons_app.lib.utils import repo2db_mapper, invalidate_cache, \
35 set_hg_app_config, get_hg_settings, get_hg_ui_settings, make_ui
35 set_hg_app_config, get_hg_settings, get_hg_ui_settings, make_ui
36 from pylons_app.model.db import User, UserLog, HgAppSettings, HgAppUi
36 from pylons_app.model.db import User, UserLog, HgAppSettings, HgAppUi
37 from pylons_app.model.forms import UserForm, ApplicationSettingsForm, \
37 from pylons_app.model.forms import UserForm, ApplicationSettingsForm, \
38 ApplicationUiSettingsForm
38 ApplicationUiSettingsForm
39 from pylons_app.model.hg_model import HgModel
39 from pylons_app.model.hg_model import HgModel
40 from pylons_app.model.user_model import UserModel
40 from pylons_app.model.user_model import UserModel
41 from pylons_app.lib.celerylib import tasks, run_task
41 import formencode
42 import formencode
42 import logging
43 import logging
43 import traceback
44 import traceback
44
45
45 log = logging.getLogger(__name__)
46 log = logging.getLogger(__name__)
46
47
47
48
48 class SettingsController(BaseController):
49 class SettingsController(BaseController):
49 """REST Controller styled on the Atom Publishing Protocol"""
50 """REST Controller styled on the Atom Publishing Protocol"""
50 # To properly map this controller, ensure your config/routing.py
51 # To properly map this controller, ensure your config/routing.py
51 # file has a resource setup:
52 # file has a resource setup:
52 # map.resource('setting', 'settings', controller='admin/settings',
53 # map.resource('setting', 'settings', controller='admin/settings',
53 # path_prefix='/admin', name_prefix='admin_')
54 # path_prefix='/admin', name_prefix='admin_')
54
55
55
56
56 @LoginRequired()
57 @LoginRequired()
57 def __before__(self):
58 def __before__(self):
58 c.admin_user = session.get('admin_user')
59 c.admin_user = session.get('admin_user')
59 c.admin_username = session.get('admin_username')
60 c.admin_username = session.get('admin_username')
60 super(SettingsController, self).__before__()
61 super(SettingsController, self).__before__()
61
62
62
63
63 @HasPermissionAllDecorator('hg.admin')
64 @HasPermissionAllDecorator('hg.admin')
64 def index(self, format='html'):
65 def index(self, format='html'):
65 """GET /admin/settings: All items in the collection"""
66 """GET /admin/settings: All items in the collection"""
66 # url('admin_settings')
67 # url('admin_settings')
67
68
68 defaults = get_hg_settings()
69 defaults = get_hg_settings()
69 defaults.update(get_hg_ui_settings())
70 defaults.update(get_hg_ui_settings())
70 return htmlfill.render(
71 return htmlfill.render(
71 render('admin/settings/settings.html'),
72 render('admin/settings/settings.html'),
72 defaults=defaults,
73 defaults=defaults,
73 encoding="UTF-8",
74 encoding="UTF-8",
74 force_defaults=False
75 force_defaults=False
75 )
76 )
76
77
77 @HasPermissionAllDecorator('hg.admin')
78 @HasPermissionAllDecorator('hg.admin')
78 def create(self):
79 def create(self):
79 """POST /admin/settings: Create a new item"""
80 """POST /admin/settings: Create a new item"""
80 # url('admin_settings')
81 # url('admin_settings')
81
82
82 @HasPermissionAllDecorator('hg.admin')
83 @HasPermissionAllDecorator('hg.admin')
83 def new(self, format='html'):
84 def new(self, format='html'):
84 """GET /admin/settings/new: Form to create a new item"""
85 """GET /admin/settings/new: Form to create a new item"""
85 # url('admin_new_setting')
86 # url('admin_new_setting')
86
87
87 @HasPermissionAllDecorator('hg.admin')
88 @HasPermissionAllDecorator('hg.admin')
88 def update(self, setting_id):
89 def update(self, setting_id):
89 """PUT /admin/settings/setting_id: Update an existing item"""
90 """PUT /admin/settings/setting_id: Update an existing item"""
90 # Forms posted to this method should contain a hidden field:
91 # Forms posted to this method should contain a hidden field:
91 # <input type="hidden" name="_method" value="PUT" />
92 # <input type="hidden" name="_method" value="PUT" />
92 # Or using helpers:
93 # Or using helpers:
93 # h.form(url('admin_setting', setting_id=ID),
94 # h.form(url('admin_setting', setting_id=ID),
94 # method='put')
95 # method='put')
95 # url('admin_setting', setting_id=ID)
96 # url('admin_setting', setting_id=ID)
96 if setting_id == 'mapping':
97 if setting_id == 'mapping':
97 rm_obsolete = request.POST.get('destroy', False)
98 rm_obsolete = request.POST.get('destroy', False)
98 log.debug('Rescanning directories with destroy=%s', rm_obsolete)
99 log.debug('Rescanning directories with destroy=%s', rm_obsolete)
99
100
100 initial = HgModel.repo_scan(g.paths[0][0], g.paths[0][1], g.baseui)
101 initial = HgModel.repo_scan(g.paths[0][0], g.paths[0][1], g.baseui)
101 repo2db_mapper(initial, rm_obsolete)
102 repo2db_mapper(initial, rm_obsolete)
102 invalidate_cache('cached_repo_list')
103 invalidate_cache('cached_repo_list')
103 h.flash(_('Repositories sucessfully rescanned'), category='success')
104 h.flash(_('Repositories sucessfully rescanned'), category='success')
104
105
106 if setting_id == 'whoosh':
107 repo_location = get_hg_ui_settings()['paths_root_path']
108 full_index = request.POST.get('full_index', False)
109 task = run_task(tasks.whoosh_index, repo_location, full_index)
110
111 h.flash(_('Whoosh reindex task scheduled'), category='success')
105 if setting_id == 'global':
112 if setting_id == 'global':
106
113
107 application_form = ApplicationSettingsForm()()
114 application_form = ApplicationSettingsForm()()
108 try:
115 try:
109 form_result = application_form.to_python(dict(request.POST))
116 form_result = application_form.to_python(dict(request.POST))
110
117
111 try:
118 try:
112 hgsettings1 = self.sa.query(HgAppSettings)\
119 hgsettings1 = self.sa.query(HgAppSettings)\
113 .filter(HgAppSettings.app_settings_name == 'title').one()
120 .filter(HgAppSettings.app_settings_name == 'title').one()
114 hgsettings1.app_settings_value = form_result['hg_app_title']
121 hgsettings1.app_settings_value = form_result['hg_app_title']
115
122
116 hgsettings2 = self.sa.query(HgAppSettings)\
123 hgsettings2 = self.sa.query(HgAppSettings)\
117 .filter(HgAppSettings.app_settings_name == 'realm').one()
124 .filter(HgAppSettings.app_settings_name == 'realm').one()
118 hgsettings2.app_settings_value = form_result['hg_app_realm']
125 hgsettings2.app_settings_value = form_result['hg_app_realm']
119
126
120
127
121 self.sa.add(hgsettings1)
128 self.sa.add(hgsettings1)
122 self.sa.add(hgsettings2)
129 self.sa.add(hgsettings2)
123 self.sa.commit()
130 self.sa.commit()
124 set_hg_app_config(config)
131 set_hg_app_config(config)
125 h.flash(_('Updated application settings'),
132 h.flash(_('Updated application settings'),
126 category='success')
133 category='success')
127
134
128 except:
135 except:
129 log.error(traceback.format_exc())
136 log.error(traceback.format_exc())
130 h.flash(_('error occured during updating application settings'),
137 h.flash(_('error occured during updating application settings'),
131 category='error')
138 category='error')
132
139
133 self.sa.rollback()
140 self.sa.rollback()
134
141
135
142
136 except formencode.Invalid as errors:
143 except formencode.Invalid as errors:
137 return htmlfill.render(
144 return htmlfill.render(
138 render('admin/settings/settings.html'),
145 render('admin/settings/settings.html'),
139 defaults=errors.value,
146 defaults=errors.value,
140 errors=errors.error_dict or {},
147 errors=errors.error_dict or {},
141 prefix_error=False,
148 prefix_error=False,
142 encoding="UTF-8")
149 encoding="UTF-8")
143
150
144 if setting_id == 'mercurial':
151 if setting_id == 'mercurial':
145 application_form = ApplicationUiSettingsForm()()
152 application_form = ApplicationUiSettingsForm()()
146 try:
153 try:
147 form_result = application_form.to_python(dict(request.POST))
154 form_result = application_form.to_python(dict(request.POST))
148
155
149 try:
156 try:
150
157
151 hgsettings1 = self.sa.query(HgAppUi)\
158 hgsettings1 = self.sa.query(HgAppUi)\
152 .filter(HgAppUi.ui_key == 'push_ssl').one()
159 .filter(HgAppUi.ui_key == 'push_ssl').one()
153 hgsettings1.ui_value = form_result['web_push_ssl']
160 hgsettings1.ui_value = form_result['web_push_ssl']
154
161
155 hgsettings2 = self.sa.query(HgAppUi)\
162 hgsettings2 = self.sa.query(HgAppUi)\
156 .filter(HgAppUi.ui_key == '/').one()
163 .filter(HgAppUi.ui_key == '/').one()
157 hgsettings2.ui_value = form_result['paths_root_path']
164 hgsettings2.ui_value = form_result['paths_root_path']
158
165
159
166
160 #HOOKS
167 #HOOKS
161 hgsettings3 = self.sa.query(HgAppUi)\
168 hgsettings3 = self.sa.query(HgAppUi)\
162 .filter(HgAppUi.ui_key == 'changegroup.update').one()
169 .filter(HgAppUi.ui_key == 'changegroup.update').one()
163 hgsettings3.ui_active = bool(form_result['hooks_changegroup_update'])
170 hgsettings3.ui_active = bool(form_result['hooks_changegroup_update'])
164
171
165 hgsettings4 = self.sa.query(HgAppUi)\
172 hgsettings4 = self.sa.query(HgAppUi)\
166 .filter(HgAppUi.ui_key == 'changegroup.repo_size').one()
173 .filter(HgAppUi.ui_key == 'changegroup.repo_size').one()
167 hgsettings4.ui_active = bool(form_result['hooks_changegroup_repo_size'])
174 hgsettings4.ui_active = bool(form_result['hooks_changegroup_repo_size'])
168
175
169
176
170
177
171
178
172 self.sa.add(hgsettings1)
179 self.sa.add(hgsettings1)
173 self.sa.add(hgsettings2)
180 self.sa.add(hgsettings2)
174 self.sa.add(hgsettings3)
181 self.sa.add(hgsettings3)
175 self.sa.add(hgsettings4)
182 self.sa.add(hgsettings4)
176 self.sa.commit()
183 self.sa.commit()
177
184
178 h.flash(_('Updated mercurial settings'),
185 h.flash(_('Updated mercurial settings'),
179 category='success')
186 category='success')
180
187
181 except:
188 except:
182 log.error(traceback.format_exc())
189 log.error(traceback.format_exc())
183 h.flash(_('error occured during updating application settings'),
190 h.flash(_('error occured during updating application settings'),
184 category='error')
191 category='error')
185
192
186 self.sa.rollback()
193 self.sa.rollback()
187
194
188
195
189 except formencode.Invalid as errors:
196 except formencode.Invalid as errors:
190 return htmlfill.render(
197 return htmlfill.render(
191 render('admin/settings/settings.html'),
198 render('admin/settings/settings.html'),
192 defaults=errors.value,
199 defaults=errors.value,
193 errors=errors.error_dict or {},
200 errors=errors.error_dict or {},
194 prefix_error=False,
201 prefix_error=False,
195 encoding="UTF-8")
202 encoding="UTF-8")
196
203
197
204
198
205
199 return redirect(url('admin_settings'))
206 return redirect(url('admin_settings'))
200
207
201 @HasPermissionAllDecorator('hg.admin')
208 @HasPermissionAllDecorator('hg.admin')
202 def delete(self, setting_id):
209 def delete(self, setting_id):
203 """DELETE /admin/settings/setting_id: Delete an existing item"""
210 """DELETE /admin/settings/setting_id: Delete an existing item"""
204 # Forms posted to this method should contain a hidden field:
211 # Forms posted to this method should contain a hidden field:
205 # <input type="hidden" name="_method" value="DELETE" />
212 # <input type="hidden" name="_method" value="DELETE" />
206 # Or using helpers:
213 # Or using helpers:
207 # h.form(url('admin_setting', setting_id=ID),
214 # h.form(url('admin_setting', setting_id=ID),
208 # method='delete')
215 # method='delete')
209 # url('admin_setting', setting_id=ID)
216 # url('admin_setting', setting_id=ID)
210
217
211 @HasPermissionAllDecorator('hg.admin')
218 @HasPermissionAllDecorator('hg.admin')
212 def show(self, setting_id, format='html'):
219 def show(self, setting_id, format='html'):
213 """GET /admin/settings/setting_id: Show a specific item"""
220 """GET /admin/settings/setting_id: Show a specific item"""
214 # url('admin_setting', setting_id=ID)
221 # url('admin_setting', setting_id=ID)
215
222
216 @HasPermissionAllDecorator('hg.admin')
223 @HasPermissionAllDecorator('hg.admin')
217 def edit(self, setting_id, format='html'):
224 def edit(self, setting_id, format='html'):
218 """GET /admin/settings/setting_id/edit: Form to edit an existing item"""
225 """GET /admin/settings/setting_id/edit: Form to edit an existing item"""
219 # url('admin_edit_setting', setting_id=ID)
226 # url('admin_edit_setting', setting_id=ID)
220
227
221
228
222 def my_account(self):
229 def my_account(self):
223 """
230 """
224 GET /_admin/my_account Displays info about my account
231 GET /_admin/my_account Displays info about my account
225 """
232 """
226 # url('admin_settings_my_account')
233 # url('admin_settings_my_account')
227 c.user = self.sa.query(User).get(c.hg_app_user.user_id)
234 c.user = self.sa.query(User).get(c.hg_app_user.user_id)
228 c.user_repos = []
235 c.user_repos = []
229 for repo in c.cached_repo_list.values():
236 for repo in c.cached_repo_list.values():
230 if repo.dbrepo.user.username == c.user.username:
237 if repo.dbrepo.user.username == c.user.username:
231 c.user_repos.append(repo)
238 c.user_repos.append(repo)
232
239
233 if c.user.username == 'default':
240 if c.user.username == 'default':
234 h.flash(_("You can't edit this user since it's"
241 h.flash(_("You can't edit this user since it's"
235 " crucial for entire application"), category='warning')
242 " crucial for entire application"), category='warning')
236 return redirect(url('users'))
243 return redirect(url('users'))
237
244
238 defaults = c.user.__dict__
245 defaults = c.user.__dict__
239 return htmlfill.render(
246 return htmlfill.render(
240 render('admin/users/user_edit_my_account.html'),
247 render('admin/users/user_edit_my_account.html'),
241 defaults=defaults,
248 defaults=defaults,
242 encoding="UTF-8",
249 encoding="UTF-8",
243 force_defaults=False
250 force_defaults=False
244 )
251 )
245
252
246 def my_account_update(self):
253 def my_account_update(self):
247 """PUT /_admin/my_account_update: Update an existing item"""
254 """PUT /_admin/my_account_update: Update an existing item"""
248 # Forms posted to this method should contain a hidden field:
255 # Forms posted to this method should contain a hidden field:
249 # <input type="hidden" name="_method" value="PUT" />
256 # <input type="hidden" name="_method" value="PUT" />
250 # Or using helpers:
257 # Or using helpers:
251 # h.form(url('admin_settings_my_account_update'),
258 # h.form(url('admin_settings_my_account_update'),
252 # method='put')
259 # method='put')
253 # url('admin_settings_my_account_update', id=ID)
260 # url('admin_settings_my_account_update', id=ID)
254 user_model = UserModel()
261 user_model = UserModel()
255 uid = c.hg_app_user.user_id
262 uid = c.hg_app_user.user_id
256 _form = UserForm(edit=True, old_data={'user_id':uid})()
263 _form = UserForm(edit=True, old_data={'user_id':uid,
264 'email':c.hg_app_user.email})()
257 form_result = {}
265 form_result = {}
258 try:
266 try:
259 form_result = _form.to_python(dict(request.POST))
267 form_result = _form.to_python(dict(request.POST))
260 user_model.update_my_account(uid, form_result)
268 user_model.update_my_account(uid, form_result)
261 h.flash(_('Your account was updated succesfully'),
269 h.flash(_('Your account was updated succesfully'),
262 category='success')
270 category='success')
263
271
264 except formencode.Invalid as errors:
272 except formencode.Invalid as errors:
265 #c.user = self.sa.query(User).get(c.hg_app_user.user_id)
273 c.user = self.sa.query(User).get(c.hg_app_user.user_id)
274 c.user_repos = []
275 for repo in c.cached_repo_list.values():
276 if repo.dbrepo.user.username == c.user.username:
277 c.user_repos.append(repo)
266 return htmlfill.render(
278 return htmlfill.render(
267 render('admin/users/user_edit_my_account.html'),
279 render('admin/users/user_edit_my_account.html'),
268 defaults=errors.value,
280 defaults=errors.value,
269 errors=errors.error_dict or {},
281 errors=errors.error_dict or {},
270 prefix_error=False,
282 prefix_error=False,
271 encoding="UTF-8")
283 encoding="UTF-8")
272 except Exception:
284 except Exception:
273 log.error(traceback.format_exc())
285 log.error(traceback.format_exc())
274 h.flash(_('error occured during update of user %s') \
286 h.flash(_('error occured during update of user %s') \
275 % form_result.get('username'), category='error')
287 % form_result.get('username'), category='error')
276
288
277 return redirect(url('my_account'))
289 return redirect(url('my_account'))
278
290
279 @HasPermissionAnyDecorator('hg.admin', 'hg.create.repository')
291 @HasPermissionAnyDecorator('hg.admin', 'hg.create.repository')
280 def create_repository(self):
292 def create_repository(self):
281 """GET /_admin/create_repository: Form to create a new item"""
293 """GET /_admin/create_repository: Form to create a new item"""
282 new_repo = request.GET.get('repo', '')
294 new_repo = request.GET.get('repo', '')
283 c.new_repo = h.repo_name_slug(new_repo)
295 c.new_repo = h.repo_name_slug(new_repo)
284
296
285 return render('admin/repos/repo_add_create_repository.html')
297 return render('admin/repos/repo_add_create_repository.html')
286
298
@@ -1,162 +1,166 b''
1 #!/usr/bin/env python
1 #!/usr/bin/env python
2 # encoding: utf-8
2 # encoding: utf-8
3 # users controller for pylons
3 # users controller for pylons
4 # Copyright (C) 2009-2010 Marcin Kuzminski <marcin@python-works.com>
4 # Copyright (C) 2009-2010 Marcin Kuzminski <marcin@python-works.com>
5 #
5 #
6 # This program is free software; you can redistribute it and/or
6 # This program is free software; you can redistribute it and/or
7 # modify it under the terms of the GNU General Public License
7 # modify it under the terms of the GNU General Public License
8 # as published by the Free Software Foundation; version 2
8 # as published by the Free Software Foundation; version 2
9 # of the License or (at your opinion) any later version of the license.
9 # of the License or (at your opinion) any later version of the license.
10 #
10 #
11 # This program is distributed in the hope that it will be useful,
11 # This program is distributed in the hope that it will be useful,
12 # but WITHOUT ANY WARRANTY; without even the implied warranty of
12 # but WITHOUT ANY WARRANTY; without even the implied warranty of
13 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 # GNU General Public License for more details.
14 # GNU General Public License for more details.
15 #
15 #
16 # You should have received a copy of the GNU General Public License
16 # You should have received a copy of the GNU General Public License
17 # along with this program; if not, write to the Free Software
17 # along with this program; if not, write to the Free Software
18 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
18 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
19 # MA 02110-1301, USA.
19 # MA 02110-1301, USA.
20 """
20 """
21 Created on April 4, 2010
21 Created on April 4, 2010
22 users controller for pylons
22 users controller for pylons
23 @author: marcink
23 @author: marcink
24 """
24 """
25
25
26 from formencode import htmlfill
26 from formencode import htmlfill
27 from pylons import request, session, tmpl_context as c, url
27 from pylons import request, session, tmpl_context as c, url
28 from pylons.controllers.util import abort, redirect
28 from pylons.controllers.util import abort, redirect
29 from pylons.i18n.translation import _
29 from pylons.i18n.translation import _
30 from pylons_app.lib import helpers as h
30 from pylons_app.lib import helpers as h
31 from pylons_app.lib.auth import LoginRequired, HasPermissionAllDecorator
31 from pylons_app.lib.auth import LoginRequired, HasPermissionAllDecorator
32 from pylons_app.lib.base import BaseController, render
32 from pylons_app.lib.base import BaseController, render
33 from pylons_app.model.db import User, UserLog
33 from pylons_app.model.db import User, UserLog
34 from pylons_app.model.forms import UserForm
34 from pylons_app.model.forms import UserForm
35 from pylons_app.model.user_model import UserModel, DefaultUserException
35 from pylons_app.model.user_model import UserModel, DefaultUserException
36 import formencode
36 import formencode
37 import logging
37 import logging
38 import traceback
38 import traceback
39
39
40 log = logging.getLogger(__name__)
40 log = logging.getLogger(__name__)
41
41
42 class UsersController(BaseController):
42 class UsersController(BaseController):
43 """REST Controller styled on the Atom Publishing Protocol"""
43 """REST Controller styled on the Atom Publishing Protocol"""
44 # To properly map this controller, ensure your config/routing.py
44 # To properly map this controller, ensure your config/routing.py
45 # file has a resource setup:
45 # file has a resource setup:
46 # map.resource('user', 'users')
46 # map.resource('user', 'users')
47
47
48 @LoginRequired()
48 @LoginRequired()
49 @HasPermissionAllDecorator('hg.admin')
49 @HasPermissionAllDecorator('hg.admin')
50 def __before__(self):
50 def __before__(self):
51 c.admin_user = session.get('admin_user')
51 c.admin_user = session.get('admin_user')
52 c.admin_username = session.get('admin_username')
52 c.admin_username = session.get('admin_username')
53 super(UsersController, self).__before__()
53 super(UsersController, self).__before__()
54
54
55
55
56 def index(self, format='html'):
56 def index(self, format='html'):
57 """GET /users: All items in the collection"""
57 """GET /users: All items in the collection"""
58 # url('users')
58 # url('users')
59
59
60 c.users_list = self.sa.query(User).all()
60 c.users_list = self.sa.query(User).all()
61 return render('admin/users/users.html')
61 return render('admin/users/users.html')
62
62
63 def create(self):
63 def create(self):
64 """POST /users: Create a new item"""
64 """POST /users: Create a new item"""
65 # url('users')
65 # url('users')
66
66
67 user_model = UserModel()
67 user_model = UserModel()
68 login_form = UserForm()()
68 login_form = UserForm()()
69 try:
69 try:
70 form_result = login_form.to_python(dict(request.POST))
70 form_result = login_form.to_python(dict(request.POST))
71 user_model.create(form_result)
71 user_model.create(form_result)
72 h.flash(_('created user %s') % form_result['username'],
72 h.flash(_('created user %s') % form_result['username'],
73 category='success')
73 category='success')
74 except formencode.Invalid as errors:
74 except formencode.Invalid as errors:
75 return htmlfill.render(
75 return htmlfill.render(
76 render('admin/users/user_add.html'),
76 render('admin/users/user_add.html'),
77 defaults=errors.value,
77 defaults=errors.value,
78 errors=errors.error_dict or {},
78 errors=errors.error_dict or {},
79 prefix_error=False,
79 prefix_error=False,
80 encoding="UTF-8")
80 encoding="UTF-8")
81 except Exception:
81 except Exception:
82 log.error(traceback.format_exc())
82 log.error(traceback.format_exc())
83 h.flash(_('error occured during creation of user %s') \
83 h.flash(_('error occured during creation of user %s') \
84 % request.POST.get('username'), category='error')
84 % request.POST.get('username'), category='error')
85 return redirect(url('users'))
85 return redirect(url('users'))
86
86
87 def new(self, format='html'):
87 def new(self, format='html'):
88 """GET /users/new: Form to create a new item"""
88 """GET /users/new: Form to create a new item"""
89 # url('new_user')
89 # url('new_user')
90 return render('admin/users/user_add.html')
90 return render('admin/users/user_add.html')
91
91
92 def update(self, id):
92 def update(self, id):
93 """PUT /users/id: Update an existing item"""
93 """PUT /users/id: Update an existing item"""
94 # Forms posted to this method should contain a hidden field:
94 # Forms posted to this method should contain a hidden field:
95 # <input type="hidden" name="_method" value="PUT" />
95 # <input type="hidden" name="_method" value="PUT" />
96 # Or using helpers:
96 # Or using helpers:
97 # h.form(url('user', id=ID),
97 # h.form(url('user', id=ID),
98 # method='put')
98 # method='put')
99 # url('user', id=ID)
99 # url('user', id=ID)
100 user_model = UserModel()
100 user_model = UserModel()
101 _form = UserForm(edit=True, old_data={'user_id':id})()
101 c.user = user_model.get_user(id)
102
103 _form = UserForm(edit=True, old_data={'user_id':id,
104 'email':c.user.email})()
102 form_result = {}
105 form_result = {}
103 try:
106 try:
104 form_result = _form.to_python(dict(request.POST))
107 form_result = _form.to_python(dict(request.POST))
105 user_model.update(id, form_result)
108 user_model.update(id, form_result)
106 h.flash(_('User updated succesfully'), category='success')
109 h.flash(_('User updated succesfully'), category='success')
107
110
108 except formencode.Invalid as errors:
111 except formencode.Invalid as errors:
109 c.user = user_model.get_user(id)
110 return htmlfill.render(
112 return htmlfill.render(
111 render('admin/users/user_edit.html'),
113 render('admin/users/user_edit.html'),
112 defaults=errors.value,
114 defaults=errors.value,
113 errors=errors.error_dict or {},
115 errors=errors.error_dict or {},
114 prefix_error=False,
116 prefix_error=False,
115 encoding="UTF-8")
117 encoding="UTF-8")
116 except Exception:
118 except Exception:
117 log.error(traceback.format_exc())
119 log.error(traceback.format_exc())
118 h.flash(_('error occured during update of user %s') \
120 h.flash(_('error occured during update of user %s') \
119 % form_result.get('username'), category='error')
121 % form_result.get('username'), category='error')
120
122
121 return redirect(url('users'))
123 return redirect(url('users'))
122
124
123 def delete(self, id):
125 def delete(self, id):
124 """DELETE /users/id: Delete an existing item"""
126 """DELETE /users/id: Delete an existing item"""
125 # Forms posted to this method should contain a hidden field:
127 # Forms posted to this method should contain a hidden field:
126 # <input type="hidden" name="_method" value="DELETE" />
128 # <input type="hidden" name="_method" value="DELETE" />
127 # Or using helpers:
129 # Or using helpers:
128 # h.form(url('user', id=ID),
130 # h.form(url('user', id=ID),
129 # method='delete')
131 # method='delete')
130 # url('user', id=ID)
132 # url('user', id=ID)
131 user_model = UserModel()
133 user_model = UserModel()
132 try:
134 try:
133 user_model.delete(id)
135 user_model.delete(id)
134 h.flash(_('sucessfully deleted user'), category='success')
136 h.flash(_('sucessfully deleted user'), category='success')
135 except DefaultUserException as e:
137 except DefaultUserException as e:
136 h.flash(str(e), category='warning')
138 h.flash(str(e), category='warning')
137 except Exception:
139 except Exception:
138 h.flash(_('An error occured during deletion of user'),
140 h.flash(_('An error occured during deletion of user'),
139 category='error')
141 category='error')
140 return redirect(url('users'))
142 return redirect(url('users'))
141
143
142 def show(self, id, format='html'):
144 def show(self, id, format='html'):
143 """GET /users/id: Show a specific item"""
145 """GET /users/id: Show a specific item"""
144 # url('user', id=ID)
146 # url('user', id=ID)
145
147
146
148
147 def edit(self, id, format='html'):
149 def edit(self, id, format='html'):
148 """GET /users/id/edit: Form to edit an existing item"""
150 """GET /users/id/edit: Form to edit an existing item"""
149 # url('edit_user', id=ID)
151 # url('edit_user', id=ID)
150 c.user = self.sa.query(User).get(id)
152 c.user = self.sa.query(User).get(id)
153 if not c.user:
154 return redirect(url('users'))
151 if c.user.username == 'default':
155 if c.user.username == 'default':
152 h.flash(_("You can't edit this user since it's"
156 h.flash(_("You can't edit this user since it's"
153 " crucial for entire application"), category='warning')
157 " crucial for entire application"), category='warning')
154 return redirect(url('users'))
158 return redirect(url('users'))
155
159
156 defaults = c.user.__dict__
160 defaults = c.user.__dict__
157 return htmlfill.render(
161 return htmlfill.render(
158 render('admin/users/user_edit.html'),
162 render('admin/users/user_edit.html'),
159 defaults=defaults,
163 defaults=defaults,
160 encoding="UTF-8",
164 encoding="UTF-8",
161 force_defaults=False
165 force_defaults=False
162 )
166 )
@@ -1,199 +1,207 b''
1 #!/usr/bin/env python
1 #!/usr/bin/env python
2 # encoding: utf-8
2 # encoding: utf-8
3 # files controller for pylons
3 # files controller for pylons
4 # Copyright (C) 2009-2010 Marcin Kuzminski <marcin@python-works.com>
4 # Copyright (C) 2009-2010 Marcin Kuzminski <marcin@python-works.com>
5
5
6 # This program is free software; you can redistribute it and/or
6 # This program is free software; you can redistribute it and/or
7 # modify it under the terms of the GNU General Public License
7 # modify it under the terms of the GNU General Public License
8 # as published by the Free Software Foundation; version 2
8 # as published by the Free Software Foundation; version 2
9 # of the License or (at your opinion) any later version of the license.
9 # of the License or (at your opinion) any later version of the license.
10 #
10 #
11 # This program is distributed in the hope that it will be useful,
11 # This program is distributed in the hope that it will be useful,
12 # but WITHOUT ANY WARRANTY; without even the implied warranty of
12 # but WITHOUT ANY WARRANTY; without even the implied warranty of
13 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 # GNU General Public License for more details.
14 # GNU General Public License for more details.
15 #
15 #
16 # You should have received a copy of the GNU General Public License
16 # You should have received a copy of the GNU General Public License
17 # along with this program; if not, write to the Free Software
17 # along with this program; if not, write to the Free Software
18 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
18 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
19 # MA 02110-1301, USA.
19 # MA 02110-1301, USA.
20 """
20 """
21 Created on April 21, 2010
21 Created on April 21, 2010
22 files controller for pylons
22 files controller for pylons
23 @author: marcink
23 @author: marcink
24 """
24 """
25 from mercurial import archival
25 from mercurial import archival
26 from pylons import request, response, session, tmpl_context as c, url
26 from pylons import request, response, session, tmpl_context as c, url
27 from pylons.controllers.util import redirect
27 from pylons.controllers.util import redirect
28 from pylons_app.lib.auth import LoginRequired, HasRepoPermissionAnyDecorator
28 from pylons_app.lib.auth import LoginRequired, HasRepoPermissionAnyDecorator
29 from pylons_app.lib.base import BaseController, render
29 from pylons_app.lib.base import BaseController, render
30 from pylons_app.lib.utils import EmptyChangeset
30 from pylons_app.lib.utils import EmptyChangeset
31 from pylons_app.model.hg_model import HgModel
31 from pylons_app.model.hg_model import HgModel
32 from vcs.exceptions import RepositoryError, ChangesetError
32 from vcs.exceptions import RepositoryError, ChangesetError
33 from vcs.nodes import FileNode
33 from vcs.nodes import FileNode
34 from vcs.utils import diffs as differ
34 from vcs.utils import diffs as differ
35 import logging
35 import logging
36 import pylons_app.lib.helpers as h
36 import pylons_app.lib.helpers as h
37 import tempfile
37 import tempfile
38
38
39 log = logging.getLogger(__name__)
39 log = logging.getLogger(__name__)
40
40
41 class FilesController(BaseController):
41 class FilesController(BaseController):
42
42
43 @LoginRequired()
43 @LoginRequired()
44 @HasRepoPermissionAnyDecorator('repository.read', 'repository.write',
44 @HasRepoPermissionAnyDecorator('repository.read', 'repository.write',
45 'repository.admin')
45 'repository.admin')
46 def __before__(self):
46 def __before__(self):
47 super(FilesController, self).__before__()
47 super(FilesController, self).__before__()
48 c.file_size_limit = 250 * 1024 #limit of file size to display
48
49
49 def index(self, repo_name, revision, f_path):
50 def index(self, repo_name, revision, f_path):
50 hg_model = HgModel()
51 hg_model = HgModel()
51 c.repo = repo = hg_model.get_repo(c.repo_name)
52 c.repo = repo = hg_model.get_repo(c.repo_name)
52 revision = request.POST.get('at_rev', None) or revision
53 revision = request.POST.get('at_rev', None) or revision
53
54
54 def get_next_rev(cur):
55 def get_next_rev(cur):
55 max_rev = len(c.repo.revisions) - 1
56 max_rev = len(c.repo.revisions) - 1
56 r = cur + 1
57 r = cur + 1
57 if r > max_rev:
58 if r > max_rev:
58 r = max_rev
59 r = max_rev
59 return r
60 return r
60
61
61 def get_prev_rev(cur):
62 def get_prev_rev(cur):
62 r = cur - 1
63 r = cur - 1
63 return r
64 return r
64
65
65 c.f_path = f_path
66 c.f_path = f_path
66
67
67
68
68 try:
69 try:
69 cur_rev = repo.get_changeset(revision).revision
70 cur_rev = repo.get_changeset(revision).revision
70 prev_rev = repo.get_changeset(get_prev_rev(cur_rev)).raw_id
71 prev_rev = repo.get_changeset(get_prev_rev(cur_rev)).raw_id
71 next_rev = repo.get_changeset(get_next_rev(cur_rev)).raw_id
72 next_rev = repo.get_changeset(get_next_rev(cur_rev)).raw_id
72
73
73 c.url_prev = url('files_home', repo_name=c.repo_name,
74 c.url_prev = url('files_home', repo_name=c.repo_name,
74 revision=prev_rev, f_path=f_path)
75 revision=prev_rev, f_path=f_path)
75 c.url_next = url('files_home', repo_name=c.repo_name,
76 c.url_next = url('files_home', repo_name=c.repo_name,
76 revision=next_rev, f_path=f_path)
77 revision=next_rev, f_path=f_path)
77
78
78 c.changeset = repo.get_changeset(revision)
79 c.changeset = repo.get_changeset(revision)
79
80
80
81 c.cur_rev = c.changeset.raw_id
81 c.cur_rev = c.changeset.raw_id
82 c.rev_nr = c.changeset.revision
82 c.rev_nr = c.changeset.revision
83 c.files_list = c.changeset.get_node(f_path)
83 c.files_list = c.changeset.get_node(f_path)
84 c.file_history = self._get_history(repo, c.files_list, f_path)
84 c.file_history = self._get_history(repo, c.files_list, f_path)
85
85
86 except (RepositoryError, ChangesetError):
86 except (RepositoryError, ChangesetError):
87 c.files_list = None
87 c.files_list = None
88
88
89 return render('files/files.html')
89 return render('files/files.html')
90
90
91 def rawfile(self, repo_name, revision, f_path):
91 def rawfile(self, repo_name, revision, f_path):
92 hg_model = HgModel()
92 hg_model = HgModel()
93 c.repo = hg_model.get_repo(c.repo_name)
93 c.repo = hg_model.get_repo(c.repo_name)
94 file_node = c.repo.get_changeset(revision).get_node(f_path)
94 file_node = c.repo.get_changeset(revision).get_node(f_path)
95 response.content_type = file_node.mimetype
95 response.content_type = file_node.mimetype
96 response.content_disposition = 'attachment; filename=%s' \
96 response.content_disposition = 'attachment; filename=%s' \
97 % f_path.split('/')[-1]
97 % f_path.split('/')[-1]
98 return file_node.content
98 return file_node.content
99
100 def raw(self, repo_name, revision, f_path):
101 hg_model = HgModel()
102 c.repo = hg_model.get_repo(c.repo_name)
103 file_node = c.repo.get_changeset(revision).get_node(f_path)
104 response.content_type = 'text/plain'
105
106 return file_node.content
99
107
100 def annotate(self, repo_name, revision, f_path):
108 def annotate(self, repo_name, revision, f_path):
101 hg_model = HgModel()
109 hg_model = HgModel()
102 c.repo = hg_model.get_repo(c.repo_name)
110 c.repo = hg_model.get_repo(c.repo_name)
103 cs = c.repo.get_changeset(revision)
111 cs = c.repo.get_changeset(revision)
104 c.file = cs.get_node(f_path)
112 c.file = cs.get_node(f_path)
105 c.file_msg = cs.get_file_message(f_path)
113 c.file_msg = cs.get_file_message(f_path)
106 c.cur_rev = cs.raw_id
114 c.cur_rev = cs.raw_id
107 c.rev_nr = cs.revision
115 c.rev_nr = cs.revision
108 c.f_path = f_path
116 c.f_path = f_path
109
117
110 return render('files/files_annotate.html')
118 return render('files/files_annotate.html')
111
119
112 def archivefile(self, repo_name, revision, fileformat):
120 def archivefile(self, repo_name, revision, fileformat):
113 archive_specs = {
121 archive_specs = {
114 '.tar.bz2': ('application/x-tar', 'tbz2'),
122 '.tar.bz2': ('application/x-tar', 'tbz2'),
115 '.tar.gz': ('application/x-tar', 'tgz'),
123 '.tar.gz': ('application/x-tar', 'tgz'),
116 '.zip': ('application/zip', 'zip'),
124 '.zip': ('application/zip', 'zip'),
117 }
125 }
118 if not archive_specs.has_key(fileformat):
126 if not archive_specs.has_key(fileformat):
119 return 'Unknown archive type %s' % fileformat
127 return 'Unknown archive type %s' % fileformat
120
128
121 def read_in_chunks(file_object, chunk_size=1024 * 40):
129 def read_in_chunks(file_object, chunk_size=1024 * 40):
122 """Lazy function (generator) to read a file piece by piece.
130 """Lazy function (generator) to read a file piece by piece.
123 Default chunk size: 40k."""
131 Default chunk size: 40k."""
124 while True:
132 while True:
125 data = file_object.read(chunk_size)
133 data = file_object.read(chunk_size)
126 if not data:
134 if not data:
127 break
135 break
128 yield data
136 yield data
129
137
130 archive = tempfile.TemporaryFile()
138 archive = tempfile.TemporaryFile()
131 repo = HgModel().get_repo(repo_name).repo
139 repo = HgModel().get_repo(repo_name).repo
132 fname = '%s-%s%s' % (repo_name, revision, fileformat)
140 fname = '%s-%s%s' % (repo_name, revision, fileformat)
133 archival.archive(repo, archive, revision, archive_specs[fileformat][1],
141 archival.archive(repo, archive, revision, archive_specs[fileformat][1],
134 prefix='%s-%s' % (repo_name, revision))
142 prefix='%s-%s' % (repo_name, revision))
135 response.content_type = archive_specs[fileformat][0]
143 response.content_type = archive_specs[fileformat][0]
136 response.content_disposition = 'attachment; filename=%s' % fname
144 response.content_disposition = 'attachment; filename=%s' % fname
137 archive.seek(0)
145 archive.seek(0)
138 return read_in_chunks(archive)
146 return read_in_chunks(archive)
139
147
140 def diff(self, repo_name, f_path):
148 def diff(self, repo_name, f_path):
141 hg_model = HgModel()
149 hg_model = HgModel()
142 diff1 = request.GET.get('diff1')
150 diff1 = request.GET.get('diff1')
143 diff2 = request.GET.get('diff2')
151 diff2 = request.GET.get('diff2')
144 c.action = request.GET.get('diff')
152 c.action = request.GET.get('diff')
145 c.no_changes = diff1 == diff2
153 c.no_changes = diff1 == diff2
146 c.f_path = f_path
154 c.f_path = f_path
147 c.repo = hg_model.get_repo(c.repo_name)
155 c.repo = hg_model.get_repo(c.repo_name)
148
156
149 try:
157 try:
150 if diff1 not in ['', None, 'None', '0' * 12]:
158 if diff1 not in ['', None, 'None', '0' * 12]:
151 c.changeset_1 = c.repo.get_changeset(diff1)
159 c.changeset_1 = c.repo.get_changeset(diff1)
152 node1 = c.changeset_1.get_node(f_path)
160 node1 = c.changeset_1.get_node(f_path)
153 else:
161 else:
154 c.changeset_1 = EmptyChangeset()
162 c.changeset_1 = EmptyChangeset()
155 node1 = FileNode('.', '')
163 node1 = FileNode('.', '')
156 if diff2 not in ['', None, 'None', '0' * 12]:
164 if diff2 not in ['', None, 'None', '0' * 12]:
157 c.changeset_2 = c.repo.get_changeset(diff2)
165 c.changeset_2 = c.repo.get_changeset(diff2)
158 node2 = c.changeset_2.get_node(f_path)
166 node2 = c.changeset_2.get_node(f_path)
159 else:
167 else:
160 c.changeset_2 = EmptyChangeset()
168 c.changeset_2 = EmptyChangeset()
161 node2 = FileNode('.', '')
169 node2 = FileNode('.', '')
162 except RepositoryError:
170 except RepositoryError:
163 return redirect(url('files_home',
171 return redirect(url('files_home',
164 repo_name=c.repo_name, f_path=f_path))
172 repo_name=c.repo_name, f_path=f_path))
165
173
166 c.diff1 = 'r%s:%s' % (c.changeset_1.revision, c.changeset_1.raw_id)
174 c.diff1 = 'r%s:%s' % (c.changeset_1.revision, c.changeset_1.raw_id)
167 c.diff2 = 'r%s:%s' % (c.changeset_2.revision, c.changeset_2.raw_id)
175 c.diff2 = 'r%s:%s' % (c.changeset_2.revision, c.changeset_2.raw_id)
168 f_udiff = differ.get_udiff(node1, node2)
176 f_udiff = differ.get_udiff(node1, node2)
169
177
170 diff = differ.DiffProcessor(f_udiff)
178 diff = differ.DiffProcessor(f_udiff)
171
179
172 if c.action == 'download':
180 if c.action == 'download':
173 diff_name = '%s_vs_%s.diff' % (diff1, diff2)
181 diff_name = '%s_vs_%s.diff' % (diff1, diff2)
174 response.content_type = 'text/plain'
182 response.content_type = 'text/plain'
175 response.content_disposition = 'attachment; filename=%s' \
183 response.content_disposition = 'attachment; filename=%s' \
176 % diff_name
184 % diff_name
177 return diff.raw_diff()
185 return diff.raw_diff()
178
186
179 elif c.action == 'raw':
187 elif c.action == 'raw':
180 c.cur_diff = '<pre class="raw">%s</pre>' % h.escape(diff.raw_diff())
188 c.cur_diff = '<pre class="raw">%s</pre>' % h.escape(diff.raw_diff())
181 elif c.action == 'diff':
189 elif c.action == 'diff':
182 c.cur_diff = diff.as_html()
190 c.cur_diff = diff.as_html()
183 else:
191 else:
184 #default option
192 #default option
185 c.cur_diff = diff.as_html()
193 c.cur_diff = diff.as_html()
186
194
187 if not c.cur_diff: c.no_changes = True
195 if not c.cur_diff: c.no_changes = True
188 return render('files/file_diff.html')
196 return render('files/file_diff.html')
189
197
190 def _get_history(self, repo, node, f_path):
198 def _get_history(self, repo, node, f_path):
191 from vcs.nodes import NodeKind
199 from vcs.nodes import NodeKind
192 if not node.kind is NodeKind.FILE:
200 if not node.kind is NodeKind.FILE:
193 return []
201 return []
194 changesets = node.history
202 changesets = node.history
195 hist_l = []
203 hist_l = []
196 for chs in changesets:
204 for chs in changesets:
197 n_desc = 'r%s:%s' % (chs.revision, chs._short)
205 n_desc = 'r%s:%s' % (chs.revision, chs._short)
198 hist_l.append((chs._short, n_desc,))
206 hist_l.append((chs._short, n_desc,))
199 return hist_l
207 return hist_l
@@ -1,118 +1,144 b''
1 #!/usr/bin/env python
1 #!/usr/bin/env python
2 # encoding: utf-8
2 # encoding: utf-8
3 # login controller for pylons
3 # login controller for pylons
4 # Copyright (C) 2009-2010 Marcin Kuzminski <marcin@python-works.com>
4 # Copyright (C) 2009-2010 Marcin Kuzminski <marcin@python-works.com>
5 #
5 #
6 # This program is free software; you can redistribute it and/or
6 # This program is free software; you can redistribute it and/or
7 # modify it under the terms of the GNU General Public License
7 # modify it under the terms of the GNU General Public License
8 # as published by the Free Software Foundation; version 2
8 # as published by the Free Software Foundation; version 2
9 # of the License or (at your opinion) any later version of the license.
9 # of the License or (at your opinion) any later version of the license.
10 #
10 #
11 # This program is distributed in the hope that it will be useful,
11 # This program is distributed in the hope that it will be useful,
12 # but WITHOUT ANY WARRANTY; without even the implied warranty of
12 # but WITHOUT ANY WARRANTY; without even the implied warranty of
13 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 # GNU General Public License for more details.
14 # GNU General Public License for more details.
15 #
15 #
16 # You should have received a copy of the GNU General Public License
16 # You should have received a copy of the GNU General Public License
17 # along with this program; if not, write to the Free Software
17 # along with this program; if not, write to the Free Software
18 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
18 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
19 # MA 02110-1301, USA.
19 # MA 02110-1301, USA.
20
20
21 """
21 """
22 Created on April 22, 2010
22 Created on April 22, 2010
23 login controller for pylons
23 login controller for pylons
24 @author: marcink
24 @author: marcink
25 """
25 """
26 from formencode import htmlfill
26 from formencode import htmlfill
27 from pylons import request, response, session, tmpl_context as c, url
27 from pylons import request, response, session, tmpl_context as c, url
28 from pylons.controllers.util import abort, redirect
28 from pylons.controllers.util import abort, redirect
29 from pylons_app.lib.auth import AuthUser, HasPermissionAnyDecorator
29 from pylons_app.lib.auth import AuthUser, HasPermissionAnyDecorator
30 from pylons_app.lib.base import BaseController, render
30 from pylons_app.lib.base import BaseController, render
31 from pylons_app.model.forms import LoginForm, RegisterForm
31 import pylons_app.lib.helpers as h
32 from pylons.i18n.translation import _
33 from pylons_app.model.forms import LoginForm, RegisterForm, PasswordResetForm
32 from pylons_app.model.user_model import UserModel
34 from pylons_app.model.user_model import UserModel
33 import formencode
35 import formencode
34 import logging
36 import logging
35
37
36 log = logging.getLogger(__name__)
38 log = logging.getLogger(__name__)
37
39
38 class LoginController(BaseController):
40 class LoginController(BaseController):
39
41
40 def __before__(self):
42 def __before__(self):
41 super(LoginController, self).__before__()
43 super(LoginController, self).__before__()
42
44
43 def index(self):
45 def index(self):
44 #redirect if already logged in
46 #redirect if already logged in
45 c.came_from = request.GET.get('came_from',None)
47 c.came_from = request.GET.get('came_from', None)
46
48
47 if c.hg_app_user.is_authenticated:
49 if c.hg_app_user.is_authenticated:
48 return redirect(url('hg_home'))
50 return redirect(url('hg_home'))
49
51
50 if request.POST:
52 if request.POST:
51 #import Login Form validator class
53 #import Login Form validator class
52 login_form = LoginForm()
54 login_form = LoginForm()
53 try:
55 try:
54 c.form_result = login_form.to_python(dict(request.POST))
56 c.form_result = login_form.to_python(dict(request.POST))
55 username = c.form_result['username']
57 username = c.form_result['username']
56 user = UserModel().get_user_by_name(username)
58 user = UserModel().get_user_by_name(username)
57 auth_user = AuthUser()
59 auth_user = AuthUser()
58 auth_user.username = user.username
60 auth_user.username = user.username
59 auth_user.is_authenticated = True
61 auth_user.is_authenticated = True
60 auth_user.is_admin = user.admin
62 auth_user.is_admin = user.admin
61 auth_user.user_id = user.user_id
63 auth_user.user_id = user.user_id
62 auth_user.name = user.name
64 auth_user.name = user.name
63 auth_user.lastname = user.lastname
65 auth_user.lastname = user.lastname
64 session['hg_app_user'] = auth_user
66 session['hg_app_user'] = auth_user
65 session.save()
67 session.save()
66 log.info('user %s is now authenticated', username)
68 log.info('user %s is now authenticated', username)
67
69
68 user.update_lastlogin()
70 user.update_lastlogin()
69
71
70 if c.came_from:
72 if c.came_from:
71 return redirect(c.came_from)
73 return redirect(c.came_from)
72 else:
74 else:
73 return redirect(url('hg_home'))
75 return redirect(url('hg_home'))
74
76
75 except formencode.Invalid as errors:
77 except formencode.Invalid as errors:
76 return htmlfill.render(
78 return htmlfill.render(
77 render('/login.html'),
79 render('/login.html'),
78 defaults=errors.value,
80 defaults=errors.value,
79 errors=errors.error_dict or {},
81 errors=errors.error_dict or {},
80 prefix_error=False,
82 prefix_error=False,
81 encoding="UTF-8")
83 encoding="UTF-8")
82
84
83 return render('/login.html')
85 return render('/login.html')
84
86
85 @HasPermissionAnyDecorator('hg.admin', 'hg.register.auto_activate',
87 @HasPermissionAnyDecorator('hg.admin', 'hg.register.auto_activate',
86 'hg.register.manual_activate')
88 'hg.register.manual_activate')
87 def register(self):
89 def register(self):
88 user_model = UserModel()
90 user_model = UserModel()
89 c.auto_active = False
91 c.auto_active = False
90 for perm in user_model.get_default().user_perms:
92 for perm in user_model.get_default().user_perms:
91 if perm.permission.permission_name == 'hg.register.auto_activate':
93 if perm.permission.permission_name == 'hg.register.auto_activate':
92 c.auto_active = True
94 c.auto_active = True
93 break
95 break
94
96
95 if request.POST:
97 if request.POST:
96
98
97 register_form = RegisterForm()()
99 register_form = RegisterForm()()
98 try:
100 try:
99 form_result = register_form.to_python(dict(request.POST))
101 form_result = register_form.to_python(dict(request.POST))
100 form_result['active'] = c.auto_active
102 form_result['active'] = c.auto_active
101 user_model.create_registration(form_result)
103 user_model.create_registration(form_result)
104 h.flash(_('You have successfully registered into hg-app'),
105 category='success')
102 return redirect(url('login_home'))
106 return redirect(url('login_home'))
103
107
104 except formencode.Invalid as errors:
108 except formencode.Invalid as errors:
105 return htmlfill.render(
109 return htmlfill.render(
106 render('/register.html'),
110 render('/register.html'),
107 defaults=errors.value,
111 defaults=errors.value,
108 errors=errors.error_dict or {},
112 errors=errors.error_dict or {},
109 prefix_error=False,
113 prefix_error=False,
110 encoding="UTF-8")
114 encoding="UTF-8")
111
115
112 return render('/register.html')
116 return render('/register.html')
113
117
118 def password_reset(self):
119 user_model = UserModel()
120 if request.POST:
121
122 password_reset_form = PasswordResetForm()()
123 try:
124 form_result = password_reset_form.to_python(dict(request.POST))
125 user_model.reset_password(form_result)
126 h.flash(_('Your new password was sent'),
127 category='success')
128 return redirect(url('login_home'))
129
130 except formencode.Invalid as errors:
131 return htmlfill.render(
132 render('/password_reset.html'),
133 defaults=errors.value,
134 errors=errors.error_dict or {},
135 prefix_error=False,
136 encoding="UTF-8")
137
138 return render('/password_reset.html')
139
114 def logout(self):
140 def logout(self):
115 session['hg_app_user'] = AuthUser()
141 session['hg_app_user'] = AuthUser()
116 session.save()
142 session.save()
117 log.info('Logging out and setting user as Empty')
143 log.info('Logging out and setting user as Empty')
118 redirect(url('hg_home'))
144 redirect(url('hg_home'))
@@ -1,113 +1,98 b''
1 #!/usr/bin/env python
1 #!/usr/bin/env python
2 # encoding: utf-8
2 # encoding: utf-8
3 # search controller for pylons
3 # search controller for pylons
4 # Copyright (C) 2009-2010 Marcin Kuzminski <marcin@python-works.com>
4 # Copyright (C) 2009-2010 Marcin Kuzminski <marcin@python-works.com>
5 #
5 #
6 # This program is free software; you can redistribute it and/or
6 # This program is free software; you can redistribute it and/or
7 # modify it under the terms of the GNU General Public License
7 # modify it under the terms of the GNU General Public License
8 # as published by the Free Software Foundation; version 2
8 # as published by the Free Software Foundation; version 2
9 # of the License or (at your opinion) any later version of the license.
9 # of the License or (at your opinion) any later version of the license.
10 #
10 #
11 # This program is distributed in the hope that it will be useful,
11 # This program is distributed in the hope that it will be useful,
12 # but WITHOUT ANY WARRANTY; without even the implied warranty of
12 # but WITHOUT ANY WARRANTY; without even the implied warranty of
13 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 # GNU General Public License for more details.
14 # GNU General Public License for more details.
15 #
15 #
16 # You should have received a copy of the GNU General Public License
16 # You should have received a copy of the GNU General Public License
17 # along with this program; if not, write to the Free Software
17 # along with this program; if not, write to the Free Software
18 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
18 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
19 # MA 02110-1301, USA.
19 # MA 02110-1301, USA.
20 """
20 """
21 Created on Aug 7, 2010
21 Created on Aug 7, 2010
22 search controller for pylons
22 search controller for pylons
23 @author: marcink
23 @author: marcink
24 """
24 """
25 from pylons import request, response, session, tmpl_context as c, url
25 from pylons import request, response, session, tmpl_context as c, url
26 from pylons.controllers.util import abort, redirect
26 from pylons.controllers.util import abort, redirect
27 from pylons_app.lib.auth import LoginRequired
27 from pylons_app.lib.auth import LoginRequired
28 from pylons_app.lib.base import BaseController, render
28 from pylons_app.lib.base import BaseController, render
29 from pylons_app.lib.indexers import ANALYZER, IDX_LOCATION, SCHEMA, IDX_NAME
29 from pylons_app.lib.indexers import IDX_LOCATION, SCHEMA, IDX_NAME, ResultWrapper
30 from webhelpers.html.builder import escape
30 from webhelpers.paginate import Page
31 from whoosh.highlight import highlight, SimpleFragmenter, HtmlFormatter, \
31 from webhelpers.util import update_params
32 ContextFragmenter
33 from pylons.i18n.translation import _
32 from pylons.i18n.translation import _
34 from whoosh.index import open_dir, EmptyIndexError
33 from whoosh.index import open_dir, EmptyIndexError
35 from whoosh.qparser import QueryParser, QueryParserError
34 from whoosh.qparser import QueryParser, QueryParserError
36 from whoosh.query import Phrase
35 from whoosh.query import Phrase
37 import logging
36 import logging
38 import traceback
37 import traceback
39
38
40 log = logging.getLogger(__name__)
39 log = logging.getLogger(__name__)
41
40
42 class SearchController(BaseController):
41 class SearchController(BaseController):
43
42
44 @LoginRequired()
43 @LoginRequired()
45 def __before__(self):
44 def __before__(self):
46 super(SearchController, self).__before__()
45 super(SearchController, self).__before__()
47
46
48
49 def index(self):
47 def index(self):
50 c.formated_results = []
48 c.formated_results = []
51 c.runtime = ''
49 c.runtime = ''
52 search_items = set()
53 c.cur_query = request.GET.get('q', None)
50 c.cur_query = request.GET.get('q', None)
54 if c.cur_query:
51 if c.cur_query:
55 cur_query = c.cur_query.lower()
52 cur_query = c.cur_query.lower()
56
53
57
58 if c.cur_query:
54 if c.cur_query:
55 p = int(request.params.get('page', 1))
56 highlight_items = set()
59 try:
57 try:
60 idx = open_dir(IDX_LOCATION, indexname=IDX_NAME)
58 idx = open_dir(IDX_LOCATION, indexname=IDX_NAME)
61 searcher = idx.searcher()
59 searcher = idx.searcher()
62
60
63 qp = QueryParser("content", schema=SCHEMA)
61 qp = QueryParser("content", schema=SCHEMA)
64 try:
62 try:
65 query = qp.parse(unicode(cur_query))
63 query = qp.parse(unicode(cur_query))
66
64
67 if isinstance(query, Phrase):
65 if isinstance(query, Phrase):
68 search_items.update(query.words)
66 highlight_items.update(query.words)
69 else:
67 else:
70 for i in query.all_terms():
68 for i in query.all_terms():
71 search_items.add(i[1])
69 if i[0] == 'content':
72
70 highlight_items.add(i[1])
73 log.debug(query)
74 log.debug(search_items)
75 results = searcher.search(query)
76 c.runtime = '%s results (%.3f seconds)' \
77 % (len(results), results.runtime)
78
71
79 analyzer = ANALYZER
72 matcher = query.matcher(searcher)
80 formatter = HtmlFormatter('span',
81 between='\n<span class="break">...</span>\n')
82
83 #how the parts are splitted within the same text part
84 fragmenter = SimpleFragmenter(200)
85 #fragmenter = ContextFragmenter(search_items)
86
73
87 for res in results:
74 log.debug(query)
88 d = {}
75 log.debug(highlight_items)
89 d.update(res)
76 results = searcher.search(query)
90 hl = highlight(escape(res['content']), search_items,
77 res_ln = len(results)
91 analyzer=analyzer,
78 c.runtime = '%s results (%.3f seconds)' \
92 fragmenter=fragmenter,
79 % (res_ln, results.runtime)
93 formatter=formatter,
80
94 top=5)
81 def url_generator(**kw):
95 f_path = res['path'][res['path'].find(res['repository']) \
82 return update_params("?q=%s" % c.cur_query, **kw)
96 + len(res['repository']):].lstrip('/')
83
97 d.update({'content_short':hl,
84 c.formated_results = Page(
98 'f_path':f_path})
85 ResultWrapper(searcher, matcher, highlight_items),
99 #del d['content']
86 page=p, item_count=res_ln,
100 c.formated_results.append(d)
87 items_per_page=10, url=url_generator)
101
88
102 except QueryParserError:
89 except QueryParserError:
103 c.runtime = _('Invalid search query. Try quoting it.')
90 c.runtime = _('Invalid search query. Try quoting it.')
104
91 searcher.close()
105 except (EmptyIndexError, IOError):
92 except (EmptyIndexError, IOError):
106 log.error(traceback.format_exc())
93 log.error(traceback.format_exc())
107 log.error('Empty Index data')
94 log.error('Empty Index data')
108 c.runtime = _('There is no index to search in. Please run whoosh indexer')
95 c.runtime = _('There is no index to search in. Please run whoosh indexer')
109
96
110
111
112 # Return a rendered template
97 # Return a rendered template
113 return render('/search/search.html')
98 return render('/search/search.html')
@@ -1,139 +1,96 b''
1 #!/usr/bin/env python
1 #!/usr/bin/env python
2 # encoding: utf-8
2 # encoding: utf-8
3 # summary controller for pylons
3 # summary controller for pylons
4 # Copyright (C) 2009-2010 Marcin Kuzminski <marcin@python-works.com>
4 # Copyright (C) 2009-2010 Marcin Kuzminski <marcin@python-works.com>
5 #
5 #
6 # This program is free software; you can redistribute it and/or
6 # This program is free software; you can redistribute it and/or
7 # modify it under the terms of the GNU General Public License
7 # modify it under the terms of the GNU General Public License
8 # as published by the Free Software Foundation; version 2
8 # as published by the Free Software Foundation; version 2
9 # of the License or (at your opinion) any later version of the license.
9 # of the License or (at your opinion) any later version of the license.
10 #
10 #
11 # This program is distributed in the hope that it will be useful,
11 # This program is distributed in the hope that it will be useful,
12 # but WITHOUT ANY WARRANTY; without even the implied warranty of
12 # but WITHOUT ANY WARRANTY; without even the implied warranty of
13 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 # GNU General Public License for more details.
14 # GNU General Public License for more details.
15 #
15 #
16 # You should have received a copy of the GNU General Public License
16 # You should have received a copy of the GNU General Public License
17 # along with this program; if not, write to the Free Software
17 # along with this program; if not, write to the Free Software
18 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
18 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
19 # MA 02110-1301, USA.
19 # MA 02110-1301, USA.
20 """
20 """
21 Created on April 18, 2010
21 Created on April 18, 2010
22 summary controller for pylons
22 summary controller for pylons
23 @author: marcink
23 @author: marcink
24 """
24 """
25 from datetime import datetime, timedelta
25 from pylons import tmpl_context as c, request, url
26 from pylons import tmpl_context as c, request
27 from pylons_app.lib.auth import LoginRequired, HasRepoPermissionAnyDecorator
26 from pylons_app.lib.auth import LoginRequired, HasRepoPermissionAnyDecorator
28 from pylons_app.lib.base import BaseController, render
27 from pylons_app.lib.base import BaseController, render
29 from pylons_app.lib.helpers import person
30 from pylons_app.lib.utils import OrderedDict
28 from pylons_app.lib.utils import OrderedDict
31 from pylons_app.model.hg_model import HgModel
29 from pylons_app.model.hg_model import HgModel
30 from pylons_app.model.db import Statistics
31 from webhelpers.paginate import Page
32 from pylons_app.lib.celerylib import run_task
33 from pylons_app.lib.celerylib.tasks import get_commits_stats
34 from datetime import datetime, timedelta
32 from time import mktime
35 from time import mktime
33 from webhelpers.paginate import Page
34 import calendar
36 import calendar
35 import logging
37 import logging
36
38
37 log = logging.getLogger(__name__)
39 log = logging.getLogger(__name__)
38
40
39 class SummaryController(BaseController):
41 class SummaryController(BaseController):
40
42
41 @LoginRequired()
43 @LoginRequired()
42 @HasRepoPermissionAnyDecorator('repository.read', 'repository.write',
44 @HasRepoPermissionAnyDecorator('repository.read', 'repository.write',
43 'repository.admin')
45 'repository.admin')
44 def __before__(self):
46 def __before__(self):
45 super(SummaryController, self).__before__()
47 super(SummaryController, self).__before__()
46
48
47 def index(self):
49 def index(self):
48 hg_model = HgModel()
50 hg_model = HgModel()
49 c.repo_info = hg_model.get_repo(c.repo_name)
51 c.repo_info = hg_model.get_repo(c.repo_name)
50 c.repo_changesets = Page(list(c.repo_info[:10]), page=1, items_per_page=20)
52 c.repo_changesets = Page(list(c.repo_info[:10]), page=1, items_per_page=20)
51 e = request.environ
53 e = request.environ
52 uri = u'%(protocol)s://%(user)s@%(host)s/%(repo_name)s' % {
54 uri = u'%(protocol)s://%(user)s@%(host)s/%(repo_name)s' % {
53 'protocol': e.get('wsgi.url_scheme'),
55 'protocol': e.get('wsgi.url_scheme'),
54 'user':str(c.hg_app_user.username),
56 'user':str(c.hg_app_user.username),
55 'host':e.get('HTTP_HOST'),
57 'host':e.get('HTTP_HOST'),
56 'repo_name':c.repo_name, }
58 'repo_name':c.repo_name, }
57 c.clone_repo_url = uri
59 c.clone_repo_url = uri
58 c.repo_tags = OrderedDict()
60 c.repo_tags = OrderedDict()
59 for name, hash in c.repo_info.tags.items()[:10]:
61 for name, hash in c.repo_info.tags.items()[:10]:
60 c.repo_tags[name] = c.repo_info.get_changeset(hash)
62 c.repo_tags[name] = c.repo_info.get_changeset(hash)
61
63
62 c.repo_branches = OrderedDict()
64 c.repo_branches = OrderedDict()
63 for name, hash in c.repo_info.branches.items()[:10]:
65 for name, hash in c.repo_info.branches.items()[:10]:
64 c.repo_branches[name] = c.repo_info.get_changeset(hash)
66 c.repo_branches[name] = c.repo_info.get_changeset(hash)
67
68 td = datetime.today() + timedelta(days=1)
69 y, m, d = td.year, td.month, td.day
70
71 ts_min_y = mktime((y - 1, (td - timedelta(days=calendar.mdays[m])).month,
72 d, 0, 0, 0, 0, 0, 0,))
73 ts_min_m = mktime((y, (td - timedelta(days=calendar.mdays[m])).month,
74 d, 0, 0, 0, 0, 0, 0,))
75
76 ts_max_y = mktime((y, m, d, 0, 0, 0, 0, 0, 0,))
77
78 run_task(get_commits_stats, c.repo_info.name, ts_min_y, ts_max_y)
79 c.ts_min = ts_min_m
80 c.ts_max = ts_max_y
81
82
83 stats = self.sa.query(Statistics)\
84 .filter(Statistics.repository == c.repo_info.dbrepo)\
85 .scalar()
65
86
66 c.commit_data = self.__get_commit_stats(c.repo_info)
87 if stats:
88 c.commit_data = stats.commit_activity
89 c.overview_data = stats.commit_activity_combined
90 else:
91 import json
92 c.commit_data = json.dumps({})
93 c.overview_data = json.dumps([[ts_min_y, 0], [ts_max_y, 0] ])
67
94
68 return render('summary/summary.html')
95 return render('summary/summary.html')
69
96
70
71
72 def __get_commit_stats(self, repo):
73 aggregate = OrderedDict()
74
75 #graph range
76 td = datetime.today() + timedelta(days=1)
77 y, m, d = td.year, td.month, td.day
78 c.ts_min = mktime((y, (td - timedelta(days=calendar.mdays[m])).month,
79 d, 0, 0, 0, 0, 0, 0,))
80 c.ts_max = mktime((y, m, d, 0, 0, 0, 0, 0, 0,))
81
82 def author_key_cleaner(k):
83 k = person(k)
84 k = k.replace('"', "'") #for js data compatibilty
85 return k
86
87 for cs in repo[:200]:#added limit 200 until fix #29 is made
88 k = '%s-%s-%s' % (cs.date.timetuple()[0], cs.date.timetuple()[1],
89 cs.date.timetuple()[2])
90 timetupple = [int(x) for x in k.split('-')]
91 timetupple.extend([0 for _ in xrange(6)])
92 k = mktime(timetupple)
93 if aggregate.has_key(author_key_cleaner(cs.author)):
94 if aggregate[author_key_cleaner(cs.author)].has_key(k):
95 aggregate[author_key_cleaner(cs.author)][k]["commits"] += 1
96 aggregate[author_key_cleaner(cs.author)][k]["added"] += len(cs.added)
97 aggregate[author_key_cleaner(cs.author)][k]["changed"] += len(cs.changed)
98 aggregate[author_key_cleaner(cs.author)][k]["removed"] += len(cs.removed)
99
100 else:
101 #aggregate[author_key_cleaner(cs.author)].update(dates_range)
102 if k >= c.ts_min and k <= c.ts_max:
103 aggregate[author_key_cleaner(cs.author)][k] = {}
104 aggregate[author_key_cleaner(cs.author)][k]["commits"] = 1
105 aggregate[author_key_cleaner(cs.author)][k]["added"] = len(cs.added)
106 aggregate[author_key_cleaner(cs.author)][k]["changed"] = len(cs.changed)
107 aggregate[author_key_cleaner(cs.author)][k]["removed"] = len(cs.removed)
108
109 else:
110 if k >= c.ts_min and k <= c.ts_max:
111 aggregate[author_key_cleaner(cs.author)] = OrderedDict()
112 #aggregate[author_key_cleaner(cs.author)].update(dates_range)
113 aggregate[author_key_cleaner(cs.author)][k] = {}
114 aggregate[author_key_cleaner(cs.author)][k]["commits"] = 1
115 aggregate[author_key_cleaner(cs.author)][k]["added"] = len(cs.added)
116 aggregate[author_key_cleaner(cs.author)][k]["changed"] = len(cs.changed)
117 aggregate[author_key_cleaner(cs.author)][k]["removed"] = len(cs.removed)
118
119 d = ''
120 tmpl0 = u""""%s":%s"""
121 tmpl1 = u"""{label:"%s",data:%s,schema:["commits"]},"""
122 for author in aggregate:
123
124 d += tmpl0 % (author,
125 tmpl1 \
126 % (author,
127 [{"time":x,
128 "commits":aggregate[author][x]['commits'],
129 "added":aggregate[author][x]['added'],
130 "changed":aggregate[author][x]['changed'],
131 "removed":aggregate[author][x]['removed'],
132 } for x in aggregate[author]]))
133 if d == '':
134 d = '"%s":{label:"%s",data:[[0,1],]}' \
135 % (author_key_cleaner(repo.contact),
136 author_key_cleaner(repo.contact))
137 return d
138
139
@@ -1,454 +1,481 b''
1 #!/usr/bin/env python
1 #!/usr/bin/env python
2 # encoding: utf-8
2 # encoding: utf-8
3 # authentication and permission libraries
3 # authentication and permission libraries
4 # Copyright (C) 2009-2010 Marcin Kuzminski <marcin@python-works.com>
4 # Copyright (C) 2009-2010 Marcin Kuzminski <marcin@python-works.com>
5 #
5 #
6 # This program is free software; you can redistribute it and/or
6 # This program is free software; you can redistribute it and/or
7 # modify it under the terms of the GNU General Public License
7 # modify it under the terms of the GNU General Public License
8 # as published by the Free Software Foundation; version 2
8 # as published by the Free Software Foundation; version 2
9 # of the License or (at your opinion) any later version of the license.
9 # of the License or (at your opinion) any later version of the license.
10 #
10 #
11 # This program is distributed in the hope that it will be useful,
11 # This program is distributed in the hope that it will be useful,
12 # but WITHOUT ANY WARRANTY; without even the implied warranty of
12 # but WITHOUT ANY WARRANTY; without even the implied warranty of
13 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 # GNU General Public License for more details.
14 # GNU General Public License for more details.
15 #
15 #
16 # You should have received a copy of the GNU General Public License
16 # You should have received a copy of the GNU General Public License
17 # along with this program; if not, write to the Free Software
17 # along with this program; if not, write to the Free Software
18 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
18 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
19 # MA 02110-1301, USA.
19 # MA 02110-1301, USA.
20 """
20 """
21 Created on April 4, 2010
21 Created on April 4, 2010
22
22
23 @author: marcink
23 @author: marcink
24 """
24 """
25 from beaker.cache import cache_region
25 from beaker.cache import cache_region
26 from pylons import config, session, url, request
26 from pylons import config, session, url, request
27 from pylons.controllers.util import abort, redirect
27 from pylons.controllers.util import abort, redirect
28 from pylons_app.lib.utils import get_repo_slug
28 from pylons_app.lib.utils import get_repo_slug
29 from pylons_app.model import meta
29 from pylons_app.model import meta
30 from pylons_app.model.db import User, RepoToPerm, Repository, Permission, \
30 from pylons_app.model.db import User, RepoToPerm, Repository, Permission, \
31 UserToPerm
31 UserToPerm
32 from sqlalchemy.exc import OperationalError
32 from sqlalchemy.exc import OperationalError
33 from sqlalchemy.orm.exc import NoResultFound, MultipleResultsFound
33 from sqlalchemy.orm.exc import NoResultFound, MultipleResultsFound
34 import bcrypt
34 import bcrypt
35 from decorator import decorator
35 from decorator import decorator
36 import logging
36 import logging
37 import random
37
38
38 log = logging.getLogger(__name__)
39 log = logging.getLogger(__name__)
39
40
41 class PasswordGenerator(object):
42 """This is a simple class for generating password from
43 different sets of characters
44 usage:
45 passwd_gen = PasswordGenerator()
46 #print 8-letter password containing only big and small letters of alphabet
47 print passwd_gen.gen_password(8, passwd_gen.ALPHABETS_BIG_SMALL)
48 """
49 ALPHABETS_NUM = r'''1234567890'''#[0]
50 ALPHABETS_SMALL = r'''qwertyuiopasdfghjklzxcvbnm'''#[1]
51 ALPHABETS_BIG = r'''QWERTYUIOPASDFGHJKLZXCVBNM'''#[2]
52 ALPHABETS_SPECIAL = r'''`-=[]\;',./~!@#$%^&*()_+{}|:"<>?''' #[3]
53 ALPHABETS_FULL = ALPHABETS_BIG + ALPHABETS_SMALL + ALPHABETS_NUM + ALPHABETS_SPECIAL#[4]
54 ALPHABETS_ALPHANUM = ALPHABETS_BIG + ALPHABETS_SMALL + ALPHABETS_NUM#[5]
55 ALPHABETS_BIG_SMALL = ALPHABETS_BIG + ALPHABETS_SMALL
56 ALPHABETS_ALPHANUM_BIG = ALPHABETS_BIG + ALPHABETS_NUM#[6]
57 ALPHABETS_ALPHANUM_SMALL = ALPHABETS_SMALL + ALPHABETS_NUM#[7]
58
59 def __init__(self, passwd=''):
60 self.passwd = passwd
61
62 def gen_password(self, len, type):
63 self.passwd = ''.join([random.choice(type) for _ in xrange(len)])
64 return self.passwd
65
66
40 def get_crypt_password(password):
67 def get_crypt_password(password):
41 """Cryptographic function used for password hashing based on sha1
68 """Cryptographic function used for password hashing based on sha1
42 @param password: password to hash
69 @param password: password to hash
43 """
70 """
44 return bcrypt.hashpw(password, bcrypt.gensalt(10))
71 return bcrypt.hashpw(password, bcrypt.gensalt(10))
45
72
46 def check_password(password, hashed):
73 def check_password(password, hashed):
47 return bcrypt.hashpw(password, hashed) == hashed
74 return bcrypt.hashpw(password, hashed) == hashed
48
75
49 @cache_region('super_short_term', 'cached_user')
76 @cache_region('super_short_term', 'cached_user')
50 def get_user_cached(username):
77 def get_user_cached(username):
51 sa = meta.Session
78 sa = meta.Session
52 try:
79 try:
53 user = sa.query(User).filter(User.username == username).one()
80 user = sa.query(User).filter(User.username == username).one()
54 finally:
81 finally:
55 meta.Session.remove()
82 meta.Session.remove()
56 return user
83 return user
57
84
58 def authfunc(environ, username, password):
85 def authfunc(environ, username, password):
59 try:
86 try:
60 user = get_user_cached(username)
87 user = get_user_cached(username)
61 except (NoResultFound, MultipleResultsFound, OperationalError) as e:
88 except (NoResultFound, MultipleResultsFound, OperationalError) as e:
62 log.error(e)
89 log.error(e)
63 user = None
90 user = None
64
91
65 if user:
92 if user:
66 if user.active:
93 if user.active:
67 if user.username == username and check_password(password, user.password):
94 if user.username == username and check_password(password, user.password):
68 log.info('user %s authenticated correctly', username)
95 log.info('user %s authenticated correctly', username)
69 return True
96 return True
70 else:
97 else:
71 log.error('user %s is disabled', username)
98 log.error('user %s is disabled', username)
72
99
73 return False
100 return False
74
101
75 class AuthUser(object):
102 class AuthUser(object):
76 """
103 """
77 A simple object that handles a mercurial username for authentication
104 A simple object that handles a mercurial username for authentication
78 """
105 """
79 def __init__(self):
106 def __init__(self):
80 self.username = 'None'
107 self.username = 'None'
81 self.name = ''
108 self.name = ''
82 self.lastname = ''
109 self.lastname = ''
83 self.email = ''
110 self.email = ''
84 self.user_id = None
111 self.user_id = None
85 self.is_authenticated = False
112 self.is_authenticated = False
86 self.is_admin = False
113 self.is_admin = False
87 self.permissions = {}
114 self.permissions = {}
88
115
89
116
90 def set_available_permissions(config):
117 def set_available_permissions(config):
91 """
118 """
92 This function will propagate pylons globals with all available defined
119 This function will propagate pylons globals with all available defined
93 permission given in db. We don't wannt to check each time from db for new
120 permission given in db. We don't wannt to check each time from db for new
94 permissions since adding a new permission also requires application restart
121 permissions since adding a new permission also requires application restart
95 ie. to decorate new views with the newly created permission
122 ie. to decorate new views with the newly created permission
96 @param config:
123 @param config:
97 """
124 """
98 log.info('getting information about all available permissions')
125 log.info('getting information about all available permissions')
99 try:
126 try:
100 sa = meta.Session
127 sa = meta.Session
101 all_perms = sa.query(Permission).all()
128 all_perms = sa.query(Permission).all()
102 finally:
129 finally:
103 meta.Session.remove()
130 meta.Session.remove()
104
131
105 config['available_permissions'] = [x.permission_name for x in all_perms]
132 config['available_permissions'] = [x.permission_name for x in all_perms]
106
133
107 def set_base_path(config):
134 def set_base_path(config):
108 config['base_path'] = config['pylons.app_globals'].base_path
135 config['base_path'] = config['pylons.app_globals'].base_path
109
136
110 def fill_data(user):
137 def fill_data(user):
111 """
138 """
112 Fills user data with those from database and log out user if not present
139 Fills user data with those from database and log out user if not present
113 in database
140 in database
114 @param user:
141 @param user:
115 """
142 """
116 sa = meta.Session
143 sa = meta.Session
117 dbuser = sa.query(User).get(user.user_id)
144 dbuser = sa.query(User).get(user.user_id)
118 if dbuser:
145 if dbuser:
119 user.username = dbuser.username
146 user.username = dbuser.username
120 user.is_admin = dbuser.admin
147 user.is_admin = dbuser.admin
121 user.name = dbuser.name
148 user.name = dbuser.name
122 user.lastname = dbuser.lastname
149 user.lastname = dbuser.lastname
123 user.email = dbuser.email
150 user.email = dbuser.email
124 else:
151 else:
125 user.is_authenticated = False
152 user.is_authenticated = False
126 meta.Session.remove()
153 meta.Session.remove()
127 return user
154 return user
128
155
129 def fill_perms(user):
156 def fill_perms(user):
130 """
157 """
131 Fills user permission attribute with permissions taken from database
158 Fills user permission attribute with permissions taken from database
132 @param user:
159 @param user:
133 """
160 """
134
161
135 sa = meta.Session
162 sa = meta.Session
136 user.permissions['repositories'] = {}
163 user.permissions['repositories'] = {}
137 user.permissions['global'] = set()
164 user.permissions['global'] = set()
138
165
139 #===========================================================================
166 #===========================================================================
140 # fetch default permissions
167 # fetch default permissions
141 #===========================================================================
168 #===========================================================================
142 default_perms = sa.query(RepoToPerm, Repository, Permission)\
169 default_perms = sa.query(RepoToPerm, Repository, Permission)\
143 .join((Repository, RepoToPerm.repository_id == Repository.repo_id))\
170 .join((Repository, RepoToPerm.repository_id == Repository.repo_id))\
144 .join((Permission, RepoToPerm.permission_id == Permission.permission_id))\
171 .join((Permission, RepoToPerm.permission_id == Permission.permission_id))\
145 .filter(RepoToPerm.user == sa.query(User).filter(User.username ==
172 .filter(RepoToPerm.user == sa.query(User).filter(User.username ==
146 'default').scalar()).all()
173 'default').scalar()).all()
147
174
148 if user.is_admin:
175 if user.is_admin:
149 #=======================================================================
176 #=======================================================================
150 # #admin have all default rights set to admin
177 # #admin have all default rights set to admin
151 #=======================================================================
178 #=======================================================================
152 user.permissions['global'].add('hg.admin')
179 user.permissions['global'].add('hg.admin')
153
180
154 for perm in default_perms:
181 for perm in default_perms:
155 p = 'repository.admin'
182 p = 'repository.admin'
156 user.permissions['repositories'][perm.RepoToPerm.repository.repo_name] = p
183 user.permissions['repositories'][perm.RepoToPerm.repository.repo_name] = p
157
184
158 else:
185 else:
159 #=======================================================================
186 #=======================================================================
160 # set default permissions
187 # set default permissions
161 #=======================================================================
188 #=======================================================================
162
189
163 #default global
190 #default global
164 default_global_perms = sa.query(UserToPerm)\
191 default_global_perms = sa.query(UserToPerm)\
165 .filter(UserToPerm.user == sa.query(User).filter(User.username ==
192 .filter(UserToPerm.user == sa.query(User).filter(User.username ==
166 'default').one())
193 'default').one())
167
194
168 for perm in default_global_perms:
195 for perm in default_global_perms:
169 user.permissions['global'].add(perm.permission.permission_name)
196 user.permissions['global'].add(perm.permission.permission_name)
170
197
171 #default repositories
198 #default repositories
172 for perm in default_perms:
199 for perm in default_perms:
173 if perm.Repository.private and not perm.Repository.user_id == user.user_id:
200 if perm.Repository.private and not perm.Repository.user_id == user.user_id:
174 #disable defaults for private repos,
201 #disable defaults for private repos,
175 p = 'repository.none'
202 p = 'repository.none'
176 elif perm.Repository.user_id == user.user_id:
203 elif perm.Repository.user_id == user.user_id:
177 #set admin if owner
204 #set admin if owner
178 p = 'repository.admin'
205 p = 'repository.admin'
179 else:
206 else:
180 p = perm.Permission.permission_name
207 p = perm.Permission.permission_name
181
208
182 user.permissions['repositories'][perm.RepoToPerm.repository.repo_name] = p
209 user.permissions['repositories'][perm.RepoToPerm.repository.repo_name] = p
183
210
184 #=======================================================================
211 #=======================================================================
185 # #overwrite default with user permissions if any
212 # #overwrite default with user permissions if any
186 #=======================================================================
213 #=======================================================================
187 user_perms = sa.query(RepoToPerm, Permission, Repository)\
214 user_perms = sa.query(RepoToPerm, Permission, Repository)\
188 .join((Repository, RepoToPerm.repository_id == Repository.repo_id))\
215 .join((Repository, RepoToPerm.repository_id == Repository.repo_id))\
189 .join((Permission, RepoToPerm.permission_id == Permission.permission_id))\
216 .join((Permission, RepoToPerm.permission_id == Permission.permission_id))\
190 .filter(RepoToPerm.user_id == user.user_id).all()
217 .filter(RepoToPerm.user_id == user.user_id).all()
191
218
192 for perm in user_perms:
219 for perm in user_perms:
193 if perm.Repository.user_id == user.user_id:#set admin if owner
220 if perm.Repository.user_id == user.user_id:#set admin if owner
194 p = 'repository.admin'
221 p = 'repository.admin'
195 else:
222 else:
196 p = perm.Permission.permission_name
223 p = perm.Permission.permission_name
197 user.permissions['repositories'][perm.RepoToPerm.repository.repo_name] = p
224 user.permissions['repositories'][perm.RepoToPerm.repository.repo_name] = p
198 meta.Session.remove()
225 meta.Session.remove()
199 return user
226 return user
200
227
201 def get_user(session):
228 def get_user(session):
202 """
229 """
203 Gets user from session, and wraps permissions into user
230 Gets user from session, and wraps permissions into user
204 @param session:
231 @param session:
205 """
232 """
206 user = session.get('hg_app_user', AuthUser())
233 user = session.get('hg_app_user', AuthUser())
207 if user.is_authenticated:
234 if user.is_authenticated:
208 user = fill_data(user)
235 user = fill_data(user)
209 user = fill_perms(user)
236 user = fill_perms(user)
210 session['hg_app_user'] = user
237 session['hg_app_user'] = user
211 session.save()
238 session.save()
212 return user
239 return user
213
240
214 #===============================================================================
241 #===============================================================================
215 # CHECK DECORATORS
242 # CHECK DECORATORS
216 #===============================================================================
243 #===============================================================================
217 class LoginRequired(object):
244 class LoginRequired(object):
218 """Must be logged in to execute this function else redirect to login page"""
245 """Must be logged in to execute this function else redirect to login page"""
219
246
220 def __call__(self, func):
247 def __call__(self, func):
221 return decorator(self.__wrapper, func)
248 return decorator(self.__wrapper, func)
222
249
223 def __wrapper(self, func, *fargs, **fkwargs):
250 def __wrapper(self, func, *fargs, **fkwargs):
224 user = session.get('hg_app_user', AuthUser())
251 user = session.get('hg_app_user', AuthUser())
225 log.debug('Checking login required for user:%s', user.username)
252 log.debug('Checking login required for user:%s', user.username)
226 if user.is_authenticated:
253 if user.is_authenticated:
227 log.debug('user %s is authenticated', user.username)
254 log.debug('user %s is authenticated', user.username)
228 return func(*fargs, **fkwargs)
255 return func(*fargs, **fkwargs)
229 else:
256 else:
230 log.warn('user %s not authenticated', user.username)
257 log.warn('user %s not authenticated', user.username)
231
258
232 p = request.environ.get('PATH_INFO')
259 p = request.environ.get('PATH_INFO')
233 if request.environ.get('QUERY_STRING'):
260 if request.environ.get('QUERY_STRING'):
234 p+='?'+request.environ.get('QUERY_STRING')
261 p += '?' + request.environ.get('QUERY_STRING')
235 log.debug('redirecting to login page with %s',p)
262 log.debug('redirecting to login page with %s', p)
236 return redirect(url('login_home',came_from=p))
263 return redirect(url('login_home', came_from=p))
237
264
238 class PermsDecorator(object):
265 class PermsDecorator(object):
239 """Base class for decorators"""
266 """Base class for decorators"""
240
267
241 def __init__(self, *required_perms):
268 def __init__(self, *required_perms):
242 available_perms = config['available_permissions']
269 available_perms = config['available_permissions']
243 for perm in required_perms:
270 for perm in required_perms:
244 if perm not in available_perms:
271 if perm not in available_perms:
245 raise Exception("'%s' permission is not defined" % perm)
272 raise Exception("'%s' permission is not defined" % perm)
246 self.required_perms = set(required_perms)
273 self.required_perms = set(required_perms)
247 self.user_perms = None
274 self.user_perms = None
248
275
249 def __call__(self, func):
276 def __call__(self, func):
250 return decorator(self.__wrapper, func)
277 return decorator(self.__wrapper, func)
251
278
252
279
253 def __wrapper(self, func, *fargs, **fkwargs):
280 def __wrapper(self, func, *fargs, **fkwargs):
254 # _wrapper.__name__ = func.__name__
281 # _wrapper.__name__ = func.__name__
255 # _wrapper.__dict__.update(func.__dict__)
282 # _wrapper.__dict__.update(func.__dict__)
256 # _wrapper.__doc__ = func.__doc__
283 # _wrapper.__doc__ = func.__doc__
257
284
258 self.user_perms = session.get('hg_app_user', AuthUser()).permissions
285 self.user_perms = session.get('hg_app_user', AuthUser()).permissions
259 log.debug('checking %s permissions %s for %s',
286 log.debug('checking %s permissions %s for %s',
260 self.__class__.__name__, self.required_perms, func.__name__)
287 self.__class__.__name__, self.required_perms, func.__name__)
261
288
262 if self.check_permissions():
289 if self.check_permissions():
263 log.debug('Permission granted for %s', func.__name__)
290 log.debug('Permission granted for %s', func.__name__)
264
291
265 return func(*fargs, **fkwargs)
292 return func(*fargs, **fkwargs)
266
293
267 else:
294 else:
268 log.warning('Permission denied for %s', func.__name__)
295 log.warning('Permission denied for %s', func.__name__)
269 #redirect with forbidden ret code
296 #redirect with forbidden ret code
270 return abort(403)
297 return abort(403)
271
298
272
299
273
300
274 def check_permissions(self):
301 def check_permissions(self):
275 """Dummy function for overriding"""
302 """Dummy function for overriding"""
276 raise Exception('You have to write this function in child class')
303 raise Exception('You have to write this function in child class')
277
304
278 class HasPermissionAllDecorator(PermsDecorator):
305 class HasPermissionAllDecorator(PermsDecorator):
279 """Checks for access permission for all given predicates. All of them
306 """Checks for access permission for all given predicates. All of them
280 have to be meet in order to fulfill the request
307 have to be meet in order to fulfill the request
281 """
308 """
282
309
283 def check_permissions(self):
310 def check_permissions(self):
284 if self.required_perms.issubset(self.user_perms.get('global')):
311 if self.required_perms.issubset(self.user_perms.get('global')):
285 return True
312 return True
286 return False
313 return False
287
314
288
315
289 class HasPermissionAnyDecorator(PermsDecorator):
316 class HasPermissionAnyDecorator(PermsDecorator):
290 """Checks for access permission for any of given predicates. In order to
317 """Checks for access permission for any of given predicates. In order to
291 fulfill the request any of predicates must be meet
318 fulfill the request any of predicates must be meet
292 """
319 """
293
320
294 def check_permissions(self):
321 def check_permissions(self):
295 if self.required_perms.intersection(self.user_perms.get('global')):
322 if self.required_perms.intersection(self.user_perms.get('global')):
296 return True
323 return True
297 return False
324 return False
298
325
299 class HasRepoPermissionAllDecorator(PermsDecorator):
326 class HasRepoPermissionAllDecorator(PermsDecorator):
300 """Checks for access permission for all given predicates for specific
327 """Checks for access permission for all given predicates for specific
301 repository. All of them have to be meet in order to fulfill the request
328 repository. All of them have to be meet in order to fulfill the request
302 """
329 """
303
330
304 def check_permissions(self):
331 def check_permissions(self):
305 repo_name = get_repo_slug(request)
332 repo_name = get_repo_slug(request)
306 try:
333 try:
307 user_perms = set([self.user_perms['repositories'][repo_name]])
334 user_perms = set([self.user_perms['repositories'][repo_name]])
308 except KeyError:
335 except KeyError:
309 return False
336 return False
310 if self.required_perms.issubset(user_perms):
337 if self.required_perms.issubset(user_perms):
311 return True
338 return True
312 return False
339 return False
313
340
314
341
315 class HasRepoPermissionAnyDecorator(PermsDecorator):
342 class HasRepoPermissionAnyDecorator(PermsDecorator):
316 """Checks for access permission for any of given predicates for specific
343 """Checks for access permission for any of given predicates for specific
317 repository. In order to fulfill the request any of predicates must be meet
344 repository. In order to fulfill the request any of predicates must be meet
318 """
345 """
319
346
320 def check_permissions(self):
347 def check_permissions(self):
321 repo_name = get_repo_slug(request)
348 repo_name = get_repo_slug(request)
322
349
323 try:
350 try:
324 user_perms = set([self.user_perms['repositories'][repo_name]])
351 user_perms = set([self.user_perms['repositories'][repo_name]])
325 except KeyError:
352 except KeyError:
326 return False
353 return False
327 if self.required_perms.intersection(user_perms):
354 if self.required_perms.intersection(user_perms):
328 return True
355 return True
329 return False
356 return False
330 #===============================================================================
357 #===============================================================================
331 # CHECK FUNCTIONS
358 # CHECK FUNCTIONS
332 #===============================================================================
359 #===============================================================================
333
360
334 class PermsFunction(object):
361 class PermsFunction(object):
335 """Base function for other check functions"""
362 """Base function for other check functions"""
336
363
337 def __init__(self, *perms):
364 def __init__(self, *perms):
338 available_perms = config['available_permissions']
365 available_perms = config['available_permissions']
339
366
340 for perm in perms:
367 for perm in perms:
341 if perm not in available_perms:
368 if perm not in available_perms:
342 raise Exception("'%s' permission in not defined" % perm)
369 raise Exception("'%s' permission in not defined" % perm)
343 self.required_perms = set(perms)
370 self.required_perms = set(perms)
344 self.user_perms = None
371 self.user_perms = None
345 self.granted_for = ''
372 self.granted_for = ''
346 self.repo_name = None
373 self.repo_name = None
347
374
348 def __call__(self, check_Location=''):
375 def __call__(self, check_Location=''):
349 user = session.get('hg_app_user', False)
376 user = session.get('hg_app_user', False)
350 if not user:
377 if not user:
351 return False
378 return False
352 self.user_perms = user.permissions
379 self.user_perms = user.permissions
353 self.granted_for = user.username
380 self.granted_for = user.username
354 log.debug('checking %s %s', self.__class__.__name__, self.required_perms)
381 log.debug('checking %s %s', self.__class__.__name__, self.required_perms)
355
382
356 if self.check_permissions():
383 if self.check_permissions():
357 log.debug('Permission granted for %s @%s', self.granted_for,
384 log.debug('Permission granted for %s @%s', self.granted_for,
358 check_Location)
385 check_Location)
359 return True
386 return True
360
387
361 else:
388 else:
362 log.warning('Permission denied for %s @%s', self.granted_for,
389 log.warning('Permission denied for %s @%s', self.granted_for,
363 check_Location)
390 check_Location)
364 return False
391 return False
365
392
366 def check_permissions(self):
393 def check_permissions(self):
367 """Dummy function for overriding"""
394 """Dummy function for overriding"""
368 raise Exception('You have to write this function in child class')
395 raise Exception('You have to write this function in child class')
369
396
370 class HasPermissionAll(PermsFunction):
397 class HasPermissionAll(PermsFunction):
371 def check_permissions(self):
398 def check_permissions(self):
372 if self.required_perms.issubset(self.user_perms.get('global')):
399 if self.required_perms.issubset(self.user_perms.get('global')):
373 return True
400 return True
374 return False
401 return False
375
402
376 class HasPermissionAny(PermsFunction):
403 class HasPermissionAny(PermsFunction):
377 def check_permissions(self):
404 def check_permissions(self):
378 if self.required_perms.intersection(self.user_perms.get('global')):
405 if self.required_perms.intersection(self.user_perms.get('global')):
379 return True
406 return True
380 return False
407 return False
381
408
382 class HasRepoPermissionAll(PermsFunction):
409 class HasRepoPermissionAll(PermsFunction):
383
410
384 def __call__(self, repo_name=None, check_Location=''):
411 def __call__(self, repo_name=None, check_Location=''):
385 self.repo_name = repo_name
412 self.repo_name = repo_name
386 return super(HasRepoPermissionAll, self).__call__(check_Location)
413 return super(HasRepoPermissionAll, self).__call__(check_Location)
387
414
388 def check_permissions(self):
415 def check_permissions(self):
389 if not self.repo_name:
416 if not self.repo_name:
390 self.repo_name = get_repo_slug(request)
417 self.repo_name = get_repo_slug(request)
391
418
392 try:
419 try:
393 self.user_perms = set([self.user_perms['repositories']\
420 self.user_perms = set([self.user_perms['repositories']\
394 [self.repo_name]])
421 [self.repo_name]])
395 except KeyError:
422 except KeyError:
396 return False
423 return False
397 self.granted_for = self.repo_name
424 self.granted_for = self.repo_name
398 if self.required_perms.issubset(self.user_perms):
425 if self.required_perms.issubset(self.user_perms):
399 return True
426 return True
400 return False
427 return False
401
428
402 class HasRepoPermissionAny(PermsFunction):
429 class HasRepoPermissionAny(PermsFunction):
403
430
404 def __call__(self, repo_name=None, check_Location=''):
431 def __call__(self, repo_name=None, check_Location=''):
405 self.repo_name = repo_name
432 self.repo_name = repo_name
406 return super(HasRepoPermissionAny, self).__call__(check_Location)
433 return super(HasRepoPermissionAny, self).__call__(check_Location)
407
434
408 def check_permissions(self):
435 def check_permissions(self):
409 if not self.repo_name:
436 if not self.repo_name:
410 self.repo_name = get_repo_slug(request)
437 self.repo_name = get_repo_slug(request)
411
438
412 try:
439 try:
413 self.user_perms = set([self.user_perms['repositories']\
440 self.user_perms = set([self.user_perms['repositories']\
414 [self.repo_name]])
441 [self.repo_name]])
415 except KeyError:
442 except KeyError:
416 return False
443 return False
417 self.granted_for = self.repo_name
444 self.granted_for = self.repo_name
418 if self.required_perms.intersection(self.user_perms):
445 if self.required_perms.intersection(self.user_perms):
419 return True
446 return True
420 return False
447 return False
421
448
422 #===============================================================================
449 #===============================================================================
423 # SPECIAL VERSION TO HANDLE MIDDLEWARE AUTH
450 # SPECIAL VERSION TO HANDLE MIDDLEWARE AUTH
424 #===============================================================================
451 #===============================================================================
425
452
426 class HasPermissionAnyMiddleware(object):
453 class HasPermissionAnyMiddleware(object):
427 def __init__(self, *perms):
454 def __init__(self, *perms):
428 self.required_perms = set(perms)
455 self.required_perms = set(perms)
429
456
430 def __call__(self, user, repo_name):
457 def __call__(self, user, repo_name):
431 usr = AuthUser()
458 usr = AuthUser()
432 usr.user_id = user.user_id
459 usr.user_id = user.user_id
433 usr.username = user.username
460 usr.username = user.username
434 usr.is_admin = user.admin
461 usr.is_admin = user.admin
435
462
436 try:
463 try:
437 self.user_perms = set([fill_perms(usr)\
464 self.user_perms = set([fill_perms(usr)\
438 .permissions['repositories'][repo_name]])
465 .permissions['repositories'][repo_name]])
439 except:
466 except:
440 self.user_perms = set()
467 self.user_perms = set()
441 self.granted_for = ''
468 self.granted_for = ''
442 self.username = user.username
469 self.username = user.username
443 self.repo_name = repo_name
470 self.repo_name = repo_name
444 return self.check_permissions()
471 return self.check_permissions()
445
472
446 def check_permissions(self):
473 def check_permissions(self):
447 log.debug('checking mercurial protocol '
474 log.debug('checking mercurial protocol '
448 'permissions for user:%s repository:%s',
475 'permissions for user:%s repository:%s',
449 self.username, self.repo_name)
476 self.username, self.repo_name)
450 if self.required_perms.intersection(self.user_perms):
477 if self.required_perms.intersection(self.user_perms):
451 log.debug('permission granted')
478 log.debug('permission granted')
452 return True
479 return True
453 log.debug('permission denied')
480 log.debug('permission denied')
454 return False
481 return False
@@ -1,263 +1,265 b''
1 #!/usr/bin/env python
1 #!/usr/bin/env python
2 # encoding: utf-8
2 # encoding: utf-8
3 # database managment for hg app
3 # database managment for hg app
4 # Copyright (C) 2009-2010 Marcin Kuzminski <marcin@python-works.com>
4 # Copyright (C) 2009-2010 Marcin Kuzminski <marcin@python-works.com>
5 #
5 #
6 # This program is free software; you can redistribute it and/or
6 # This program is free software; you can redistribute it and/or
7 # modify it under the terms of the GNU General Public License
7 # modify it under the terms of the GNU General Public License
8 # as published by the Free Software Foundation; version 2
8 # as published by the Free Software Foundation; version 2
9 # of the License or (at your opinion) any later version of the license.
9 # of the License or (at your opinion) any later version of the license.
10 #
10 #
11 # This program is distributed in the hope that it will be useful,
11 # This program is distributed in the hope that it will be useful,
12 # but WITHOUT ANY WARRANTY; without even the implied warranty of
12 # but WITHOUT ANY WARRANTY; without even the implied warranty of
13 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 # GNU General Public License for more details.
14 # GNU General Public License for more details.
15 #
15 #
16 # You should have received a copy of the GNU General Public License
16 # You should have received a copy of the GNU General Public License
17 # along with this program; if not, write to the Free Software
17 # along with this program; if not, write to the Free Software
18 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
18 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
19 # MA 02110-1301, USA.
19 # MA 02110-1301, USA.
20
20
21 """
21 """
22 Created on April 10, 2010
22 Created on April 10, 2010
23 database managment and creation for hg app
23 database managment and creation for hg app
24 @author: marcink
24 @author: marcink
25 """
25 """
26
26
27 from os.path import dirname as dn, join as jn
27 from os.path import dirname as dn, join as jn
28 import os
28 import os
29 import sys
29 import sys
30 import uuid
30 import uuid
31 ROOT = dn(dn(dn(os.path.realpath(__file__))))
31 ROOT = dn(dn(dn(os.path.realpath(__file__))))
32 sys.path.append(ROOT)
32 sys.path.append(ROOT)
33
33
34 from pylons_app.lib.auth import get_crypt_password
34 from pylons_app.lib.auth import get_crypt_password
35 from pylons_app.lib.utils import ask_ok
35 from pylons_app.lib.utils import ask_ok
36 from pylons_app.model import init_model
36 from pylons_app.model import init_model
37 from pylons_app.model.db import User, Permission, HgAppUi, HgAppSettings, \
37 from pylons_app.model.db import User, Permission, HgAppUi, HgAppSettings, \
38 UserToPerm
38 UserToPerm
39 from pylons_app.model import meta
39 from pylons_app.model import meta
40 from sqlalchemy.engine import create_engine
40 from sqlalchemy.engine import create_engine
41 import logging
41 import logging
42
42
43 log = logging.getLogger(__name__)
43 log = logging.getLogger(__name__)
44
44
45 class DbManage(object):
45 class DbManage(object):
46 def __init__(self, log_sql, dbname,tests=False):
46 def __init__(self, log_sql, dbname, tests=False):
47 self.dbname = dbname
47 self.dbname = dbname
48 self.tests = tests
48 self.tests = tests
49 dburi = 'sqlite:////%s' % jn(ROOT, self.dbname)
49 dburi = 'sqlite:////%s' % jn(ROOT, self.dbname)
50 engine = create_engine(dburi, echo=log_sql)
50 engine = create_engine(dburi, echo=log_sql)
51 init_model(engine)
51 init_model(engine)
52 self.sa = meta.Session
52 self.sa = meta.Session
53 self.db_exists = False
53 self.db_exists = False
54
54
55 def check_for_db(self, override):
55 def check_for_db(self, override):
56 log.info('checking for exisiting db')
56 log.info('checking for exisiting db')
57 if os.path.isfile(jn(ROOT, self.dbname)):
57 if os.path.isfile(jn(ROOT, self.dbname)):
58 self.db_exists = True
58 self.db_exists = True
59 log.info('database exisist')
59 log.info('database exisist')
60 if not override:
60 if not override:
61 raise Exception('database already exists')
61 raise Exception('database already exists')
62
62
63 def create_tables(self, override=False):
63 def create_tables(self, override=False):
64 """
64 """
65 Create a auth database
65 Create a auth database
66 """
66 """
67 self.check_for_db(override)
67 self.check_for_db(override)
68 if override:
68 if override:
69 log.info("database exisist and it's going to be destroyed")
69 log.info("database exisist and it's going to be destroyed")
70 if self.tests:
70 if self.tests:
71 destroy=True
71 destroy = True
72 else:
72 else:
73 destroy = ask_ok('Are you sure to destroy old database ? [y/n]')
73 destroy = ask_ok('Are you sure to destroy old database ? [y/n]')
74 if not destroy:
74 if not destroy:
75 sys.exit()
75 sys.exit()
76 if self.db_exists and destroy:
76 if self.db_exists and destroy:
77 os.remove(jn(ROOT, self.dbname))
77 os.remove(jn(ROOT, self.dbname))
78 checkfirst = not override
78 checkfirst = not override
79 meta.Base.metadata.create_all(checkfirst=checkfirst)
79 meta.Base.metadata.create_all(checkfirst=checkfirst)
80 log.info('Created tables for %s', self.dbname)
80 log.info('Created tables for %s', self.dbname)
81
81
82 def admin_prompt(self):
82 def admin_prompt(self):
83 if not self.tests:
83 if not self.tests:
84 import getpass
84 import getpass
85 username = raw_input('Specify admin username:')
85 username = raw_input('Specify admin username:')
86 password = getpass.getpass('Specify admin password:')
86 password = getpass.getpass('Specify admin password:')
87 self.create_user(username, password, True)
87 email = raw_input('Specify admin email:')
88 self.create_user(username, password, email, True)
88 else:
89 else:
89 log.info('creating admin and regular test users')
90 log.info('creating admin and regular test users')
90 self.create_user('test_admin', 'test', True)
91 self.create_user('test_admin', 'test', 'test_admin@mail.com', True)
91 self.create_user('test_regular', 'test', False)
92 self.create_user('test_regular', 'test', 'test_regular@mail.com', False)
93 self.create_user('test_regular2', 'test', 'test_regular2@mail.com', False)
92
94
93
95
94
96
95 def config_prompt(self,test_repo_path=''):
97 def config_prompt(self, test_repo_path=''):
96 log.info('Setting up repositories config')
98 log.info('Setting up repositories config')
97
99
98 if not self.tests and not test_repo_path:
100 if not self.tests and not test_repo_path:
99 path = raw_input('Specify valid full path to your repositories'
101 path = raw_input('Specify valid full path to your repositories'
100 ' you can change this later in application settings:')
102 ' you can change this later in application settings:')
101 else:
103 else:
102 path = test_repo_path
104 path = test_repo_path
103
105
104 if not os.path.isdir(path):
106 if not os.path.isdir(path):
105 log.error('You entered wrong path: %s',path)
107 log.error('You entered wrong path: %s', path)
106 sys.exit()
108 sys.exit()
107
109
108 hooks1 = HgAppUi()
110 hooks1 = HgAppUi()
109 hooks1.ui_section = 'hooks'
111 hooks1.ui_section = 'hooks'
110 hooks1.ui_key = 'changegroup.update'
112 hooks1.ui_key = 'changegroup.update'
111 hooks1.ui_value = 'hg update >&2'
113 hooks1.ui_value = 'hg update >&2'
112
114
113 hooks2 = HgAppUi()
115 hooks2 = HgAppUi()
114 hooks2.ui_section = 'hooks'
116 hooks2.ui_section = 'hooks'
115 hooks2.ui_key = 'changegroup.repo_size'
117 hooks2.ui_key = 'changegroup.repo_size'
116 hooks2.ui_value = 'python:pylons_app.lib.hooks.repo_size'
118 hooks2.ui_value = 'python:pylons_app.lib.hooks.repo_size'
117
119
118 web1 = HgAppUi()
120 web1 = HgAppUi()
119 web1.ui_section = 'web'
121 web1.ui_section = 'web'
120 web1.ui_key = 'push_ssl'
122 web1.ui_key = 'push_ssl'
121 web1.ui_value = 'false'
123 web1.ui_value = 'false'
122
124
123 web2 = HgAppUi()
125 web2 = HgAppUi()
124 web2.ui_section = 'web'
126 web2.ui_section = 'web'
125 web2.ui_key = 'allow_archive'
127 web2.ui_key = 'allow_archive'
126 web2.ui_value = 'gz zip bz2'
128 web2.ui_value = 'gz zip bz2'
127
129
128 web3 = HgAppUi()
130 web3 = HgAppUi()
129 web3.ui_section = 'web'
131 web3.ui_section = 'web'
130 web3.ui_key = 'allow_push'
132 web3.ui_key = 'allow_push'
131 web3.ui_value = '*'
133 web3.ui_value = '*'
132
134
133 web4 = HgAppUi()
135 web4 = HgAppUi()
134 web4.ui_section = 'web'
136 web4.ui_section = 'web'
135 web4.ui_key = 'baseurl'
137 web4.ui_key = 'baseurl'
136 web4.ui_value = '/'
138 web4.ui_value = '/'
137
139
138 paths = HgAppUi()
140 paths = HgAppUi()
139 paths.ui_section = 'paths'
141 paths.ui_section = 'paths'
140 paths.ui_key = '/'
142 paths.ui_key = '/'
141 paths.ui_value = os.path.join(path, '*')
143 paths.ui_value = os.path.join(path, '*')
142
144
143
145
144 hgsettings1 = HgAppSettings()
146 hgsettings1 = HgAppSettings()
145
147
146 hgsettings1.app_settings_name = 'realm'
148 hgsettings1.app_settings_name = 'realm'
147 hgsettings1.app_settings_value = 'hg-app authentication'
149 hgsettings1.app_settings_value = 'hg-app authentication'
148
150
149 hgsettings2 = HgAppSettings()
151 hgsettings2 = HgAppSettings()
150 hgsettings2.app_settings_name = 'title'
152 hgsettings2.app_settings_name = 'title'
151 hgsettings2.app_settings_value = 'hg-app'
153 hgsettings2.app_settings_value = 'hg-app'
152
154
153 try:
155 try:
154 self.sa.add(hooks1)
156 self.sa.add(hooks1)
155 self.sa.add(hooks2)
157 self.sa.add(hooks2)
156 self.sa.add(web1)
158 self.sa.add(web1)
157 self.sa.add(web2)
159 self.sa.add(web2)
158 self.sa.add(web3)
160 self.sa.add(web3)
159 self.sa.add(web4)
161 self.sa.add(web4)
160 self.sa.add(paths)
162 self.sa.add(paths)
161 self.sa.add(hgsettings1)
163 self.sa.add(hgsettings1)
162 self.sa.add(hgsettings2)
164 self.sa.add(hgsettings2)
163 self.sa.commit()
165 self.sa.commit()
164 except:
166 except:
165 self.sa.rollback()
167 self.sa.rollback()
166 raise
168 raise
167 log.info('created ui config')
169 log.info('created ui config')
168
170
169 def create_user(self, username, password, admin=False):
171 def create_user(self, username, password, email='', admin=False):
170 log.info('creating administrator user %s', username)
172 log.info('creating administrator user %s', username)
171 new_user = User()
173 new_user = User()
172 new_user.username = username
174 new_user.username = username
173 new_user.password = get_crypt_password(password)
175 new_user.password = get_crypt_password(password)
174 new_user.name = 'Hg'
176 new_user.name = 'Hg'
175 new_user.lastname = 'Admin'
177 new_user.lastname = 'Admin'
176 new_user.email = 'admin@localhost'
178 new_user.email = email
177 new_user.admin = admin
179 new_user.admin = admin
178 new_user.active = True
180 new_user.active = True
179
181
180 try:
182 try:
181 self.sa.add(new_user)
183 self.sa.add(new_user)
182 self.sa.commit()
184 self.sa.commit()
183 except:
185 except:
184 self.sa.rollback()
186 self.sa.rollback()
185 raise
187 raise
186
188
187 def create_default_user(self):
189 def create_default_user(self):
188 log.info('creating default user')
190 log.info('creating default user')
189 #create default user for handling default permissions.
191 #create default user for handling default permissions.
190 def_user = User()
192 def_user = User()
191 def_user.username = 'default'
193 def_user.username = 'default'
192 def_user.password = get_crypt_password(str(uuid.uuid1())[:8])
194 def_user.password = get_crypt_password(str(uuid.uuid1())[:8])
193 def_user.name = 'default'
195 def_user.name = 'default'
194 def_user.lastname = 'default'
196 def_user.lastname = 'default'
195 def_user.email = 'default@default.com'
197 def_user.email = 'default@default.com'
196 def_user.admin = False
198 def_user.admin = False
197 def_user.active = False
199 def_user.active = False
198 try:
200 try:
199 self.sa.add(def_user)
201 self.sa.add(def_user)
200 self.sa.commit()
202 self.sa.commit()
201 except:
203 except:
202 self.sa.rollback()
204 self.sa.rollback()
203 raise
205 raise
204
206
205 def create_permissions(self):
207 def create_permissions(self):
206 #module.(access|create|change|delete)_[name]
208 #module.(access|create|change|delete)_[name]
207 #module.(read|write|owner)
209 #module.(read|write|owner)
208 perms = [('repository.none', 'Repository no access'),
210 perms = [('repository.none', 'Repository no access'),
209 ('repository.read', 'Repository read access'),
211 ('repository.read', 'Repository read access'),
210 ('repository.write', 'Repository write access'),
212 ('repository.write', 'Repository write access'),
211 ('repository.admin', 'Repository admin access'),
213 ('repository.admin', 'Repository admin access'),
212 ('hg.admin', 'Hg Administrator'),
214 ('hg.admin', 'Hg Administrator'),
213 ('hg.create.repository', 'Repository create'),
215 ('hg.create.repository', 'Repository create'),
214 ('hg.create.none', 'Repository creation disabled'),
216 ('hg.create.none', 'Repository creation disabled'),
215 ('hg.register.none', 'Register disabled'),
217 ('hg.register.none', 'Register disabled'),
216 ('hg.register.manual_activate', 'Register new user with hg-app without manual activation'),
218 ('hg.register.manual_activate', 'Register new user with hg-app without manual activation'),
217 ('hg.register.auto_activate', 'Register new user with hg-app without auto activation'),
219 ('hg.register.auto_activate', 'Register new user with hg-app without auto activation'),
218 ]
220 ]
219
221
220 for p in perms:
222 for p in perms:
221 new_perm = Permission()
223 new_perm = Permission()
222 new_perm.permission_name = p[0]
224 new_perm.permission_name = p[0]
223 new_perm.permission_longname = p[1]
225 new_perm.permission_longname = p[1]
224 try:
226 try:
225 self.sa.add(new_perm)
227 self.sa.add(new_perm)
226 self.sa.commit()
228 self.sa.commit()
227 except:
229 except:
228 self.sa.rollback()
230 self.sa.rollback()
229 raise
231 raise
230
232
231 def populate_default_permissions(self):
233 def populate_default_permissions(self):
232 log.info('creating default user permissions')
234 log.info('creating default user permissions')
233
235
234 default_user = self.sa.query(User)\
236 default_user = self.sa.query(User)\
235 .filter(User.username == 'default').scalar()
237 .filter(User.username == 'default').scalar()
236
238
237 reg_perm = UserToPerm()
239 reg_perm = UserToPerm()
238 reg_perm.user = default_user
240 reg_perm.user = default_user
239 reg_perm.permission = self.sa.query(Permission)\
241 reg_perm.permission = self.sa.query(Permission)\
240 .filter(Permission.permission_name == 'hg.register.manual_activate')\
242 .filter(Permission.permission_name == 'hg.register.manual_activate')\
241 .scalar()
243 .scalar()
242
244
243 create_repo_perm = UserToPerm()
245 create_repo_perm = UserToPerm()
244 create_repo_perm.user = default_user
246 create_repo_perm.user = default_user
245 create_repo_perm.permission = self.sa.query(Permission)\
247 create_repo_perm.permission = self.sa.query(Permission)\
246 .filter(Permission.permission_name == 'hg.create.repository')\
248 .filter(Permission.permission_name == 'hg.create.repository')\
247 .scalar()
249 .scalar()
248
250
249 default_repo_perm = UserToPerm()
251 default_repo_perm = UserToPerm()
250 default_repo_perm.user = default_user
252 default_repo_perm.user = default_user
251 default_repo_perm.permission = self.sa.query(Permission)\
253 default_repo_perm.permission = self.sa.query(Permission)\
252 .filter(Permission.permission_name == 'repository.read')\
254 .filter(Permission.permission_name == 'repository.read')\
253 .scalar()
255 .scalar()
254
256
255 try:
257 try:
256 self.sa.add(reg_perm)
258 self.sa.add(reg_perm)
257 self.sa.add(create_repo_perm)
259 self.sa.add(create_repo_perm)
258 self.sa.add(default_repo_perm)
260 self.sa.add(default_repo_perm)
259 self.sa.commit()
261 self.sa.commit()
260 except:
262 except:
261 self.sa.rollback()
263 self.sa.rollback()
262 raise
264 raise
263
265
@@ -1,369 +1,374 b''
1 """Helper functions
1 """Helper functions
2
2
3 Consists of functions to typically be used within templates, but also
3 Consists of functions to typically be used within templates, but also
4 available to Controllers. This module is available to both as 'h'.
4 available to Controllers. This module is available to both as 'h'.
5 """
5 """
6 from pygments.formatters import HtmlFormatter
6 from pygments.formatters import HtmlFormatter
7 from pygments import highlight as code_highlight
7 from pygments import highlight as code_highlight
8 from pylons import url, app_globals as g
8 from pylons import url, app_globals as g
9 from pylons.i18n.translation import _, ungettext
9 from pylons.i18n.translation import _, ungettext
10 from vcs.utils.annotate import annotate_highlight
10 from vcs.utils.annotate import annotate_highlight
11 from webhelpers.html import literal, HTML, escape
11 from webhelpers.html import literal, HTML, escape
12 from webhelpers.html.tools import *
12 from webhelpers.html.tools import *
13 from webhelpers.html.builder import make_tag
13 from webhelpers.html.builder import make_tag
14 from webhelpers.html.tags import auto_discovery_link, checkbox, css_classes, \
14 from webhelpers.html.tags import auto_discovery_link, checkbox, css_classes, \
15 end_form, file, form, hidden, image, javascript_link, link_to, link_to_if, \
15 end_form, file, form, hidden, image, javascript_link, link_to, link_to_if, \
16 link_to_unless, ol, required_legend, select, stylesheet_link, submit, text, \
16 link_to_unless, ol, required_legend, select, stylesheet_link, submit, text, \
17 password, textarea, title, ul, xml_declaration, radio
17 password, textarea, title, ul, xml_declaration, radio
18 from webhelpers.html.tools import auto_link, button_to, highlight, js_obfuscate, \
18 from webhelpers.html.tools import auto_link, button_to, highlight, js_obfuscate, \
19 mail_to, strip_links, strip_tags, tag_re
19 mail_to, strip_links, strip_tags, tag_re
20 from webhelpers.number import format_byte_size, format_bit_size
20 from webhelpers.number import format_byte_size, format_bit_size
21 from webhelpers.pylonslib import Flash as _Flash
21 from webhelpers.pylonslib import Flash as _Flash
22 from webhelpers.pylonslib.secure_form import secure_form
22 from webhelpers.pylonslib.secure_form import secure_form
23 from webhelpers.text import chop_at, collapse, convert_accented_entities, \
23 from webhelpers.text import chop_at, collapse, convert_accented_entities, \
24 convert_misc_entities, lchop, plural, rchop, remove_formatting, \
24 convert_misc_entities, lchop, plural, rchop, remove_formatting, \
25 replace_whitespace, urlify, truncate, wrap_paragraphs
25 replace_whitespace, urlify, truncate, wrap_paragraphs
26
26
27 #Custom helpers here :)
27 #Custom helpers here :)
28 class _Link(object):
28 class _Link(object):
29 '''
29 '''
30 Make a url based on label and url with help of url_for
30 Make a url based on label and url with help of url_for
31 @param label:name of link if not defined url is used
31 @param label:name of link if not defined url is used
32 @param url: the url for link
32 @param url: the url for link
33 '''
33 '''
34
34
35 def __call__(self, label='', *url_, **urlargs):
35 def __call__(self, label='', *url_, **urlargs):
36 if label is None or '':
36 if label is None or '':
37 label = url
37 label = url
38 link_fn = link_to(label, url(*url_, **urlargs))
38 link_fn = link_to(label, url(*url_, **urlargs))
39 return link_fn
39 return link_fn
40
40
41 link = _Link()
41 link = _Link()
42
42
43 class _GetError(object):
43 class _GetError(object):
44
44
45 def __call__(self, field_name, form_errors):
45 def __call__(self, field_name, form_errors):
46 tmpl = """<span class="error_msg">%s</span>"""
46 tmpl = """<span class="error_msg">%s</span>"""
47 if form_errors and form_errors.has_key(field_name):
47 if form_errors and form_errors.has_key(field_name):
48 return literal(tmpl % form_errors.get(field_name))
48 return literal(tmpl % form_errors.get(field_name))
49
49
50 get_error = _GetError()
50 get_error = _GetError()
51
51
52 def recursive_replace(str, replace=' '):
52 def recursive_replace(str, replace=' '):
53 """
53 """
54 Recursive replace of given sign to just one instance
54 Recursive replace of given sign to just one instance
55 @param str: given string
55 @param str: given string
56 @param replace:char to find and replace multiple instances
56 @param replace:char to find and replace multiple instances
57
57
58 Examples::
58 Examples::
59 >>> recursive_replace("Mighty---Mighty-Bo--sstones",'-')
59 >>> recursive_replace("Mighty---Mighty-Bo--sstones",'-')
60 'Mighty-Mighty-Bo-sstones'
60 'Mighty-Mighty-Bo-sstones'
61 """
61 """
62
62
63 if str.find(replace * 2) == -1:
63 if str.find(replace * 2) == -1:
64 return str
64 return str
65 else:
65 else:
66 str = str.replace(replace * 2, replace)
66 str = str.replace(replace * 2, replace)
67 return recursive_replace(str, replace)
67 return recursive_replace(str, replace)
68
68
69 class _ToolTip(object):
69 class _ToolTip(object):
70
70
71 def __call__(self, tooltip_title, trim_at=50):
71 def __call__(self, tooltip_title, trim_at=50):
72 """
72 """
73 Special function just to wrap our text into nice formatted autowrapped
73 Special function just to wrap our text into nice formatted autowrapped
74 text
74 text
75 @param tooltip_title:
75 @param tooltip_title:
76 """
76 """
77
77
78 return wrap_paragraphs(escape(tooltip_title), trim_at)\
78 return wrap_paragraphs(escape(tooltip_title), trim_at)\
79 .replace('\n', '<br/>')
79 .replace('\n', '<br/>')
80
80
81 def activate(self):
81 def activate(self):
82 """
82 """
83 Adds tooltip mechanism to the given Html all tooltips have to have
83 Adds tooltip mechanism to the given Html all tooltips have to have
84 set class tooltip and set attribute tooltip_title.
84 set class tooltip and set attribute tooltip_title.
85 Then a tooltip will be generated based on that
85 Then a tooltip will be generated based on that
86 All with yui js tooltip
86 All with yui js tooltip
87 """
87 """
88
88
89 js = '''
89 js = '''
90 YAHOO.util.Event.onDOMReady(function(){
90 YAHOO.util.Event.onDOMReady(function(){
91 function toolTipsId(){
91 function toolTipsId(){
92 var ids = [];
92 var ids = [];
93 var tts = YAHOO.util.Dom.getElementsByClassName('tooltip');
93 var tts = YAHOO.util.Dom.getElementsByClassName('tooltip');
94
94
95 for (var i = 0; i < tts.length; i++) {
95 for (var i = 0; i < tts.length; i++) {
96 //if element doesn not have and id autgenerate one for tooltip
96 //if element doesn not have and id autgenerate one for tooltip
97
97
98 if (!tts[i].id){
98 if (!tts[i].id){
99 tts[i].id='tt'+i*100;
99 tts[i].id='tt'+i*100;
100 }
100 }
101 ids.push(tts[i].id);
101 ids.push(tts[i].id);
102 }
102 }
103 return ids
103 return ids
104 };
104 };
105 var myToolTips = new YAHOO.widget.Tooltip("tooltip", {
105 var myToolTips = new YAHOO.widget.Tooltip("tooltip", {
106 context: toolTipsId(),
106 context: toolTipsId(),
107 monitorresize:false,
107 monitorresize:false,
108 xyoffset :[0,0],
108 xyoffset :[0,0],
109 autodismissdelay:300000,
109 autodismissdelay:300000,
110 hidedelay:5,
110 hidedelay:5,
111 showdelay:20,
111 showdelay:20,
112 });
112 });
113
113
114 //Mouse Over event disabled for new repositories since they dont
114 //Mouse Over event disabled for new repositories since they dont
115 //have last commit message
115 //have last commit message
116 myToolTips.contextMouseOverEvent.subscribe(
116 myToolTips.contextMouseOverEvent.subscribe(
117 function(type, args) {
117 function(type, args) {
118 var context = args[0];
118 var context = args[0];
119 var txt = context.getAttribute('tooltip_title');
119 var txt = context.getAttribute('tooltip_title');
120 if(txt){
120 if(txt){
121 return true;
121 return true;
122 }
122 }
123 else{
123 else{
124 return false;
124 return false;
125 }
125 }
126 });
126 });
127
127
128
128
129 // Set the text for the tooltip just before we display it. Lazy method
129 // Set the text for the tooltip just before we display it. Lazy method
130 myToolTips.contextTriggerEvent.subscribe(
130 myToolTips.contextTriggerEvent.subscribe(
131 function(type, args) {
131 function(type, args) {
132
132
133
133
134 var context = args[0];
134 var context = args[0];
135
135
136 var txt = context.getAttribute('tooltip_title');
136 var txt = context.getAttribute('tooltip_title');
137 this.cfg.setProperty("text", txt);
137 this.cfg.setProperty("text", txt);
138
138
139
139
140 // positioning of tooltip
140 // positioning of tooltip
141 var tt_w = this.element.clientWidth;
141 var tt_w = this.element.clientWidth;
142 var tt_h = this.element.clientHeight;
142 var tt_h = this.element.clientHeight;
143
143
144 var context_w = context.offsetWidth;
144 var context_w = context.offsetWidth;
145 var context_h = context.offsetHeight;
145 var context_h = context.offsetHeight;
146
146
147 var pos_x = YAHOO.util.Dom.getX(context);
147 var pos_x = YAHOO.util.Dom.getX(context);
148 var pos_y = YAHOO.util.Dom.getY(context);
148 var pos_y = YAHOO.util.Dom.getY(context);
149
149
150 var display_strategy = 'top';
150 var display_strategy = 'top';
151 var xy_pos = [0,0];
151 var xy_pos = [0,0];
152 switch (display_strategy){
152 switch (display_strategy){
153
153
154 case 'top':
154 case 'top':
155 var cur_x = (pos_x+context_w/2)-(tt_w/2);
155 var cur_x = (pos_x+context_w/2)-(tt_w/2);
156 var cur_y = pos_y-tt_h-4;
156 var cur_y = pos_y-tt_h-4;
157 xy_pos = [cur_x,cur_y];
157 xy_pos = [cur_x,cur_y];
158 break;
158 break;
159 case 'bottom':
159 case 'bottom':
160 var cur_x = (pos_x+context_w/2)-(tt_w/2);
160 var cur_x = (pos_x+context_w/2)-(tt_w/2);
161 var cur_y = pos_y+context_h+4;
161 var cur_y = pos_y+context_h+4;
162 xy_pos = [cur_x,cur_y];
162 xy_pos = [cur_x,cur_y];
163 break;
163 break;
164 case 'left':
164 case 'left':
165 var cur_x = (pos_x-tt_w-4);
165 var cur_x = (pos_x-tt_w-4);
166 var cur_y = pos_y-((tt_h/2)-context_h/2);
166 var cur_y = pos_y-((tt_h/2)-context_h/2);
167 xy_pos = [cur_x,cur_y];
167 xy_pos = [cur_x,cur_y];
168 break;
168 break;
169 case 'right':
169 case 'right':
170 var cur_x = (pos_x+context_w+4);
170 var cur_x = (pos_x+context_w+4);
171 var cur_y = pos_y-((tt_h/2)-context_h/2);
171 var cur_y = pos_y-((tt_h/2)-context_h/2);
172 xy_pos = [cur_x,cur_y];
172 xy_pos = [cur_x,cur_y];
173 break;
173 break;
174 default:
174 default:
175 var cur_x = (pos_x+context_w/2)-(tt_w/2);
175 var cur_x = (pos_x+context_w/2)-(tt_w/2);
176 var cur_y = pos_y-tt_h-4;
176 var cur_y = pos_y-tt_h-4;
177 xy_pos = [cur_x,cur_y];
177 xy_pos = [cur_x,cur_y];
178 break;
178 break;
179
179
180 }
180 }
181
181
182 this.cfg.setProperty("xy",xy_pos);
182 this.cfg.setProperty("xy",xy_pos);
183
183
184 });
184 });
185
185
186 //Mouse out
186 //Mouse out
187 myToolTips.contextMouseOutEvent.subscribe(
187 myToolTips.contextMouseOutEvent.subscribe(
188 function(type, args) {
188 function(type, args) {
189 var context = args[0];
189 var context = args[0];
190
190
191 });
191 });
192 });
192 });
193 '''
193 '''
194 return literal(js)
194 return literal(js)
195
195
196 tooltip = _ToolTip()
196 tooltip = _ToolTip()
197
197
198 class _FilesBreadCrumbs(object):
198 class _FilesBreadCrumbs(object):
199
199
200 def __call__(self, repo_name, rev, paths):
200 def __call__(self, repo_name, rev, paths):
201 url_l = [link_to(repo_name, url('files_home',
201 url_l = [link_to(repo_name, url('files_home',
202 repo_name=repo_name,
202 repo_name=repo_name,
203 revision=rev, f_path=''))]
203 revision=rev, f_path=''))]
204 paths_l = paths.split('/')
204 paths_l = paths.split('/')
205
205
206 for cnt, p in enumerate(paths_l, 1):
206 for cnt, p in enumerate(paths_l, 1):
207 if p != '':
207 if p != '':
208 url_l.append(link_to(p, url('files_home',
208 url_l.append(link_to(p, url('files_home',
209 repo_name=repo_name,
209 repo_name=repo_name,
210 revision=rev,
210 revision=rev,
211 f_path='/'.join(paths_l[:cnt]))))
211 f_path='/'.join(paths_l[:cnt]))))
212
212
213 return literal('/'.join(url_l))
213 return literal('/'.join(url_l))
214
214
215 files_breadcrumbs = _FilesBreadCrumbs()
215 files_breadcrumbs = _FilesBreadCrumbs()
216
216
217 def pygmentize(filenode, **kwargs):
217 def pygmentize(filenode, **kwargs):
218 """
218 """
219 pygmentize function using pygments
219 pygmentize function using pygments
220 @param filenode:
220 @param filenode:
221 """
221 """
222 return literal(code_highlight(filenode.content,
222 return literal(code_highlight(filenode.content,
223 filenode.lexer, HtmlFormatter(**kwargs)))
223 filenode.lexer, HtmlFormatter(**kwargs)))
224
224
225 def pygmentize_annotation(filenode, **kwargs):
225 def pygmentize_annotation(filenode, **kwargs):
226 """
226 """
227 pygmentize function for annotation
227 pygmentize function for annotation
228 @param filenode:
228 @param filenode:
229 """
229 """
230
230
231 color_dict = {}
231 color_dict = {}
232 def gen_color():
232 def gen_color():
233 """generator for getting 10k of evenly distibuted colors using hsv color
233 """generator for getting 10k of evenly distibuted colors using hsv color
234 and golden ratio.
234 and golden ratio.
235 """
235 """
236 import colorsys
236 import colorsys
237 n = 10000
237 n = 10000
238 golden_ratio = 0.618033988749895
238 golden_ratio = 0.618033988749895
239 h = 0.22717784590367374
239 h = 0.22717784590367374
240 #generate 10k nice web friendly colors in the same order
240 #generate 10k nice web friendly colors in the same order
241 for c in xrange(n):
241 for c in xrange(n):
242 h += golden_ratio
242 h += golden_ratio
243 h %= 1
243 h %= 1
244 HSV_tuple = [h, 0.95, 0.95]
244 HSV_tuple = [h, 0.95, 0.95]
245 RGB_tuple = colorsys.hsv_to_rgb(*HSV_tuple)
245 RGB_tuple = colorsys.hsv_to_rgb(*HSV_tuple)
246 yield map(lambda x:str(int(x * 256)), RGB_tuple)
246 yield map(lambda x:str(int(x * 256)), RGB_tuple)
247
247
248 cgenerator = gen_color()
248 cgenerator = gen_color()
249
249
250 def get_color_string(cs):
250 def get_color_string(cs):
251 if color_dict.has_key(cs):
251 if color_dict.has_key(cs):
252 col = color_dict[cs]
252 col = color_dict[cs]
253 else:
253 else:
254 col = color_dict[cs] = cgenerator.next()
254 col = color_dict[cs] = cgenerator.next()
255 return "color: rgb(%s)! important;" % (', '.join(col))
255 return "color: rgb(%s)! important;" % (', '.join(col))
256
256
257 def url_func(changeset):
257 def url_func(changeset):
258 tooltip_html = "<div style='font-size:0.8em'><b>Author:</b>" + \
258 tooltip_html = "<div style='font-size:0.8em'><b>Author:</b>" + \
259 " %s<br/><b>Date:</b> %s</b><br/><b>Message:</b> %s<br/></div>"
259 " %s<br/><b>Date:</b> %s</b><br/><b>Message:</b> %s<br/></div>"
260
260
261 tooltip_html = tooltip_html % (changeset.author,
261 tooltip_html = tooltip_html % (changeset.author,
262 changeset.date,
262 changeset.date,
263 tooltip(changeset.message))
263 tooltip(changeset.message))
264 lnk_format = 'r%-5s:%s' % (changeset.revision,
264 lnk_format = 'r%-5s:%s' % (changeset.revision,
265 changeset.raw_id)
265 changeset.raw_id)
266 uri = link_to(
266 uri = link_to(
267 lnk_format,
267 lnk_format,
268 url('changeset_home', repo_name=changeset.repository.name,
268 url('changeset_home', repo_name=changeset.repository.name,
269 revision=changeset.raw_id),
269 revision=changeset.raw_id),
270 style=get_color_string(changeset.raw_id),
270 style=get_color_string(changeset.raw_id),
271 class_='tooltip',
271 class_='tooltip',
272 tooltip_title=tooltip_html
272 tooltip_title=tooltip_html
273 )
273 )
274
274
275 uri += '\n'
275 uri += '\n'
276 return uri
276 return uri
277 return literal(annotate_highlight(filenode, url_func, **kwargs))
277 return literal(annotate_highlight(filenode, url_func, **kwargs))
278
278
279 def repo_name_slug(value):
279 def repo_name_slug(value):
280 """
280 """Return slug of name of repository
281 Return slug of name of repository
281 This function is called on each creation/modification
282 of repository to prevent bad names in repo
282 """
283 """
283 slug = urlify(value)
284 slug = remove_formatting(value)
284 for c in """=[]\;'"<>,/~!@#$%^&*()+{}|:""":
285 slug = strip_tags(slug)
286
287 for c in """=[]\;'"<>,/~!@#$%^&*()+{}|: """:
285 slug = slug.replace(c, '-')
288 slug = slug.replace(c, '-')
286 slug = recursive_replace(slug, '-')
289 slug = recursive_replace(slug, '-')
290 slug = collapse(slug, '-')
287 return slug
291 return slug
288
292
289 def get_changeset_safe(repo, rev):
293 def get_changeset_safe(repo, rev):
290 from vcs.backends.base import BaseRepository
294 from vcs.backends.base import BaseRepository
291 from vcs.exceptions import RepositoryError
295 from vcs.exceptions import RepositoryError
292 if not isinstance(repo, BaseRepository):
296 if not isinstance(repo, BaseRepository):
293 raise Exception('You must pass an Repository '
297 raise Exception('You must pass an Repository '
294 'object as first argument got %s', type(repo))
298 'object as first argument got %s', type(repo))
295
299
296 try:
300 try:
297 cs = repo.get_changeset(rev)
301 cs = repo.get_changeset(rev)
298 except RepositoryError:
302 except RepositoryError:
299 from pylons_app.lib.utils import EmptyChangeset
303 from pylons_app.lib.utils import EmptyChangeset
300 cs = EmptyChangeset()
304 cs = EmptyChangeset()
301 return cs
305 return cs
302
306
303
307
304 flash = _Flash()
308 flash = _Flash()
305
309
306
310
307 #===============================================================================
311 #===============================================================================
308 # MERCURIAL FILTERS available via h.
312 # MERCURIAL FILTERS available via h.
309 #===============================================================================
313 #===============================================================================
310 from mercurial import util
314 from mercurial import util
311 from mercurial.templatefilters import age as _age, person as _person
315 from mercurial.templatefilters import age as _age, person as _person
312
316
313 age = lambda x:_age(x)
317 age = lambda x:_age(x)
314 capitalize = lambda x: x.capitalize()
318 capitalize = lambda x: x.capitalize()
315 date = lambda x: util.datestr(x)
319 date = lambda x: util.datestr(x)
316 email = util.email
320 email = util.email
317 email_or_none = lambda x: util.email(x) if util.email(x) != x else None
321 email_or_none = lambda x: util.email(x) if util.email(x) != x else None
318 person = lambda x: _person(x)
322 person = lambda x: _person(x)
319 hgdate = lambda x: "%d %d" % x
323 hgdate = lambda x: "%d %d" % x
320 isodate = lambda x: util.datestr(x, '%Y-%m-%d %H:%M %1%2')
324 isodate = lambda x: util.datestr(x, '%Y-%m-%d %H:%M %1%2')
321 isodatesec = lambda x: util.datestr(x, '%Y-%m-%d %H:%M:%S %1%2')
325 isodatesec = lambda x: util.datestr(x, '%Y-%m-%d %H:%M:%S %1%2')
322 localdate = lambda x: (x[0], util.makedate()[1])
326 localdate = lambda x: (x[0], util.makedate()[1])
323 rfc822date = lambda x: util.datestr(x, "%a, %d %b %Y %H:%M:%S %1%2")
327 rfc822date = lambda x: util.datestr(x, "%a, %d %b %Y %H:%M:%S %1%2")
328 rfc822date_notz = lambda x: util.datestr(x, "%a, %d %b %Y %H:%M:%S")
324 rfc3339date = lambda x: util.datestr(x, "%Y-%m-%dT%H:%M:%S%1:%2")
329 rfc3339date = lambda x: util.datestr(x, "%Y-%m-%dT%H:%M:%S%1:%2")
325 time_ago = lambda x: util.datestr(_age(x), "%a, %d %b %Y %H:%M:%S %1%2")
330 time_ago = lambda x: util.datestr(_age(x), "%a, %d %b %Y %H:%M:%S %1%2")
326
331
327
332
328 #===============================================================================
333 #===============================================================================
329 # PERMS
334 # PERMS
330 #===============================================================================
335 #===============================================================================
331 from pylons_app.lib.auth import HasPermissionAny, HasPermissionAll, \
336 from pylons_app.lib.auth import HasPermissionAny, HasPermissionAll, \
332 HasRepoPermissionAny, HasRepoPermissionAll
337 HasRepoPermissionAny, HasRepoPermissionAll
333
338
334 #===============================================================================
339 #===============================================================================
335 # GRAVATAR URL
340 # GRAVATAR URL
336 #===============================================================================
341 #===============================================================================
337 import hashlib
342 import hashlib
338 import urllib
343 import urllib
339 from pylons import request
344 from pylons import request
340
345
341 def gravatar_url(email_address, size=30):
346 def gravatar_url(email_address, size=30):
342 ssl_enabled = 'https' == request.environ.get('HTTP_X_URL_SCHEME')
347 ssl_enabled = 'https' == request.environ.get('HTTP_X_URL_SCHEME')
343 default = 'identicon'
348 default = 'identicon'
344 baseurl_nossl = "http://www.gravatar.com/avatar/"
349 baseurl_nossl = "http://www.gravatar.com/avatar/"
345 baseurl_ssl = "https://secure.gravatar.com/avatar/"
350 baseurl_ssl = "https://secure.gravatar.com/avatar/"
346 baseurl = baseurl_ssl if ssl_enabled else baseurl_nossl
351 baseurl = baseurl_ssl if ssl_enabled else baseurl_nossl
347
352
348
353
349 # construct the url
354 # construct the url
350 gravatar_url = baseurl + hashlib.md5(email_address.lower()).hexdigest() + "?"
355 gravatar_url = baseurl + hashlib.md5(email_address.lower()).hexdigest() + "?"
351 gravatar_url += urllib.urlencode({'d':default, 's':str(size)})
356 gravatar_url += urllib.urlencode({'d':default, 's':str(size)})
352
357
353 return gravatar_url
358 return gravatar_url
354
359
355 def safe_unicode(str):
360 def safe_unicode(str):
356 """safe unicode function. In case of UnicodeDecode error we try to return
361 """safe unicode function. In case of UnicodeDecode error we try to return
357 unicode with errors replace, if this failes we return unicode with
362 unicode with errors replace, if this failes we return unicode with
358 string_escape decoding """
363 string_escape decoding """
359
364
360 try:
365 try:
361 u_str = unicode(str)
366 u_str = unicode(str)
362 except UnicodeDecodeError:
367 except UnicodeDecodeError:
363 try:
368 try:
364 u_str = unicode(str, 'utf-8', 'replace')
369 u_str = unicode(str, 'utf-8', 'replace')
365 except UnicodeDecodeError:
370 except UnicodeDecodeError:
366 #incase we have a decode error just represent as byte string
371 #incase we have a decode error just represent as byte string
367 u_str = unicode(str(str).encode('string_escape'))
372 u_str = unicode(str(str).encode('string_escape'))
368
373
369 return u_str
374 return u_str
@@ -1,41 +1,139 b''
1 import sys
1 from os.path import dirname as dn, join as jn
2 from pylons_app.config.environment import load_environment
3 from pylons_app.model.hg_model import HgModel
4 from shutil import rmtree
5 from webhelpers.html.builder import escape
6 from vcs.utils.lazy import LazyProperty
7
8 from whoosh.analysis import RegexTokenizer, LowercaseFilter, StopFilter
9 from whoosh.fields import TEXT, ID, STORED, Schema, FieldType
10 from whoosh.index import create_in, open_dir
11 from whoosh.formats import Characters
12 from whoosh.highlight import highlight, SimpleFragmenter, HtmlFormatter
13
2 import os
14 import os
3 from pidlock import LockHeld, DaemonLock
15 import sys
4 import traceback
16 import traceback
5
17
6 from os.path import dirname as dn
7 from os.path import join as jn
8
9 #to get the pylons_app import
18 #to get the pylons_app import
10 sys.path.append(dn(dn(dn(os.path.realpath(__file__)))))
19 sys.path.append(dn(dn(dn(os.path.realpath(__file__)))))
11
20
12 from pylons_app.config.environment import load_environment
13 from pylons_app.model.hg_model import HgModel
14 from whoosh.analysis import RegexTokenizer, LowercaseFilter, StopFilter
15 from whoosh.fields import TEXT, ID, STORED, Schema
16 from whoosh.index import create_in, open_dir
17 from shutil import rmtree
18
21
19 #LOCATION WE KEEP THE INDEX
22 #LOCATION WE KEEP THE INDEX
20 IDX_LOCATION = jn(dn(dn(dn(dn(os.path.abspath(__file__))))), 'data', 'index')
23 IDX_LOCATION = jn(dn(dn(dn(dn(os.path.abspath(__file__))))), 'data', 'index')
21
24
22 #EXTENSIONS WE WANT TO INDEX CONTENT OFF
25 #EXTENSIONS WE WANT TO INDEX CONTENT OFF
23 INDEX_EXTENSIONS = ['action', 'adp', 'ashx', 'asmx', 'aspx', 'asx', 'axd', 'c',
26 INDEX_EXTENSIONS = ['action', 'adp', 'ashx', 'asmx', 'aspx', 'asx', 'axd', 'c',
24 'cfm', 'cpp', 'cs', 'css', 'diff', 'do', 'el', 'erl', 'h',
27 'cfg', 'cfm', 'cpp', 'cs', 'css', 'diff', 'do', 'el', 'erl',
25 'htm', 'html', 'ini', 'java', 'js', 'jsp', 'jspx', 'lisp',
28 'h', 'htm', 'html', 'ini', 'java', 'js', 'jsp', 'jspx', 'lisp',
26 'lua', 'm', 'mako', 'ml', 'pas', 'patch', 'php', 'php3',
29 'lua', 'm', 'mako', 'ml', 'pas', 'patch', 'php', 'php3',
27 'php4', 'phtml', 'pm', 'py', 'rb', 'rst', 's', 'sh', 'sql',
30 'php4', 'phtml', 'pm', 'py', 'rb', 'rst', 's', 'sh', 'sql',
28 'tpl', 'txt', 'vim', 'wss', 'xhtml', 'xml','xsl','xslt',
31 'tpl', 'txt', 'vim', 'wss', 'xhtml', 'xml', 'xsl', 'xslt',
29 'yaws']
32 'yaws']
30
33
31 #CUSTOM ANALYZER wordsplit + lowercase filter
34 #CUSTOM ANALYZER wordsplit + lowercase filter
32 ANALYZER = RegexTokenizer(expression=r"\w+") | LowercaseFilter()
35 ANALYZER = RegexTokenizer(expression=r"\w+") | LowercaseFilter()
33
36
37
34 #INDEX SCHEMA DEFINITION
38 #INDEX SCHEMA DEFINITION
35 SCHEMA = Schema(owner=TEXT(),
39 SCHEMA = Schema(owner=TEXT(),
36 repository=TEXT(stored=True),
40 repository=TEXT(stored=True),
37 path=ID(stored=True, unique=True),
41 path=ID(stored=True, unique=True),
38 content=TEXT(stored=True, analyzer=ANALYZER),
42 content=FieldType(format=Characters(ANALYZER),
39 modtime=STORED(),extension=TEXT(stored=True))
43 scorable=True, stored=True),
44 modtime=STORED(), extension=TEXT(stored=True))
45
46
47 IDX_NAME = 'HG_INDEX'
48 FORMATTER = HtmlFormatter('span', between='\n<span class="break">...</span>\n')
49 FRAGMENTER = SimpleFragmenter(200)
50
51 class ResultWrapper(object):
52 def __init__(self, searcher, matcher, highlight_items):
53 self.searcher = searcher
54 self.matcher = matcher
55 self.highlight_items = highlight_items
56 self.fragment_size = 200 / 2
57
58 @LazyProperty
59 def doc_ids(self):
60 docs_id = []
61 while self.matcher.is_active():
62 docnum = self.matcher.id()
63 chunks = [offsets for offsets in self.get_chunks()]
64 docs_id.append([docnum, chunks])
65 self.matcher.next()
66 return docs_id
67
68 def __str__(self):
69 return '<%s at %s>' % (self.__class__.__name__, len(self.doc_ids))
70
71 def __repr__(self):
72 return self.__str__()
73
74 def __len__(self):
75 return len(self.doc_ids)
76
77 def __iter__(self):
78 """
79 Allows Iteration over results,and lazy generate content
80
81 *Requires* implementation of ``__getitem__`` method.
82 """
83 for docid in self.doc_ids:
84 yield self.get_full_content(docid)
40
85
41 IDX_NAME = 'HG_INDEX' No newline at end of file
86 def __getslice__(self, i, j):
87 """
88 Slicing of resultWrapper
89 """
90 slice = []
91 for docid in self.doc_ids[i:j]:
92 slice.append(self.get_full_content(docid))
93 return slice
94
95
96 def get_full_content(self, docid):
97 res = self.searcher.stored_fields(docid[0])
98 f_path = res['path'][res['path'].find(res['repository']) \
99 + len(res['repository']):].lstrip('/')
100
101 content_short = self.get_short_content(res, docid[1])
102 res.update({'content_short':content_short,
103 'content_short_hl':self.highlight(content_short),
104 'f_path':f_path})
105
106 return res
107
108 def get_short_content(self, res, chunks):
109
110 return ''.join([res['content'][chunk[0]:chunk[1]] for chunk in chunks])
111
112 def get_chunks(self):
113 """
114 Smart function that implements chunking the content
115 but not overlap chunks so it doesn't highlight the same
116 close occurences twice.
117 @param matcher:
118 @param size:
119 """
120 memory = [(0, 0)]
121 for span in self.matcher.spans():
122 start = span.startchar or 0
123 end = span.endchar or 0
124 start_offseted = max(0, start - self.fragment_size)
125 end_offseted = end + self.fragment_size
126
127 if start_offseted < memory[-1][1]:
128 start_offseted = memory[-1][1]
129 memory.append((start_offseted, end_offseted,))
130 yield (start_offseted, end_offseted,)
131
132 def highlight(self, content, top=5):
133 hl = highlight(escape(content),
134 self.highlight_items,
135 analyzer=ANALYZER,
136 fragmenter=FRAGMENTER,
137 formatter=FORMATTER,
138 top=top)
139 return hl
@@ -1,226 +1,238 b''
1 #!/usr/bin/env python
1 #!/usr/bin/env python
2 # encoding: utf-8
2 # encoding: utf-8
3 # whoosh indexer daemon for hg-app
3 # whoosh indexer daemon for hg-app
4 # Copyright (C) 2009-2010 Marcin Kuzminski <marcin@python-works.com>
4 # Copyright (C) 2009-2010 Marcin Kuzminski <marcin@python-works.com>
5 #
5 #
6 # This program is free software; you can redistribute it and/or
6 # This program is free software; you can redistribute it and/or
7 # modify it under the terms of the GNU General Public License
7 # modify it under the terms of the GNU General Public License
8 # as published by the Free Software Foundation; version 2
8 # as published by the Free Software Foundation; version 2
9 # of the License or (at your opinion) any later version of the license.
9 # of the License or (at your opinion) any later version of the license.
10 #
10 #
11 # This program is distributed in the hope that it will be useful,
11 # This program is distributed in the hope that it will be useful,
12 # but WITHOUT ANY WARRANTY; without even the implied warranty of
12 # but WITHOUT ANY WARRANTY; without even the implied warranty of
13 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 # GNU General Public License for more details.
14 # GNU General Public License for more details.
15 #
15 #
16 # You should have received a copy of the GNU General Public License
16 # You should have received a copy of the GNU General Public License
17 # along with this program; if not, write to the Free Software
17 # along with this program; if not, write to the Free Software
18 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
18 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
19 # MA 02110-1301, USA.
19 # MA 02110-1301, USA.
20 """
20 """
21 Created on Jan 26, 2010
21 Created on Jan 26, 2010
22
22
23 @author: marcink
23 @author: marcink
24 A deamon will read from task table and run tasks
24 A deamon will read from task table and run tasks
25 """
25 """
26 import sys
26 import sys
27 import os
27 import os
28 from os.path import dirname as dn
28 from os.path import dirname as dn
29 from os.path import join as jn
29 from os.path import join as jn
30
30
31 #to get the pylons_app import
31 #to get the pylons_app import
32 project_path = dn(dn(dn(dn(os.path.realpath(__file__)))))
32 project_path = dn(dn(dn(dn(os.path.realpath(__file__)))))
33 sys.path.append(project_path)
33 sys.path.append(project_path)
34
34
35 from pidlock import LockHeld, DaemonLock
35 from pylons_app.lib.pidlock import LockHeld, DaemonLock
36 import traceback
37 from pylons_app.config.environment import load_environment
38 from pylons_app.model.hg_model import HgModel
36 from pylons_app.model.hg_model import HgModel
39 from pylons_app.lib.helpers import safe_unicode
37 from pylons_app.lib.helpers import safe_unicode
40 from whoosh.index import create_in, open_dir
38 from whoosh.index import create_in, open_dir
41 from shutil import rmtree
39 from shutil import rmtree
42 from pylons_app.lib.indexers import ANALYZER, INDEX_EXTENSIONS, IDX_LOCATION, \
40 from pylons_app.lib.indexers import INDEX_EXTENSIONS, IDX_LOCATION, SCHEMA, IDX_NAME
43 SCHEMA, IDX_NAME
44
41
45 import logging
42 import logging
46 import logging.config
43
47 logging.config.fileConfig(jn(project_path, 'development.ini'))
48 log = logging.getLogger('whooshIndexer')
44 log = logging.getLogger('whooshIndexer')
45 # create logger
46 log.setLevel(logging.DEBUG)
47 log.propagate = False
48 # create console handler and set level to debug
49 ch = logging.StreamHandler()
50 ch.setLevel(logging.DEBUG)
51
52 # create formatter
53 formatter = logging.Formatter("%(asctime)s - %(name)s - %(levelname)s - %(message)s")
54
55 # add formatter to ch
56 ch.setFormatter(formatter)
57
58 # add ch to logger
59 log.addHandler(ch)
49
60
50 def scan_paths(root_location):
61 def scan_paths(root_location):
51 return HgModel.repo_scan('/', root_location, None, True)
62 return HgModel.repo_scan('/', root_location, None, True)
52
63
53 class WhooshIndexingDaemon(object):
64 class WhooshIndexingDaemon(object):
54 """Deamon for atomic jobs"""
65 """Deamon for atomic jobs"""
55
66
56 def __init__(self, indexname='HG_INDEX', repo_location=None):
67 def __init__(self, indexname='HG_INDEX', repo_location=None):
57 self.indexname = indexname
68 self.indexname = indexname
58 self.repo_location = repo_location
69 self.repo_location = repo_location
59 self.initial = False
70 self.initial = False
60 if not os.path.isdir(IDX_LOCATION):
71 if not os.path.isdir(IDX_LOCATION):
61 os.mkdir(IDX_LOCATION)
72 os.mkdir(IDX_LOCATION)
62 log.info('Cannot run incremental index since it does not'
73 log.info('Cannot run incremental index since it does not'
63 ' yet exist running full build')
74 ' yet exist running full build')
64 self.initial = True
75 self.initial = True
65
76
66 def get_paths(self, root_dir):
77 def get_paths(self, root_dir):
67 """recursive walk in root dir and return a set of all path in that dir
78 """recursive walk in root dir and return a set of all path in that dir
68 excluding files in .hg dir"""
79 excluding files in .hg dir"""
69 index_paths_ = set()
80 index_paths_ = set()
70 for path, dirs, files in os.walk(root_dir):
81 for path, dirs, files in os.walk(root_dir):
71 if path.find('.hg') == -1:
82 if path.find('.hg') == -1:
72 for f in files:
83 for f in files:
73 index_paths_.add(jn(path, f))
84 index_paths_.add(jn(path, f))
74
85
75 return index_paths_
86 return index_paths_
76
87
77 def add_doc(self, writer, path, repo):
88 def add_doc(self, writer, path, repo):
78 """Adding doc to writer"""
89 """Adding doc to writer"""
79
90
80 ext = unicode(path.split('/')[-1].split('.')[-1].lower())
91 ext = unicode(path.split('/')[-1].split('.')[-1].lower())
81 #we just index the content of choosen files
92 #we just index the content of choosen files
82 if ext in INDEX_EXTENSIONS:
93 if ext in INDEX_EXTENSIONS:
83 log.debug(' >> %s [WITH CONTENT]' % path)
94 log.debug(' >> %s [WITH CONTENT]' % path)
84 fobj = open(path, 'rb')
95 fobj = open(path, 'rb')
85 content = fobj.read()
96 content = fobj.read()
86 fobj.close()
97 fobj.close()
87 u_content = safe_unicode(content)
98 u_content = safe_unicode(content)
88 else:
99 else:
89 log.debug(' >> %s' % path)
100 log.debug(' >> %s' % path)
90 #just index file name without it's content
101 #just index file name without it's content
91 u_content = u''
102 u_content = u''
92
103
93
104
94
105
95 try:
106 try:
96 os.stat(path)
107 os.stat(path)
97 writer.add_document(owner=unicode(repo.contact),
108 writer.add_document(owner=unicode(repo.contact),
98 repository=u"%s" % repo.name,
109 repository=u"%s" % repo.name,
99 path=u"%s" % path,
110 path=u"%s" % path,
100 content=u_content,
111 content=u_content,
101 modtime=os.path.getmtime(path),
112 modtime=os.path.getmtime(path),
102 extension=ext)
113 extension=ext)
103 except OSError, e:
114 except OSError, e:
104 import errno
115 import errno
105 if e.errno == errno.ENOENT:
116 if e.errno == errno.ENOENT:
106 log.debug('path %s does not exist or is a broken symlink' % path)
117 log.debug('path %s does not exist or is a broken symlink' % path)
107 else:
118 else:
108 raise e
119 raise e
109
120
110
121
111 def build_index(self):
122 def build_index(self):
112 if os.path.exists(IDX_LOCATION):
123 if os.path.exists(IDX_LOCATION):
113 log.debug('removing previos index')
124 log.debug('removing previos index')
114 rmtree(IDX_LOCATION)
125 rmtree(IDX_LOCATION)
115
126
116 if not os.path.exists(IDX_LOCATION):
127 if not os.path.exists(IDX_LOCATION):
117 os.mkdir(IDX_LOCATION)
128 os.mkdir(IDX_LOCATION)
118
129
119 idx = create_in(IDX_LOCATION, SCHEMA, indexname=IDX_NAME)
130 idx = create_in(IDX_LOCATION, SCHEMA, indexname=IDX_NAME)
120 writer = idx.writer()
131 writer = idx.writer()
121
132
122 for cnt, repo in enumerate(scan_paths(self.repo_location).values()):
133 for cnt, repo in enumerate(scan_paths(self.repo_location).values()):
123 log.debug('building index @ %s' % repo.path)
134 log.debug('building index @ %s' % repo.path)
124
135
125 for idx_path in self.get_paths(repo.path):
136 for idx_path in self.get_paths(repo.path):
126 self.add_doc(writer, idx_path, repo)
137 self.add_doc(writer, idx_path, repo)
127 writer.commit(merge=True)
138 writer.commit(merge=True)
128
139
129 log.debug('>>> FINISHED BUILDING INDEX <<<')
140 log.debug('>>> FINISHED BUILDING INDEX <<<')
130
141
131
142
132 def update_index(self):
143 def update_index(self):
133 log.debug('STARTING INCREMENTAL INDEXING UPDATE')
144 log.debug('STARTING INCREMENTAL INDEXING UPDATE')
134
145
135 idx = open_dir(IDX_LOCATION, indexname=self.indexname)
146 idx = open_dir(IDX_LOCATION, indexname=self.indexname)
136 # The set of all paths in the index
147 # The set of all paths in the index
137 indexed_paths = set()
148 indexed_paths = set()
138 # The set of all paths we need to re-index
149 # The set of all paths we need to re-index
139 to_index = set()
150 to_index = set()
140
151
141 reader = idx.reader()
152 reader = idx.reader()
142 writer = idx.writer()
153 writer = idx.writer()
143
154
144 # Loop over the stored fields in the index
155 # Loop over the stored fields in the index
145 for fields in reader.all_stored_fields():
156 for fields in reader.all_stored_fields():
146 indexed_path = fields['path']
157 indexed_path = fields['path']
147 indexed_paths.add(indexed_path)
158 indexed_paths.add(indexed_path)
148
159
149 if not os.path.exists(indexed_path):
160 if not os.path.exists(indexed_path):
150 # This file was deleted since it was indexed
161 # This file was deleted since it was indexed
151 log.debug('removing from index %s' % indexed_path)
162 log.debug('removing from index %s' % indexed_path)
152 writer.delete_by_term('path', indexed_path)
163 writer.delete_by_term('path', indexed_path)
153
164
154 else:
165 else:
155 # Check if this file was changed since it
166 # Check if this file was changed since it
156 # was indexed
167 # was indexed
157 indexed_time = fields['modtime']
168 indexed_time = fields['modtime']
158
169
159 mtime = os.path.getmtime(indexed_path)
170 mtime = os.path.getmtime(indexed_path)
160
171
161 if mtime > indexed_time:
172 if mtime > indexed_time:
162
173
163 # The file has changed, delete it and add it to the list of
174 # The file has changed, delete it and add it to the list of
164 # files to reindex
175 # files to reindex
165 log.debug('adding to reindex list %s' % indexed_path)
176 log.debug('adding to reindex list %s' % indexed_path)
166 writer.delete_by_term('path', indexed_path)
177 writer.delete_by_term('path', indexed_path)
167 to_index.add(indexed_path)
178 to_index.add(indexed_path)
168 #writer.commit()
179 #writer.commit()
169
180
170 # Loop over the files in the filesystem
181 # Loop over the files in the filesystem
171 # Assume we have a function that gathers the filenames of the
182 # Assume we have a function that gathers the filenames of the
172 # documents to be indexed
183 # documents to be indexed
173 for repo in scan_paths(self.repo_location).values():
184 for repo in scan_paths(self.repo_location).values():
174 for path in self.get_paths(repo.path):
185 for path in self.get_paths(repo.path):
175 if path in to_index or path not in indexed_paths:
186 if path in to_index or path not in indexed_paths:
176 # This is either a file that's changed, or a new file
187 # This is either a file that's changed, or a new file
177 # that wasn't indexed before. So index it!
188 # that wasn't indexed before. So index it!
178 self.add_doc(writer, path, repo)
189 self.add_doc(writer, path, repo)
179 log.debug('reindexing %s' % path)
190 log.debug('reindexing %s' % path)
180
191
181 writer.commit(merge=True)
192 writer.commit(merge=True)
182 #idx.optimize()
193 #idx.optimize()
183 log.debug('>>> FINISHED <<<')
194 log.debug('>>> FINISHED <<<')
184
195
185 def run(self, full_index=False):
196 def run(self, full_index=False):
186 """Run daemon"""
197 """Run daemon"""
187 if full_index or self.initial:
198 if full_index or self.initial:
188 self.build_index()
199 self.build_index()
189 else:
200 else:
190 self.update_index()
201 self.update_index()
191
202
192 if __name__ == "__main__":
203 if __name__ == "__main__":
193 arg = sys.argv[1:]
204 arg = sys.argv[1:]
194 if len(arg) != 2:
205 if len(arg) != 2:
195 sys.stderr.write('Please specify indexing type [full|incremental]'
206 sys.stderr.write('Please specify indexing type [full|incremental]'
196 'and path to repositories as script args \n')
207 'and path to repositories as script args \n')
197 sys.exit()
208 sys.exit()
198
209
199
210
200 if arg[0] == 'full':
211 if arg[0] == 'full':
201 full_index = True
212 full_index = True
202 elif arg[0] == 'incremental':
213 elif arg[0] == 'incremental':
203 # False means looking just for changes
214 # False means looking just for changes
204 full_index = False
215 full_index = False
205 else:
216 else:
206 sys.stdout.write('Please use [full|incremental]'
217 sys.stdout.write('Please use [full|incremental]'
207 ' as script first arg \n')
218 ' as script first arg \n')
208 sys.exit()
219 sys.exit()
209
220
210 if not os.path.isdir(arg[1]):
221 if not os.path.isdir(arg[1]):
211 sys.stderr.write('%s is not a valid path \n' % arg[1])
222 sys.stderr.write('%s is not a valid path \n' % arg[1])
212 sys.exit()
223 sys.exit()
213 else:
224 else:
214 if arg[1].endswith('/'):
225 if arg[1].endswith('/'):
215 repo_location = arg[1] + '*'
226 repo_location = arg[1] + '*'
216 else:
227 else:
217 repo_location = arg[1] + '/*'
228 repo_location = arg[1] + '/*'
218
229
219 try:
230 try:
220 l = DaemonLock()
231 l = DaemonLock()
221 WhooshIndexingDaemon(repo_location=repo_location)\
232 WhooshIndexingDaemon(repo_location=repo_location)\
222 .run(full_index=full_index)
233 .run(full_index=full_index)
223 l.release()
234 l.release()
235 reload(logging)
224 except LockHeld:
236 except LockHeld:
225 sys.exit(1)
237 sys.exit(1)
226
238
@@ -1,127 +1,127 b''
1 import os, time
1 import os, time
2 import sys
2 import sys
3 from warnings import warn
3 from warnings import warn
4
4
5 class LockHeld(Exception):pass
5 class LockHeld(Exception):pass
6
6
7
7
8 class DaemonLock(object):
8 class DaemonLock(object):
9 '''daemon locking
9 """daemon locking
10 USAGE:
10 USAGE:
11 try:
11 try:
12 l = lock()
12 l = lock()
13 main()
13 main()
14 l.release()
14 l.release()
15 except LockHeld:
15 except LockHeld:
16 sys.exit(1)
16 sys.exit(1)
17 '''
17 """
18
18
19 def __init__(self, file=None, callbackfn=None,
19 def __init__(self, file=None, callbackfn=None,
20 desc='daemon lock', debug=False):
20 desc='daemon lock', debug=False):
21
21
22 self.pidfile = file if file else os.path.join(os.path.dirname(__file__),
22 self.pidfile = file if file else os.path.join(os.path.dirname(__file__),
23 'running.lock')
23 'running.lock')
24 self.callbackfn = callbackfn
24 self.callbackfn = callbackfn
25 self.desc = desc
25 self.desc = desc
26 self.debug = debug
26 self.debug = debug
27 self.held = False
27 self.held = False
28 #run the lock automatically !
28 #run the lock automatically !
29 self.lock()
29 self.lock()
30
30
31 def __del__(self):
31 def __del__(self):
32 if self.held:
32 if self.held:
33
33
34 # warn("use lock.release instead of del lock",
34 # warn("use lock.release instead of del lock",
35 # category = DeprecationWarning,
35 # category = DeprecationWarning,
36 # stacklevel = 2)
36 # stacklevel = 2)
37
37
38 # ensure the lock will be removed
38 # ensure the lock will be removed
39 self.release()
39 self.release()
40
40
41
41
42 def lock(self):
42 def lock(self):
43 '''
43 """
44 locking function, if lock is present it will raise LockHeld exception
44 locking function, if lock is present it will raise LockHeld exception
45 '''
45 """
46 lockname = '%s' % (os.getpid())
46 lockname = '%s' % (os.getpid())
47
47
48 self.trylock()
48 self.trylock()
49 self.makelock(lockname, self.pidfile)
49 self.makelock(lockname, self.pidfile)
50 return True
50 return True
51
51
52 def trylock(self):
52 def trylock(self):
53 running_pid = False
53 running_pid = False
54 try:
54 try:
55 pidfile = open(self.pidfile, "r")
55 pidfile = open(self.pidfile, "r")
56 pidfile.seek(0)
56 pidfile.seek(0)
57 running_pid = pidfile.readline()
57 running_pid = pidfile.readline()
58 if self.debug:
58 if self.debug:
59 print 'lock file present running_pid: %s, checking for execution'\
59 print 'lock file present running_pid: %s, checking for execution'\
60 % running_pid
60 % running_pid
61 # Now we check the PID from lock file matches to the current
61 # Now we check the PID from lock file matches to the current
62 # process PID
62 # process PID
63 if running_pid:
63 if running_pid:
64 if os.path.exists("/proc/%s" % running_pid):
64 if os.path.exists("/proc/%s" % running_pid):
65 print "You already have an instance of the program running"
65 print "You already have an instance of the program running"
66 print "It is running as process %s" % running_pid
66 print "It is running as process %s" % running_pid
67 raise LockHeld
67 raise LockHeld
68 else:
68 else:
69 print "Lock File is there but the program is not running"
69 print "Lock File is there but the program is not running"
70 print "Removing lock file for the: %s" % running_pid
70 print "Removing lock file for the: %s" % running_pid
71 self.release()
71 self.release()
72 except IOError, e:
72 except IOError, e:
73 if e.errno != 2:
73 if e.errno != 2:
74 raise
74 raise
75
75
76
76
77 def release(self):
77 def release(self):
78 '''
78 """
79 releases the pid by removing the pidfile
79 releases the pid by removing the pidfile
80 '''
80 """
81 if self.callbackfn:
81 if self.callbackfn:
82 #execute callback function on release
82 #execute callback function on release
83 if self.debug:
83 if self.debug:
84 print 'executing callback function %s' % self.callbackfn
84 print 'executing callback function %s' % self.callbackfn
85 self.callbackfn()
85 self.callbackfn()
86 try:
86 try:
87 if self.debug:
87 if self.debug:
88 print 'removing pidfile %s' % self.pidfile
88 print 'removing pidfile %s' % self.pidfile
89 os.remove(self.pidfile)
89 os.remove(self.pidfile)
90 self.held = False
90 self.held = False
91 except OSError, e:
91 except OSError, e:
92 if self.debug:
92 if self.debug:
93 print 'removing pidfile failed %s' % e
93 print 'removing pidfile failed %s' % e
94 pass
94 pass
95
95
96 def makelock(self, lockname, pidfile):
96 def makelock(self, lockname, pidfile):
97 '''
97 """
98 this function will make an actual lock
98 this function will make an actual lock
99 @param lockname: acctual pid of file
99 @param lockname: acctual pid of file
100 @param pidfile: the file to write the pid in
100 @param pidfile: the file to write the pid in
101 '''
101 """
102 if self.debug:
102 if self.debug:
103 print 'creating a file %s and pid: %s' % (pidfile, lockname)
103 print 'creating a file %s and pid: %s' % (pidfile, lockname)
104 pidfile = open(self.pidfile, "wb")
104 pidfile = open(self.pidfile, "wb")
105 pidfile.write(lockname)
105 pidfile.write(lockname)
106 pidfile.close
106 pidfile.close
107 self.held = True
107 self.held = True
108
108
109
109
110 def main():
110 def main():
111 print 'func is running'
111 print 'func is running'
112 cnt = 20
112 cnt = 20
113 while 1:
113 while 1:
114 print cnt
114 print cnt
115 if cnt == 0:
115 if cnt == 0:
116 break
116 break
117 time.sleep(1)
117 time.sleep(1)
118 cnt -= 1
118 cnt -= 1
119
119
120
120
121 if __name__ == "__main__":
121 if __name__ == "__main__":
122 try:
122 try:
123 l = DaemonLock(desc='test lock')
123 l = DaemonLock(desc='test lock')
124 main()
124 main()
125 l.release()
125 l.release()
126 except LockHeld:
126 except LockHeld:
127 sys.exit(1)
127 sys.exit(1)
@@ -1,57 +1,59 b''
1 from sqlalchemy.interfaces import ConnectionProxy
1 from sqlalchemy.interfaces import ConnectionProxy
2 import time
2 import time
3 import logging
3 from sqlalchemy import log
4 log = logging.getLogger('timerproxy')
5 BLACK, RED, GREEN, YELLOW, BLUE, MAGENTA, CYAN, WHITE = xrange(30, 38)
4 BLACK, RED, GREEN, YELLOW, BLUE, MAGENTA, CYAN, WHITE = xrange(30, 38)
6
5
7 def color_sql(sql):
6 def color_sql(sql):
8 COLOR_SEQ = "\033[1;%dm"
7 COLOR_SEQ = "\033[1;%dm"
9 COLOR_SQL = YELLOW
8 COLOR_SQL = YELLOW
10 normal = '\x1b[0m'
9 normal = '\x1b[0m'
11 return COLOR_SEQ % COLOR_SQL + sql + normal
10 return COLOR_SEQ % COLOR_SQL + sql + normal
12
11
13 def one_space_trim(s):
12 def one_space_trim(s):
14 if s.find(" ") == -1:
13 if s.find(" ") == -1:
15 return s
14 return s
16 else:
15 else:
17 s = s.replace(' ', ' ')
16 s = s.replace(' ', ' ')
18 return one_space_trim(s)
17 return one_space_trim(s)
19
18
20 def format_sql(sql):
19 def format_sql(sql):
21 sql = color_sql(sql)
20 sql = color_sql(sql)
22 sql = sql.replace('\n', '')
21 sql = sql.replace('\n', '')
23 sql = one_space_trim(sql)
22 sql = one_space_trim(sql)
24 sql = sql\
23 sql = sql\
25 .replace(',',',\n\t')\
24 .replace(',', ',\n\t')\
26 .replace('SELECT', '\n\tSELECT \n\t')\
25 .replace('SELECT', '\n\tSELECT \n\t')\
27 .replace('UPDATE', '\n\tUPDATE \n\t')\
26 .replace('UPDATE', '\n\tUPDATE \n\t')\
28 .replace('DELETE', '\n\tDELETE \n\t')\
27 .replace('DELETE', '\n\tDELETE \n\t')\
29 .replace('FROM', '\n\tFROM')\
28 .replace('FROM', '\n\tFROM')\
30 .replace('ORDER BY', '\n\tORDER BY')\
29 .replace('ORDER BY', '\n\tORDER BY')\
31 .replace('LIMIT', '\n\tLIMIT')\
30 .replace('LIMIT', '\n\tLIMIT')\
32 .replace('WHERE', '\n\tWHERE')\
31 .replace('WHERE', '\n\tWHERE')\
33 .replace('AND', '\n\tAND')\
32 .replace('AND', '\n\tAND')\
34 .replace('LEFT', '\n\tLEFT')\
33 .replace('LEFT', '\n\tLEFT')\
35 .replace('INNER', '\n\tINNER')\
34 .replace('INNER', '\n\tINNER')\
36 .replace('INSERT', '\n\tINSERT')\
35 .replace('INSERT', '\n\tINSERT')\
37 .replace('DELETE', '\n\tDELETE')
36 .replace('DELETE', '\n\tDELETE')
38 return sql
37 return sql
39
38
40
39
41 class TimerProxy(ConnectionProxy):
40 class TimerProxy(ConnectionProxy):
41
42 def __init__(self):
43 super(TimerProxy, self).__init__()
44 self.logging_name = 'timerProxy'
45 self.log = log.instance_logger(self, True)
46
42 def cursor_execute(self, execute, cursor, statement, parameters, context, executemany):
47 def cursor_execute(self, execute, cursor, statement, parameters, context, executemany):
48
43 now = time.time()
49 now = time.time()
44 try:
50 try:
45 log.info(">>>>> STARTING QUERY >>>>>")
51 self.log.info(">>>>> STARTING QUERY >>>>>")
46 return execute(cursor, statement, parameters, context)
52 return execute(cursor, statement, parameters, context)
47 finally:
53 finally:
48 total = time.time() - now
54 total = time.time() - now
49 try:
55 try:
50 log.info(format_sql("Query: %s" % statement % parameters))
56 self.log.info(format_sql("Query: %s" % statement % parameters))
51 except TypeError:
57 except TypeError:
52 log.info(format_sql("Query: %s %s" % (statement, parameters)))
58 self.log.info(format_sql("Query: %s %s" % (statement, parameters)))
53 log.info("<<<<< TOTAL TIME: %f <<<<<" % total)
59 self.log.info("<<<<< TOTAL TIME: %f <<<<<" % total)
54
55
56
57
@@ -1,364 +1,438 b''
1 #!/usr/bin/env python
1 #!/usr/bin/env python
2 # encoding: utf-8
2 # encoding: utf-8
3 # Utilities for hg app
3 # Utilities for hg app
4 # Copyright (C) 2009-2010 Marcin Kuzminski <marcin@python-works.com>
4 # Copyright (C) 2009-2010 Marcin Kuzminski <marcin@python-works.com>
5 # This program is free software; you can redistribute it and/or
5 # This program is free software; you can redistribute it and/or
6 # modify it under the terms of the GNU General Public License
6 # modify it under the terms of the GNU General Public License
7 # as published by the Free Software Foundation; version 2
7 # as published by the Free Software Foundation; version 2
8 # of the License or (at your opinion) any later version of the license.
8 # of the License or (at your opinion) any later version of the license.
9 #
9 #
10 # This program is distributed in the hope that it will be useful,
10 # This program is distributed in the hope that it will be useful,
11 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # but WITHOUT ANY WARRANTY; without even the implied warranty of
12 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 # GNU General Public License for more details.
13 # GNU General Public License for more details.
14 #
14 #
15 # You should have received a copy of the GNU General Public License
15 # You should have received a copy of the GNU General Public License
16 # along with this program; if not, write to the Free Software
16 # along with this program; if not, write to the Free Software
17 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
17 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
18 # MA 02110-1301, USA.
18 # MA 02110-1301, USA.
19
19
20 """
20 """
21 Created on April 18, 2010
21 Created on April 18, 2010
22 Utilities for hg app
22 Utilities for hg app
23 @author: marcink
23 @author: marcink
24 """
24 """
25 from beaker.cache import cache_region
25 from beaker.cache import cache_region
26 from mercurial import ui, config, hg
26 from mercurial import ui, config, hg
27 from mercurial.error import RepoError
27 from mercurial.error import RepoError
28 from pylons_app.model import meta
28 from pylons_app.model import meta
29 from pylons_app.model.db import Repository, User, HgAppUi, HgAppSettings
29 from pylons_app.model.db import Repository, User, HgAppUi, HgAppSettings
30 from vcs.backends.base import BaseChangeset
30 from vcs.backends.base import BaseChangeset
31 from vcs.utils.lazy import LazyProperty
31 from vcs.utils.lazy import LazyProperty
32 import logging
32 import logging
33 import os
33 import os
34
34 log = logging.getLogger(__name__)
35 log = logging.getLogger(__name__)
35
36
36
37
37 def get_repo_slug(request):
38 def get_repo_slug(request):
38 return request.environ['pylons.routes_dict'].get('repo_name')
39 return request.environ['pylons.routes_dict'].get('repo_name')
39
40
40 def is_mercurial(environ):
41 def is_mercurial(environ):
41 """
42 """
42 Returns True if request's target is mercurial server - header
43 Returns True if request's target is mercurial server - header
43 ``HTTP_ACCEPT`` of such request would start with ``application/mercurial``.
44 ``HTTP_ACCEPT`` of such request would start with ``application/mercurial``.
44 """
45 """
45 http_accept = environ.get('HTTP_ACCEPT')
46 http_accept = environ.get('HTTP_ACCEPT')
46 if http_accept and http_accept.startswith('application/mercurial'):
47 if http_accept and http_accept.startswith('application/mercurial'):
47 return True
48 return True
48 return False
49 return False
49
50
50 def check_repo_dir(paths):
51 def check_repo_dir(paths):
51 repos_path = paths[0][1].split('/')
52 repos_path = paths[0][1].split('/')
52 if repos_path[-1] in ['*', '**']:
53 if repos_path[-1] in ['*', '**']:
53 repos_path = repos_path[:-1]
54 repos_path = repos_path[:-1]
54 if repos_path[0] != '/':
55 if repos_path[0] != '/':
55 repos_path[0] = '/'
56 repos_path[0] = '/'
56 if not os.path.isdir(os.path.join(*repos_path)):
57 if not os.path.isdir(os.path.join(*repos_path)):
57 raise Exception('Not a valid repository in %s' % paths[0][1])
58 raise Exception('Not a valid repository in %s' % paths[0][1])
58
59
59 def check_repo_fast(repo_name, base_path):
60 def check_repo_fast(repo_name, base_path):
60 if os.path.isdir(os.path.join(base_path, repo_name)):return False
61 if os.path.isdir(os.path.join(base_path, repo_name)):return False
61 return True
62 return True
62
63
63 def check_repo(repo_name, base_path, verify=True):
64 def check_repo(repo_name, base_path, verify=True):
64
65
65 repo_path = os.path.join(base_path, repo_name)
66 repo_path = os.path.join(base_path, repo_name)
66
67
67 try:
68 try:
68 if not check_repo_fast(repo_name, base_path):
69 if not check_repo_fast(repo_name, base_path):
69 return False
70 return False
70 r = hg.repository(ui.ui(), repo_path)
71 r = hg.repository(ui.ui(), repo_path)
71 if verify:
72 if verify:
72 hg.verify(r)
73 hg.verify(r)
73 #here we hnow that repo exists it was verified
74 #here we hnow that repo exists it was verified
74 log.info('%s repo is already created', repo_name)
75 log.info('%s repo is already created', repo_name)
75 return False
76 return False
76 except RepoError:
77 except RepoError:
77 #it means that there is no valid repo there...
78 #it means that there is no valid repo there...
78 log.info('%s repo is free for creation', repo_name)
79 log.info('%s repo is free for creation', repo_name)
79 return True
80 return True
80
81
81 def ask_ok(prompt, retries=4, complaint='Yes or no, please!'):
82 def ask_ok(prompt, retries=4, complaint='Yes or no, please!'):
82 while True:
83 while True:
83 ok = raw_input(prompt)
84 ok = raw_input(prompt)
84 if ok in ('y', 'ye', 'yes'): return True
85 if ok in ('y', 'ye', 'yes'): return True
85 if ok in ('n', 'no', 'nop', 'nope'): return False
86 if ok in ('n', 'no', 'nop', 'nope'): return False
86 retries = retries - 1
87 retries = retries - 1
87 if retries < 0: raise IOError
88 if retries < 0: raise IOError
88 print complaint
89 print complaint
89
90
90 @cache_region('super_short_term', 'cached_hg_ui')
91 @cache_region('super_short_term', 'cached_hg_ui')
91 def get_hg_ui_cached():
92 def get_hg_ui_cached():
92 try:
93 try:
93 sa = meta.Session
94 sa = meta.Session
94 ret = sa.query(HgAppUi).all()
95 ret = sa.query(HgAppUi).all()
95 finally:
96 finally:
96 meta.Session.remove()
97 meta.Session.remove()
97 return ret
98 return ret
98
99
99
100
100 def get_hg_settings():
101 def get_hg_settings():
101 try:
102 try:
102 sa = meta.Session
103 sa = meta.Session
103 ret = sa.query(HgAppSettings).all()
104 ret = sa.query(HgAppSettings).all()
104 finally:
105 finally:
105 meta.Session.remove()
106 meta.Session.remove()
106
107
107 if not ret:
108 if not ret:
108 raise Exception('Could not get application settings !')
109 raise Exception('Could not get application settings !')
109 settings = {}
110 settings = {}
110 for each in ret:
111 for each in ret:
111 settings['hg_app_' + each.app_settings_name] = each.app_settings_value
112 settings['hg_app_' + each.app_settings_name] = each.app_settings_value
112
113
113 return settings
114 return settings
114
115
115 def get_hg_ui_settings():
116 def get_hg_ui_settings():
116 try:
117 try:
117 sa = meta.Session
118 sa = meta.Session
118 ret = sa.query(HgAppUi).all()
119 ret = sa.query(HgAppUi).all()
119 finally:
120 finally:
120 meta.Session.remove()
121 meta.Session.remove()
121
122
122 if not ret:
123 if not ret:
123 raise Exception('Could not get application ui settings !')
124 raise Exception('Could not get application ui settings !')
124 settings = {}
125 settings = {}
125 for each in ret:
126 for each in ret:
126 k = each.ui_key
127 k = each.ui_key
127 v = each.ui_value
128 v = each.ui_value
128 if k == '/':
129 if k == '/':
129 k = 'root_path'
130 k = 'root_path'
130
131
131 if k.find('.') != -1:
132 if k.find('.') != -1:
132 k = k.replace('.', '_')
133 k = k.replace('.', '_')
133
134
134 if each.ui_section == 'hooks':
135 if each.ui_section == 'hooks':
135 v = each.ui_active
136 v = each.ui_active
136
137
137 settings[each.ui_section + '_' + k] = v
138 settings[each.ui_section + '_' + k] = v
138
139
139 return settings
140 return settings
140
141
141 #propagated from mercurial documentation
142 #propagated from mercurial documentation
142 ui_sections = ['alias', 'auth',
143 ui_sections = ['alias', 'auth',
143 'decode/encode', 'defaults',
144 'decode/encode', 'defaults',
144 'diff', 'email',
145 'diff', 'email',
145 'extensions', 'format',
146 'extensions', 'format',
146 'merge-patterns', 'merge-tools',
147 'merge-patterns', 'merge-tools',
147 'hooks', 'http_proxy',
148 'hooks', 'http_proxy',
148 'smtp', 'patch',
149 'smtp', 'patch',
149 'paths', 'profiling',
150 'paths', 'profiling',
150 'server', 'trusted',
151 'server', 'trusted',
151 'ui', 'web', ]
152 'ui', 'web', ]
152
153
153 def make_ui(read_from='file', path=None, checkpaths=True):
154 def make_ui(read_from='file', path=None, checkpaths=True):
154 """
155 """
155 A function that will read python rc files or database
156 A function that will read python rc files or database
156 and make an mercurial ui object from read options
157 and make an mercurial ui object from read options
157
158
158 @param path: path to mercurial config file
159 @param path: path to mercurial config file
159 @param checkpaths: check the path
160 @param checkpaths: check the path
160 @param read_from: read from 'file' or 'db'
161 @param read_from: read from 'file' or 'db'
161 """
162 """
162
163
163 baseui = ui.ui()
164 baseui = ui.ui()
164
165
165 if read_from == 'file':
166 if read_from == 'file':
166 if not os.path.isfile(path):
167 if not os.path.isfile(path):
167 log.warning('Unable to read config file %s' % path)
168 log.warning('Unable to read config file %s' % path)
168 return False
169 return False
169 log.debug('reading hgrc from %s', path)
170 log.debug('reading hgrc from %s', path)
170 cfg = config.config()
171 cfg = config.config()
171 cfg.read(path)
172 cfg.read(path)
172 for section in ui_sections:
173 for section in ui_sections:
173 for k, v in cfg.items(section):
174 for k, v in cfg.items(section):
174 baseui.setconfig(section, k, v)
175 baseui.setconfig(section, k, v)
175 log.debug('settings ui from file[%s]%s:%s', section, k, v)
176 log.debug('settings ui from file[%s]%s:%s', section, k, v)
176 if checkpaths:check_repo_dir(cfg.items('paths'))
177 if checkpaths:check_repo_dir(cfg.items('paths'))
177
178
178
179
179 elif read_from == 'db':
180 elif read_from == 'db':
180 hg_ui = get_hg_ui_cached()
181 hg_ui = get_hg_ui_cached()
181 for ui_ in hg_ui:
182 for ui_ in hg_ui:
182 if ui_.ui_active:
183 if ui_.ui_active:
183 log.debug('settings ui from db[%s]%s:%s', ui_.ui_section, ui_.ui_key, ui_.ui_value)
184 log.debug('settings ui from db[%s]%s:%s', ui_.ui_section, ui_.ui_key, ui_.ui_value)
184 baseui.setconfig(ui_.ui_section, ui_.ui_key, ui_.ui_value)
185 baseui.setconfig(ui_.ui_section, ui_.ui_key, ui_.ui_value)
185
186
186
187
187 return baseui
188 return baseui
188
189
189
190
190 def set_hg_app_config(config):
191 def set_hg_app_config(config):
191 hgsettings = get_hg_settings()
192 hgsettings = get_hg_settings()
192
193
193 for k, v in hgsettings.items():
194 for k, v in hgsettings.items():
194 config[k] = v
195 config[k] = v
195
196
196 def invalidate_cache(name, *args):
197 def invalidate_cache(name, *args):
197 """Invalidates given name cache"""
198 """Invalidates given name cache"""
198
199
199 from beaker.cache import region_invalidate
200 from beaker.cache import region_invalidate
200 log.info('INVALIDATING CACHE FOR %s', name)
201 log.info('INVALIDATING CACHE FOR %s', name)
201
202
202 """propagate our arguments to make sure invalidation works. First
203 """propagate our arguments to make sure invalidation works. First
203 argument has to be the name of cached func name give to cache decorator
204 argument has to be the name of cached func name give to cache decorator
204 without that the invalidation would not work"""
205 without that the invalidation would not work"""
205 tmp = [name]
206 tmp = [name]
206 tmp.extend(args)
207 tmp.extend(args)
207 args = tuple(tmp)
208 args = tuple(tmp)
208
209
209 if name == 'cached_repo_list':
210 if name == 'cached_repo_list':
210 from pylons_app.model.hg_model import _get_repos_cached
211 from pylons_app.model.hg_model import _get_repos_cached
211 region_invalidate(_get_repos_cached, None, *args)
212 region_invalidate(_get_repos_cached, None, *args)
212
213
213 if name == 'full_changelog':
214 if name == 'full_changelog':
214 from pylons_app.model.hg_model import _full_changelog_cached
215 from pylons_app.model.hg_model import _full_changelog_cached
215 region_invalidate(_full_changelog_cached, None, *args)
216 region_invalidate(_full_changelog_cached, None, *args)
216
217
217 class EmptyChangeset(BaseChangeset):
218 class EmptyChangeset(BaseChangeset):
218
219
219 revision = -1
220 revision = -1
220 message = ''
221 message = ''
222 author = ''
221
223
222 @LazyProperty
224 @LazyProperty
223 def raw_id(self):
225 def raw_id(self):
224 """
226 """
225 Returns raw string identifing this changeset, useful for web
227 Returns raw string identifing this changeset, useful for web
226 representation.
228 representation.
227 """
229 """
228 return '0' * 12
230 return '0' * 12
229
231
230
232
231 def repo2db_mapper(initial_repo_list, remove_obsolete=False):
233 def repo2db_mapper(initial_repo_list, remove_obsolete=False):
232 """
234 """
233 maps all found repositories into db
235 maps all found repositories into db
234 """
236 """
235 from pylons_app.model.repo_model import RepoModel
237 from pylons_app.model.repo_model import RepoModel
236
238
237 sa = meta.Session
239 sa = meta.Session
238 user = sa.query(User).filter(User.admin == True).first()
240 user = sa.query(User).filter(User.admin == True).first()
239
241
240 rm = RepoModel()
242 rm = RepoModel()
241
243
242 for name, repo in initial_repo_list.items():
244 for name, repo in initial_repo_list.items():
243 if not sa.query(Repository).filter(Repository.repo_name == name).scalar():
245 if not sa.query(Repository).filter(Repository.repo_name == name).scalar():
244 log.info('repository %s not found creating default', name)
246 log.info('repository %s not found creating default', name)
245
247
246 form_data = {
248 form_data = {
247 'repo_name':name,
249 'repo_name':name,
248 'description':repo.description if repo.description != 'unknown' else \
250 'description':repo.description if repo.description != 'unknown' else \
249 'auto description for %s' % name,
251 'auto description for %s' % name,
250 'private':False
252 'private':False
251 }
253 }
252 rm.create(form_data, user, just_db=True)
254 rm.create(form_data, user, just_db=True)
253
255
254
256
255 if remove_obsolete:
257 if remove_obsolete:
256 #remove from database those repositories that are not in the filesystem
258 #remove from database those repositories that are not in the filesystem
257 for repo in sa.query(Repository).all():
259 for repo in sa.query(Repository).all():
258 if repo.repo_name not in initial_repo_list.keys():
260 if repo.repo_name not in initial_repo_list.keys():
259 sa.delete(repo)
261 sa.delete(repo)
260 sa.commit()
262 sa.commit()
261
263
262
264
263 meta.Session.remove()
265 meta.Session.remove()
264
266
265 from UserDict import DictMixin
267 from UserDict import DictMixin
266
268
267 class OrderedDict(dict, DictMixin):
269 class OrderedDict(dict, DictMixin):
268
270
269 def __init__(self, *args, **kwds):
271 def __init__(self, *args, **kwds):
270 if len(args) > 1:
272 if len(args) > 1:
271 raise TypeError('expected at most 1 arguments, got %d' % len(args))
273 raise TypeError('expected at most 1 arguments, got %d' % len(args))
272 try:
274 try:
273 self.__end
275 self.__end
274 except AttributeError:
276 except AttributeError:
275 self.clear()
277 self.clear()
276 self.update(*args, **kwds)
278 self.update(*args, **kwds)
277
279
278 def clear(self):
280 def clear(self):
279 self.__end = end = []
281 self.__end = end = []
280 end += [None, end, end] # sentinel node for doubly linked list
282 end += [None, end, end] # sentinel node for doubly linked list
281 self.__map = {} # key --> [key, prev, next]
283 self.__map = {} # key --> [key, prev, next]
282 dict.clear(self)
284 dict.clear(self)
283
285
284 def __setitem__(self, key, value):
286 def __setitem__(self, key, value):
285 if key not in self:
287 if key not in self:
286 end = self.__end
288 end = self.__end
287 curr = end[1]
289 curr = end[1]
288 curr[2] = end[1] = self.__map[key] = [key, curr, end]
290 curr[2] = end[1] = self.__map[key] = [key, curr, end]
289 dict.__setitem__(self, key, value)
291 dict.__setitem__(self, key, value)
290
292
291 def __delitem__(self, key):
293 def __delitem__(self, key):
292 dict.__delitem__(self, key)
294 dict.__delitem__(self, key)
293 key, prev, next = self.__map.pop(key)
295 key, prev, next = self.__map.pop(key)
294 prev[2] = next
296 prev[2] = next
295 next[1] = prev
297 next[1] = prev
296
298
297 def __iter__(self):
299 def __iter__(self):
298 end = self.__end
300 end = self.__end
299 curr = end[2]
301 curr = end[2]
300 while curr is not end:
302 while curr is not end:
301 yield curr[0]
303 yield curr[0]
302 curr = curr[2]
304 curr = curr[2]
303
305
304 def __reversed__(self):
306 def __reversed__(self):
305 end = self.__end
307 end = self.__end
306 curr = end[1]
308 curr = end[1]
307 while curr is not end:
309 while curr is not end:
308 yield curr[0]
310 yield curr[0]
309 curr = curr[1]
311 curr = curr[1]
310
312
311 def popitem(self, last=True):
313 def popitem(self, last=True):
312 if not self:
314 if not self:
313 raise KeyError('dictionary is empty')
315 raise KeyError('dictionary is empty')
314 if last:
316 if last:
315 key = reversed(self).next()
317 key = reversed(self).next()
316 else:
318 else:
317 key = iter(self).next()
319 key = iter(self).next()
318 value = self.pop(key)
320 value = self.pop(key)
319 return key, value
321 return key, value
320
322
321 def __reduce__(self):
323 def __reduce__(self):
322 items = [[k, self[k]] for k in self]
324 items = [[k, self[k]] for k in self]
323 tmp = self.__map, self.__end
325 tmp = self.__map, self.__end
324 del self.__map, self.__end
326 del self.__map, self.__end
325 inst_dict = vars(self).copy()
327 inst_dict = vars(self).copy()
326 self.__map, self.__end = tmp
328 self.__map, self.__end = tmp
327 if inst_dict:
329 if inst_dict:
328 return (self.__class__, (items,), inst_dict)
330 return (self.__class__, (items,), inst_dict)
329 return self.__class__, (items,)
331 return self.__class__, (items,)
330
332
331 def keys(self):
333 def keys(self):
332 return list(self)
334 return list(self)
333
335
334 setdefault = DictMixin.setdefault
336 setdefault = DictMixin.setdefault
335 update = DictMixin.update
337 update = DictMixin.update
336 pop = DictMixin.pop
338 pop = DictMixin.pop
337 values = DictMixin.values
339 values = DictMixin.values
338 items = DictMixin.items
340 items = DictMixin.items
339 iterkeys = DictMixin.iterkeys
341 iterkeys = DictMixin.iterkeys
340 itervalues = DictMixin.itervalues
342 itervalues = DictMixin.itervalues
341 iteritems = DictMixin.iteritems
343 iteritems = DictMixin.iteritems
342
344
343 def __repr__(self):
345 def __repr__(self):
344 if not self:
346 if not self:
345 return '%s()' % (self.__class__.__name__,)
347 return '%s()' % (self.__class__.__name__,)
346 return '%s(%r)' % (self.__class__.__name__, self.items())
348 return '%s(%r)' % (self.__class__.__name__, self.items())
347
349
348 def copy(self):
350 def copy(self):
349 return self.__class__(self)
351 return self.__class__(self)
350
352
351 @classmethod
353 @classmethod
352 def fromkeys(cls, iterable, value=None):
354 def fromkeys(cls, iterable, value=None):
353 d = cls()
355 d = cls()
354 for key in iterable:
356 for key in iterable:
355 d[key] = value
357 d[key] = value
356 return d
358 return d
357
359
358 def __eq__(self, other):
360 def __eq__(self, other):
359 if isinstance(other, OrderedDict):
361 if isinstance(other, OrderedDict):
360 return len(self) == len(other) and self.items() == other.items()
362 return len(self) == len(other) and self.items() == other.items()
361 return dict.__eq__(self, other)
363 return dict.__eq__(self, other)
362
364
363 def __ne__(self, other):
365 def __ne__(self, other):
364 return not self == other
366 return not self == other
367
368
369 #===============================================================================
370 # TEST FUNCTIONS
371 #===============================================================================
372 def create_test_index(repo_location, full_index):
373 """Makes default test index
374 @param repo_location:
375 @param full_index:
376 """
377 from pylons_app.lib.indexers.daemon import WhooshIndexingDaemon
378 from pylons_app.lib.pidlock import DaemonLock, LockHeld
379 from pylons_app.lib.indexers import IDX_LOCATION
380 import shutil
381
382 if os.path.exists(IDX_LOCATION):
383 shutil.rmtree(IDX_LOCATION)
384
385 try:
386 l = DaemonLock()
387 WhooshIndexingDaemon(repo_location=repo_location)\
388 .run(full_index=full_index)
389 l.release()
390 except LockHeld:
391 pass
392
393 def create_test_env(repos_test_path, config):
394 """Makes a fresh database and
395 install test repository into tmp dir
396 """
397 from pylons_app.lib.db_manage import DbManage
398 import tarfile
399 import shutil
400 from os.path import dirname as dn, join as jn, abspath
401
402 log = logging.getLogger('TestEnvCreator')
403 # create logger
404 log.setLevel(logging.DEBUG)
405 log.propagate = True
406 # create console handler and set level to debug
407 ch = logging.StreamHandler()
408 ch.setLevel(logging.DEBUG)
409
410 # create formatter
411 formatter = logging.Formatter("%(asctime)s - %(name)s - %(levelname)s - %(message)s")
412
413 # add formatter to ch
414 ch.setFormatter(formatter)
415
416 # add ch to logger
417 log.addHandler(ch)
418
419 #PART ONE create db
420 log.debug('making test db')
421 dbname = config['sqlalchemy.db1.url'].split('/')[-1]
422 dbmanage = DbManage(log_sql=True, dbname=dbname, tests=True)
423 dbmanage.create_tables(override=True)
424 dbmanage.config_prompt(repos_test_path)
425 dbmanage.create_default_user()
426 dbmanage.admin_prompt()
427 dbmanage.create_permissions()
428 dbmanage.populate_default_permissions()
429
430 #PART TWO make test repo
431 log.debug('making test vcs repo')
432 if os.path.isdir('/tmp/vcs_test'):
433 shutil.rmtree('/tmp/vcs_test')
434
435 cur_dir = dn(dn(abspath(__file__)))
436 tar = tarfile.open(jn(cur_dir, 'tests', "vcs_test.tar.gz"))
437 tar.extractall('/tmp')
438 tar.close()
@@ -1,30 +1,23 b''
1 """The application's model objects"""
1 """The application's model objects"""
2 import logging
2 import logging
3 import sqlalchemy as sa
4 from sqlalchemy import orm
5 from pylons_app.model import meta
3 from pylons_app.model import meta
6 from pylons_app.model.meta import Session
7 log = logging.getLogger(__name__)
4 log = logging.getLogger(__name__)
8
5
9 # Add these two imports:
10 import datetime
11 from sqlalchemy import schema, types
12
13 def init_model(engine):
6 def init_model(engine):
14 """Call me before using any of the tables or classes in the model"""
7 """Call me before using any of the tables or classes in the model"""
15 log.info("INITIALIZING DB MODELS")
8 log.info("INITIALIZING DB MODELS")
16 meta.Base.metadata.bind = engine
9 meta.Base.metadata.bind = engine
17 #meta.Base2.metadata.bind = engine2
10 #meta.Base2.metadata.bind = engine2
18
11
19 #THIS IS A TEST FOR EXECUTING SCRIPT AND LOAD PYLONS APPLICATION GLOBALS
12 #THIS IS A TEST FOR EXECUTING SCRIPT AND LOAD PYLONS APPLICATION GLOBALS
20 #from paste.deploy import appconfig
13 #from paste.deploy import appconfig
21 #from pylons import config
14 #from pylons import config
22 #from sqlalchemy import engine_from_config
15 #from sqlalchemy import engine_from_config
23 #from pylons_app.config.environment import load_environment
16 #from pylons_app.config.environment import load_environment
24 #
17 #
25 #conf = appconfig('config:development.ini', relative_to = './../../')
18 #conf = appconfig('config:development.ini', relative_to = './../../')
26 #load_environment(conf.global_conf, conf.local_conf)
19 #load_environment(conf.global_conf, conf.local_conf)
27 #
20 #
28 #engine = engine_from_config(config, 'sqlalchemy.')
21 #engine = engine_from_config(config, 'sqlalchemy.')
29 #init_model(engine)
22 #init_model(engine)
30 # DO SOMETHING
23 # DO SOMETHING
@@ -1,125 +1,134 b''
1 from pylons_app.model.meta import Base
1 from pylons_app.model.meta import Base
2 from sqlalchemy import *
2 from sqlalchemy import *
3 from sqlalchemy.orm import relation, backref
3 from sqlalchemy.orm import relation, backref
4 from sqlalchemy.orm.session import Session
4 from sqlalchemy.orm.session import Session
5 from vcs.utils.lazy import LazyProperty
5 from vcs.utils.lazy import LazyProperty
6 import logging
6 import logging
7
7
8 log = logging.getLogger(__name__)
8 log = logging.getLogger(__name__)
9
9
10 class HgAppSettings(Base):
10 class HgAppSettings(Base):
11 __tablename__ = 'hg_app_settings'
11 __tablename__ = 'hg_app_settings'
12 __table_args__ = (UniqueConstraint('app_settings_name'), {'useexisting':True})
12 __table_args__ = (UniqueConstraint('app_settings_name'), {'useexisting':True})
13 app_settings_id = Column("app_settings_id", INTEGER(), nullable=False, unique=True, default=None, primary_key=True)
13 app_settings_id = Column("app_settings_id", INTEGER(), nullable=False, unique=True, default=None, primary_key=True)
14 app_settings_name = Column("app_settings_name", TEXT(length=None, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
14 app_settings_name = Column("app_settings_name", TEXT(length=None, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
15 app_settings_value = Column("app_settings_value", TEXT(length=None, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
15 app_settings_value = Column("app_settings_value", TEXT(length=None, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
16
16
17 class HgAppUi(Base):
17 class HgAppUi(Base):
18 __tablename__ = 'hg_app_ui'
18 __tablename__ = 'hg_app_ui'
19 __table_args__ = {'useexisting':True}
19 __table_args__ = {'useexisting':True}
20 ui_id = Column("ui_id", INTEGER(), nullable=False, unique=True, default=None, primary_key=True)
20 ui_id = Column("ui_id", INTEGER(), nullable=False, unique=True, default=None, primary_key=True)
21 ui_section = Column("ui_section", TEXT(length=None, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
21 ui_section = Column("ui_section", TEXT(length=None, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
22 ui_key = Column("ui_key", TEXT(length=None, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
22 ui_key = Column("ui_key", TEXT(length=None, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
23 ui_value = Column("ui_value", TEXT(length=None, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
23 ui_value = Column("ui_value", TEXT(length=None, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
24 ui_active = Column("ui_active", BOOLEAN(), nullable=True, unique=None, default=True)
24 ui_active = Column("ui_active", BOOLEAN(), nullable=True, unique=None, default=True)
25
25
26
26
27 class User(Base):
27 class User(Base):
28 __tablename__ = 'users'
28 __tablename__ = 'users'
29 __table_args__ = (UniqueConstraint('username'), {'useexisting':True})
29 __table_args__ = (UniqueConstraint('username'), UniqueConstraint('email'), {'useexisting':True})
30 user_id = Column("user_id", INTEGER(), nullable=False, unique=True, default=None, primary_key=True)
30 user_id = Column("user_id", INTEGER(), nullable=False, unique=True, default=None, primary_key=True)
31 username = Column("username", TEXT(length=None, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
31 username = Column("username", TEXT(length=None, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
32 password = Column("password", TEXT(length=None, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
32 password = Column("password", TEXT(length=None, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
33 active = Column("active", BOOLEAN(), nullable=True, unique=None, default=None)
33 active = Column("active", BOOLEAN(), nullable=True, unique=None, default=None)
34 admin = Column("admin", BOOLEAN(), nullable=True, unique=None, default=False)
34 admin = Column("admin", BOOLEAN(), nullable=True, unique=None, default=False)
35 name = Column("name", TEXT(length=None, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
35 name = Column("name", TEXT(length=None, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
36 lastname = Column("lastname", TEXT(length=None, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
36 lastname = Column("lastname", TEXT(length=None, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
37 email = Column("email", TEXT(length=None, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
37 email = Column("email", TEXT(length=None, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
38 last_login = Column("last_login", DATETIME(timezone=False), nullable=True, unique=None, default=None)
38 last_login = Column("last_login", DATETIME(timezone=False), nullable=True, unique=None, default=None)
39
39
40 user_log = relation('UserLog')
40 user_log = relation('UserLog')
41 user_perms = relation('UserToPerm', primaryjoin="User.user_id==UserToPerm.user_id")
41 user_perms = relation('UserToPerm', primaryjoin="User.user_id==UserToPerm.user_id")
42
42
43 @LazyProperty
43 @LazyProperty
44 def full_contact(self):
44 def full_contact(self):
45 return '%s %s <%s>' % (self.name, self.lastname, self.email)
45 return '%s %s <%s>' % (self.name, self.lastname, self.email)
46
46
47 def __repr__(self):
47 def __repr__(self):
48 return "<User('id:%s:%s')>" % (self.user_id, self.username)
48 return "<User('id:%s:%s')>" % (self.user_id, self.username)
49
49
50 def update_lastlogin(self):
50 def update_lastlogin(self):
51 """Update user lastlogin"""
51 """Update user lastlogin"""
52 import datetime
52 import datetime
53
53
54 try:
54 try:
55 session = Session.object_session(self)
55 session = Session.object_session(self)
56 self.last_login = datetime.datetime.now()
56 self.last_login = datetime.datetime.now()
57 session.add(self)
57 session.add(self)
58 session.commit()
58 session.commit()
59 log.debug('updated user %s lastlogin',self.username)
59 log.debug('updated user %s lastlogin', self.username)
60 except Exception:
60 except Exception:
61 session.rollback()
61 session.rollback()
62
62
63
63
64 class UserLog(Base):
64 class UserLog(Base):
65 __tablename__ = 'user_logs'
65 __tablename__ = 'user_logs'
66 __table_args__ = {'useexisting':True}
66 __table_args__ = {'useexisting':True}
67 user_log_id = Column("user_log_id", INTEGER(), nullable=False, unique=True, default=None, primary_key=True)
67 user_log_id = Column("user_log_id", INTEGER(), nullable=False, unique=True, default=None, primary_key=True)
68 user_id = Column("user_id", INTEGER(), ForeignKey(u'users.user_id'), nullable=False, unique=None, default=None)
68 user_id = Column("user_id", INTEGER(), ForeignKey(u'users.user_id'), nullable=False, unique=None, default=None)
69 user_ip = Column("user_ip", TEXT(length=None, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
69 user_ip = Column("user_ip", TEXT(length=None, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
70 repository = Column("repository", TEXT(length=None, convert_unicode=False, assert_unicode=None), ForeignKey(u'repositories.repo_name'), nullable=False, unique=None, default=None)
70 repository = Column("repository", TEXT(length=None, convert_unicode=False, assert_unicode=None), ForeignKey(u'repositories.repo_name'), nullable=False, unique=None, default=None)
71 action = Column("action", TEXT(length=None, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
71 action = Column("action", TEXT(length=None, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
72 action_date = Column("action_date", DATETIME(timezone=False), nullable=True, unique=None, default=None)
72 action_date = Column("action_date", DATETIME(timezone=False), nullable=True, unique=None, default=None)
73
73
74 user = relation('User')
74 user = relation('User')
75
75
76 class Repository(Base):
76 class Repository(Base):
77 __tablename__ = 'repositories'
77 __tablename__ = 'repositories'
78 __table_args__ = (UniqueConstraint('repo_name'), {'useexisting':True},)
78 __table_args__ = (UniqueConstraint('repo_name'), {'useexisting':True},)
79 repo_id = Column("repo_id", INTEGER(), nullable=False, unique=True, default=None, primary_key=True)
79 repo_id = Column("repo_id", INTEGER(), nullable=False, unique=True, default=None, primary_key=True)
80 repo_name = Column("repo_name", TEXT(length=None, convert_unicode=False, assert_unicode=None), nullable=False, unique=True, default=None)
80 repo_name = Column("repo_name", TEXT(length=None, convert_unicode=False, assert_unicode=None), nullable=False, unique=True, default=None)
81 user_id = Column("user_id", INTEGER(), ForeignKey(u'users.user_id'), nullable=False, unique=False, default=None)
81 user_id = Column("user_id", INTEGER(), ForeignKey(u'users.user_id'), nullable=False, unique=False, default=None)
82 private = Column("private", BOOLEAN(), nullable=True, unique=None, default=None)
82 private = Column("private", BOOLEAN(), nullable=True, unique=None, default=None)
83 description = Column("description", TEXT(length=None, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
83 description = Column("description", TEXT(length=None, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
84
84
85 user = relation('User')
85 user = relation('User')
86 repo_to_perm = relation('RepoToPerm', cascade='all')
86 repo_to_perm = relation('RepoToPerm', cascade='all')
87
87
88 def __repr__(self):
88 def __repr__(self):
89 return "<Repository('id:%s:%s')>" % (self.repo_id, self.repo_name)
89 return "<Repository('id:%s:%s')>" % (self.repo_id, self.repo_name)
90
90
91 class Permission(Base):
91 class Permission(Base):
92 __tablename__ = 'permissions'
92 __tablename__ = 'permissions'
93 __table_args__ = {'useexisting':True}
93 __table_args__ = {'useexisting':True}
94 permission_id = Column("permission_id", INTEGER(), nullable=False, unique=True, default=None, primary_key=True)
94 permission_id = Column("permission_id", INTEGER(), nullable=False, unique=True, default=None, primary_key=True)
95 permission_name = Column("permission_name", TEXT(length=None, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
95 permission_name = Column("permission_name", TEXT(length=None, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
96 permission_longname = Column("permission_longname", TEXT(length=None, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
96 permission_longname = Column("permission_longname", TEXT(length=None, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
97
97
98 def __repr__(self):
98 def __repr__(self):
99 return "<Permission('%s:%s')>" % (self.permission_id, self.permission_name)
99 return "<Permission('%s:%s')>" % (self.permission_id, self.permission_name)
100
100
101 class RepoToPerm(Base):
101 class RepoToPerm(Base):
102 __tablename__ = 'repo_to_perm'
102 __tablename__ = 'repo_to_perm'
103 __table_args__ = (UniqueConstraint('user_id', 'repository_id'), {'useexisting':True})
103 __table_args__ = (UniqueConstraint('user_id', 'repository_id'), {'useexisting':True})
104 repo_to_perm_id = Column("repo_to_perm_id", INTEGER(), nullable=False, unique=True, default=None, primary_key=True)
104 repo_to_perm_id = Column("repo_to_perm_id", INTEGER(), nullable=False, unique=True, default=None, primary_key=True)
105 user_id = Column("user_id", INTEGER(), ForeignKey(u'users.user_id'), nullable=False, unique=None, default=None)
105 user_id = Column("user_id", INTEGER(), ForeignKey(u'users.user_id'), nullable=False, unique=None, default=None)
106 permission_id = Column("permission_id", INTEGER(), ForeignKey(u'permissions.permission_id'), nullable=False, unique=None, default=None)
106 permission_id = Column("permission_id", INTEGER(), ForeignKey(u'permissions.permission_id'), nullable=False, unique=None, default=None)
107 repository_id = Column("repository_id", INTEGER(), ForeignKey(u'repositories.repo_id'), nullable=False, unique=None, default=None)
107 repository_id = Column("repository_id", INTEGER(), ForeignKey(u'repositories.repo_id'), nullable=False, unique=None, default=None)
108
108
109 user = relation('User')
109 user = relation('User')
110 permission = relation('Permission')
110 permission = relation('Permission')
111 repository = relation('Repository')
111 repository = relation('Repository')
112
112
113 class UserToPerm(Base):
113 class UserToPerm(Base):
114 __tablename__ = 'user_to_perm'
114 __tablename__ = 'user_to_perm'
115 __table_args__ = (UniqueConstraint('user_id', 'permission_id'), {'useexisting':True})
115 __table_args__ = (UniqueConstraint('user_id', 'permission_id'), {'useexisting':True})
116 user_to_perm_id = Column("user_to_perm_id", INTEGER(), nullable=False, unique=True, default=None, primary_key=True)
116 user_to_perm_id = Column("user_to_perm_id", INTEGER(), nullable=False, unique=True, default=None, primary_key=True)
117 user_id = Column("user_id", INTEGER(), ForeignKey(u'users.user_id'), nullable=False, unique=None, default=None)
117 user_id = Column("user_id", INTEGER(), ForeignKey(u'users.user_id'), nullable=False, unique=None, default=None)
118 permission_id = Column("permission_id", INTEGER(), ForeignKey(u'permissions.permission_id'), nullable=False, unique=None, default=None)
118 permission_id = Column("permission_id", INTEGER(), ForeignKey(u'permissions.permission_id'), nullable=False, unique=None, default=None)
119
119
120 user = relation('User')
120 user = relation('User')
121 permission = relation('Permission')
121 permission = relation('Permission')
122
122
123
123 class Statistics(Base):
124 __tablename__ = 'statistics'
125 __table_args__ = (UniqueConstraint('repository_id'), {'useexisting':True})
126 stat_id = Column("stat_id", INTEGER(), nullable=False, unique=True, default=None, primary_key=True)
127 repository_id = Column("repository_id", INTEGER(), ForeignKey(u'repositories.repo_id'), nullable=False, unique=True, default=None)
128 stat_on_revision = Column("stat_on_revision", INTEGER(), nullable=False)
129 commit_activity = Column("commit_activity", BLOB(), nullable=False)#JSON data
130 commit_activity_combined = Column("commit_activity_combined", BLOB(), nullable=False)#JSON data
131 languages = Column("languages", BLOB(), nullable=False)#JSON data
132
133 repository = relation('Repository')
124
134
125
@@ -1,315 +1,351 b''
1 """ this is forms validation classes
1 """ this is forms validation classes
2 http://formencode.org/module-formencode.validators.html
2 http://formencode.org/module-formencode.validators.html
3 for list off all availible validators
3 for list off all availible validators
4
4
5 we can create our own validators
5 we can create our own validators
6
6
7 The table below outlines the options which can be used in a schema in addition to the validators themselves
7 The table below outlines the options which can be used in a schema in addition to the validators themselves
8 pre_validators [] These validators will be applied before the schema
8 pre_validators [] These validators will be applied before the schema
9 chained_validators [] These validators will be applied after the schema
9 chained_validators [] These validators will be applied after the schema
10 allow_extra_fields False If True, then it is not an error when keys that aren't associated with a validator are present
10 allow_extra_fields False If True, then it is not an error when keys that aren't associated with a validator are present
11 filter_extra_fields False If True, then keys that aren't associated with a validator are removed
11 filter_extra_fields False If True, then keys that aren't associated with a validator are removed
12 if_key_missing NoDefault If this is given, then any keys that aren't available but are expected will be replaced with this value (and then validated). This does not override a present .if_missing attribute on validators. NoDefault is a special FormEncode class to mean that no default values has been specified and therefore missing keys shouldn't take a default value.
12 if_key_missing NoDefault If this is given, then any keys that aren't available but are expected will be replaced with this value (and then validated). This does not override a present .if_missing attribute on validators. NoDefault is a special FormEncode class to mean that no default values has been specified and therefore missing keys shouldn't take a default value.
13 ignore_key_missing False If True, then missing keys will be missing in the result, if the validator doesn't have .if_missing on it already
13 ignore_key_missing False If True, then missing keys will be missing in the result, if the validator doesn't have .if_missing on it already
14
14
15
15
16 <name> = formencode.validators.<name of validator>
16 <name> = formencode.validators.<name of validator>
17 <name> must equal form name
17 <name> must equal form name
18 list=[1,2,3,4,5]
18 list=[1,2,3,4,5]
19 for SELECT use formencode.All(OneOf(list), Int())
19 for SELECT use formencode.All(OneOf(list), Int())
20
20
21 """
21 """
22 from formencode import All
22 from formencode import All
23 from formencode.validators import UnicodeString, OneOf, Int, Number, Regex, \
23 from formencode.validators import UnicodeString, OneOf, Int, Number, Regex, \
24 Email, Bool, StringBoolean
24 Email, Bool, StringBoolean
25 from pylons import session
25 from pylons import session
26 from pylons.i18n.translation import _
26 from pylons.i18n.translation import _
27 from pylons_app.lib.auth import check_password, get_crypt_password
27 from pylons_app.lib.auth import check_password, get_crypt_password
28 from pylons_app.model import meta
28 from pylons_app.model import meta
29 from pylons_app.model.user_model import UserModel
29 from pylons_app.model.user_model import UserModel
30 from pylons_app.model.db import User, Repository
30 from pylons_app.model.db import User, Repository
31 from sqlalchemy.exc import OperationalError
31 from sqlalchemy.exc import OperationalError
32 from sqlalchemy.orm.exc import NoResultFound, MultipleResultsFound
32 from sqlalchemy.orm.exc import NoResultFound, MultipleResultsFound
33 from webhelpers.pylonslib.secure_form import authentication_token
33 from webhelpers.pylonslib.secure_form import authentication_token
34 import formencode
34 import formencode
35 import logging
35 import logging
36 import os
36 import os
37 import pylons_app.lib.helpers as h
37 import pylons_app.lib.helpers as h
38 log = logging.getLogger(__name__)
38 log = logging.getLogger(__name__)
39
39
40
40
41 #this is needed to translate the messages using _() in validators
41 #this is needed to translate the messages using _() in validators
42 class State_obj(object):
42 class State_obj(object):
43 _ = staticmethod(_)
43 _ = staticmethod(_)
44
44
45 #===============================================================================
45 #===============================================================================
46 # VALIDATORS
46 # VALIDATORS
47 #===============================================================================
47 #===============================================================================
48 class ValidAuthToken(formencode.validators.FancyValidator):
48 class ValidAuthToken(formencode.validators.FancyValidator):
49 messages = {'invalid_token':_('Token mismatch')}
49 messages = {'invalid_token':_('Token mismatch')}
50
50
51 def validate_python(self, value, state):
51 def validate_python(self, value, state):
52
52
53 if value != authentication_token():
53 if value != authentication_token():
54 raise formencode.Invalid(self.message('invalid_token', state,
54 raise formencode.Invalid(self.message('invalid_token', state,
55 search_number=value), value, state)
55 search_number=value), value, state)
56
56
57 def ValidUsername(edit, old_data):
57 def ValidUsername(edit, old_data):
58 class _ValidUsername(formencode.validators.FancyValidator):
58 class _ValidUsername(formencode.validators.FancyValidator):
59
59
60 def validate_python(self, value, state):
60 def validate_python(self, value, state):
61 if value in ['default', 'new_user']:
61 if value in ['default', 'new_user']:
62 raise formencode.Invalid(_('Invalid username'), value, state)
62 raise formencode.Invalid(_('Invalid username'), value, state)
63 #check if user is uniq
63 #check if user is uniq
64 sa = meta.Session
64 sa = meta.Session
65 old_un = None
65 old_un = None
66 if edit:
66 if edit:
67 old_un = sa.query(User).get(old_data.get('user_id')).username
67 old_un = sa.query(User).get(old_data.get('user_id')).username
68
68
69 if old_un != value or not edit:
69 if old_un != value or not edit:
70 if sa.query(User).filter(User.username == value).scalar():
70 if sa.query(User).filter(User.username == value).scalar():
71 raise formencode.Invalid(_('This username already exists') ,
71 raise formencode.Invalid(_('This username already exists') ,
72 value, state)
72 value, state)
73 meta.Session.remove()
73 meta.Session.remove()
74
74
75 return _ValidUsername
75 return _ValidUsername
76
76
77 class ValidPassword(formencode.validators.FancyValidator):
77 class ValidPassword(formencode.validators.FancyValidator):
78
78
79 def to_python(self, value, state):
79 def to_python(self, value, state):
80 if value:
80 if value:
81 return get_crypt_password(value)
81 return get_crypt_password(value)
82
82
83 class ValidAuth(formencode.validators.FancyValidator):
83 class ValidAuth(formencode.validators.FancyValidator):
84 messages = {
84 messages = {
85 'invalid_password':_('invalid password'),
85 'invalid_password':_('invalid password'),
86 'invalid_login':_('invalid user name'),
86 'invalid_login':_('invalid user name'),
87 'disabled_account':_('Your acccount is disabled')
87 'disabled_account':_('Your acccount is disabled')
88
88
89 }
89 }
90 #error mapping
90 #error mapping
91 e_dict = {'username':messages['invalid_login'],
91 e_dict = {'username':messages['invalid_login'],
92 'password':messages['invalid_password']}
92 'password':messages['invalid_password']}
93 e_dict_disable = {'username':messages['disabled_account']}
93 e_dict_disable = {'username':messages['disabled_account']}
94
94
95 def validate_python(self, value, state):
95 def validate_python(self, value, state):
96 password = value['password']
96 password = value['password']
97 username = value['username']
97 username = value['username']
98 user = UserModel().get_user_by_name(username)
98 user = UserModel().get_user_by_name(username)
99 if user is None:
99 if user is None:
100 raise formencode.Invalid(self.message('invalid_password',
100 raise formencode.Invalid(self.message('invalid_password',
101 state=State_obj), value, state,
101 state=State_obj), value, state,
102 error_dict=self.e_dict)
102 error_dict=self.e_dict)
103 if user:
103 if user:
104 if user.active:
104 if user.active:
105 if user.username == username and check_password(password,
105 if user.username == username and check_password(password,
106 user.password):
106 user.password):
107 return value
107 return value
108 else:
108 else:
109 log.warning('user %s not authenticated', username)
109 log.warning('user %s not authenticated', username)
110 raise formencode.Invalid(self.message('invalid_password',
110 raise formencode.Invalid(self.message('invalid_password',
111 state=State_obj), value, state,
111 state=State_obj), value, state,
112 error_dict=self.e_dict)
112 error_dict=self.e_dict)
113 else:
113 else:
114 log.warning('user %s is disabled', username)
114 log.warning('user %s is disabled', username)
115 raise formencode.Invalid(self.message('disabled_account',
115 raise formencode.Invalid(self.message('disabled_account',
116 state=State_obj),
116 state=State_obj),
117 value, state,
117 value, state,
118 error_dict=self.e_dict_disable)
118 error_dict=self.e_dict_disable)
119
119
120 class ValidRepoUser(formencode.validators.FancyValidator):
120 class ValidRepoUser(formencode.validators.FancyValidator):
121
121
122 def to_python(self, value, state):
122 def to_python(self, value, state):
123 try:
123 try:
124 self.user_db = meta.Session.query(User)\
124 self.user_db = meta.Session.query(User)\
125 .filter(User.active == True)\
125 .filter(User.active == True)\
126 .filter(User.username == value).one()
126 .filter(User.username == value).one()
127 except Exception:
127 except Exception:
128 raise formencode.Invalid(_('This username is not valid'),
128 raise formencode.Invalid(_('This username is not valid'),
129 value, state)
129 value, state)
130 finally:
130 finally:
131 meta.Session.remove()
131 meta.Session.remove()
132
132
133 return self.user_db.user_id
133 return self.user_db.user_id
134
134
135 def ValidRepoName(edit, old_data):
135 def ValidRepoName(edit, old_data):
136 class _ValidRepoName(formencode.validators.FancyValidator):
136 class _ValidRepoName(formencode.validators.FancyValidator):
137
137
138 def to_python(self, value, state):
138 def to_python(self, value, state):
139 slug = h.repo_name_slug(value)
139 slug = h.repo_name_slug(value)
140 if slug in ['_admin']:
140 if slug in ['_admin']:
141 raise formencode.Invalid(_('This repository name is disallowed'),
141 raise formencode.Invalid(_('This repository name is disallowed'),
142 value, state)
142 value, state)
143 if old_data.get('repo_name') != value or not edit:
143 if old_data.get('repo_name') != value or not edit:
144 sa = meta.Session
144 sa = meta.Session
145 if sa.query(Repository).filter(Repository.repo_name == slug).scalar():
145 if sa.query(Repository).filter(Repository.repo_name == slug).scalar():
146 raise formencode.Invalid(_('This repository already exists') ,
146 raise formencode.Invalid(_('This repository already exists') ,
147 value, state)
147 value, state)
148 meta.Session.remove()
148 meta.Session.remove()
149 return slug
149 return slug
150
150
151
151
152 return _ValidRepoName
152 return _ValidRepoName
153
153
154 class ValidPerms(formencode.validators.FancyValidator):
154 class ValidPerms(formencode.validators.FancyValidator):
155 messages = {'perm_new_user_name':_('This username is not valid')}
155 messages = {'perm_new_user_name':_('This username is not valid')}
156
156
157 def to_python(self, value, state):
157 def to_python(self, value, state):
158 perms_update = []
158 perms_update = []
159 perms_new = []
159 perms_new = []
160 #build a list of permission to update and new permission to create
160 #build a list of permission to update and new permission to create
161 for k, v in value.items():
161 for k, v in value.items():
162 if k.startswith('perm_'):
162 if k.startswith('perm_'):
163 if k.startswith('perm_new_user'):
163 if k.startswith('perm_new_user'):
164 new_perm = value.get('perm_new_user', False)
164 new_perm = value.get('perm_new_user', False)
165 new_user = value.get('perm_new_user_name', False)
165 new_user = value.get('perm_new_user_name', False)
166 if new_user and new_perm:
166 if new_user and new_perm:
167 if (new_user, new_perm) not in perms_new:
167 if (new_user, new_perm) not in perms_new:
168 perms_new.append((new_user, new_perm))
168 perms_new.append((new_user, new_perm))
169 else:
169 else:
170 usr = k[5:]
170 usr = k[5:]
171 if usr == 'default':
171 if usr == 'default':
172 if value['private']:
172 if value['private']:
173 #set none for default when updating to private repo
173 #set none for default when updating to private repo
174 v = 'repository.none'
174 v = 'repository.none'
175 perms_update.append((usr, v))
175 perms_update.append((usr, v))
176 value['perms_updates'] = perms_update
176 value['perms_updates'] = perms_update
177 value['perms_new'] = perms_new
177 value['perms_new'] = perms_new
178 sa = meta.Session
178 sa = meta.Session
179 for k, v in perms_new:
179 for k, v in perms_new:
180 try:
180 try:
181 self.user_db = sa.query(User)\
181 self.user_db = sa.query(User)\
182 .filter(User.active == True)\
182 .filter(User.active == True)\
183 .filter(User.username == k).one()
183 .filter(User.username == k).one()
184 except Exception:
184 except Exception:
185 msg = self.message('perm_new_user_name',
185 msg = self.message('perm_new_user_name',
186 state=State_obj)
186 state=State_obj)
187 raise formencode.Invalid(msg, value, state, error_dict={'perm_new_user_name':msg})
187 raise formencode.Invalid(msg, value, state, error_dict={'perm_new_user_name':msg})
188 return value
188 return value
189
189
190 class ValidSettings(formencode.validators.FancyValidator):
190 class ValidSettings(formencode.validators.FancyValidator):
191
191
192 def to_python(self, value, state):
192 def to_python(self, value, state):
193 #settings form can't edit user
193 #settings form can't edit user
194 if value.has_key('user'):
194 if value.has_key('user'):
195 del['value']['user']
195 del['value']['user']
196
196
197 return value
197 return value
198
198
199 class ValidPath(formencode.validators.FancyValidator):
199 class ValidPath(formencode.validators.FancyValidator):
200 def to_python(self, value, state):
200 def to_python(self, value, state):
201 isdir = os.path.isdir(value.replace('*', ''))
201 isdir = os.path.isdir(value.replace('*', ''))
202 if (value.endswith('/*') or value.endswith('/**')) and isdir:
202 if (value.endswith('/*') or value.endswith('/**')) and isdir:
203 return value
203 return value
204 elif not isdir:
204 elif not isdir:
205 msg = _('This is not a valid path')
205 msg = _('This is not a valid path')
206 else:
206 else:
207 msg = _('You need to specify * or ** at the end of path (ie. /tmp/*)')
207 msg = _('You need to specify * or ** at the end of path (ie. /tmp/*)')
208
208
209 raise formencode.Invalid(msg, value, state,
209 raise formencode.Invalid(msg, value, state,
210 error_dict={'paths_root_path':msg})
210 error_dict={'paths_root_path':msg})
211
211
212 def UniqSystemEmail(old_data):
213 class _UniqSystemEmail(formencode.validators.FancyValidator):
214 def to_python(self, value, state):
215 if old_data.get('email') != value:
216 sa = meta.Session
217 try:
218 user = sa.query(User).filter(User.email == value).scalar()
219 if user:
220 raise formencode.Invalid(_("That e-mail address is already taken") ,
221 value, state)
222 finally:
223 meta.Session.remove()
224
225 return value
226
227 return _UniqSystemEmail
228
229 class ValidSystemEmail(formencode.validators.FancyValidator):
230 def to_python(self, value, state):
231 sa = meta.Session
232 try:
233 user = sa.query(User).filter(User.email == value).scalar()
234 if user is None:
235 raise formencode.Invalid(_("That e-mail address doesn't exist.") ,
236 value, state)
237 finally:
238 meta.Session.remove()
239
240 return value
241
212 #===============================================================================
242 #===============================================================================
213 # FORMS
243 # FORMS
214 #===============================================================================
244 #===============================================================================
215 class LoginForm(formencode.Schema):
245 class LoginForm(formencode.Schema):
216 allow_extra_fields = True
246 allow_extra_fields = True
217 filter_extra_fields = True
247 filter_extra_fields = True
218 username = UnicodeString(
248 username = UnicodeString(
219 strip=True,
249 strip=True,
220 min=3,
250 min=3,
221 not_empty=True,
251 not_empty=True,
222 messages={
252 messages={
223 'empty':_('Please enter a login'),
253 'empty':_('Please enter a login'),
224 'tooShort':_('Enter a value %(min)i characters long or more')}
254 'tooShort':_('Enter a value %(min)i characters long or more')}
225 )
255 )
226
256
227 password = UnicodeString(
257 password = UnicodeString(
228 strip=True,
258 strip=True,
229 min=3,
259 min=3,
230 not_empty=True,
260 not_empty=True,
231 messages={
261 messages={
232 'empty':_('Please enter a password'),
262 'empty':_('Please enter a password'),
233 'tooShort':_('Enter a value %(min)i characters long or more')}
263 'tooShort':_('Enter a value %(min)i characters long or more')}
234 )
264 )
235
265
236
266
237 #chained validators have access to all data
267 #chained validators have access to all data
238 chained_validators = [ValidAuth]
268 chained_validators = [ValidAuth]
239
269
240 def UserForm(edit=False, old_data={}):
270 def UserForm(edit=False, old_data={}):
241 class _UserForm(formencode.Schema):
271 class _UserForm(formencode.Schema):
242 allow_extra_fields = True
272 allow_extra_fields = True
243 filter_extra_fields = True
273 filter_extra_fields = True
244 username = All(UnicodeString(strip=True, min=3, not_empty=True), ValidUsername(edit, old_data))
274 username = All(UnicodeString(strip=True, min=3, not_empty=True), ValidUsername(edit, old_data))
245 if edit:
275 if edit:
246 new_password = All(UnicodeString(strip=True, min=3, not_empty=False), ValidPassword)
276 new_password = All(UnicodeString(strip=True, min=3, not_empty=False), ValidPassword)
247 admin = StringBoolean(if_missing=False)
277 admin = StringBoolean(if_missing=False)
248 else:
278 else:
249 password = All(UnicodeString(strip=True, min=8, not_empty=True), ValidPassword)
279 password = All(UnicodeString(strip=True, min=8, not_empty=True), ValidPassword)
250 active = StringBoolean(if_missing=False)
280 active = StringBoolean(if_missing=False)
251 name = UnicodeString(strip=True, min=3, not_empty=True)
281 name = UnicodeString(strip=True, min=3, not_empty=True)
252 lastname = UnicodeString(strip=True, min=3, not_empty=True)
282 lastname = UnicodeString(strip=True, min=3, not_empty=True)
253 email = Email(not_empty=True)
283 email = All(Email(not_empty=True), UniqSystemEmail(old_data))
254
284
255 return _UserForm
285 return _UserForm
256
286
257 RegisterForm = UserForm
287 RegisterForm = UserForm
258
288
259
289 def PasswordResetForm():
290 class _PasswordResetForm(formencode.Schema):
291 allow_extra_fields = True
292 filter_extra_fields = True
293 email = All(ValidSystemEmail(), Email(not_empty=True))
294 return _PasswordResetForm
295
260 def RepoForm(edit=False, old_data={}):
296 def RepoForm(edit=False, old_data={}):
261 class _RepoForm(formencode.Schema):
297 class _RepoForm(formencode.Schema):
262 allow_extra_fields = True
298 allow_extra_fields = True
263 filter_extra_fields = False
299 filter_extra_fields = False
264 repo_name = All(UnicodeString(strip=True, min=1, not_empty=True), ValidRepoName(edit, old_data))
300 repo_name = All(UnicodeString(strip=True, min=1, not_empty=True), ValidRepoName(edit, old_data))
265 description = UnicodeString(strip=True, min=3, not_empty=True)
301 description = UnicodeString(strip=True, min=3, not_empty=True)
266 private = StringBoolean(if_missing=False)
302 private = StringBoolean(if_missing=False)
267
303
268 if edit:
304 if edit:
269 user = All(Int(not_empty=True), ValidRepoUser)
305 user = All(Int(not_empty=True), ValidRepoUser)
270
306
271 chained_validators = [ValidPerms]
307 chained_validators = [ValidPerms]
272 return _RepoForm
308 return _RepoForm
273
309
274 def RepoSettingsForm(edit=False, old_data={}):
310 def RepoSettingsForm(edit=False, old_data={}):
275 class _RepoForm(formencode.Schema):
311 class _RepoForm(formencode.Schema):
276 allow_extra_fields = True
312 allow_extra_fields = True
277 filter_extra_fields = False
313 filter_extra_fields = False
278 repo_name = All(UnicodeString(strip=True, min=1, not_empty=True), ValidRepoName(edit, old_data))
314 repo_name = All(UnicodeString(strip=True, min=1, not_empty=True), ValidRepoName(edit, old_data))
279 description = UnicodeString(strip=True, min=3, not_empty=True)
315 description = UnicodeString(strip=True, min=3, not_empty=True)
280 private = StringBoolean(if_missing=False)
316 private = StringBoolean(if_missing=False)
281
317
282 chained_validators = [ValidPerms, ValidSettings]
318 chained_validators = [ValidPerms, ValidSettings]
283 return _RepoForm
319 return _RepoForm
284
320
285
321
286 def ApplicationSettingsForm():
322 def ApplicationSettingsForm():
287 class _ApplicationSettingsForm(formencode.Schema):
323 class _ApplicationSettingsForm(formencode.Schema):
288 allow_extra_fields = True
324 allow_extra_fields = True
289 filter_extra_fields = False
325 filter_extra_fields = False
290 hg_app_title = UnicodeString(strip=True, min=3, not_empty=True)
326 hg_app_title = UnicodeString(strip=True, min=3, not_empty=True)
291 hg_app_realm = UnicodeString(strip=True, min=3, not_empty=True)
327 hg_app_realm = UnicodeString(strip=True, min=3, not_empty=True)
292
328
293 return _ApplicationSettingsForm
329 return _ApplicationSettingsForm
294
330
295 def ApplicationUiSettingsForm():
331 def ApplicationUiSettingsForm():
296 class _ApplicationUiSettingsForm(formencode.Schema):
332 class _ApplicationUiSettingsForm(formencode.Schema):
297 allow_extra_fields = True
333 allow_extra_fields = True
298 filter_extra_fields = False
334 filter_extra_fields = False
299 web_push_ssl = OneOf(['true', 'false'], if_missing='false')
335 web_push_ssl = OneOf(['true', 'false'], if_missing='false')
300 paths_root_path = All(ValidPath(), UnicodeString(strip=True, min=3, not_empty=True))
336 paths_root_path = All(ValidPath(), UnicodeString(strip=True, min=3, not_empty=True))
301 hooks_changegroup_update = OneOf(['True', 'False'], if_missing=False)
337 hooks_changegroup_update = OneOf(['True', 'False'], if_missing=False)
302 hooks_changegroup_repo_size = OneOf(['True', 'False'], if_missing=False)
338 hooks_changegroup_repo_size = OneOf(['True', 'False'], if_missing=False)
303
339
304 return _ApplicationUiSettingsForm
340 return _ApplicationUiSettingsForm
305
341
306 def DefaultPermissionsForm(perms_choices, register_choices, create_choices):
342 def DefaultPermissionsForm(perms_choices, register_choices, create_choices):
307 class _DefaultPermissionsForm(formencode.Schema):
343 class _DefaultPermissionsForm(formencode.Schema):
308 allow_extra_fields = True
344 allow_extra_fields = True
309 filter_extra_fields = True
345 filter_extra_fields = True
310 overwrite_default = OneOf(['true', 'false'], if_missing='false')
346 overwrite_default = OneOf(['true', 'false'], if_missing='false')
311 default_perm = OneOf(perms_choices)
347 default_perm = OneOf(perms_choices)
312 default_register = OneOf(register_choices)
348 default_register = OneOf(register_choices)
313 default_create = OneOf(create_choices)
349 default_create = OneOf(create_choices)
314
350
315 return _DefaultPermissionsForm
351 return _DefaultPermissionsForm
@@ -1,174 +1,169 b''
1 #!/usr/bin/env python
1 #!/usr/bin/env python
2 # encoding: utf-8
2 # encoding: utf-8
3 # Model for hg app
3 # Model for hg app
4 # Copyright (C) 2009-2010 Marcin Kuzminski <marcin@python-works.com>
4 # Copyright (C) 2009-2010 Marcin Kuzminski <marcin@python-works.com>
5 #
5 #
6 # This program is free software; you can redistribute it and/or
6 # This program is free software; you can redistribute it and/or
7 # modify it under the terms of the GNU General Public License
7 # modify it under the terms of the GNU General Public License
8 # as published by the Free Software Foundation; version 2
8 # as published by the Free Software Foundation; version 2
9 # of the License or (at your opinion) any later version of the license.
9 # of the License or (at your opinion) any later version of the license.
10 #
10 #
11 # This program is distributed in the hope that it will be useful,
11 # This program is distributed in the hope that it will be useful,
12 # but WITHOUT ANY WARRANTY; without even the implied warranty of
12 # but WITHOUT ANY WARRANTY; without even the implied warranty of
13 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 # GNU General Public License for more details.
14 # GNU General Public License for more details.
15 #
15 #
16 # You should have received a copy of the GNU General Public License
16 # You should have received a copy of the GNU General Public License
17 # along with this program; if not, write to the Free Software
17 # along with this program; if not, write to the Free Software
18 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
18 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
19 # MA 02110-1301, USA.
19 # MA 02110-1301, USA.
20 """
20 """
21 Created on April 9, 2010
21 Created on April 9, 2010
22 Model for hg app
22 Model for hg app
23 @author: marcink
23 @author: marcink
24 """
24 """
25 from beaker.cache import cache_region
25 from beaker.cache import cache_region
26 from mercurial import ui
26 from mercurial import ui
27 from mercurial.hgweb.hgwebdir_mod import findrepos
27 from mercurial.hgweb.hgwebdir_mod import findrepos
28 from pylons.i18n.translation import _
28 from pylons.i18n.translation import _
29 from pylons_app.lib.auth import HasRepoPermissionAny
29 from pylons_app.lib.auth import HasRepoPermissionAny
30 from pylons_app.model import meta
30 from pylons_app.model import meta
31 from pylons_app.model.db import Repository, User
31 from pylons_app.model.db import Repository, User
32 from pylons_app.lib import helpers as h
32 from pylons_app.lib import helpers as h
33 from vcs.exceptions import RepositoryError, VCSError
33 from vcs.exceptions import RepositoryError, VCSError
34 import logging
34 import logging
35 import os
35 import os
36 import sys
36 import sys
37 log = logging.getLogger(__name__)
37 log = logging.getLogger(__name__)
38
38
39 try:
39 try:
40 from vcs.backends.hg import MercurialRepository
40 from vcs.backends.hg import MercurialRepository
41 except ImportError:
41 except ImportError:
42 sys.stderr.write('You have to import vcs module')
42 sys.stderr.write('You have to import vcs module')
43 raise Exception('Unable to import vcs')
43 raise Exception('Unable to import vcs')
44
44
45 def _get_repos_cached_initial(app_globals, initial):
45 def _get_repos_cached_initial(app_globals, initial):
46 """
46 """return cached dict with repos
47 return cached dict with repos
48 """
47 """
49 g = app_globals
48 g = app_globals
50 return HgModel.repo_scan(g.paths[0][0], g.paths[0][1], g.baseui, initial)
49 return HgModel.repo_scan(g.paths[0][0], g.paths[0][1], g.baseui, initial)
51
50
52 @cache_region('long_term', 'cached_repo_list')
51 @cache_region('long_term', 'cached_repo_list')
53 def _get_repos_cached():
52 def _get_repos_cached():
54 """
53 """return cached dict with repos
55 return cached dict with repos
56 """
54 """
57 log.info('getting all repositories list')
55 log.info('getting all repositories list')
58 from pylons import app_globals as g
56 from pylons import app_globals as g
59 return HgModel.repo_scan(g.paths[0][0], g.paths[0][1], g.baseui)
57 return HgModel.repo_scan(g.paths[0][0], g.paths[0][1], g.baseui)
60
58
61 @cache_region('super_short_term', 'cached_repos_switcher_list')
59 @cache_region('super_short_term', 'cached_repos_switcher_list')
62 def _get_repos_switcher_cached(cached_repo_list):
60 def _get_repos_switcher_cached(cached_repo_list):
63 repos_lst = []
61 repos_lst = []
64 for repo in sorted(x.name.lower() for x in cached_repo_list.values()):
62 for repo in [x for x in cached_repo_list.values()]:
65 if HasRepoPermissionAny('repository.write', 'repository.read', 'repository.admin')(repo, 'main page check'):
63 if HasRepoPermissionAny('repository.write', 'repository.read',
66 repos_lst.append(repo)
64 'repository.admin')(repo.name.lower(), 'main page check'):
65 repos_lst.append((repo.name, repo.dbrepo.private,))
67
66
68 return repos_lst
67 return sorted(repos_lst, key=lambda k:k[0])
69
68
70 @cache_region('long_term', 'full_changelog')
69 @cache_region('long_term', 'full_changelog')
71 def _full_changelog_cached(repo_name):
70 def _full_changelog_cached(repo_name):
72 log.info('getting full changelog for %s', repo_name)
71 log.info('getting full changelog for %s', repo_name)
73 return list(reversed(list(HgModel().get_repo(repo_name))))
72 return list(reversed(list(HgModel().get_repo(repo_name))))
74
73
75 class HgModel(object):
74 class HgModel(object):
76 """
75 """Mercurial Model
77 Mercurial Model
78 """
76 """
79
77
80 def __init__(self):
78 def __init__(self):
81 """
79 pass
82 Constructor
83 """
84
80
85 @staticmethod
81 @staticmethod
86 def repo_scan(repos_prefix, repos_path, baseui, initial=False):
82 def repo_scan(repos_prefix, repos_path, baseui, initial=False):
87 """
83 """
88 Listing of repositories in given path. This path should not be a
84 Listing of repositories in given path. This path should not be a
89 repository itself. Return a dictionary of repository objects
85 repository itself. Return a dictionary of repository objects
90 :param repos_path: path to directory it could take syntax with
86 :param repos_path: path to directory it could take syntax with
91 * or ** for deep recursive displaying repositories
87 * or ** for deep recursive displaying repositories
92 """
88 """
93 sa = meta.Session()
89 sa = meta.Session()
94 def check_repo_dir(path):
90 def check_repo_dir(path):
95 """
91 """Checks the repository
96 Checks the repository
97 :param path:
92 :param path:
98 """
93 """
99 repos_path = path.split('/')
94 repos_path = path.split('/')
100 if repos_path[-1] in ['*', '**']:
95 if repos_path[-1] in ['*', '**']:
101 repos_path = repos_path[:-1]
96 repos_path = repos_path[:-1]
102 if repos_path[0] != '/':
97 if repos_path[0] != '/':
103 repos_path[0] = '/'
98 repos_path[0] = '/'
104 if not os.path.isdir(os.path.join(*repos_path)):
99 if not os.path.isdir(os.path.join(*repos_path)):
105 raise RepositoryError('Not a valid repository in %s' % path[0][1])
100 raise RepositoryError('Not a valid repository in %s' % path)
106 if not repos_path.endswith('*'):
101 if not repos_path.endswith('*'):
107 raise VCSError('You need to specify * or ** at the end of path '
102 raise VCSError('You need to specify * or ** at the end of path '
108 'for recursive scanning')
103 'for recursive scanning')
109
104
110 check_repo_dir(repos_path)
105 check_repo_dir(repos_path)
111 log.info('scanning for repositories in %s', repos_path)
106 log.info('scanning for repositories in %s', repos_path)
112 repos = findrepos([(repos_prefix, repos_path)])
107 repos = findrepos([(repos_prefix, repos_path)])
113 if not isinstance(baseui, ui.ui):
108 if not isinstance(baseui, ui.ui):
114 baseui = ui.ui()
109 baseui = ui.ui()
115
110
116 repos_list = {}
111 repos_list = {}
117 for name, path in repos:
112 for name, path in repos:
118 try:
113 try:
119 #name = name.split('/')[-1]
114 #name = name.split('/')[-1]
120 if repos_list.has_key(name):
115 if repos_list.has_key(name):
121 raise RepositoryError('Duplicate repository name %s found in'
116 raise RepositoryError('Duplicate repository name %s found in'
122 ' %s' % (name, path))
117 ' %s' % (name, path))
123 else:
118 else:
124
119
125 repos_list[name] = MercurialRepository(path, baseui=baseui)
120 repos_list[name] = MercurialRepository(path, baseui=baseui)
126 repos_list[name].name = name
121 repos_list[name].name = name
127
122
128 dbrepo = None
123 dbrepo = None
129 if not initial:
124 if not initial:
130 dbrepo = sa.query(Repository)\
125 dbrepo = sa.query(Repository)\
131 .filter(Repository.repo_name == name).scalar()
126 .filter(Repository.repo_name == name).scalar()
132
127
133 if dbrepo:
128 if dbrepo:
134 log.info('Adding db instance to cached list')
129 log.info('Adding db instance to cached list')
135 repos_list[name].dbrepo = dbrepo
130 repos_list[name].dbrepo = dbrepo
136 repos_list[name].description = dbrepo.description
131 repos_list[name].description = dbrepo.description
137 if dbrepo.user:
132 if dbrepo.user:
138 repos_list[name].contact = dbrepo.user.full_contact
133 repos_list[name].contact = dbrepo.user.full_contact
139 else:
134 else:
140 repos_list[name].contact = sa.query(User)\
135 repos_list[name].contact = sa.query(User)\
141 .filter(User.admin == True).first().full_contact
136 .filter(User.admin == True).first().full_contact
142 except OSError:
137 except OSError:
143 continue
138 continue
144 meta.Session.remove()
139 meta.Session.remove()
145 return repos_list
140 return repos_list
146
141
147 def get_repos(self):
142 def get_repos(self):
148 for name, repo in _get_repos_cached().items():
143 for name, repo in _get_repos_cached().items():
149 if repo._get_hidden():
144 if repo._get_hidden():
150 #skip hidden web repository
145 #skip hidden web repository
151 continue
146 continue
152
147
153 last_change = repo.last_change
148 last_change = repo.last_change
154 tip = h.get_changeset_safe(repo, 'tip')
149 tip = h.get_changeset_safe(repo, 'tip')
155
150
156 tmp_d = {}
151 tmp_d = {}
157 tmp_d['name'] = repo.name
152 tmp_d['name'] = repo.name
158 tmp_d['name_sort'] = tmp_d['name'].lower()
153 tmp_d['name_sort'] = tmp_d['name'].lower()
159 tmp_d['description'] = repo.description
154 tmp_d['description'] = repo.description
160 tmp_d['description_sort'] = tmp_d['description']
155 tmp_d['description_sort'] = tmp_d['description']
161 tmp_d['last_change'] = last_change
156 tmp_d['last_change'] = last_change
162 tmp_d['last_change_sort'] = last_change[1] - last_change[0]
157 tmp_d['last_change_sort'] = last_change[1] - last_change[0]
163 tmp_d['tip'] = tip.raw_id
158 tmp_d['tip'] = tip.raw_id
164 tmp_d['tip_sort'] = tip.revision
159 tmp_d['tip_sort'] = tip.revision
165 tmp_d['rev'] = tip.revision
160 tmp_d['rev'] = tip.revision
166 tmp_d['contact'] = repo.contact
161 tmp_d['contact'] = repo.contact
167 tmp_d['contact_sort'] = tmp_d['contact']
162 tmp_d['contact_sort'] = tmp_d['contact']
168 tmp_d['repo_archives'] = list(repo._get_archives())
163 tmp_d['repo_archives'] = list(repo._get_archives())
169 tmp_d['last_msg'] = tip.message
164 tmp_d['last_msg'] = tip.message
170 tmp_d['repo'] = repo
165 tmp_d['repo'] = repo
171 yield tmp_d
166 yield tmp_d
172
167
173 def get_repo(self, repo_name):
168 def get_repo(self, repo_name):
174 return _get_repos_cached()[repo_name]
169 return _get_repos_cached()[repo_name]
@@ -1,15 +1,58 b''
1 """SQLAlchemy Metadata and Session object"""
1 """SQLAlchemy Metadata and Session object"""
2 from sqlalchemy.ext.declarative import declarative_base
2 from sqlalchemy.ext.declarative import declarative_base
3 from sqlalchemy.orm import scoped_session, sessionmaker
3 from sqlalchemy.orm import scoped_session, sessionmaker
4 from pylons_app.model import caching_query
5 from beaker import cache
6 import os
7 from os.path import join as jn, dirname as dn, abspath
8 import time
9
10 # Beaker CacheManager. A home base for cache configurations.
11 cache_manager = cache.CacheManager()
4
12
5 __all__ = ['Base', 'Session']
13 __all__ = ['Base', 'Session']
6 #
14 #
7 # SQLAlchemy session manager. Updated by model.init_model()
15 # SQLAlchemy session manager. Updated by model.init_model()
8 #
16 #
9 Session = scoped_session(sessionmaker())
17 Session = scoped_session(
10 #
18 sessionmaker(
19 query_cls=caching_query.query_callable(cache_manager)
20 )
21 )
11
22
12 # The declarative Base
23 # The declarative Base
13 Base = declarative_base()
24 Base = declarative_base()
14 #For another db...
25 #For another db...
15 #Base2 = declarative_base()
26 #Base2 = declarative_base()
27
28 #===============================================================================
29 # CACHE OPTIONS
30 #===============================================================================
31 cache_dir = jn(dn(dn(dn(abspath(__file__)))), 'data', 'cache')
32 if not os.path.isdir(cache_dir):
33 os.mkdir(cache_dir)
34 # set start_time to current time
35 # to re-cache everything
36 # upon application startup
37 start_time = time.time()
38 # configure the "sqlalchemy" cache region.
39 cache_manager.regions['sql_cache_short'] = {
40 'type':'memory',
41 'data_dir':cache_dir,
42 'expire':10,
43 'start_time':start_time
44 }
45 cache_manager.regions['sql_cache_med'] = {
46 'type':'memory',
47 'data_dir':cache_dir,
48 'expire':360,
49 'start_time':start_time
50 }
51 cache_manager.regions['sql_cache_long'] = {
52 'type':'file',
53 'data_dir':cache_dir,
54 'expire':3600,
55 'start_time':start_time
56 }
57 #to use cache use this in query
58 #.options(FromCache("sqlalchemy_cache_type", "cachekey"))
@@ -1,131 +1,135 b''
1 #!/usr/bin/env python
1 #!/usr/bin/env python
2 # encoding: utf-8
2 # encoding: utf-8
3 # Model for users
3 # Model for users
4 # Copyright (C) 2009-2010 Marcin Kuzminski <marcin@python-works.com>
4 # Copyright (C) 2009-2010 Marcin Kuzminski <marcin@python-works.com>
5
5 #
6 # This program is free software; you can redistribute it and/or
6 # This program is free software; you can redistribute it and/or
7 # modify it under the terms of the GNU General Public License
7 # modify it under the terms of the GNU General Public License
8 # as published by the Free Software Foundation; version 2
8 # as published by the Free Software Foundation; version 2
9 # of the License or (at your opinion) any later version of the license.
9 # of the License or (at your opinion) any later version of the license.
10 #
10 #
11 # This program is distributed in the hope that it will be useful,
11 # This program is distributed in the hope that it will be useful,
12 # but WITHOUT ANY WARRANTY; without even the implied warranty of
12 # but WITHOUT ANY WARRANTY; without even the implied warranty of
13 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 # GNU General Public License for more details.
14 # GNU General Public License for more details.
15 #
15 #
16 # You should have received a copy of the GNU General Public License
16 # You should have received a copy of the GNU General Public License
17 # along with this program; if not, write to the Free Software
17 # along with this program; if not, write to the Free Software
18 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
18 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
19 # MA 02110-1301, USA.
19 # MA 02110-1301, USA.
20
20
21 """
21 """
22 Created on April 9, 2010
22 Created on April 9, 2010
23 Model for users
23 Model for users
24 @author: marcink
24 @author: marcink
25 """
25 """
26
26 from pylons_app.lib import auth
27 from pylons.i18n.translation import _
28 from pylons_app.lib.celerylib import tasks, run_task
27 from pylons_app.model.db import User
29 from pylons_app.model.db import User
28 from pylons_app.model.meta import Session
30 from pylons_app.model.meta import Session
29 from pylons.i18n.translation import _
31 import traceback
30 import logging
32 import logging
31 log = logging.getLogger(__name__)
33 log = logging.getLogger(__name__)
32
34
33 class DefaultUserException(Exception):pass
35 class DefaultUserException(Exception):pass
34
36
35 class UserModel(object):
37 class UserModel(object):
36
38
37 def __init__(self):
39 def __init__(self):
38 self.sa = Session()
40 self.sa = Session()
39
41
40 def get_default(self):
42 def get_default(self):
41 return self.sa.query(User).filter(User.username == 'default').scalar()
43 return self.sa.query(User).filter(User.username == 'default').scalar()
42
44
43 def get_user(self, id):
45 def get_user(self, id):
44 return self.sa.query(User).get(id)
46 return self.sa.query(User).get(id)
45
47
46 def get_user_by_name(self,name):
48 def get_user_by_name(self, name):
47 return self.sa.query(User).filter(User.username == name).scalar()
49 return self.sa.query(User).filter(User.username == name).scalar()
48
50
49 def create(self, form_data):
51 def create(self, form_data):
50 try:
52 try:
51 new_user = User()
53 new_user = User()
52 for k, v in form_data.items():
54 for k, v in form_data.items():
53 setattr(new_user, k, v)
55 setattr(new_user, k, v)
54
56
55 self.sa.add(new_user)
57 self.sa.add(new_user)
56 self.sa.commit()
58 self.sa.commit()
57 except Exception as e:
59 except:
58 log.error(e)
60 log.error(traceback.format_exc())
59 self.sa.rollback()
61 self.sa.rollback()
60 raise
62 raise
61
63
62 def create_registration(self, form_data):
64 def create_registration(self, form_data):
63 try:
65 try:
64 new_user = User()
66 new_user = User()
65 for k, v in form_data.items():
67 for k, v in form_data.items():
66 if k != 'admin':
68 if k != 'admin':
67 setattr(new_user, k, v)
69 setattr(new_user, k, v)
68
70
69 self.sa.add(new_user)
71 self.sa.add(new_user)
70 self.sa.commit()
72 self.sa.commit()
71 except Exception as e:
73 except:
72 log.error(e)
74 log.error(traceback.format_exc())
73 self.sa.rollback()
75 self.sa.rollback()
74 raise
76 raise
75
77
76 def update(self, uid, form_data):
78 def update(self, uid, form_data):
77 try:
79 try:
78 new_user = self.sa.query(User).get(uid)
80 new_user = self.sa.query(User).get(uid)
79 if new_user.username == 'default':
81 if new_user.username == 'default':
80 raise DefaultUserException(
82 raise DefaultUserException(
81 _("You can't Edit this user since it's"
83 _("You can't Edit this user since it's"
82 " crucial for entire application"))
84 " crucial for entire application"))
83 for k, v in form_data.items():
85 for k, v in form_data.items():
84 if k == 'new_password' and v != '':
86 if k == 'new_password' and v != '':
85 new_user.password = v
87 new_user.password = v
86 else:
88 else:
87 setattr(new_user, k, v)
89 setattr(new_user, k, v)
88
90
89 self.sa.add(new_user)
91 self.sa.add(new_user)
90 self.sa.commit()
92 self.sa.commit()
91 except Exception as e:
93 except:
92 log.error(e)
94 log.error(traceback.format_exc())
93 self.sa.rollback()
95 self.sa.rollback()
94 raise
96 raise
95
97
96 def update_my_account(self, uid, form_data):
98 def update_my_account(self, uid, form_data):
97 try:
99 try:
98 new_user = self.sa.query(User).get(uid)
100 new_user = self.sa.query(User).get(uid)
99 if new_user.username == 'default':
101 if new_user.username == 'default':
100 raise DefaultUserException(
102 raise DefaultUserException(
101 _("You can't Edit this user since it's"
103 _("You can't Edit this user since it's"
102 " crucial for entire application"))
104 " crucial for entire application"))
103 for k, v in form_data.items():
105 for k, v in form_data.items():
104 if k == 'new_password' and v != '':
106 if k == 'new_password' and v != '':
105 new_user.password = v
107 new_user.password = v
106 else:
108 else:
107 if k not in ['admin', 'active']:
109 if k not in ['admin', 'active']:
108 setattr(new_user, k, v)
110 setattr(new_user, k, v)
109
111
110 self.sa.add(new_user)
112 self.sa.add(new_user)
111 self.sa.commit()
113 self.sa.commit()
112 except Exception as e:
114 except:
113 log.error(e)
115 log.error(traceback.format_exc())
114 self.sa.rollback()
116 self.sa.rollback()
115 raise
117 raise
116
118
117 def delete(self, id):
119 def delete(self, id):
118
119 try:
120 try:
120
121
121 user = self.sa.query(User).get(id)
122 user = self.sa.query(User).get(id)
122 if user.username == 'default':
123 if user.username == 'default':
123 raise DefaultUserException(
124 raise DefaultUserException(
124 _("You can't remove this user since it's"
125 _("You can't remove this user since it's"
125 " crucial for entire application"))
126 " crucial for entire application"))
126 self.sa.delete(user)
127 self.sa.delete(user)
127 self.sa.commit()
128 self.sa.commit()
128 except Exception as e:
129 except:
129 log.error(e)
130 log.error(traceback.format_exc())
130 self.sa.rollback()
131 self.sa.rollback()
131 raise
132 raise
133
134 def reset_password(self, data):
135 run_task(tasks.reset_user_password, data['email'])
@@ -1,3633 +1,3660 b''
1 /* -----------------------------------------------------------
1 /* -----------------------------------------------------------
2 main stylesheet
2 main stylesheet
3 ----------------------------------------------------------- */
3 ----------------------------------------------------------- */
4
4
5 html
5 html
6 {
6 {
7 height: 100%;
7 height: 100%;
8 }
8 }
9
9
10 body
10 body
11 {
11 {
12 margin: 0;
12 margin: 0;
13 padding: 0;
13 padding: 0;
14 height: 100%;
14 height: 100%;
15 background: #d1d1d1 url("../images/background.png") repeat;
15 background: #d1d1d1 url("../images/background.png") repeat;
16 font-family: Lucida Grande, Verdana, Lucida Sans Regular, Lucida Sans Unicode, Arial, sans-serif;
16 font-family: Lucida Grande, Verdana, Lucida Sans Regular, Lucida Sans Unicode, Arial, sans-serif;
17 font-size: 11px;
17 font-size: 11px;
18 }
18 }
19
19
20 /* -----------------------------------------------------------
20 /* -----------------------------------------------------------
21 images
21 images
22 ----------------------------------------------------------- */
22 ----------------------------------------------------------- */
23
23
24 img
24 img
25 {
25 {
26 border: none;
26 border: none;
27 }
27 }
28
28
29 img.icon{
29 img.icon{
30 vertical-align: bottom;
30 vertical-align: bottom;
31
31
32 }
32 }
33 /* -----------------------------------------------------------
33 /* -----------------------------------------------------------
34 anchors
34 anchors
35 ----------------------------------------------------------- */
35 ----------------------------------------------------------- */
36
36
37 a
37 a
38 {
38 {
39 color: #0066CC;
39 color: #0066CC;
40 text-decoration: none;
40 text-decoration: none;
41 cursor: pointer;
41 cursor: pointer;
42 }
42 }
43
43
44 a:hover
44 a:hover
45 {
45 {
46 color: #000000;
46 color: #000000;
47 text-decoration: underline;
47 text-decoration: underline;
48 }
48 }
49
49
50 /* -----------------------------------------------------------
50 /* -----------------------------------------------------------
51 headings
51 headings
52 ----------------------------------------------------------- */
52 ----------------------------------------------------------- */
53
53
54 h1, h2, h3, h4, h5, h6
54 h1, h2, h3, h4, h5, h6
55 {
55 {
56 color: #292929;
56 color: #292929;
57 font-weight: bold;
57 font-weight: bold;
58 }
58 }
59
59
60 h1
60 h1
61 {
61 {
62 font-size: 22px;
62 font-size: 22px;
63 }
63 }
64
64
65 h2
65 h2
66 {
66 {
67 font-size: 20px;
67 font-size: 20px;
68 }
68 }
69
69
70 h3
70 h3
71 {
71 {
72 font-size: 18px;
72 font-size: 18px;
73 }
73 }
74
74
75 h4
75 h4
76 {
76 {
77 font-size: 16px;
77 font-size: 16px;
78 }
78 }
79
79
80 h5
80 h5
81 {
81 {
82 font-size: 14px;
82 font-size: 14px;
83 }
83 }
84
84
85 h6
85 h6
86 {
86 {
87 font-size: 11px;
87 font-size: 11px;
88 }
88 }
89
89
90 /* -----------------------------------------------------------
90 /* -----------------------------------------------------------
91 lists
91 lists
92 ----------------------------------------------------------- */
92 ----------------------------------------------------------- */
93
93
94 ul.circle { list-style-type: circle; }
94 ul.circle { list-style-type: circle; }
95 ul.disc { list-style-type: disc; }
95 ul.disc { list-style-type: disc; }
96 ul.square { list-style-type: square; }
96 ul.square { list-style-type: square; }
97 ol.lower-roman { list-style-type: lower-roman; }
97 ol.lower-roman { list-style-type: lower-roman; }
98 ol.upper-roman { list-style-type: upper-roman; }
98 ol.upper-roman { list-style-type: upper-roman; }
99 ol.lower-alpha { list-style-type: lower-alpha; }
99 ol.lower-alpha { list-style-type: lower-alpha; }
100 ol.upper-alpha { list-style-type: upper-alpha; }
100 ol.upper-alpha { list-style-type: upper-alpha; }
101 ol.decimal { list-style-type: decimal; }
101 ol.decimal { list-style-type: decimal; }
102
102
103 /* -----------------------------------------------------------
103 /* -----------------------------------------------------------
104 colors
104 colors
105 ----------------------------------------------------------- */
105 ----------------------------------------------------------- */
106
106
107 div.color
107 div.color
108 {
108 {
109 margin: 7px 0 0 60px;
109 margin: 7px 0 0 60px;
110 padding: 1px 1px 1px 0px;
110 padding: 1px 1px 1px 0px;
111 clear: both;
111 clear: both;
112 overflow: hidden;
112 overflow: hidden;
113 position: absolute;
113 position: absolute;
114 background: #FFFFFF;
114 background: #FFFFFF;
115 }
115 }
116
116
117 div.color a
117 div.color a
118 {
118 {
119 margin: 0 0 0 1px;
119 margin: 0 0 0 1px;
120 padding: 0;
120 padding: 0;
121 width: 15px;
121 width: 15px;
122 height: 15px;
122 height: 15px;
123 display: block;
123 display: block;
124 float: left;
124 float: left;
125 }
125 }
126
126
127 div.color a.blue
127 div.color a.blue
128 {
128 {
129 background: #376ea6;
129 background: #376ea6;
130 }
130 }
131
131
132 div.color a.green
132 div.color a.green
133 {
133 {
134 background: #85924b;
134 background: #85924b;
135 }
135 }
136
136
137 div.color a.brown
137 div.color a.brown
138 {
138 {
139 background: #9b6e42;
139 background: #9b6e42;
140 }
140 }
141
141
142 div.color a.purple
142 div.color a.purple
143 {
143 {
144 background: #88528b;
144 background: #88528b;
145 }
145 }
146
146
147 div.color a.red
147 div.color a.red
148 {
148 {
149 background: #bd3220;
149 background: #bd3220;
150 }
150 }
151
151
152 div.color a.greyblue
152 div.color a.greyblue
153 {
153 {
154 background: #566e86;
154 background: #566e86;
155 }
155 }
156
156
157 /* -----------------------------------------------------------
157 /* -----------------------------------------------------------
158 options
158 options
159 ----------------------------------------------------------- */
159 ----------------------------------------------------------- */
160
160
161 div.options
161 div.options
162 {
162 {
163 margin: 7px 0 0 162px;
163 margin: 7px 0 0 162px;
164 padding: 0;
164 padding: 0;
165 clear: both;
165 clear: both;
166 overflow: hidden;
166 overflow: hidden;
167 position: absolute;
167 position: absolute;
168 background: #FFFFFF;
168 background: #FFFFFF;
169 }
169 }
170
170
171 div.options a
171 div.options a
172 {
172 {
173 margin: 0;
173 margin: 0;
174 padding: 3px 8px 3px 8px;
174 padding: 3px 8px 3px 8px;
175 height: 1%;
175 height: 1%;
176 display: block;
176 display: block;
177 text-decoration: none;
177 text-decoration: none;
178 }
178 }
179
179
180 div.options a:hover
180 div.options a:hover
181 {
181 {
182 text-decoration: none;
182 text-decoration: none;
183 }
183 }
184
184
185 /* -----------------------------------------------------------
185 /* -----------------------------------------------------------
186 header
186 header
187 ----------------------------------------------------------- */
187 ----------------------------------------------------------- */
188
188
189 #header
189 #header
190 {
190 {
191 margin: 0;
191 margin: 0;
192 padding: 0 30px 0 30px;
192 padding: 0 30px 0 30px;
193 background: #b0b0b0 url("../images/header_background.png") repeat;
193 background: #b0b0b0 url("../images/header_background.png") repeat;
194 }
194 }
195
195
196
196
197 /* -----------------------------------------------------------
197 /* -----------------------------------------------------------
198 header -> user
198 header -> user
199 ----------------------------------------------------------- */
199 ----------------------------------------------------------- */
200
200
201 #header ul#logged-user
201 #header ul#logged-user
202 {
202 {
203 margin: 0;
203 margin: 0;
204 padding: 0;
204 padding: 0;
205 float: right;
205 float: right;
206 }
206 }
207
207
208 #header ul#logged-user li
208 #header ul#logged-user li
209 {
209 {
210 margin: 0;
210 margin: 0;
211 padding: 10px 12px 10px 12px;
211 padding: 10px 12px 10px 12px;
212 list-style: none;
212 list-style: none;
213 float: left;
213 float: left;
214 border-left: 1px solid #bbbbbb;
214 border-left: 1px solid #bbbbbb;
215 border-right: 1px solid #a5a5a5;
215 border-right: 1px solid #a5a5a5;
216 }
216 }
217
217
218 #header ul#logged-user li.first
218 #header ul#logged-user li.first
219 {
219 {
220 border-left: none;
220 border-left: none;
221 margin:-6px;
221 margin:-6px;
222 }
222 }
223 #header ul#logged-user li.first div.account
223 #header ul#logged-user li.first div.account
224 {
224 {
225 padding-top: 4px;
225 padding-top: 4px;
226 float: left;
226 float: left;
227 }
227 }
228
228
229
229
230 #header ul#logged-user li.last
230 #header ul#logged-user li.last
231 {
231 {
232 border-right: none;
232 border-right: none;
233 }
233 }
234
234
235 #header ul#logged-user li a
235 #header ul#logged-user li a
236 {
236 {
237 color: #4e4e4e;
237 color: #4e4e4e;
238 font-weight: bold;
238 font-weight: bold;
239 text-decoration: none;
239 text-decoration: none;
240 }
240 }
241
241
242 #header ul#logged-user li a:hover
242 #header ul#logged-user li a:hover
243 {
243 {
244 color: #376ea6;
244 color: #376ea6;
245 text-decoration: underline;
245 text-decoration: underline;
246 }
246 }
247
247
248 #header ul#logged-user li.highlight a
248 #header ul#logged-user li.highlight a
249 {
249 {
250 color: #ffffff;
250 color: #ffffff;
251 }
251 }
252
252
253 #header ul#logged-user li.highlight a:hover
253 #header ul#logged-user li.highlight a:hover
254 {
254 {
255 color: #376ea6;
255 color: #376ea6;
256 }
256 }
257
257
258 #header #header-inner
258 #header #header-inner
259 {
259 {
260 margin: 0;
260 margin: 0;
261 padding: 0;
261 padding: 0;
262 height: 40px;
262 height: 40px;
263 clear: both;
263 clear: both;
264 position: relative;
264 position: relative;
265 background: #003367 url("../images/colors/blue/header_inner.png") repeat-x;
265 background: #003367 url("../images/colors/blue/header_inner.png") repeat-x;
266 border-bottom: 6px solid #ffffff;
266 border-bottom: 6px solid #ffffff;
267 }
267 }
268
268
269 /* -----------------------------------------------------------
269 /* -----------------------------------------------------------
270 header -> home
270 header -> home
271 ----------------------------------------------------------- */
271 ----------------------------------------------------------- */
272
272
273 #header #header-inner #home
273 #header #header-inner #home
274 {
274 {
275 float: left;
275 float: left;
276 }
276 }
277
277
278 #header #header-inner #home a
278 #header #header-inner #home a
279 {
279 {
280 margin: 0;
280 margin: 0;
281 padding: 0;
281 padding: 0;
282 height: 40px;
282 height: 40px;
283 width: 46px;
283 width: 46px;
284 display: block;
284 display: block;
285 background: url("../images/colors/blue/button_home.png");
285 background: url("../images/colors/blue/button_home.png");
286 background-position: 0 0;
286 background-position: 0 0;
287 }
287 }
288
288
289 #header #header-inner #home a:hover
289 #header #header-inner #home a:hover
290 {
290 {
291 background-position: 0 -40px;
291 background-position: 0 -40px;
292 }
292 }
293
293
294 /* -----------------------------------------------------------
294 /* -----------------------------------------------------------
295 header -> logo
295 header -> logo
296 ----------------------------------------------------------- */
296 ----------------------------------------------------------- */
297
297
298 #header #header-inner #logo
298 #header #header-inner #logo
299 {
299 {
300 float: left;
300 float: left;
301 }
301 }
302
302
303 #header #header-inner #logo h1
303 #header #header-inner #logo h1
304 {
304 {
305 margin: 13px 0 0 13px;
305 margin: 13px 0 0 13px;
306 padding: 0;
306 padding: 0;
307 color: #FFFFFF;
307 color: #FFFFFF;
308 font-size: 14px;
308 font-size: 14px;
309 text-transform: uppercase;
309 text-transform: uppercase;
310 }
310 }
311
311
312 #header #header-inner #logo a
312 #header #header-inner #logo a
313 {
313 {
314 color: #ffffff;
314 color: #ffffff;
315 text-decoration: none;
315 text-decoration: none;
316 }
316 }
317
317
318 #header #header-inner #logo a:hover
318 #header #header-inner #logo a:hover
319 {
319 {
320 color: #dabf29;
320 color: #dabf29;
321 }
321 }
322
322
323 /* -----------------------------------------------------------
323 /* -----------------------------------------------------------
324 header -> quick
324 header -> quick
325 ----------------------------------------------------------- */
325 ----------------------------------------------------------- */
326 #header #header-inner #quick,
326 #header #header-inner #quick,
327 #header #header-inner #quick ul
327 #header #header-inner #quick ul
328 {
328 {
329 margin: 10px 5px 0 0;
329 margin: 10px 5px 0 0;
330 padding: 0;
330 padding: 0;
331 position: relative;
331 position: relative;
332 float: right;
332 float: right;
333 list-style-type: none;
333 list-style-type: none;
334 list-style-position: outside;
334 list-style-position: outside;
335 }
335 }
336
336
337 #header #header-inner #quick li
337 #header #header-inner #quick li
338 {
338 {
339 margin: 0 5px 0 0;
339 margin: 0 5px 0 0;
340 padding: 0;
340 padding: 0;
341 position: relative;
341 position: relative;
342 float: left;
342 float: left;
343 }
343 }
344
344
345 #header #header-inner #quick li a
345 #header #header-inner #quick li a
346 {
346 {
347 top: 0;
347 top: 0;
348 left: 0;
348 left: 0;
349 padding: 0;
349 padding: 0;
350 height: 1%;
350 height: 1%;
351 display: block;
351 display: block;
352 clear: both;
352 clear: both;
353 overflow: hidden;
353 overflow: hidden;
354 background: #336699 url("../../resources/images/colors/blue/quick_l.png") no-repeat top left;
354 background: #336699 url("../../resources/images/colors/blue/quick_l.png") no-repeat top left;
355 color: #FFFFFF;
355 color: #FFFFFF;
356 font-weight: bold;
356 font-weight: bold;
357 text-decoration: none;
357 text-decoration: none;
358 }
358 }
359
359
360 #header #header-inner #quick li span
360 #header #header-inner #quick li span
361 {
361 {
362 top: 0;
362 top: 0;
363 right: 0;
363 right: 0;
364 margin: 0;
364 margin: 0;
365 padding: 10px 12px 8px 10px;
365 padding: 10px 12px 8px 10px;
366 height: 1%;
366 height: 1%;
367 display: block;
367 display: block;
368 float: left;
368 float: left;
369 background: url("../../resources/images/colors/blue/quick_r.png") no-repeat top right;
369 background: url("../../resources/images/colors/blue/quick_r.png") no-repeat top right;
370 border-left: 1px solid #3f6f9f;
370 border-left: 1px solid #3f6f9f;
371 }
371 }
372
372
373 #header #header-inner #quick li span.normal
373 #header #header-inner #quick li span.normal
374 {
374 {
375 padding: 10px 12px 8px 12px;
375 padding: 10px 12px 8px 12px;
376 border: none;
376 border: none;
377 }
377 }
378
378
379 #header #header-inner #quick li span.icon
379 #header #header-inner #quick li span.icon
380 {
380 {
381 top: 0;
381 top: 0;
382 left: 0;
382 left: 0;
383 padding: 8px 8px 4px 8px;
383 padding: 8px 8px 4px 8px;
384 background: url("../../resources/images/colors/blue/quick_l.png") no-repeat top left;
384 background: url("../../resources/images/colors/blue/quick_l.png") no-repeat top left;
385 border-left: none;
385 border-left: none;
386 border-right: 1px solid #2e5c89;
386 border-right: 1px solid #2e5c89;
387 }
387 }
388
388
389 #header #header-inner #quick li a:hover
389 #header #header-inner #quick li a:hover
390 {
390 {
391 background: #4e4e4e url("../../resources/images/colors/blue/quick_l_selected.png") no-repeat top left;
391 background: #4e4e4e url("../../resources/images/colors/blue/quick_l_selected.png") no-repeat top left;
392 }
392 }
393
393
394 #header #header-inner #quick li a:hover span
394 #header #header-inner #quick li a:hover span
395 {
395 {
396 background: url("../../resources/images/colors/blue/quick_r_selected.png") no-repeat top right;
396 background: url("../../resources/images/colors/blue/quick_r_selected.png") no-repeat top right;
397 border-left: 1px solid #545454;
397 border-left: 1px solid #545454;
398 }
398 }
399
399
400 #header #header-inner #quick li a:hover span.normal
400 #header #header-inner #quick li a:hover span.normal
401 {
401 {
402 border: none;
402 border: none;
403 }
403 }
404
404
405 #header #header-inner #quick li a:hover span.icon
405 #header #header-inner #quick li a:hover span.icon
406 {
406 {
407 background: url("../../resources/images/colors/blue/quick_l_selected.png") no-repeat top left;
407 background: url("../../resources/images/colors/blue/quick_l_selected.png") no-repeat top left;
408 border-left: none;
408 border-left: none;
409 border-right: 1px solid #464646;
409 border-right: 1px solid #464646;
410 }
410 }
411
411
412 #header #header-inner #quick ul
412 #header #header-inner #quick ul
413 {
413 {
414 top: 29px;
414 top: 29px;
415 right: 0;
415 right: 0;
416 margin: 0;
416 margin: 0;
417 padding: 0;
417 padding: 0;
418 width: 200px;
418 width: 200px;
419 display: none;
419 display: none;
420 position: absolute;
420 position: absolute;
421 background: #FFFFFF;
421 background: #FFFFFF;
422 border: 1px solid #666;
422 border: 1px solid #666;
423 border-top: 1px solid #003367;
423 border-top: 1px solid #003367;
424 z-index: 100;
424 z-index: 100;
425 }
425 }
426
426
427 #header #header-inner #quick ul.repo_switcher{
427 #header #header-inner #quick ul.repo_switcher{
428 max-height:275px;
428 max-height:275px;
429 overflow-x:hidden;
429 overflow-x:hidden;
430 overflow-y:auto;
430 overflow-y:auto;
431 white-space:nowrap;
431 white-space:nowrap;
432 }
432 }
433
433
434 #header #header-inner #quick li ul li
434 #header #header-inner #quick li ul li
435 {
435 {
436 border-bottom: 1px solid #dddddd;
436 border-bottom: 1px solid #dddddd;
437 }
437 }
438
438
439 #header #header-inner #quick li ul li.last
439 #header #header-inner #quick li ul li.last
440 {
440 {
441 border: none;
441 border: none;
442 }
442 }
443
443
444 #header #header-inner #quick li ul li a
444 #header #header-inner #quick li ul li a
445 {
445 {
446 margin: 0;
446 margin: 0;
447 padding: 7px 9px 7px 9px;
447 padding: 7px 9px 7px 9px;
448 height: 1%;
448 height: 1%;
449 width: 182px;
449 width: 182px;
450 height: auto;
450 height: auto;
451 display: block;
451 display: block;
452 float: left;
452 float: left;
453 background: #FFFFFF;
453 background: #FFFFFF;
454 color: #0066CC;
454 color: #0066CC;
455 font-weight: normal;
455 font-weight: normal;
456 }
456 }
457
457
458 #header #header-inner #quick li ul li a.childs
458 #header #header-inner #quick li ul li a.childs
459 {
459 {
460 margin: 0;
460 margin: 0;
461 padding: 7px 9px 7px 24px;
461 padding: 7px 9px 7px 24px;
462 width: 167px;
462 width: 167px;
463 background: #FFFFFF url("../../resources/images/plus.png") no-repeat 8px 9px;
463 background: #FFFFFF url("../../resources/images/plus.png") no-repeat 8px 9px;
464 }
464 }
465
465
466 #header #header-inner #quick li ul li a:hover
466 #header #header-inner #quick li ul li a:hover
467 {
467 {
468 color: #000000;
468 color: #000000;
469 background: #FFFFFF;
469 background: #FFFFFF;
470 }
470 }
471
471
472 #header #header-inner #quick li ul li a.childs:hover
472 #header #header-inner #quick li ul li a.childs:hover
473 {
473 {
474 background: #FFFFFF url("../../resources/images/minus.png") no-repeat 8px 9px;
474 background: #FFFFFF url("../../resources/images/minus.png") no-repeat 8px 9px;
475 }
475 }
476
476
477 #header #header-inner #quick ul ul
477 #header #header-inner #quick ul ul
478 {
478 {
479 top: auto;
479 top: auto;
480 }
480 }
481
481
482 #header #header-inner #quick li ul ul
482 #header #header-inner #quick li ul ul
483 {
483 {
484 right: 200px;
484 right: 200px;
485 max-height: 275px;
485 max-height: 275px;
486 overflow: auto;
486 overflow: auto;
487 overflow-x: hidden;
487 overflow-x: hidden;
488 white-space:nowrap;
488 white-space:nowrap;
489 }
489 }
490
490
491 #header #header-inner #quick li:hover ul ul,
491 #header #header-inner #quick li:hover ul ul,
492 #header #header-inner #quick li:hover ul ul ul,
492 #header #header-inner #quick li:hover ul ul ul,
493 #header #header-inner #quick li:hover ul ul ul ul
493 #header #header-inner #quick li:hover ul ul ul ul
494 {
494 {
495 display: none;
495 display: none;
496 }
496 }
497
497
498 #header #header-inner #quick li:hover ul,
498 #header #header-inner #quick li:hover ul,
499 #header #header-inner #quick li li:hover ul,
499 #header #header-inner #quick li li:hover ul,
500 #header #header-inner #quick li li li:hover ul,
500 #header #header-inner #quick li li li:hover ul,
501 #header #header-inner #quick li li li li:hover ul
501 #header #header-inner #quick li li li li:hover ul
502 {
502 {
503 display: block;
503 display: block;
504 }
504 }
505
505
506
506
507 /*ICONS*/
507 /*ICONS*/
508 #header #header-inner #quick li ul li a.journal,
509 #header #header-inner #quick li ul li a.journal:hover
510 {
511 background:url("../images/icons/book.png") no-repeat scroll 4px 9px #FFFFFF;
512 margin:0;
513 padding:12px 9px 7px 24px;
514 width:167px;
515
516 }
517 #header #header-inner #quick li ul li a.private_repo,
518 #header #header-inner #quick li ul li a.private_repo:hover
519 {
520 background:url("../images/icons/lock.png") no-repeat scroll 4px 9px #FFFFFF;
521 margin:0;
522 padding:12px 9px 7px 24px;
523 width:167px;
524
525 }
526 #header #header-inner #quick li ul li a.public_repo,
527 #header #header-inner #quick li ul li a.public_repo:hover
528 {
529 background:url("../images/icons/lock_open.png") no-repeat scroll 4px 9px #FFFFFF;
530 margin:0;
531 padding:12px 9px 7px 24px;
532 width:167px;
533
534 }
508
535
509 #header #header-inner #quick li ul li a.repos,
536 #header #header-inner #quick li ul li a.repos,
510 #header #header-inner #quick li ul li a.repos:hover
537 #header #header-inner #quick li ul li a.repos:hover
511 {
538 {
512 background:url("../images/icons/folder_edit.png") no-repeat scroll 4px 9px #FFFFFF;
539 background:url("../images/icons/folder_edit.png") no-repeat scroll 4px 9px #FFFFFF;
513 margin:0;
540 margin:0;
514 padding:12px 9px 7px 24px;
541 padding:12px 9px 7px 24px;
515 width:167px;
542 width:167px;
516
543
517 }
544 }
518 #header #header-inner #quick li ul li a.users,
545 #header #header-inner #quick li ul li a.users,
519 #header #header-inner #quick li ul li a.users:hover
546 #header #header-inner #quick li ul li a.users:hover
520 {
547 {
521 background: #FFFFFF url("../images/icons/user_edit.png") no-repeat 4px 9px;
548 background: #FFFFFF url("../images/icons/user_edit.png") no-repeat 4px 9px;
522 margin:0;
549 margin:0;
523 padding:12px 9px 7px 24px;
550 padding:12px 9px 7px 24px;
524 width:167px;
551 width:167px;
525 }
552 }
526 #header #header-inner #quick li ul li a.settings,
553 #header #header-inner #quick li ul li a.settings,
527 #header #header-inner #quick li ul li a.settings:hover
554 #header #header-inner #quick li ul li a.settings:hover
528 {
555 {
529 background: #FFFFFF url("../images/icons/cog.png") no-repeat 4px 9px;
556 background: #FFFFFF url("../images/icons/cog.png") no-repeat 4px 9px;
530 margin:0;
557 margin:0;
531 padding:12px 9px 7px 24px;
558 padding:12px 9px 7px 24px;
532 width:167px;
559 width:167px;
533 }
560 }
534
561
535 #header #header-inner #quick li ul li a.permissions,
562 #header #header-inner #quick li ul li a.permissions,
536 #header #header-inner #quick li ul li a.permissions:hover
563 #header #header-inner #quick li ul li a.permissions:hover
537 {
564 {
538
565
539 background: #FFFFFF url("../images/icons/key.png") no-repeat 4px 9px;
566 background: #FFFFFF url("../images/icons/key.png") no-repeat 4px 9px;
540 margin:0;
567 margin:0;
541 padding:12px 9px 7px 24px;
568 padding:12px 9px 7px 24px;
542 width:167px;
569 width:167px;
543 }
570 }
544
571
545 #header #header-inner #quick li ul li a.branches,#header #header-inner #quick li ul li a.branches:hover
572 #header #header-inner #quick li ul li a.branches,#header #header-inner #quick li ul li a.branches:hover
546 {
573 {
547
574
548 background: #FFFFFF url("../images/icons/arrow_branch.png") no-repeat 4px 9px;
575 background: #FFFFFF url("../images/icons/arrow_branch.png") no-repeat 4px 9px;
549 margin:0;
576 margin:0;
550 padding:12px 9px 7px 24px;
577 padding:12px 9px 7px 24px;
551 width:167px;
578 width:167px;
552 }
579 }
553
580
554 #header #header-inner #quick li ul li a.tags,#header #header-inner #quick li ul li a.tags:hover
581 #header #header-inner #quick li ul li a.tags,#header #header-inner #quick li ul li a.tags:hover
555 {
582 {
556
583
557 background: #FFFFFF url("../images/icons/tag_blue.png") no-repeat 4px 9px;
584 background: #FFFFFF url("../images/icons/tag_blue.png") no-repeat 4px 9px;
558 margin:0;
585 margin:0;
559 padding:12px 9px 7px 24px;
586 padding:12px 9px 7px 24px;
560 width:167px;
587 width:167px;
561 }
588 }
562 /* -----------------------------------------------------------
589 /* -----------------------------------------------------------
563 header corners
590 header corners
564 ----------------------------------------------------------- */
591 ----------------------------------------------------------- */
565
592
566 #header #header-inner div.corner
593 #header #header-inner div.corner
567 {
594 {
568 height: 6px;
595 height: 6px;
569 width: 6px;
596 width: 6px;
570 position: absolute;
597 position: absolute;
571 background: url("../images/colors/blue/header_inner_corners.png") no-repeat;
598 background: url("../images/colors/blue/header_inner_corners.png") no-repeat;
572 }
599 }
573
600
574 #header #header-inner div.tl
601 #header #header-inner div.tl
575 {
602 {
576 top: 0;
603 top: 0;
577 left: 0;
604 left: 0;
578 background-position: 0 0;
605 background-position: 0 0;
579 }
606 }
580
607
581 #header #header-inner div.tr
608 #header #header-inner div.tr
582 {
609 {
583 top: 0;
610 top: 0;
584 right: 0;
611 right: 0;
585 background-position: -6px 0;
612 background-position: -6px 0;
586 }
613 }
587
614
588 /* -----------------------------------------------------------
615 /* -----------------------------------------------------------
589 content
616 content
590 ----------------------------------------------------------- */
617 ----------------------------------------------------------- */
591
618
592 #content
619 #content
593 {
620 {
594 margin: 10px 0 0 0;
621 margin: 10px 0 0 0;
595 padding: 0;
622 padding: 0;
596 min-height: 100%;
623 min-height: 100%;
597 clear: both;
624 clear: both;
598 overflow: hidden;
625 overflow: hidden;
599 background: url("../images/content.png") repeat-y top left;
626 background: url("../images/content.png") repeat-y top left;
600 }
627 }
601
628
602 /* -----------------------------------------------------------
629 /* -----------------------------------------------------------
603 content -> left
630 content -> left
604 ----------------------------------------------------------- */
631 ----------------------------------------------------------- */
605
632
606 #content #left
633 #content #left
607 {
634 {
608 left: 0;
635 left: 0;
609 width: 280px;
636 width: 280px;
610 position: absolute;
637 position: absolute;
611 }
638 }
612
639
613 /* -----------------------------------------------------------
640 /* -----------------------------------------------------------
614 content -> left -> menu
641 content -> left -> menu
615 ----------------------------------------------------------- */
642 ----------------------------------------------------------- */
616
643
617 #content #left #menu
644 #content #left #menu
618 {
645 {
619 margin: 5px 10px 0 60px;
646 margin: 5px 10px 0 60px;
620 padding: 0;
647 padding: 0;
621 clear: both;
648 clear: both;
622 overflow: hidden;
649 overflow: hidden;
623 }
650 }
624
651
625 /* -----------------------------------------------------------
652 /* -----------------------------------------------------------
626 content -> left -> menu / heading
653 content -> left -> menu / heading
627 ----------------------------------------------------------- */
654 ----------------------------------------------------------- */
628
655
629 #content #left #menu h6
656 #content #left #menu h6
630 {
657 {
631 margin: 5px 0 0 0;
658 margin: 5px 0 0 0;
632 padding: 0;
659 padding: 0;
633 clear: both;
660 clear: both;
634 overflow: hidden;
661 overflow: hidden;
635 background: #dfdfdf url("../images/menu.png") repeat-x;
662 background: #dfdfdf url("../images/menu.png") repeat-x;
636 color: #6e6e6e;
663 color: #6e6e6e;
637 }
664 }
638
665
639 #content #left #menu h6 a
666 #content #left #menu h6 a
640 {
667 {
641 margin: 0;
668 margin: 0;
642 padding: 0;
669 padding: 0;
643 height: 1%;
670 height: 1%;
644 display: block;
671 display: block;
645 clear: both;
672 clear: both;
646 overflow: hidden;
673 overflow: hidden;
647 background: url("../images/menu_l.png") no-repeat top left;
674 background: url("../images/menu_l.png") no-repeat top left;
648 color: #6e6e6e;
675 color: #6e6e6e;
649 text-decoration: none;
676 text-decoration: none;
650 }
677 }
651
678
652 #content #left #menu h6 span
679 #content #left #menu h6 span
653 {
680 {
654 margin: 0;
681 margin: 0;
655 padding: 9px 10px 10px 10px;
682 padding: 9px 10px 10px 10px;
656 height: 1%;
683 height: 1%;
657 display: block;
684 display: block;
658 background: url("../images/menu_r.png") no-repeat top right;
685 background: url("../images/menu_r.png") no-repeat top right;
659 }
686 }
660
687
661 #content #left #menu h6.selected
688 #content #left #menu h6.selected
662 {
689 {
663 background: #00376e url("../images/colors/blue/menu_selected.png") repeat-x;
690 background: #00376e url("../images/colors/blue/menu_selected.png") repeat-x;
664 color: #FFFFFF;
691 color: #FFFFFF;
665 }
692 }
666
693
667 #content #left #menu h6.selected a
694 #content #left #menu h6.selected a
668 {
695 {
669 background: url("../images/colors/blue/menu_l_selected.png") no-repeat top left;
696 background: url("../images/colors/blue/menu_l_selected.png") no-repeat top left;
670 color: #ffffff;
697 color: #ffffff;
671 }
698 }
672
699
673 #content #left #menu h6.selected span
700 #content #left #menu h6.selected span
674 {
701 {
675 background: url("../images/colors/blue/menu_r_selected.png") no-repeat top right;
702 background: url("../images/colors/blue/menu_r_selected.png") no-repeat top right;
676 }
703 }
677
704
678 /* -----------------------------------------------------------
705 /* -----------------------------------------------------------
679 content -> left -> menu / links
706 content -> left -> menu / links
680 ----------------------------------------------------------- */
707 ----------------------------------------------------------- */
681
708
682 #content #left #menu ul
709 #content #left #menu ul
683 {
710 {
684 margin: 0;
711 margin: 0;
685 padding: 0;
712 padding: 0;
686 background: #376ea6;
713 background: #376ea6;
687 }
714 }
688
715
689 #content #left #menu ul.opened
716 #content #left #menu ul.opened
690 {
717 {
691 display: block;
718 display: block;
692 }
719 }
693
720
694 #content #left #menu ul.closed
721 #content #left #menu ul.closed
695 {
722 {
696 display: none;
723 display: none;
697 }
724 }
698
725
699 #content #left #menu li
726 #content #left #menu li
700 {
727 {
701 margin: 0;
728 margin: 0;
702 padding: 0;
729 padding: 0;
703 clear: both;
730 clear: both;
704 overflow: hidden;
731 overflow: hidden;
705 list-style: none;
732 list-style: none;
706 border-bottom: 1px solid #5f8bb7;
733 border-bottom: 1px solid #5f8bb7;
707 color: #ffffff;
734 color: #ffffff;
708 }
735 }
709
736
710 #content #left #menu li a
737 #content #left #menu li a
711 {
738 {
712 margin: 0 0 0 6px;
739 margin: 0 0 0 6px;
713 padding: 8px 0 8px 18px;
740 padding: 8px 0 8px 18px;
714 height: 1%;
741 height: 1%;
715 display: block;
742 display: block;
716 float: left;
743 float: left;
717 background: url("../images/colors/colors/blue/menu_arrow.png") no-repeat 0 9px;
744 background: url("../images/colors/colors/blue/menu_arrow.png") no-repeat 0 9px;
718 color: #ffffff;
745 color: #ffffff;
719 text-decoration: none;
746 text-decoration: none;
720 }
747 }
721
748
722 #content #left #menu li a:hover
749 #content #left #menu li a:hover
723 {
750 {
724 color: #b9dcff;
751 color: #b9dcff;
725 }
752 }
726
753
727 /* -----------------------------------------------------------
754 /* -----------------------------------------------------------
728 content -> left -> menu / collapsible
755 content -> left -> menu / collapsible
729 ----------------------------------------------------------- */
756 ----------------------------------------------------------- */
730
757
731 #content #left #menu li.collapsible
758 #content #left #menu li.collapsible
732 {
759 {
733 background: url("../images/colors/blue/menu_border.png") no-repeat top left;
760 background: url("../images/colors/blue/menu_border.png") no-repeat top left;
734 }
761 }
735
762
736 #content #left #menu li.collapsible a
763 #content #left #menu li.collapsible a
737 {
764 {
738 margin: 0 0 0 6px;
765 margin: 0 0 0 6px;
739 padding: 8px 0 8px 0;
766 padding: 8px 0 8px 0;
740 height: 1%;
767 height: 1%;
741 display: block;
768 display: block;
742 background: transparent;
769 background: transparent;
743 float: left;
770 float: left;
744 font-weight: bold;
771 font-weight: bold;
745 }
772 }
746
773
747 #content #left #menu li.collapsible a.plus
774 #content #left #menu li.collapsible a.plus
748 {
775 {
749 margin: 0;
776 margin: 0;
750 padding: 8px 0 9px 24px;
777 padding: 8px 0 9px 24px;
751 height: 10px;
778 height: 10px;
752 width: 10px;
779 width: 10px;
753 display: block;
780 display: block;
754 float: left;
781 float: left;
755 background: url("../images/menu_plus.png") no-repeat 5px 10px;
782 background: url("../images/menu_plus.png") no-repeat 5px 10px;
756 border: none;
783 border: none;
757 }
784 }
758
785
759 #content #left #menu li.collapsible a.minus
786 #content #left #menu li.collapsible a.minus
760 {
787 {
761 margin: 0;
788 margin: 0;
762 padding: 8px 0 9px 24px;
789 padding: 8px 0 9px 24px;
763 height: 10px;
790 height: 10px;
764 width: 10px;
791 width: 10px;
765 display: block;
792 display: block;
766 float: left;
793 float: left;
767 background: url("../images/menu_minus.png") no-repeat 5px 10px;
794 background: url("../images/menu_minus.png") no-repeat 5px 10px;
768 border: none;
795 border: none;
769 }
796 }
770
797
771 #content #left #menu li ul
798 #content #left #menu li ul
772 {
799 {
773 margin: 0;
800 margin: 0;
774 padding: 0;
801 padding: 0;
775 border-left: 18px solid #285889;
802 border-left: 18px solid #285889;
776 }
803 }
777
804
778 #content #left #menu li ul.expanded
805 #content #left #menu li ul.expanded
779 {
806 {
780 display: block;
807 display: block;
781 }
808 }
782
809
783 #content #left #menu li ul.collapsed
810 #content #left #menu li ul.collapsed
784 {
811 {
785 display: none;
812 display: none;
786 }
813 }
787
814
788 #content #left #menu li ul li
815 #content #left #menu li ul li
789 {
816 {
790 margin: 0;
817 margin: 0;
791 padding: 0;
818 padding: 0;
792 clear: both;
819 clear: both;
793 overflow: hidden;
820 overflow: hidden;
794 list-style: none;
821 list-style: none;
795 border-bottom: 1px solid #5f8bb7;
822 border-bottom: 1px solid #5f8bb7;
796 color: #ffffff;
823 color: #ffffff;
797 }
824 }
798
825
799 #content #left #menu li.collapsible ul li a
826 #content #left #menu li.collapsible ul li a
800 {
827 {
801 font-weight: normal;
828 font-weight: normal;
802 }
829 }
803
830
804 #content #left #menu li.last
831 #content #left #menu li.last
805 {
832 {
806 border-bottom: none;
833 border-bottom: none;
807 }
834 }
808
835
809 /* -----------------------------------------------------------
836 /* -----------------------------------------------------------
810 content -> left -> date picker
837 content -> left -> date picker
811 ----------------------------------------------------------- */
838 ----------------------------------------------------------- */
812
839
813 #content #left #date-picker
840 #content #left #date-picker
814 {
841 {
815 margin: 10px 10px 0 60px;
842 margin: 10px 10px 0 60px;
816 padding: 0;
843 padding: 0;
817 clear: both;
844 clear: both;
818 overflow: hidden;
845 overflow: hidden;
819 }
846 }
820
847
821 #content #left #date-picker .ui-datepicker
848 #content #left #date-picker .ui-datepicker
822 {
849 {
823 width: auto;
850 width: auto;
824 padding: 0;
851 padding: 0;
825 clear: both;
852 clear: both;
826 overflow: hidden;
853 overflow: hidden;
827 background: #FFFFFF;
854 background: #FFFFFF;
828 border: 1px solid #d1d1d1;
855 border: 1px solid #d1d1d1;
829 }
856 }
830
857
831 #content #left #date-picker .ui-datepicker .ui-datepicker-header
858 #content #left #date-picker .ui-datepicker .ui-datepicker-header
832 {
859 {
833 padding: 5px 0;
860 padding: 5px 0;
834 }
861 }
835
862
836 #content #left #date-picker .ui-datepicker .ui-datepicker-prev
863 #content #left #date-picker .ui-datepicker .ui-datepicker-prev
837 {
864 {
838 top: 5px;
865 top: 5px;
839 left: 4px;
866 left: 4px;
840 }
867 }
841
868
842 #content #left #date-picker .ui-datepicker .ui-datepicker-next
869 #content #left #date-picker .ui-datepicker .ui-datepicker-next
843 {
870 {
844 top: 5px;
871 top: 5px;
845 right: 4px;
872 right: 4px;
846 }
873 }
847
874
848 #content #left #date-picker .ui-datepicker .ui-datepicker-prev-hover
875 #content #left #date-picker .ui-datepicker .ui-datepicker-prev-hover
849 {
876 {
850 top: 5px;
877 top: 5px;
851 left: 4px;
878 left: 4px;
852 }
879 }
853
880
854 #content #left #date-picker .ui-datepicker .ui-datepicker-next-hover
881 #content #left #date-picker .ui-datepicker .ui-datepicker-next-hover
855 {
882 {
856 top: 5px;
883 top: 5px;
857 right: 4px;
884 right: 4px;
858 }
885 }
859
886
860 /* -----------------------------------------------------------
887 /* -----------------------------------------------------------
861 content -> right
888 content -> right
862 ----------------------------------------------------------- */
889 ----------------------------------------------------------- */
863
890
864 #content #right
891 #content #right
865 {
892 {
866 margin: 0 60px 10px 290px;
893 margin: 0 60px 10px 290px;
867 }
894 }
868
895
869 /* -----------------------------------------------------------
896 /* -----------------------------------------------------------
870 content -> right -> box
897 content -> right -> box
871 ----------------------------------------------------------- */
898 ----------------------------------------------------------- */
872
899
873 #content div.box
900 #content div.box
874 {
901 {
875 margin: 0 0 10px 0;
902 margin: 0 0 10px 0;
876 padding: 0 0 10px 0;
903 padding: 0 0 10px 0;
877 clear: both;
904 clear: both;
878 overflow: hidden;
905 overflow: hidden;
879 background: #ffffff;
906 background: #ffffff;
880 }
907 }
881
908
882 #content div.box-left
909 #content div.box-left
883 {
910 {
884 margin: 0 0 10px;
911 margin: 0 0 10px;
885 width: 49%;
912 width: 49%;
886 clear: none;
913 clear: none;
887 float: left;
914 float: left;
888 }
915 }
889
916
890 #content div.box-right
917 #content div.box-right
891 {
918 {
892 margin: 0 0 10px;
919 margin: 0 0 10px;
893 width: 49%;
920 width: 49%;
894 clear: none;
921 clear: none;
895 float: right;
922 float: right;
896 }
923 }
897
924
898 /* -----------------------------------------------------------
925 /* -----------------------------------------------------------
899 content -> right -> box / title
926 content -> right -> box / title
900 ----------------------------------------------------------- */
927 ----------------------------------------------------------- */
901
928
902 #content div.box div.title
929 #content div.box div.title
903 {
930 {
904 margin: 0 0 20px 0;
931 margin: 0 0 20px 0;
905 padding: 0;
932 padding: 0;
906 clear: both;
933 clear: both;
907 overflow: hidden;
934 overflow: hidden;
908 background: #336699 url("../images/colors/blue/title.png") repeat-x;
935 background: #336699 url("../images/colors/blue/title.png") repeat-x;
909 }
936 }
910
937
911 #content div.box div.title h5
938 #content div.box div.title h5
912 {
939 {
913 margin: 0;
940 margin: 0;
914 padding: 11px 0 11px 10px;
941 padding: 11px 0 11px 10px;
915 float: left;
942 float: left;
916 border: none;
943 border: none;
917 color: #ffffff;
944 color: #ffffff;
918 text-transform: uppercase;
945 text-transform: uppercase;
919 }
946 }
920
947
921 #content div.box div.title ul.links
948 #content div.box div.title ul.links
922 {
949 {
923 margin: 0;
950 margin: 0;
924 padding: 0;
951 padding: 0;
925 float: right;
952 float: right;
926 }
953 }
927
954
928 #content div.box div.title ul.links li
955 #content div.box div.title ul.links li
929 {
956 {
930 margin: 0;
957 margin: 0;
931 padding: 0;
958 padding: 0;
932 list-style: none;
959 list-style: none;
933 float: left;
960 float: left;
934 }
961 }
935
962
936 #content div.box div.title ul.links li a
963 #content div.box div.title ul.links li a
937 {
964 {
938 margin: 0;
965 margin: 0;
939 padding: 13px 16px 12px 16px;
966 padding: 13px 16px 12px 16px;
940 height: 1%;
967 height: 1%;
941 display: block;
968 display: block;
942 float: left;
969 float: left;
943 background: url("../images/colors/blue/title_link.png") no-repeat top left;
970 background: url("../images/colors/blue/title_link.png") no-repeat top left;
944 border-left: 1px solid #316293;
971 border-left: 1px solid #316293;
945 color: #ffffff;
972 color: #ffffff;
946 font-size: 11px;
973 font-size: 11px;
947 font-weight: bold;
974 font-weight: bold;
948 text-decoration: none;
975 text-decoration: none;
949 }
976 }
950
977
951 #content div.box div.title ul.links li a:hover
978 #content div.box div.title ul.links li a:hover
952 {
979 {
953 color: #bfe3ff;
980 color: #bfe3ff;
954 }
981 }
955
982
956 #content div.box div.title ul.links li.ui-tabs-selected a
983 #content div.box div.title ul.links li.ui-tabs-selected a
957 {
984 {
958 background: url("../../../resources/images/colors/blue/title_tab_selected.png") no-repeat bottom center;
985 background: url("../../../resources/images/colors/blue/title_tab_selected.png") no-repeat bottom center;
959 color: #bfe3ff;
986 color: #bfe3ff;
960 }
987 }
961
988
962 /* -----------------------------------------------------------
989 /* -----------------------------------------------------------
963 content -> right -> box / headings
990 content -> right -> box / headings
964 ----------------------------------------------------------- */
991 ----------------------------------------------------------- */
965
992
966 #content div.box h1,
993 #content div.box h1,
967 #content div.box h2,
994 #content div.box h2,
968 #content div.box h3,
995 #content div.box h3,
969 #content div.box h4,
996 #content div.box h4,
970 #content div.box h5,
997 #content div.box h5,
971 #content div.box h6
998 #content div.box h6
972 {
999 {
973 margin: 10px 20px 10px 20px;
1000 margin: 10px 20px 10px 20px;
974 padding: 0 0 15px 0;
1001 padding: 0 0 15px 0;
975 clear: both;
1002 clear: both;
976 overflow: hidden;
1003 overflow: hidden;
977 border-bottom: 1px solid #DDDDDD;
1004 border-bottom: 1px solid #DDDDDD;
978 }
1005 }
979
1006
980 /* -----------------------------------------------------------
1007 /* -----------------------------------------------------------
981 content -> right -> box / paragraphs
1008 content -> right -> box / paragraphs
982 ----------------------------------------------------------- */
1009 ----------------------------------------------------------- */
983
1010
984 #content div.box p
1011 #content div.box p
985 {
1012 {
986 margin: 0 24px 10px 24px;
1013 margin: 0 24px 10px 24px;
987 padding: 0;
1014 padding: 0;
988 color: #5f5f5f;
1015 color: #5f5f5f;
989 font-size: 12px;
1016 font-size: 12px;
990 line-height: 150%;
1017 line-height: 150%;
991 }
1018 }
992
1019
993 #content div.box blockquote
1020 #content div.box blockquote
994 {
1021 {
995 margin: 0 34px 0 34px;
1022 margin: 0 34px 0 34px;
996 padding: 0 0 0 14px;
1023 padding: 0 0 0 14px;
997 border-left: 4px solid #DDDDDD;
1024 border-left: 4px solid #DDDDDD;
998 color: #5f5f5f;
1025 color: #5f5f5f;
999 font-size: 11px;
1026 font-size: 11px;
1000 line-height: 150%;
1027 line-height: 150%;
1001 }
1028 }
1002
1029
1003 #content div.box blockquote p
1030 #content div.box blockquote p
1004 {
1031 {
1005 margin: 10px 0 10px 0;
1032 margin: 10px 0 10px 0;
1006 padding: 0;
1033 padding: 0;
1007 }
1034 }
1008
1035
1009 /* -----------------------------------------------------------
1036 /* -----------------------------------------------------------
1010 content -> right -> box / lists
1037 content -> right -> box / lists
1011 ----------------------------------------------------------- */
1038 ----------------------------------------------------------- */
1012
1039
1013 #content div.box dl
1040 #content div.box dl
1014 {
1041 {
1015 margin: 10px 24px 10px 24px;
1042 margin: 10px 24px 10px 24px;
1016 }
1043 }
1017
1044
1018 #content div.box dt
1045 #content div.box dt
1019 {
1046 {
1020 margin: 0;
1047 margin: 0;
1021 font-size: 12px;
1048 font-size: 12px;
1022 }
1049 }
1023
1050
1024 #content div.box dd
1051 #content div.box dd
1025 {
1052 {
1026 margin: 0;
1053 margin: 0;
1027 padding: 8px 0 8px 15px;
1054 padding: 8px 0 8px 15px;
1028 font-size: 12px;
1055 font-size: 12px;
1029 }
1056 }
1030
1057
1031 #content div.box ul.left
1058 #content div.box ul.left
1032 {
1059 {
1033 float: left;
1060 float: left;
1034 }
1061 }
1035
1062
1036 #content div.box ol.left
1063 #content div.box ol.left
1037 {
1064 {
1038 float: left;
1065 float: left;
1039 }
1066 }
1040
1067
1041 #content div.box li
1068 #content div.box li
1042 {
1069 {
1043 padding: 4px 0 4px 0;
1070 padding: 4px 0 4px 0;
1044 font-size: 12px;
1071 font-size: 12px;
1045 }
1072 }
1046
1073
1047 #content div.box ol.lower-roman,
1074 #content div.box ol.lower-roman,
1048 #content div.box ol.upper-roman
1075 #content div.box ol.upper-roman
1049 {
1076 {
1050 margin: 10px 24px 10px 44px;
1077 margin: 10px 24px 10px 44px;
1051 }
1078 }
1052
1079
1053 #content div.box ol.lower-alpha,
1080 #content div.box ol.lower-alpha,
1054 #content div.box ol.upper-alpha
1081 #content div.box ol.upper-alpha
1055 {
1082 {
1056 margin: 10px 24px 10px 44px;
1083 margin: 10px 24px 10px 44px;
1057 }
1084 }
1058
1085
1059 #content div.box ol.decimal
1086 #content div.box ol.decimal
1060 {
1087 {
1061 margin: 10px 24px 10px 44px;
1088 margin: 10px 24px 10px 44px;
1062 }
1089 }
1063
1090
1064 #content div.box ul.disc,
1091 #content div.box ul.disc,
1065 #content div.box ul.circle
1092 #content div.box ul.circle
1066 {
1093 {
1067 margin: 10px 24px 10px 38px;
1094 margin: 10px 24px 10px 38px;
1068 }
1095 }
1069
1096
1070 #content div.box ul.square
1097 #content div.box ul.square
1071 {
1098 {
1072 margin: 10px 24px 10px 40px;
1099 margin: 10px 24px 10px 40px;
1073 }
1100 }
1074
1101
1075 /* -----------------------------------------------------------
1102 /* -----------------------------------------------------------
1076 content -> right -> box / images
1103 content -> right -> box / images
1077 ----------------------------------------------------------- */
1104 ----------------------------------------------------------- */
1078
1105
1079 #content div.box img.left
1106 #content div.box img.left
1080 {
1107 {
1081 margin: 10px 10px 10px 0;
1108 margin: 10px 10px 10px 0;
1082 border: none;
1109 border: none;
1083 float: left;
1110 float: left;
1084 }
1111 }
1085
1112
1086 #content div.box img.right
1113 #content div.box img.right
1087 {
1114 {
1088 margin: 10px 0 10px 10px;
1115 margin: 10px 0 10px 10px;
1089 border: none;
1116 border: none;
1090 float: right;
1117 float: right;
1091 }
1118 }
1092
1119
1093 /* -----------------------------------------------------------
1120 /* -----------------------------------------------------------
1094 content -> right -> box / messages
1121 content -> right -> box / messages
1095 ----------------------------------------------------------- */
1122 ----------------------------------------------------------- */
1096
1123
1097 #content div.box div.messages
1124 #content div.box div.messages
1098 {
1125 {
1099 margin: 0 20px 0 20px;
1126 margin: 0 20px 0 20px;
1100 padding: 0;
1127 padding: 0;
1101 clear: both;
1128 clear: both;
1102 overflow: hidden;
1129 overflow: hidden;
1103 }
1130 }
1104
1131
1105 #content div.box div.message
1132 #content div.box div.message
1106 {
1133 {
1107 margin: 0 0 0px 0;
1134 margin: 0 0 0px 0;
1108 padding: 0 0 10px 0;
1135 padding: 0 0 10px 0;
1109 clear: both;
1136 clear: both;
1110 overflow: hidden;
1137 overflow: hidden;
1111 }
1138 }
1112
1139
1113 #content div.box div.message div.image
1140 #content div.box div.message div.image
1114 {
1141 {
1115 margin: 9px 0 0 5px;
1142 margin: 9px 0 0 5px;
1116 padding: 6px;
1143 padding: 6px;
1117 float: left;
1144 float: left;
1118 }
1145 }
1119
1146
1120 #content div.box div.message div.image img
1147 #content div.box div.message div.image img
1121 {
1148 {
1122 margin: 0;
1149 margin: 0;
1123 vertical-align: middle;
1150 vertical-align: middle;
1124 }
1151 }
1125
1152
1126 #content div.box div.message div.text
1153 #content div.box div.message div.text
1127 {
1154 {
1128 margin: 0;
1155 margin: 0;
1129 padding: 9px 6px 9px 6px;
1156 padding: 9px 6px 9px 6px;
1130 float: left;
1157 float: left;
1131 }
1158 }
1132
1159
1133 #content div.box div.message div.dismiss
1160 #content div.box div.message div.dismiss
1134 {
1161 {
1135 margin: 0;
1162 margin: 0;
1136 padding: 0;
1163 padding: 0;
1137 float: right;
1164 float: right;
1138 }
1165 }
1139
1166
1140 #content div.box div.message div.dismiss a
1167 #content div.box div.message div.dismiss a
1141 {
1168 {
1142 margin: 15px 14px 0 0;
1169 margin: 15px 14px 0 0;
1143 padding: 0;
1170 padding: 0;
1144 height: 16px;
1171 height: 16px;
1145 width: 16px;
1172 width: 16px;
1146 display: block;
1173 display: block;
1147 background: url("../images/icons/cross.png") no-repeat;
1174 background: url("../images/icons/cross.png") no-repeat;
1148 }
1175 }
1149
1176
1150 #content div.box div.message div.text h1,
1177 #content div.box div.message div.text h1,
1151 #content div.box div.message div.text h2,
1178 #content div.box div.message div.text h2,
1152 #content div.box div.message div.text h3,
1179 #content div.box div.message div.text h3,
1153 #content div.box div.message div.text h4,
1180 #content div.box div.message div.text h4,
1154 #content div.box div.message div.text h5,
1181 #content div.box div.message div.text h5,
1155 #content div.box div.message div.text h6
1182 #content div.box div.message div.text h6
1156 {
1183 {
1157 margin: 0;
1184 margin: 0;
1158 padding: 0px;
1185 padding: 0px;
1159 border: none;
1186 border: none;
1160 }
1187 }
1161
1188
1162 #content div.box div.message div.text span
1189 #content div.box div.message div.text span
1163 {
1190 {
1164 margin: 0;
1191 margin: 0;
1165 padding: 5px 0 0 0;
1192 padding: 5px 0 0 0;
1166 height: 1%;
1193 height: 1%;
1167 display: block;
1194 display: block;
1168 }
1195 }
1169
1196
1170 #content div.box div.message-error
1197 #content div.box div.message-error
1171 {
1198 {
1172 height: 1%;
1199 height: 1%;
1173 clear: both;
1200 clear: both;
1174 overflow: hidden;
1201 overflow: hidden;
1175 background: #FBE3E4;
1202 background: #FBE3E4;
1176 border: 1px solid #FBC2C4;
1203 border: 1px solid #FBC2C4;
1177 color: #860006;
1204 color: #860006;
1178 }
1205 }
1179
1206
1180 #content div.box div.message-error h6
1207 #content div.box div.message-error h6
1181 {
1208 {
1182 color: #860006;
1209 color: #860006;
1183 }
1210 }
1184
1211
1185 #content div.box div.message-warning
1212 #content div.box div.message-warning
1186 {
1213 {
1187 height: 1%;
1214 height: 1%;
1188 clear: both;
1215 clear: both;
1189 overflow: hidden;
1216 overflow: hidden;
1190 background: #FFF6BF;
1217 background: #FFF6BF;
1191 border: 1px solid #FFD324;
1218 border: 1px solid #FFD324;
1192 color: #5f5200;
1219 color: #5f5200;
1193 }
1220 }
1194
1221
1195 #content div.box div.message-warning h6
1222 #content div.box div.message-warning h6
1196 {
1223 {
1197 color: #5f5200;
1224 color: #5f5200;
1198 }
1225 }
1199
1226
1200 #content div.box div.message-notice
1227 #content div.box div.message-notice
1201 {
1228 {
1202 height: 1%;
1229 height: 1%;
1203 clear: both;
1230 clear: both;
1204 overflow: hidden;
1231 overflow: hidden;
1205 background: #8FBDE0;
1232 background: #8FBDE0;
1206 border: 1px solid #6BACDE;
1233 border: 1px solid #6BACDE;
1207 color: #003863;
1234 color: #003863;
1208 }
1235 }
1209
1236
1210 #content div.box div.message-notice h6
1237 #content div.box div.message-notice h6
1211 {
1238 {
1212 color: #003863;
1239 color: #003863;
1213 }
1240 }
1214
1241
1215 #content div.box div.message-success
1242 #content div.box div.message-success
1216 {
1243 {
1217 height: 1%;
1244 height: 1%;
1218 clear: both;
1245 clear: both;
1219 overflow: hidden;
1246 overflow: hidden;
1220 background: #E6EFC2;
1247 background: #E6EFC2;
1221 border: 1px solid #C6D880;
1248 border: 1px solid #C6D880;
1222 color: #4e6100;
1249 color: #4e6100;
1223 }
1250 }
1224
1251
1225 #content div.box div.message-success h6
1252 #content div.box div.message-success h6
1226 {
1253 {
1227 color: #4e6100;
1254 color: #4e6100;
1228 }
1255 }
1229
1256
1230 /* -----------------------------------------------------------
1257 /* -----------------------------------------------------------
1231 content -> right -> box / forms
1258 content -> right -> box / forms
1232 ----------------------------------------------------------- */
1259 ----------------------------------------------------------- */
1233
1260
1234 #content div.box div.form
1261 #content div.box div.form
1235 {
1262 {
1236 margin: 0;
1263 margin: 0;
1237 padding: 0 20px 10px 20px;
1264 padding: 0 20px 10px 20px;
1238 clear: both;
1265 clear: both;
1239 overflow: hidden;
1266 overflow: hidden;
1240 }
1267 }
1241
1268
1242 #content div.box div.form div.fields
1269 #content div.box div.form div.fields
1243 {
1270 {
1244 margin: 0;
1271 margin: 0;
1245 padding: 0;
1272 padding: 0;
1246 clear: both;
1273 clear: both;
1247 overflow: hidden;
1274 overflow: hidden;
1248 }
1275 }
1249
1276
1250 #content div.box div.form div.fields div.field
1277 #content div.box div.form div.fields div.field
1251 {
1278 {
1252 margin: 0;
1279 margin: 0;
1253 padding: 10px 0 10px 0;
1280 padding: 10px 0 10px 0;
1254 height: 1%;
1281 height: 1%;
1255 border-bottom: 1px solid #DDDDDD;
1282 border-bottom: 1px solid #DDDDDD;
1256 clear: both;
1283 clear: both;
1257 overflow: hidden;
1284 overflow: hidden;
1258 }
1285 }
1259
1286
1260 #content div.box div.form div.fields div.field-first
1287 #content div.box div.form div.fields div.field-first
1261 {
1288 {
1262 padding: 0 0 10px 0;
1289 padding: 0 0 10px 0;
1263 }
1290 }
1264
1291
1265 #content div.box div.form div.fields div.field span.error-message
1292 #content div.box div.form div.fields div.field span.error-message
1266 {
1293 {
1267 margin: 8px 0 0 0;
1294 margin: 8px 0 0 0;
1268 padding: 0;
1295 padding: 0;
1269 height: 1%;
1296 height: 1%;
1270 display: block;
1297 display: block;
1271 color: #FF0000;
1298 color: #FF0000;
1272 }
1299 }
1273
1300
1274 #content div.box div.form div.fields div.field span.success
1301 #content div.box div.form div.fields div.field span.success
1275 {
1302 {
1276 margin: 8px 0 0 0;
1303 margin: 8px 0 0 0;
1277 padding: 0;
1304 padding: 0;
1278 height: 1%;
1305 height: 1%;
1279 display: block;
1306 display: block;
1280 color: #316309;
1307 color: #316309;
1281 }
1308 }
1282
1309
1283 /* -----------------------------------------------------------
1310 /* -----------------------------------------------------------
1284 content -> right -> forms -> labels
1311 content -> right -> forms -> labels
1285 ----------------------------------------------------------- */
1312 ----------------------------------------------------------- */
1286
1313
1287 #content div.box div.form div.fields div.field div.label
1314 #content div.box div.form div.fields div.field div.label
1288 {
1315 {
1289 left: 310px;
1316 left: 310px;
1290 margin: 0;
1317 margin: 0;
1291 padding: 8px 0 0 5px;
1318 padding: 8px 0 0 5px;
1292 width: auto;
1319 width: auto;
1293 position: absolute;
1320 position: absolute;
1294 }
1321 }
1295
1322
1296 #content div.box-left div.form div.fields div.field div.label,
1323 #content div.box-left div.form div.fields div.field div.label,
1297 #content div.box-right div.form div.fields div.field div.label
1324 #content div.box-right div.form div.fields div.field div.label
1298 {
1325 {
1299 left: 0;
1326 left: 0;
1300 margin: 0;
1327 margin: 0;
1301 padding: 0 0 8px 0;
1328 padding: 0 0 8px 0;
1302 width: auto;
1329 width: auto;
1303 position: relative;
1330 position: relative;
1304 clear: both;
1331 clear: both;
1305 overflow: hidden;
1332 overflow: hidden;
1306
1333
1307 }
1334 }
1308
1335
1309 /* -----------------------------------------------------------
1336 /* -----------------------------------------------------------
1310 content -> right -> forms -> label (select)
1337 content -> right -> forms -> label (select)
1311 ----------------------------------------------------------- */
1338 ----------------------------------------------------------- */
1312
1339
1313 #content div.box div.form div.fields div.field div.label-select
1340 #content div.box div.form div.fields div.field div.label-select
1314 {
1341 {
1315 padding: 2px 0 0 5px;
1342 padding: 2px 0 0 5px;
1316 }
1343 }
1317
1344
1318 #content div.box-left div.form div.fields div.field div.label-select,
1345 #content div.box-left div.form div.fields div.field div.label-select,
1319 #content div.box-right div.form div.fields div.field div.label-select
1346 #content div.box-right div.form div.fields div.field div.label-select
1320 {
1347 {
1321 padding: 0 0 8px 0;
1348 padding: 0 0 8px 0;
1322 }
1349 }
1323
1350
1324 /* -----------------------------------------------------------
1351 /* -----------------------------------------------------------
1325 content -> right -> forms -> label (checkbox)
1352 content -> right -> forms -> label (checkbox)
1326 ----------------------------------------------------------- */
1353 ----------------------------------------------------------- */
1327
1354
1328 #content div.box div.form div.fields div.field div.label-checkbox
1355 #content div.box div.form div.fields div.field div.label-checkbox
1329 {
1356 {
1330 padding:0 0 0 5px !important;
1357 padding:0 0 0 5px !important;
1331 }
1358 }
1332
1359
1333 /* -----------------------------------------------------------
1360 /* -----------------------------------------------------------
1334 content -> right -> forms -> label (radio)
1361 content -> right -> forms -> label (radio)
1335 ----------------------------------------------------------- */
1362 ----------------------------------------------------------- */
1336
1363
1337 #content div.box div.form div.fields div.field div.label-radio
1364 #content div.box div.form div.fields div.field div.label-radio
1338 {
1365 {
1339 padding:0 0 0 5px !important;
1366 padding:0 0 0 5px !important;
1340 }
1367 }
1341
1368
1342 /* -----------------------------------------------------------
1369 /* -----------------------------------------------------------
1343 content -> right -> forms -> label (textarea)
1370 content -> right -> forms -> label (textarea)
1344 ----------------------------------------------------------- */
1371 ----------------------------------------------------------- */
1345
1372
1346 #content div.box div.form div.fields div.field div.label-textarea
1373 #content div.box div.form div.fields div.field div.label-textarea
1347 {
1374 {
1348 padding:0 0 0 5px !important;
1375 padding:0 0 0 5px !important;
1349 }
1376 }
1350
1377
1351 #content div.box-left div.form div.fields div.field div.label-textarea,
1378 #content div.box-left div.form div.fields div.field div.label-textarea,
1352 #content div.box-right div.form div.fields div.field div.label-textarea
1379 #content div.box-right div.form div.fields div.field div.label-textarea
1353 {
1380 {
1354 padding: 0 0 8px 0 !important;
1381 padding: 0 0 8px 0 !important;
1355 }
1382 }
1356
1383
1357 /* -----------------------------------------------------------
1384 /* -----------------------------------------------------------
1358 content -> right -> forms -> labels (label)
1385 content -> right -> forms -> labels (label)
1359 ----------------------------------------------------------- */
1386 ----------------------------------------------------------- */
1360
1387
1361 #content div.box div.form div.fields div.field div.label label
1388 #content div.box div.form div.fields div.field div.label label
1362 {
1389 {
1363 color: #393939;
1390 color: #393939;
1364 font-weight: bold;
1391 font-weight: bold;
1365 }
1392 }
1366
1393
1367 #content div.box div.form div.fields div.field div.label span
1394 #content div.box div.form div.fields div.field div.label span
1368 {
1395 {
1369 margin: 0;
1396 margin: 0;
1370 padding: 2px 0 0 0;
1397 padding: 2px 0 0 0;
1371 height: 1%;
1398 height: 1%;
1372 display: block;
1399 display: block;
1373 color: #363636;
1400 color: #363636;
1374 }
1401 }
1375
1402
1376 /* -----------------------------------------------------------
1403 /* -----------------------------------------------------------
1377 content -> right -> forms -> input
1404 content -> right -> forms -> input
1378 ----------------------------------------------------------- */
1405 ----------------------------------------------------------- */
1379
1406
1380 #content div.box div.form div.fields div.field div.input
1407 #content div.box div.form div.fields div.field div.input
1381 {
1408 {
1382 margin: 0 0 0 200px;
1409 margin: 0 0 0 200px;
1383 padding: 0;
1410 padding: 0;
1384 }
1411 }
1385
1412
1386 #content div.box-left div.form div.fields div.field div.input,
1413 #content div.box-left div.form div.fields div.field div.input,
1387 #content div.box-right div.form div.fields div.field div.input
1414 #content div.box-right div.form div.fields div.field div.input
1388 {
1415 {
1389 margin: 0;
1416 margin: 0;
1390 padding: 7px 7px 6px 7px;
1417 padding: 7px 7px 6px 7px;
1391 clear: both;
1418 clear: both;
1392 overflow: hidden;
1419 overflow: hidden;
1393 border-top: 1px solid #b3b3b3;
1420 border-top: 1px solid #b3b3b3;
1394 border-left: 1px solid #b3b3b3;
1421 border-left: 1px solid #b3b3b3;
1395 border-right: 1px solid #eaeaea;
1422 border-right: 1px solid #eaeaea;
1396 border-bottom: 1px solid #eaeaea;
1423 border-bottom: 1px solid #eaeaea;
1397
1424
1398 }
1425 }
1399
1426
1400 #content div.box div.form div.fields div.field div.input input
1427 #content div.box div.form div.fields div.field div.input input
1401 {
1428 {
1402 margin: 0;
1429 margin: 0;
1403 padding: 7px 7px 6px 7px;
1430 padding: 7px 7px 6px 7px;
1404 background: #FFFFFF;
1431 background: #FFFFFF;
1405 border-top: 1px solid #b3b3b3;
1432 border-top: 1px solid #b3b3b3;
1406 border-left: 1px solid #b3b3b3;
1433 border-left: 1px solid #b3b3b3;
1407 border-right: 1px solid #eaeaea;
1434 border-right: 1px solid #eaeaea;
1408 border-bottom: 1px solid #eaeaea;
1435 border-bottom: 1px solid #eaeaea;
1409 color: #000000;
1436 color: #000000;
1410 font-family: Lucida Grande, Verdana, Lucida Sans Regular, Lucida Sans Unicode, Arial, sans-serif;
1437 font-family: Lucida Grande, Verdana, Lucida Sans Regular, Lucida Sans Unicode, Arial, sans-serif;
1411 font-size: 11px;
1438 font-size: 11px;
1412 float: left;
1439 float: left;
1413 }
1440 }
1414
1441
1415 #content div.box-left div.form div.fields div.field div.input input,
1442 #content div.box-left div.form div.fields div.field div.input input,
1416 #content div.box-right div.form div.fields div.field div.input input
1443 #content div.box-right div.form div.fields div.field div.input input
1417 {
1444 {
1418 width: 100%;
1445 width: 100%;
1419 padding: 0;
1446 padding: 0;
1420 border: none;
1447 border: none;
1421 }
1448 }
1422
1449
1423 #content div.box div.form div.fields div.field div.input input.small
1450 #content div.box div.form div.fields div.field div.input input.small
1424 {
1451 {
1425 width: 30%;
1452 width: 30%;
1426 }
1453 }
1427
1454
1428 #content div.box div.form div.fields div.field div.input input.medium
1455 #content div.box div.form div.fields div.field div.input input.medium
1429 {
1456 {
1430 width: 55%;
1457 width: 55%;
1431 }
1458 }
1432
1459
1433 #content div.box div.form div.fields div.field div.input input.large
1460 #content div.box div.form div.fields div.field div.input input.large
1434 {
1461 {
1435 width: 85%;
1462 width: 85%;
1436 }
1463 }
1437
1464
1438 #content div.box div.form div.fields div.field div.input input.date
1465 #content div.box div.form div.fields div.field div.input input.date
1439 {
1466 {
1440 width: 177px;
1467 width: 177px;
1441 }
1468 }
1442
1469
1443 #content div.box div.form div.fields div.field div.input input.button
1470 #content div.box div.form div.fields div.field div.input input.button
1444 {
1471 {
1445 margin: 0;
1472 margin: 0;
1446 padding: 4px 8px 4px 8px;
1473 padding: 4px 8px 4px 8px;
1447 background: #D4D0C8;
1474 background: #D4D0C8;
1448 border-top: 1px solid #FFFFFF;
1475 border-top: 1px solid #FFFFFF;
1449 border-left: 1px solid #FFFFFF;
1476 border-left: 1px solid #FFFFFF;
1450 border-right: 1px solid #404040;
1477 border-right: 1px solid #404040;
1451 border-bottom: 1px solid #404040;
1478 border-bottom: 1px solid #404040;
1452 color: #000000;
1479 color: #000000;
1453 }
1480 }
1454
1481
1455 #content div.box div.form div.fields div.field div.input input.error
1482 #content div.box div.form div.fields div.field div.input input.error
1456 {
1483 {
1457 background: #FBE3E4;
1484 background: #FBE3E4;
1458 border-top: 1px solid #e1b2b3;
1485 border-top: 1px solid #e1b2b3;
1459 border-left: 1px solid #e1b2b3;
1486 border-left: 1px solid #e1b2b3;
1460 border-right: 1px solid #FBC2C4;
1487 border-right: 1px solid #FBC2C4;
1461 border-bottom: 1px solid #FBC2C4;
1488 border-bottom: 1px solid #FBC2C4;
1462 }
1489 }
1463
1490
1464 #content div.box div.form div.fields div.field div.input input.success
1491 #content div.box div.form div.fields div.field div.input input.success
1465 {
1492 {
1466 background: #E6EFC2;
1493 background: #E6EFC2;
1467 border-top: 1px solid #cebb98;
1494 border-top: 1px solid #cebb98;
1468 border-left: 1px solid #cebb98;
1495 border-left: 1px solid #cebb98;
1469 border-right: 1px solid #c6d880;
1496 border-right: 1px solid #c6d880;
1470 border-bottom: 1px solid #c6d880;
1497 border-bottom: 1px solid #c6d880;
1471 }
1498 }
1472
1499
1473 #content div.box div.form div.fields div.field div.input img.ui-datepicker-trigger
1500 #content div.box div.form div.fields div.field div.input img.ui-datepicker-trigger
1474 {
1501 {
1475 margin: 0 0 0 6px;
1502 margin: 0 0 0 6px;
1476 }
1503 }
1477
1504
1478 /* -----------------------------------------------------------
1505 /* -----------------------------------------------------------
1479 content -> right -> forms -> input (file styling)
1506 content -> right -> forms -> input (file styling)
1480 ----------------------------------------------------------- */
1507 ----------------------------------------------------------- */
1481
1508
1482 #content div.box div.form div.fields div.field div.input a.ui-input-file
1509 #content div.box div.form div.fields div.field div.input a.ui-input-file
1483 {
1510 {
1484 margin: 0 0 0 6px;
1511 margin: 0 0 0 6px;
1485 padding: 0;
1512 padding: 0;
1486 width: 28px;
1513 width: 28px;
1487 height: 28px;
1514 height: 28px;
1488 display: inline;
1515 display: inline;
1489 position: absolute;
1516 position: absolute;
1490 overflow: hidden;
1517 overflow: hidden;
1491 cursor: pointer;
1518 cursor: pointer;
1492 background: #e5e3e3 url("../images/button_browse.png") no-repeat;
1519 background: #e5e3e3 url("../images/button_browse.png") no-repeat;
1493 border: none;
1520 border: none;
1494 text-decoration: none;
1521 text-decoration: none;
1495 }
1522 }
1496
1523
1497 #content div.box div.form div.fields div.field div.input a:hover.ui-input-file
1524 #content div.box div.form div.fields div.field div.input a:hover.ui-input-file
1498 {
1525 {
1499 background: #e5e3e3 url("../images/button_browse_selected.png") no-repeat;
1526 background: #e5e3e3 url("../images/button_browse_selected.png") no-repeat;
1500 }
1527 }
1501
1528
1502 /* -----------------------------------------------------------
1529 /* -----------------------------------------------------------
1503 content -> right -> forms -> textarea
1530 content -> right -> forms -> textarea
1504 ----------------------------------------------------------- */
1531 ----------------------------------------------------------- */
1505
1532
1506 #content div.box div.form div.fields div.field div.textarea
1533 #content div.box div.form div.fields div.field div.textarea
1507 {
1534 {
1508 margin: 0 0 0 200px;
1535 margin: 0 0 0 200px;
1509 padding: 10px;
1536 padding: 10px;
1510 border-top: 1px solid #b3b3b3;
1537 border-top: 1px solid #b3b3b3;
1511 border-left: 1px solid #b3b3b3;
1538 border-left: 1px solid #b3b3b3;
1512 border-right: 1px solid #eaeaea;
1539 border-right: 1px solid #eaeaea;
1513 border-bottom: 1px solid #eaeaea;
1540 border-bottom: 1px solid #eaeaea;
1514 }
1541 }
1515
1542
1516 #content div.box div.form div.fields div.field div.textarea-editor
1543 #content div.box div.form div.fields div.field div.textarea-editor
1517 {
1544 {
1518 padding: 0;
1545 padding: 0;
1519 border: 1px solid #dddddd;
1546 border: 1px solid #dddddd;
1520 }
1547 }
1521
1548
1522 #content div.box-left div.form div.fields div.field div.textarea,
1549 #content div.box-left div.form div.fields div.field div.textarea,
1523 #content div.box-right div.form div.fields div.field div.textarea
1550 #content div.box-right div.form div.fields div.field div.textarea
1524 {
1551 {
1525 margin: 0;
1552 margin: 0;
1526 }
1553 }
1527
1554
1528 #content div.box div.form div.fields div.field div.textarea textarea
1555 #content div.box div.form div.fields div.field div.textarea textarea
1529 {
1556 {
1530 margin: 0;
1557 margin: 0;
1531 padding: 0;
1558 padding: 0;
1532 width: 100%;
1559 width: 100%;
1533 height: 220px;
1560 height: 220px;
1534 overflow: hidden;
1561 overflow: hidden;
1535 background: #FFFFFF;
1562 background: #FFFFFF;
1536 border-width: 0;
1563 border-width: 0;
1537 color: #000000;
1564 color: #000000;
1538 font-family: Lucida Grande, Verdana, Lucida Sans Regular, Lucida Sans Unicode, Arial, sans-serif;
1565 font-family: Lucida Grande, Verdana, Lucida Sans Regular, Lucida Sans Unicode, Arial, sans-serif;
1539 font-size: 11px;
1566 font-size: 11px;
1540 outline: none;
1567 outline: none;
1541 }
1568 }
1542
1569
1543 #content div.box-left div.form div.fields div.field div.textarea textarea,
1570 #content div.box-left div.form div.fields div.field div.textarea textarea,
1544 #content div.box-right div.form div.fields div.field div.textarea textarea
1571 #content div.box-right div.form div.fields div.field div.textarea textarea
1545 {
1572 {
1546 width: 100%;
1573 width: 100%;
1547 height: 100px;
1574 height: 100px;
1548 }
1575 }
1549
1576
1550 #content div.box div.form div.fields div.field div.textarea textarea.error
1577 #content div.box div.form div.fields div.field div.textarea textarea.error
1551 {
1578 {
1552 padding: 3px 10px 10px 23px;
1579 padding: 3px 10px 10px 23px;
1553 background-color: #FBE3E4;
1580 background-color: #FBE3E4;
1554 background-image: url("../../../resources/images/icons/exclamation.png");
1581 background-image: url("../../../resources/images/icons/exclamation.png");
1555 background-repeat: no-repeat;
1582 background-repeat: no-repeat;
1556 background-position: 3px 3px;
1583 background-position: 3px 3px;
1557 border: 1px solid #FBC2C4;
1584 border: 1px solid #FBC2C4;
1558 }
1585 }
1559
1586
1560 #content div.box div.form div.fields div.field div.textarea textarea.success
1587 #content div.box div.form div.fields div.field div.textarea textarea.success
1561 {
1588 {
1562 padding: 3px 10px 10px 23px;
1589 padding: 3px 10px 10px 23px;
1563 background-color: #E6EFC2;
1590 background-color: #E6EFC2;
1564 background-image: url("../../../resources/images/icons/accept.png");
1591 background-image: url("../../../resources/images/icons/accept.png");
1565 background-repeat: no-repeat;
1592 background-repeat: no-repeat;
1566 background-position: 3px 3px;
1593 background-position: 3px 3px;
1567 border: 1px solid #C6D880;
1594 border: 1px solid #C6D880;
1568 }
1595 }
1569
1596
1570 /* -----------------------------------------------------------
1597 /* -----------------------------------------------------------
1571 content -> right -> forms -> textarea (tinymce editor)
1598 content -> right -> forms -> textarea (tinymce editor)
1572 ----------------------------------------------------------- */
1599 ----------------------------------------------------------- */
1573
1600
1574 #content div.box div.form div.fields div.field div.textarea table
1601 #content div.box div.form div.fields div.field div.textarea table
1575 {
1602 {
1576 margin: 0;
1603 margin: 0;
1577 padding: 0;
1604 padding: 0;
1578 width: 100%;
1605 width: 100%;
1579 border: none;
1606 border: none;
1580 }
1607 }
1581
1608
1582 #content div.box div.form div.fields div.field div.textarea table td
1609 #content div.box div.form div.fields div.field div.textarea table td
1583 {
1610 {
1584 padding: 0;
1611 padding: 0;
1585 background: #DDDDDD;
1612 background: #DDDDDD;
1586 border: none;
1613 border: none;
1587 }
1614 }
1588
1615
1589 #content div.box div.form div.fields div.field div.textarea table td table
1616 #content div.box div.form div.fields div.field div.textarea table td table
1590 {
1617 {
1591 margin: 0;
1618 margin: 0;
1592 padding: 0;
1619 padding: 0;
1593 width: auto;
1620 width: auto;
1594 border: none;
1621 border: none;
1595 }
1622 }
1596
1623
1597 #content div.box div.form div.fields div.field div.textarea table td table td
1624 #content div.box div.form div.fields div.field div.textarea table td table td
1598 {
1625 {
1599 padding: 5px 5px 5px 0;
1626 padding: 5px 5px 5px 0;
1600 font-family: Lucida Grande, Verdana, Lucida Sans Regular, Lucida Sans Unicode, Arial, sans-serif;
1627 font-family: Lucida Grande, Verdana, Lucida Sans Regular, Lucida Sans Unicode, Arial, sans-serif;
1601 font-size: 11px;
1628 font-size: 11px;
1602 }
1629 }
1603
1630
1604 #content div.box div.form div.fields div.field div.textarea table td table td a
1631 #content div.box div.form div.fields div.field div.textarea table td table td a
1605 {
1632 {
1606 border: none;
1633 border: none;
1607 }
1634 }
1608
1635
1609 #content div.box div.form div.fields div.field div.textarea table td table td a.mceButtonActive
1636 #content div.box div.form div.fields div.field div.textarea table td table td a.mceButtonActive
1610 {
1637 {
1611 background: #b1b1b1;
1638 background: #b1b1b1;
1612 }
1639 }
1613
1640
1614 /* -----------------------------------------------------------
1641 /* -----------------------------------------------------------
1615 content -> right -> forms -> select
1642 content -> right -> forms -> select
1616 ----------------------------------------------------------- */
1643 ----------------------------------------------------------- */
1617
1644
1618 #content div.box div.form div.fields div.field div.select
1645 #content div.box div.form div.fields div.field div.select
1619 {
1646 {
1620 margin: 0 0 0 200px;
1647 margin: 0 0 0 200px;
1621 padding: 0;
1648 padding: 0;
1622 }
1649 }
1623
1650
1624 #content div.box div.form div.fields div.field div.select a:hover
1651 #content div.box div.form div.fields div.field div.select a:hover
1625 {
1652 {
1626 color: #000000;
1653 color: #000000;
1627 text-decoration: none;
1654 text-decoration: none;
1628 }
1655 }
1629
1656
1630 #content div.box div.form div.fields div.field div.select select
1657 #content div.box div.form div.fields div.field div.select select
1631 {
1658 {
1632 margin: 0;
1659 margin: 0;
1633 }
1660 }
1634
1661
1635 /* -----------------------------------------------------------
1662 /* -----------------------------------------------------------
1636 content -> right -> forms -> select (jquery styling)
1663 content -> right -> forms -> select (jquery styling)
1637 ----------------------------------------------------------- */
1664 ----------------------------------------------------------- */
1638
1665
1639 #content div.box div.form div.fields div.field div.select a.ui-selectmenu-focus
1666 #content div.box div.form div.fields div.field div.select a.ui-selectmenu-focus
1640 {
1667 {
1641 border: 1px solid #666666;
1668 border: 1px solid #666666;
1642 }
1669 }
1643
1670
1644 #content div.box div.form div.fields div.field div.select a.ui-selectmenu
1671 #content div.box div.form div.fields div.field div.select a.ui-selectmenu
1645 {
1672 {
1646 color: #565656;
1673 color: #565656;
1647 text-decoration: none;
1674 text-decoration: none;
1648 }
1675 }
1649
1676
1650 #content div.box div.form div.fields div.field div.select a.ui-selectmenu:hover
1677 #content div.box div.form div.fields div.field div.select a.ui-selectmenu:hover
1651 {
1678 {
1652 color: #000000;
1679 color: #000000;
1653 text-decoration: none;
1680 text-decoration: none;
1654 }
1681 }
1655
1682
1656 #content div.box div.form div.fields div.field div.select a.ui-selectmenu-focus span.ui-icon
1683 #content div.box div.form div.fields div.field div.select a.ui-selectmenu-focus span.ui-icon
1657 {
1684 {
1658 background-image: url(../images/ui/ui-icons_222222_256x240.png);
1685 background-image: url(../images/ui/ui-icons_222222_256x240.png);
1659 }
1686 }
1660
1687
1661 /* -----------------------------------------------------------
1688 /* -----------------------------------------------------------
1662 content -> right -> forms -> element focus
1689 content -> right -> forms -> element focus
1663 ----------------------------------------------------------- */
1690 ----------------------------------------------------------- */
1664
1691
1665 #content div.box div.form div.fields div.field input[type=text]:focus,
1692 #content div.box div.form div.fields div.field input[type=text]:focus,
1666 #content div.box div.form div.fields div.field input[type=password]:focus,
1693 #content div.box div.form div.fields div.field input[type=password]:focus,
1667 #content div.box div.form div.fields div.field input[type=file]:focus,
1694 #content div.box div.form div.fields div.field input[type=file]:focus,
1668 #content div.box div.form div.fields div.field textarea:focus,
1695 #content div.box div.form div.fields div.field textarea:focus,
1669 #content div.box div.form div.fields div.field select:focus
1696 #content div.box div.form div.fields div.field select:focus
1670 {
1697 {
1671 background: #f6f6f6;
1698 background: #f6f6f6;
1672 border-color: #666;
1699 border-color: #666;
1673 }
1700 }
1674
1701
1675 /* -----------------------------------------------------------
1702 /* -----------------------------------------------------------
1676 content -> right -> forms -> checkboxes
1703 content -> right -> forms -> checkboxes
1677 ----------------------------------------------------------- */
1704 ----------------------------------------------------------- */
1678
1705
1679 #content div.box div.form div.fields div.field div.checkboxes
1706 #content div.box div.form div.fields div.field div.checkboxes
1680 {
1707 {
1681 margin: 0 0 0 200px;
1708 margin: 0 0 0 200px;
1682 padding: 0;
1709 padding: 0;
1683 }
1710 }
1684
1711
1685 #content div.box div.form div.fields div.field div.checkboxes div.checkbox
1712 #content div.box div.form div.fields div.field div.checkboxes div.checkbox
1686 {
1713 {
1687 margin: 0;
1714 margin: 0;
1688 padding: 2px 0 2px 0;
1715 padding: 2px 0 2px 0;
1689 clear: both;
1716 clear: both;
1690 overflow: hidden;
1717 overflow: hidden;
1691 }
1718 }
1692
1719
1693 #content div.box div.form div.fields div.field div.checkboxes div.checkbox input
1720 #content div.box div.form div.fields div.field div.checkboxes div.checkbox input
1694 {
1721 {
1695 margin: 0;
1722 margin: 0;
1696 float: left;
1723 float: left;
1697 }
1724 }
1698
1725
1699 #content div.box div.form div.fields div.field div.checkboxes div.checkbox label
1726 #content div.box div.form div.fields div.field div.checkboxes div.checkbox label
1700 {
1727 {
1701 margin: 3px 0 0 4px;
1728 margin: 3px 0 0 4px;
1702 height: 1%;
1729 height: 1%;
1703 display: block;
1730 display: block;
1704 float: left;
1731 float: left;
1705 }
1732 }
1706
1733
1707 /* -----------------------------------------------------------
1734 /* -----------------------------------------------------------
1708 content -> right -> forms -> radios
1735 content -> right -> forms -> radios
1709 ----------------------------------------------------------- */
1736 ----------------------------------------------------------- */
1710
1737
1711 #content div.box div.form div.fields div.field div.radios
1738 #content div.box div.form div.fields div.field div.radios
1712 {
1739 {
1713 margin: 0 0 0 200px;
1740 margin: 0 0 0 200px;
1714 padding: 0;
1741 padding: 0;
1715 }
1742 }
1716
1743
1717 #content div.box div.form div.fields div.field div.radios div.radio
1744 #content div.box div.form div.fields div.field div.radios div.radio
1718 {
1745 {
1719 margin: 0;
1746 margin: 0;
1720 padding: 2px 0 2px 0;
1747 padding: 2px 0 2px 0;
1721 clear: both;
1748 clear: both;
1722 overflow: hidden;
1749 overflow: hidden;
1723 }
1750 }
1724
1751
1725 #content div.box div.form div.fields div.field div.radios div.radio input
1752 #content div.box div.form div.fields div.field div.radios div.radio input
1726 {
1753 {
1727 margin: 0;
1754 margin: 0;
1728 float: left;
1755 float: left;
1729 }
1756 }
1730
1757
1731 #content div.box div.form div.fields div.field div.radios div.radio label
1758 #content div.box div.form div.fields div.field div.radios div.radio label
1732 {
1759 {
1733 margin: 3px 0 0 4px;
1760 margin: 3px 0 0 4px;
1734 height: 1%;
1761 height: 1%;
1735 display: block;
1762 display: block;
1736 float: left;
1763 float: left;
1737 }
1764 }
1738 /* -----------------------------------------------------------
1765 /* -----------------------------------------------------------
1739 content -> right -> forms -> button
1766 content -> right -> forms -> button
1740 ----------------------------------------------------------- */
1767 ----------------------------------------------------------- */
1741
1768
1742 div.form div.fields div.field div.button
1769 div.form div.fields div.field div.button
1743 {
1770 {
1744 margin: 0;
1771 margin: 0;
1745 padding: 0 0 0 8px;
1772 padding: 0 0 0 8px;
1746 float: left;
1773 float: left;
1747 }
1774 }
1748
1775
1749 div.form div.fields div.field div.button input
1776 div.form div.fields div.field div.button input
1750 {
1777 {
1751 margin: 0;
1778 margin: 0;
1752 color: #000000;
1779 color: #000000;
1753 font-family: Lucida Grande, Verdana, Lucida Sans Regular, Lucida Sans Unicode, Arial, sans-serif;
1780 font-family: Lucida Grande, Verdana, Lucida Sans Regular, Lucida Sans Unicode, Arial, sans-serif;
1754 font-size: 11px;
1781 font-size: 11px;
1755 font-weight: bold;
1782 font-weight: bold;
1756 }
1783 }
1757
1784
1758 div.form div.fields div.field div.button .ui-state-default
1785 div.form div.fields div.field div.button .ui-state-default
1759 {
1786 {
1760 margin: 0;
1787 margin: 0;
1761 padding: 6px 12px 6px 12px;
1788 padding: 6px 12px 6px 12px;
1762 background: #e5e3e3 url("../images/button.png") repeat-x;
1789 background: #e5e3e3 url("../images/button.png") repeat-x;
1763 border-top: 1px solid #DDDDDD;
1790 border-top: 1px solid #DDDDDD;
1764 border-left: 1px solid #c6c6c6;
1791 border-left: 1px solid #c6c6c6;
1765 border-right: 1px solid #DDDDDD;
1792 border-right: 1px solid #DDDDDD;
1766 border-bottom: 1px solid #c6c6c6;
1793 border-bottom: 1px solid #c6c6c6;
1767 color: #515151;
1794 color: #515151;
1768 outline: none;
1795 outline: none;
1769 }
1796 }
1770
1797
1771 div.form div.fields div.field div.button .ui-state-hover
1798 div.form div.fields div.field div.button .ui-state-hover
1772 {
1799 {
1773 margin: 0;
1800 margin: 0;
1774 padding: 6px 12px 6px 12px;
1801 padding: 6px 12px 6px 12px;
1775 background: #b4b4b4 url("../images/button_selected.png") repeat-x;
1802 background: #b4b4b4 url("../images/button_selected.png") repeat-x;
1776 border-top: 1px solid #cccccc;
1803 border-top: 1px solid #cccccc;
1777 border-left: 1px solid #bebebe;
1804 border-left: 1px solid #bebebe;
1778 border-right: 1px solid #b1b1b1;
1805 border-right: 1px solid #b1b1b1;
1779 border-bottom: 1px solid #afafaf;
1806 border-bottom: 1px solid #afafaf;
1780 color: #515151;
1807 color: #515151;
1781 outline: none;
1808 outline: none;
1782 }
1809 }
1783
1810
1784 div.form div.fields div.field div.highlight
1811 div.form div.fields div.field div.highlight
1785 {
1812 {
1786 display: inline;
1813 display: inline;
1787 }
1814 }
1788
1815
1789 div.form div.fields div.field div.highlight .ui-state-default
1816 div.form div.fields div.field div.highlight .ui-state-default
1790 {
1817 {
1791 margin: 0;
1818 margin: 0;
1792 padding: 6px 12px 6px 12px;
1819 padding: 6px 12px 6px 12px;
1793 background: #4e85bb url("../images/colors/blue/button_highlight.png") repeat-x;
1820 background: #4e85bb url("../images/colors/blue/button_highlight.png") repeat-x;
1794 border-top: 1px solid #5c91a4;
1821 border-top: 1px solid #5c91a4;
1795 border-left: 1px solid #2a6f89;
1822 border-left: 1px solid #2a6f89;
1796 border-right: 1px solid #2b7089;
1823 border-right: 1px solid #2b7089;
1797 border-bottom: 1px solid #1a6480;
1824 border-bottom: 1px solid #1a6480;
1798 color: #FFFFFF;
1825 color: #FFFFFF;
1799 }
1826 }
1800
1827
1801 div.form div.fields div.field div.highlight .ui-state-hover
1828 div.form div.fields div.field div.highlight .ui-state-hover
1802 {
1829 {
1803 margin: 0;
1830 margin: 0;
1804 padding: 6px 12px 6px 12px;
1831 padding: 6px 12px 6px 12px;
1805 background: #46a0c1 url("../images/colors/blue/button_highlight_selected.png") repeat-x;
1832 background: #46a0c1 url("../images/colors/blue/button_highlight_selected.png") repeat-x;
1806 border-top: 1px solid #78acbf;
1833 border-top: 1px solid #78acbf;
1807 border-left: 1px solid #34819e;
1834 border-left: 1px solid #34819e;
1808 border-right: 1px solid #35829f;
1835 border-right: 1px solid #35829f;
1809 border-bottom: 1px solid #257897;
1836 border-bottom: 1px solid #257897;
1810 color: #FFFFFF;
1837 color: #FFFFFF;
1811 }
1838 }
1812
1839
1813
1840
1814 /* -----------------------------------------------------------
1841 /* -----------------------------------------------------------
1815 content -> right -> forms -> buttons
1842 content -> right -> forms -> buttons
1816 ----------------------------------------------------------- */
1843 ----------------------------------------------------------- */
1817
1844
1818 #content div.box div.form div.fields div.buttons
1845 #content div.box div.form div.fields div.buttons
1819 {
1846 {
1820 margin: 10px 0 0 200px;
1847 margin: 10px 0 0 200px;
1821 padding: 0;
1848 padding: 0;
1822 }
1849 }
1823
1850
1824 #content div.box-left div.form div.fields div.buttons,
1851 #content div.box-left div.form div.fields div.buttons,
1825 #content div.box-right div.form div.fields div.buttons
1852 #content div.box-right div.form div.fields div.buttons
1826 {
1853 {
1827 margin: 10px 0 0 0;
1854 margin: 10px 0 0 0;
1828 }
1855 }
1829
1856
1830 #content div.box div.form div.fields div.buttons input
1857 #content div.box div.form div.fields div.buttons input
1831 {
1858 {
1832 margin: 0;
1859 margin: 0;
1833 color: #000000;
1860 color: #000000;
1834 font-family: Lucida Grande, Verdana, Lucida Sans Regular, Lucida Sans Unicode, Arial, sans-serif;
1861 font-family: Lucida Grande, Verdana, Lucida Sans Regular, Lucida Sans Unicode, Arial, sans-serif;
1835 font-size: 11px;
1862 font-size: 11px;
1836 font-weight: bold;
1863 font-weight: bold;
1837 }
1864 }
1838 /* -----------------------------------------------------------
1865 /* -----------------------------------------------------------
1839 content -> right -> forms -> buttons
1866 content -> right -> forms -> buttons
1840 ----------------------------------------------------------- */
1867 ----------------------------------------------------------- */
1841
1868
1842 div.form div.fields div.buttons
1869 div.form div.fields div.buttons
1843 {
1870 {
1844 margin: 10px 0 0 200px;
1871 margin: 10px 0 0 200px;
1845 padding: 0;
1872 padding: 0;
1846 }
1873 }
1847
1874
1848 div.box-left div.form div.fields div.buttons,
1875 div.box-left div.form div.fields div.buttons,
1849 div.box-right div.form div.fields div.buttons
1876 div.box-right div.form div.fields div.buttons
1850 {
1877 {
1851 margin: 10px 0 0 0;
1878 margin: 10px 0 0 0;
1852 }
1879 }
1853
1880
1854 div.form div.fields div.buttons input
1881 div.form div.fields div.buttons input
1855 {
1882 {
1856 margin: 0;
1883 margin: 0;
1857 color: #000000;
1884 color: #000000;
1858 font-family: Lucida Grande, Verdana, Lucida Sans Regular, Lucida Sans Unicode, Arial, sans-serif;
1885 font-family: Lucida Grande, Verdana, Lucida Sans Regular, Lucida Sans Unicode, Arial, sans-serif;
1859 font-size: 11px;
1886 font-size: 11px;
1860 font-weight: bold;
1887 font-weight: bold;
1861 }
1888 }
1862
1889
1863 /* -----------------------------------------------------------
1890 /* -----------------------------------------------------------
1864 content -> right -> forms -> buttons (jquery styling)
1891 content -> right -> forms -> buttons (jquery styling)
1865 ----------------------------------------------------------- */
1892 ----------------------------------------------------------- */
1866
1893
1867 #content div.box div.form div.fields div.buttons input.ui-state-default
1894 #content div.box div.form div.fields div.buttons input.ui-state-default
1868 {
1895 {
1869 margin: 0;
1896 margin: 0;
1870 padding: 6px 12px 6px 12px;
1897 padding: 6px 12px 6px 12px;
1871 background: #e5e3e3 url("../images/button.png") repeat-x;
1898 background: #e5e3e3 url("../images/button.png") repeat-x;
1872 border-top: 1px solid #DDDDDD;
1899 border-top: 1px solid #DDDDDD;
1873 border-left: 1px solid #c6c6c6;
1900 border-left: 1px solid #c6c6c6;
1874 border-right: 1px solid #DDDDDD;
1901 border-right: 1px solid #DDDDDD;
1875 border-bottom: 1px solid #c6c6c6;
1902 border-bottom: 1px solid #c6c6c6;
1876 color: #515151;
1903 color: #515151;
1877 outline: none;
1904 outline: none;
1878 }
1905 }
1879
1906
1880 #content div.box div.form div.fields div.buttons input.ui-state-hover
1907 #content div.box div.form div.fields div.buttons input.ui-state-hover
1881 {
1908 {
1882 margin: 0;
1909 margin: 0;
1883 padding: 6px 12px 6px 12px;
1910 padding: 6px 12px 6px 12px;
1884 background: #b4b4b4 url("../images/button_selected.png") repeat-x;
1911 background: #b4b4b4 url("../images/button_selected.png") repeat-x;
1885 border-top: 1px solid #cccccc;
1912 border-top: 1px solid #cccccc;
1886 border-left: 1px solid #bebebe;
1913 border-left: 1px solid #bebebe;
1887 border-right: 1px solid #b1b1b1;
1914 border-right: 1px solid #b1b1b1;
1888 border-bottom: 1px solid #afafaf;
1915 border-bottom: 1px solid #afafaf;
1889 color: #515151;
1916 color: #515151;
1890 outline: none;
1917 outline: none;
1891 }
1918 }
1892
1919
1893 #content div.box div.form div.fields div.buttons div.highlight
1920 #content div.box div.form div.fields div.buttons div.highlight
1894 {
1921 {
1895 display: inline;
1922 display: inline;
1896 }
1923 }
1897
1924
1898 #content div.box div.form div.fields div.buttons div.highlight input.ui-state-default
1925 #content div.box div.form div.fields div.buttons div.highlight input.ui-state-default
1899 {
1926 {
1900 margin: 0;
1927 margin: 0;
1901 padding: 6px 12px 6px 12px;
1928 padding: 6px 12px 6px 12px;
1902 background: #4e85bb url("../images/colors/blue/button_highlight.png") repeat-x;
1929 background: #4e85bb url("../images/colors/blue/button_highlight.png") repeat-x;
1903 border-top: 1px solid #5c91a4;
1930 border-top: 1px solid #5c91a4;
1904 border-left: 1px solid #2a6f89;
1931 border-left: 1px solid #2a6f89;
1905 border-right: 1px solid #2b7089;
1932 border-right: 1px solid #2b7089;
1906 border-bottom: 1px solid #1a6480;
1933 border-bottom: 1px solid #1a6480;
1907 color: #FFFFFF;
1934 color: #FFFFFF;
1908 }
1935 }
1909
1936
1910 #content div.box div.form div.fields div.buttons div.highlight input.ui-state-hover
1937 #content div.box div.form div.fields div.buttons div.highlight input.ui-state-hover
1911 {
1938 {
1912 margin: 0;
1939 margin: 0;
1913 padding: 6px 12px 6px 12px;
1940 padding: 6px 12px 6px 12px;
1914 background: #46a0c1 url("../images/colors/blue/button_highlight_selected.png") repeat-x;
1941 background: #46a0c1 url("../images/colors/blue/button_highlight_selected.png") repeat-x;
1915 border-top: 1px solid #78acbf;
1942 border-top: 1px solid #78acbf;
1916 border-left: 1px solid #34819e;
1943 border-left: 1px solid #34819e;
1917 border-right: 1px solid #35829f;
1944 border-right: 1px solid #35829f;
1918 border-bottom: 1px solid #257897;
1945 border-bottom: 1px solid #257897;
1919 color: #FFFFFF;
1946 color: #FFFFFF;
1920 }
1947 }
1921
1948
1922 /* -----------------------------------------------------------
1949 /* -----------------------------------------------------------
1923 content -> right -> box / tables
1950 content -> right -> box / tables
1924 ----------------------------------------------------------- */
1951 ----------------------------------------------------------- */
1925
1952
1926 #content div.box div.table
1953 #content div.box div.table
1927 {
1954 {
1928 margin: 0;
1955 margin: 0;
1929 padding: 0 20px 10px 20px;
1956 padding: 0 20px 10px 20px;
1930 clear: both;
1957 clear: both;
1931 overflow: hidden;
1958 overflow: hidden;
1932 }
1959 }
1933
1960
1934 #content div.box table
1961 #content div.box table
1935 {
1962 {
1936 margin: 0;
1963 margin: 0;
1937 padding: 0;
1964 padding: 0;
1938 width: 100%;
1965 width: 100%;
1939 border-collapse: collapse;
1966 border-collapse: collapse;
1940 }
1967 }
1941
1968
1942 #content div.box table th
1969 #content div.box table th
1943 {
1970 {
1944 padding: 10px;
1971 padding: 10px;
1945 background: #eeeeee;
1972 background: #eeeeee;
1946 border-bottom: 1px solid #dddddd;
1973 border-bottom: 1px solid #dddddd;
1947 }
1974 }
1948
1975
1949 #content div.box table th.left
1976 #content div.box table th.left
1950 {
1977 {
1951 text-align: left;
1978 text-align: left;
1952 }
1979 }
1953
1980
1954 #content div.box table th.right
1981 #content div.box table th.right
1955 {
1982 {
1956 text-align: right;
1983 text-align: right;
1957 }
1984 }
1958
1985
1959 #content div.box table th.center
1986 #content div.box table th.center
1960 {
1987 {
1961 text-align: center;
1988 text-align: center;
1962 }
1989 }
1963
1990
1964 #content div.box table th.selected
1991 #content div.box table th.selected
1965 {
1992 {
1966 padding: 0;
1993 padding: 0;
1967 vertical-align: middle;
1994 vertical-align: middle;
1968 }
1995 }
1969
1996
1970 #content div.box table th.selected input
1997 #content div.box table th.selected input
1971 {
1998 {
1972 margin: 0;
1999 margin: 0;
1973 }
2000 }
1974
2001
1975 #content div.box table td
2002 #content div.box table td
1976 {
2003 {
1977 padding: 5px;
2004 padding: 5px;
1978 background: #ffffff;
2005 background: #ffffff;
1979 border-bottom: 1px solid #cdcdcd;
2006 border-bottom: 1px solid #cdcdcd;
1980 vertical-align:middle;
2007 vertical-align:middle;
1981 }
2008 }
1982
2009
1983 #content div.box table tr.selected td
2010 #content div.box table tr.selected td
1984 {
2011 {
1985 background: #FFFFCC;
2012 background: #FFFFCC;
1986 }
2013 }
1987
2014
1988 #content div.box table td.selected
2015 #content div.box table td.selected
1989 {
2016 {
1990 padding: 0;
2017 padding: 0;
1991 width: 3%;
2018 width: 3%;
1992 text-align: center;
2019 text-align: center;
1993 vertical-align: middle;
2020 vertical-align: middle;
1994 }
2021 }
1995
2022
1996 #content div.box table td.selected input
2023 #content div.box table td.selected input
1997 {
2024 {
1998 margin: 0;
2025 margin: 0;
1999 }
2026 }
2000
2027
2001 #content div.box table td.action
2028 #content div.box table td.action
2002 {
2029 {
2003 width: 45%;
2030 width: 45%;
2004 text-align: left;
2031 text-align: left;
2005 }
2032 }
2006
2033
2007 #content div.box table td.user
2034 #content div.box table td.user
2008 {
2035 {
2009 width: 10%;
2036 width: 10%;
2010 text-align: center;
2037 text-align: center;
2011 }
2038 }
2012
2039
2013 #content div.box table td.date
2040 #content div.box table td.date
2014 {
2041 {
2015 width: 33%;
2042 width: 33%;
2016 text-align: center;
2043 text-align: center;
2017 }
2044 }
2018
2045
2019 #content div.box table td.address
2046 #content div.box table td.address
2020 {
2047 {
2021 width: 10%;
2048 width: 10%;
2022 text-align: center;
2049 text-align: center;
2023 }
2050 }
2024
2051
2025 /* -----------------------------------------------------------
2052 /* -----------------------------------------------------------
2026 content -> right -> box / table action
2053 content -> right -> box / table action
2027 ----------------------------------------------------------- */
2054 ----------------------------------------------------------- */
2028
2055
2029 #content div.box div.action
2056 #content div.box div.action
2030 {
2057 {
2031 margin: 10px 0 0 0;
2058 margin: 10px 0 0 0;
2032 padding: 0;
2059 padding: 0;
2033 float: right;
2060 float: right;
2034 background: #FFFFFF;
2061 background: #FFFFFF;
2035 text-align: right;
2062 text-align: right;
2036 }
2063 }
2037
2064
2038 #content div.box div.action a:hover
2065 #content div.box div.action a:hover
2039 {
2066 {
2040 color: #000000;
2067 color: #000000;
2041 text-decoration: none;
2068 text-decoration: none;
2042 }
2069 }
2043
2070
2044 #content div.box div.action select
2071 #content div.box div.action select
2045 {
2072 {
2046 margin: 0;
2073 margin: 0;
2047 font-family: Lucida Grande, Verdana, Lucida Sans Regular, Lucida Sans Unicode, Arial, sans-serif;
2074 font-family: Lucida Grande, Verdana, Lucida Sans Regular, Lucida Sans Unicode, Arial, sans-serif;
2048 font-size: 11px;
2075 font-size: 11px;
2049 }
2076 }
2050
2077
2051 #content div.box div.action div.button
2078 #content div.box div.action div.button
2052 {
2079 {
2053 margin: 6px 0 0 0;
2080 margin: 6px 0 0 0;
2054 padding: 0;
2081 padding: 0;
2055 text-align: right;
2082 text-align: right;
2056 }
2083 }
2057
2084
2058 #content div.box div.action div.button input
2085 #content div.box div.action div.button input
2059 {
2086 {
2060 margin: 0;
2087 margin: 0;
2061 color: #000000;
2088 color: #000000;
2062 font-family: Lucida Grande, Verdana, Lucida Sans Regular, Lucida Sans Unicode, Arial, sans-serif;
2089 font-family: Lucida Grande, Verdana, Lucida Sans Regular, Lucida Sans Unicode, Arial, sans-serif;
2063 font-size: 11px;
2090 font-size: 11px;
2064 font-weight: bold;
2091 font-weight: bold;
2065 }
2092 }
2066
2093
2067 #content div.box div.action div.button input.ui-state-default
2094 #content div.box div.action div.button input.ui-state-default
2068 {
2095 {
2069 margin: 0;
2096 margin: 0;
2070 padding: 6px 12px 6px 12px;
2097 padding: 6px 12px 6px 12px;
2071 background: #e5e3e3 url("../images/button.png") repeat-x;
2098 background: #e5e3e3 url("../images/button.png") repeat-x;
2072 border-top: 1px solid #DDDDDD;
2099 border-top: 1px solid #DDDDDD;
2073 border-left: 1px solid #c6c6c6;
2100 border-left: 1px solid #c6c6c6;
2074 border-right: 1px solid #DDDDDD;
2101 border-right: 1px solid #DDDDDD;
2075 border-bottom: 1px solid #c6c6c6;
2102 border-bottom: 1px solid #c6c6c6;
2076 color: #515151;
2103 color: #515151;
2077 }
2104 }
2078
2105
2079 #content div.box div.action div.button input.ui-state-hover
2106 #content div.box div.action div.button input.ui-state-hover
2080 {
2107 {
2081 margin: 0;
2108 margin: 0;
2082 padding: 6px 12px 6px 12px;
2109 padding: 6px 12px 6px 12px;
2083 background: #b4b4b4 url("../images/button_selected.png") repeat-x;
2110 background: #b4b4b4 url("../images/button_selected.png") repeat-x;
2084 border-top: 1px solid #cccccc;
2111 border-top: 1px solid #cccccc;
2085 border-left: 1px solid #bebebe;
2112 border-left: 1px solid #bebebe;
2086 border-right: 1px solid #b1b1b1;
2113 border-right: 1px solid #b1b1b1;
2087 border-bottom: 1px solid #afafaf;
2114 border-bottom: 1px solid #afafaf;
2088 color: #515151;
2115 color: #515151;
2089 }
2116 }
2090
2117
2091 #content div.box div.action .ui-selectmenu
2118 #content div.box div.action .ui-selectmenu
2092 {
2119 {
2093 margin: 0;
2120 margin: 0;
2094 padding: 0;
2121 padding: 0;
2095 }
2122 }
2096
2123
2097 #content div.box div.action a.ui-selectmenu-focus
2124 #content div.box div.action a.ui-selectmenu-focus
2098 {
2125 {
2099 border: 1px solid #666666;
2126 border: 1px solid #666666;
2100 }
2127 }
2101
2128
2102 #content div.box div.action a.ui-selectmenu-focus span.ui-icon
2129 #content div.box div.action a.ui-selectmenu-focus span.ui-icon
2103 {
2130 {
2104 background-image: url(../images/ui/ui-icons_222222_256x240.png);
2131 background-image: url(../images/ui/ui-icons_222222_256x240.png);
2105 }
2132 }
2106
2133
2107 /* -----------------------------------------------------------
2134 /* -----------------------------------------------------------
2108 content -> right -> pagination
2135 content -> right -> pagination
2109 ----------------------------------------------------------- */
2136 ----------------------------------------------------------- */
2110
2137
2111 #content div.box div.pagination
2138 #content div.box div.pagination
2112 {
2139 {
2113 margin: 10px 0 0 0;
2140 margin: 10px 0 0 0;
2114 padding: 0;
2141 padding: 0;
2115 height: 1%;
2142 height: 1%;
2116 clear: both;
2143 clear: both;
2117 overflow: hidden;
2144 overflow: hidden;
2118 }
2145 }
2119
2146
2120 #content div.box div.pagination div.results
2147 #content div.box div.pagination div.results
2121 {
2148 {
2122 margin: 0;
2149 margin: 0;
2123 padding: 0;
2150 padding: 0;
2124 text-align: left;
2151 text-align: left;
2125 float: left
2152 float: left
2126 }
2153 }
2127
2154
2128 #content div.box div.pagination div.results span
2155 #content div.box div.pagination div.results span
2129 {
2156 {
2130 margin: 0;
2157 margin: 0;
2131 padding: 6px 8px 6px 8px;
2158 padding: 6px 8px 6px 8px;
2132 height: 1%;
2159 height: 1%;
2133 display: block;
2160 display: block;
2134 float: left;
2161 float: left;
2135 background: #ebebeb url("../images/pager.png") repeat-x;
2162 background: #ebebeb url("../images/pager.png") repeat-x;
2136 border-top: 1px solid #dedede;
2163 border-top: 1px solid #dedede;
2137 border-left: 1px solid #cfcfcf;
2164 border-left: 1px solid #cfcfcf;
2138 border-right: 1px solid #c4c4c4;
2165 border-right: 1px solid #c4c4c4;
2139 border-bottom: 1px solid #c4c4c4;
2166 border-bottom: 1px solid #c4c4c4;
2140 color: #4A4A4A;
2167 color: #4A4A4A;
2141 font-weight: bold;
2168 font-weight: bold;
2142 }
2169 }
2143
2170
2144 #content div.box div.pagination ul.pager
2171 #content div.box div.pagination ul.pager
2145 {
2172 {
2146 margin: 0;
2173 margin: 0;
2147 padding: 0;
2174 padding: 0;
2148 float: right;
2175 float: right;
2149 text-align: right;
2176 text-align: right;
2150 }
2177 }
2151
2178
2152 #content div.box div.pagination ul.pager li
2179 #content div.box div.pagination ul.pager li
2153 {
2180 {
2154 margin: 0 0 0 4px;
2181 margin: 0 0 0 4px;
2155 padding: 0;
2182 padding: 0;
2156 height: 1%;
2183 height: 1%;
2157 float: left;
2184 float: left;
2158 list-style: none;
2185 list-style: none;
2159 background: #ebebeb url("../images/pager.png") repeat-x;
2186 background: #ebebeb url("../images/pager.png") repeat-x;
2160 border-top: 1px solid #dedede;
2187 border-top: 1px solid #dedede;
2161 border-left: 1px solid #cfcfcf;
2188 border-left: 1px solid #cfcfcf;
2162 border-right: 1px solid #c4c4c4;
2189 border-right: 1px solid #c4c4c4;
2163 border-bottom: 1px solid #c4c4c4;
2190 border-bottom: 1px solid #c4c4c4;
2164 color: #4A4A4A;
2191 color: #4A4A4A;
2165 font-weight: bold;
2192 font-weight: bold;
2166 }
2193 }
2167
2194
2168 #content div.box div.pagination ul.pager li.separator
2195 #content div.box div.pagination ul.pager li.separator
2169 {
2196 {
2170 padding: 6px;
2197 padding: 6px;
2171 }
2198 }
2172
2199
2173 #content div.box div.pagination ul.pager li.current
2200 #content div.box div.pagination ul.pager li.current
2174 {
2201 {
2175 padding: 6px;
2202 padding: 6px;
2176 background: #b4b4b4 url("../images/pager_selected.png") repeat-x;
2203 background: #b4b4b4 url("../images/pager_selected.png") repeat-x;
2177 border-top: 1px solid #cccccc;
2204 border-top: 1px solid #cccccc;
2178 border-left: 1px solid #bebebe;
2205 border-left: 1px solid #bebebe;
2179 border-right: 1px solid #b1b1b1;
2206 border-right: 1px solid #b1b1b1;
2180 border-bottom: 1px solid #afafaf;
2207 border-bottom: 1px solid #afafaf;
2181 color: #515151;
2208 color: #515151;
2182 }
2209 }
2183
2210
2184 #content div.box div.pagination ul.pager li.disabled
2211 #content div.box div.pagination ul.pager li.disabled
2185 {
2212 {
2186 padding: 6px;
2213 padding: 6px;
2187 color: #B4B4B4;
2214 color: #B4B4B4;
2188 }
2215 }
2189
2216
2190 #content div.box div.pagination ul.pager li a
2217 #content div.box div.pagination ul.pager li a
2191 {
2218 {
2192 margin: 0;
2219 margin: 0;
2193 padding: 6px;
2220 padding: 6px;
2194 height: 1%;
2221 height: 1%;
2195 display: block;
2222 display: block;
2196 float: left;
2223 float: left;
2197 color: #515151;
2224 color: #515151;
2198 text-decoration: none;
2225 text-decoration: none;
2199 }
2226 }
2200
2227
2201 #content div.box div.pagination ul.pager li a:hover,
2228 #content div.box div.pagination ul.pager li a:hover,
2202 #content div.box div.pagination ul.pager li a:active
2229 #content div.box div.pagination ul.pager li a:active
2203 {
2230 {
2204 margin: -1px;
2231 margin: -1px;
2205 background: #b4b4b4 url("../images/pager_selected.png") repeat-x;
2232 background: #b4b4b4 url("../images/pager_selected.png") repeat-x;
2206 border-top: 1px solid #cccccc;
2233 border-top: 1px solid #cccccc;
2207 border-left: 1px solid #bebebe;
2234 border-left: 1px solid #bebebe;
2208 border-right: 1px solid #b1b1b1;
2235 border-right: 1px solid #b1b1b1;
2209 border-bottom: 1px solid #afafaf;
2236 border-bottom: 1px solid #afafaf;
2210 }
2237 }
2211
2238
2212 /* -----------------------------------------------------------
2239 /* -----------------------------------------------------------
2213 content -> webhelpers pagination
2240 content -> webhelpers pagination
2214 ----------------------------------------------------------- */
2241 ----------------------------------------------------------- */
2215
2242
2216 #content div.box div.pagination-wh
2243 #content div.box div.pagination-wh
2217 {
2244 {
2218 margin: 10px 0 0 0;
2245 margin: 10px 0 0 0;
2219 padding: 0;
2246 padding: 0;
2220 height: 1%;
2247 height: 1%;
2221 clear: both;
2248 clear: both;
2222 overflow: hidden;
2249 overflow: hidden;
2223 text-align: right;
2250 text-align: right;
2224 }
2251 }
2225
2252
2226 #content div.box div.pagination-wh div.results
2253 #content div.box div.pagination-wh div.results
2227 {
2254 {
2228 margin: 0;
2255 margin: 0;
2229 padding: 0;
2256 padding: 0;
2230 text-align: left;
2257 text-align: left;
2231 float: left
2258 float: left
2232 }
2259 }
2233
2260
2234 #content div.box div.pagination-wh div.results span
2261 #content div.box div.pagination-wh div.results span
2235 {
2262 {
2236 margin: 0;
2263 margin: 0;
2237 padding: 6px 8px 6px 8px;
2264 padding: 6px 8px 6px 8px;
2238 height: 1%;
2265 height: 1%;
2239 display: block;
2266 display: block;
2240 float: left;
2267 float: left;
2241 background: #ebebeb url("../images/pager.png") repeat-x;
2268 background: #ebebeb url("../images/pager.png") repeat-x;
2242 border-top: 1px solid #dedede;
2269 border-top: 1px solid #dedede;
2243 border-left: 1px solid #cfcfcf;
2270 border-left: 1px solid #cfcfcf;
2244 border-right: 1px solid #c4c4c4;
2271 border-right: 1px solid #c4c4c4;
2245 border-bottom: 1px solid #c4c4c4;
2272 border-bottom: 1px solid #c4c4c4;
2246 color: #4A4A4A;
2273 color: #4A4A4A;
2247 font-weight: bold;
2274 font-weight: bold;
2248 }
2275 }
2249
2276
2250 #content div.box div.pagination-left{
2277 #content div.box div.pagination-left{
2251 float:left;
2278 float:left;
2252 }
2279 }
2253 #content div.box div.pagination-right{
2280 #content div.box div.pagination-right{
2254 float:right;
2281 float:right;
2255 }
2282 }
2256
2283
2257 #content div.box div.pagination-wh a,
2284 #content div.box div.pagination-wh a,
2258 #content div.box div.pagination-wh span.pager_dotdot
2285 #content div.box div.pagination-wh span.pager_dotdot
2259 {
2286 {
2260 margin: 0 0 0 4px;
2287 margin: 0 0 0 4px;
2261 padding: 6px;
2288 padding: 6px;
2262 height: 1%;
2289 height: 1%;
2263 float: left;
2290 float: left;
2264 background: #ebebeb url("../images/pager.png") repeat-x;
2291 background: #ebebeb url("../images/pager.png") repeat-x;
2265 border-top: 1px solid #dedede;
2292 border-top: 1px solid #dedede;
2266 border-left: 1px solid #cfcfcf;
2293 border-left: 1px solid #cfcfcf;
2267 border-right: 1px solid #c4c4c4;
2294 border-right: 1px solid #c4c4c4;
2268 border-bottom: 1px solid #c4c4c4;
2295 border-bottom: 1px solid #c4c4c4;
2269 color: #4A4A4A;
2296 color: #4A4A4A;
2270 font-weight: bold;
2297 font-weight: bold;
2271 }
2298 }
2272 #content div.box div.pagination-wh span.pager_curpage
2299 #content div.box div.pagination-wh span.pager_curpage
2273 {
2300 {
2274 margin: 0 0 0 4px;
2301 margin: 0 0 0 4px;
2275 padding: 6px;
2302 padding: 6px;
2276 height: 1%;
2303 height: 1%;
2277 float: left;
2304 float: left;
2278 background: #b4b4b4 url("../images/pager_selected.png") repeat-x;
2305 background: #b4b4b4 url("../images/pager_selected.png") repeat-x;
2279 border-top: 1px solid #cccccc;
2306 border-top: 1px solid #cccccc;
2280 border-left: 1px solid #bebebe;
2307 border-left: 1px solid #bebebe;
2281 border-right: 1px solid #b1b1b1;
2308 border-right: 1px solid #b1b1b1;
2282 border-bottom: 1px solid #afafaf;
2309 border-bottom: 1px solid #afafaf;
2283 color: #515151;
2310 color: #515151;
2284 font-weight: bold;
2311 font-weight: bold;
2285 }
2312 }
2286
2313
2287 #content div.box div.pagination-wh a.disabled
2314 #content div.box div.pagination-wh a.disabled
2288 {
2315 {
2289 padding: 6px;
2316 padding: 6px;
2290 color: #B4B4B4;
2317 color: #B4B4B4;
2291 }
2318 }
2292
2319
2293
2320
2294 #content div.box div.pagination-wh a:hover,
2321 #content div.box div.pagination-wh a:hover,
2295 #content div.box div.pagination-wh a:active
2322 #content div.box div.pagination-wh a:active
2296 {
2323 {
2297 background: #b4b4b4 url("../images/pager_selected.png") repeat-x;
2324 background: #b4b4b4 url("../images/pager_selected.png") repeat-x;
2298 border-top: 1px solid #cccccc;
2325 border-top: 1px solid #cccccc;
2299 border-left: 1px solid #bebebe;
2326 border-left: 1px solid #bebebe;
2300 border-right: 1px solid #b1b1b1;
2327 border-right: 1px solid #b1b1b1;
2301 border-bottom: 1px solid #afafaf;
2328 border-bottom: 1px solid #afafaf;
2302 text-decoration: none;
2329 text-decoration: none;
2303 }
2330 }
2304
2331
2305
2332
2306 /* -----------------------------------------------------------
2333 /* -----------------------------------------------------------
2307 content -> right -> traffic chart
2334 content -> right -> traffic chart
2308 ----------------------------------------------------------- */
2335 ----------------------------------------------------------- */
2309
2336
2310 #content div.box div.traffic
2337 #content div.box div.traffic
2311 {
2338 {
2312 margin: 0;
2339 margin: 0;
2313 padding: 0 20px 10px 20px;
2340 padding: 0 20px 10px 20px;
2314 clear: both;
2341 clear: both;
2315 overflow: hidden;
2342 overflow: hidden;
2316 }
2343 }
2317
2344
2318 #content div.box div.traffic div.legend
2345 #content div.box div.traffic div.legend
2319 {
2346 {
2320 margin: 0 0 10px 0;
2347 margin: 0 0 10px 0;
2321 padding: 0 0 10px 0;
2348 padding: 0 0 10px 0;
2322 clear: both;
2349 clear: both;
2323 overflow: hidden;
2350 overflow: hidden;
2324 border-bottom: 1px solid #dddddd;
2351 border-bottom: 1px solid #dddddd;
2325 }
2352 }
2326
2353
2327 #content div.box div.traffic div.legend h6
2354 #content div.box div.traffic div.legend h6
2328 {
2355 {
2329 margin: 0;
2356 margin: 0;
2330 padding: 0;
2357 padding: 0;
2331 float: left;
2358 float: left;
2332 border: none;
2359 border: none;
2333 }
2360 }
2334
2361
2335 #content div.box div.traffic div.legend ul
2362 #content div.box div.traffic div.legend ul
2336 {
2363 {
2337 margin: 0;
2364 margin: 0;
2338 padding: 0;
2365 padding: 0;
2339 float: right;
2366 float: right;
2340 }
2367 }
2341
2368
2342 #content div.box div.traffic div.legend li
2369 #content div.box div.traffic div.legend li
2343 {
2370 {
2344 margin: 0;
2371 margin: 0;
2345 padding: 0 8px 0 4px;
2372 padding: 0 8px 0 4px;
2346 list-style: none;
2373 list-style: none;
2347 float: left;
2374 float: left;
2348 font-size: 11px;
2375 font-size: 11px;
2349 }
2376 }
2350
2377
2351 #content div.box div.traffic div.legend li.visits
2378 #content div.box div.traffic div.legend li.visits
2352 {
2379 {
2353 border-left: 12px solid #edc240;
2380 border-left: 12px solid #edc240;
2354 }
2381 }
2355
2382
2356 #content div.box div.traffic div.legend li.pageviews
2383 #content div.box div.traffic div.legend li.pageviews
2357 {
2384 {
2358 border-left: 12px solid #afd8f8;
2385 border-left: 12px solid #afd8f8;
2359 }
2386 }
2360
2387
2361 #content div.box div.traffic table
2388 #content div.box div.traffic table
2362 {
2389 {
2363 width: auto;
2390 width: auto;
2364 }
2391 }
2365
2392
2366 #content div.box div.traffic table td
2393 #content div.box div.traffic table td
2367 {
2394 {
2368 padding: 2px 3px 3px 3px;
2395 padding: 2px 3px 3px 3px;
2369 background: transparent;
2396 background: transparent;
2370 border: none;
2397 border: none;
2371 }
2398 }
2372
2399
2373 #content div.box div.traffic table td.legendLabel
2400 #content div.box div.traffic table td.legendLabel
2374 {
2401 {
2375 padding: 0 3px 2px 3px;
2402 padding: 0 3px 2px 3px;
2376 }
2403 }
2377
2404
2378 /* -----------------------------------------------------------
2405 /* -----------------------------------------------------------
2379 footer
2406 footer
2380 ----------------------------------------------------------- */
2407 ----------------------------------------------------------- */
2381
2408
2382 #footer
2409 #footer
2383 {
2410 {
2384 margin: 0;
2411 margin: 0;
2385 padding: 5px 0 5px 0;
2412 padding: 5px 0 5px 0;
2386 clear: both;
2413 clear: both;
2387 overflow: hidden;
2414 overflow: hidden;
2388 background: #2a2a2a;
2415 background: #2a2a2a;
2389 text-align: right;
2416 text-align: right;
2390 }
2417 }
2391
2418
2392 #footer p
2419 #footer p
2393 {
2420 {
2394 margin: 0 80px 0 80px;
2421 margin: 0 80px 0 80px;
2395 padding: 10px 0 10px 0;
2422 padding: 10px 0 10px 0;
2396 color: #ffffff;
2423 color: #ffffff;
2397 }
2424 }
2398
2425
2399 /* -----------------------------------------------------------
2426 /* -----------------------------------------------------------
2400 login
2427 login
2401 ----------------------------------------------------------- */
2428 ----------------------------------------------------------- */
2402
2429
2403 #login
2430 #login
2404 {
2431 {
2405 margin: 10% auto 0 auto;
2432 margin: 10% auto 0 auto;
2406 padding: 0;
2433 padding: 0;
2407 width: 420px;
2434 width: 420px;
2408 }
2435 }
2409
2436
2410 /* -----------------------------------------------------------
2437 /* -----------------------------------------------------------
2411 login -> colors
2438 login -> colors
2412 ----------------------------------------------------------- */
2439 ----------------------------------------------------------- */
2413
2440
2414 #login div.color
2441 #login div.color
2415 {
2442 {
2416 margin: 10px auto 0 auto;
2443 margin: 10px auto 0 auto;
2417 padding: 3px 3px 3px 0;
2444 padding: 3px 3px 3px 0;
2418 clear: both;
2445 clear: both;
2419 overflow: hidden;
2446 overflow: hidden;
2420 background: #FFFFFF;
2447 background: #FFFFFF;
2421 }
2448 }
2422
2449
2423 #login div.color a
2450 #login div.color a
2424 {
2451 {
2425 margin: 0 0 0 3px;
2452 margin: 0 0 0 3px;
2426 padding: 0;
2453 padding: 0;
2427 width: 20px;
2454 width: 20px;
2428 height: 20px;
2455 height: 20px;
2429 display: block;
2456 display: block;
2430 float: left;
2457 float: left;
2431 }
2458 }
2432
2459
2433 /* -----------------------------------------------------------
2460 /* -----------------------------------------------------------
2434 login -> title
2461 login -> title
2435 ----------------------------------------------------------- */
2462 ----------------------------------------------------------- */
2436
2463
2437 #login div.title
2464 #login div.title
2438 {
2465 {
2439 margin: 0 auto;
2466 margin: 0 auto;
2440 padding: 0;
2467 padding: 0;
2441 width: 420px;
2468 width: 420px;
2442 clear: both;
2469 clear: both;
2443 overflow: hidden;
2470 overflow: hidden;
2444 position: relative;
2471 position: relative;
2445 background: #003367 url("../images/colors/blue/header_inner.png") repeat-x;
2472 background: #003367 url("../images/colors/blue/header_inner.png") repeat-x;
2446 }
2473 }
2447
2474
2448 #login div.title h5
2475 #login div.title h5
2449 {
2476 {
2450 margin: 10px;
2477 margin: 10px;
2451 padding: 0;
2478 padding: 0;
2452 color: #ffffff;
2479 color: #ffffff;
2453 }
2480 }
2454
2481
2455 /* -----------------------------------------------------------
2482 /* -----------------------------------------------------------
2456 login -> title / corners
2483 login -> title / corners
2457 ----------------------------------------------------------- */
2484 ----------------------------------------------------------- */
2458
2485
2459 #login div.title div.corner
2486 #login div.title div.corner
2460 {
2487 {
2461 height: 6px;
2488 height: 6px;
2462 width: 6px;
2489 width: 6px;
2463 position: absolute;
2490 position: absolute;
2464 background: url("../images/colors/blue/login_corners.png") no-repeat;
2491 background: url("../images/colors/blue/login_corners.png") no-repeat;
2465 }
2492 }
2466
2493
2467 #login div.title div.tl
2494 #login div.title div.tl
2468 {
2495 {
2469 top: 0;
2496 top: 0;
2470 left: 0;
2497 left: 0;
2471 background-position: 0 0;
2498 background-position: 0 0;
2472 }
2499 }
2473
2500
2474 #login div.title div.tr
2501 #login div.title div.tr
2475 {
2502 {
2476 top: 0;
2503 top: 0;
2477 right: 0;
2504 right: 0;
2478 background-position: -6px 0;
2505 background-position: -6px 0;
2479 }
2506 }
2480
2507
2481 #login div.inner
2508 #login div.inner
2482 {
2509 {
2483 margin: 0 auto;
2510 margin: 0 auto;
2484 padding: 20px;
2511 padding: 20px;
2485 width: 380px;
2512 width: 380px;
2486 background: #FFFFFF url("../images/login.png") no-repeat top left;
2513 background: #FFFFFF url("../images/login.png") no-repeat top left;
2487 border-top: none;
2514 border-top: none;
2488 border-bottom: none;
2515 border-bottom: none;
2489 }
2516 }
2490
2517
2491 /* -----------------------------------------------------------
2518 /* -----------------------------------------------------------
2492 login -> form
2519 login -> form
2493 ----------------------------------------------------------- */
2520 ----------------------------------------------------------- */
2494
2521
2495 #login div.form
2522 #login div.form
2496 {
2523 {
2497 margin: 0;
2524 margin: 0;
2498 padding: 0;
2525 padding: 0;
2499 clear: both;
2526 clear: both;
2500 overflow: hidden;
2527 overflow: hidden;
2501 }
2528 }
2502
2529
2503 #login div.form div.fields
2530 #login div.form div.fields
2504 {
2531 {
2505 margin: 0;
2532 margin: 0;
2506 padding: 0;
2533 padding: 0;
2507 clear: both;
2534 clear: both;
2508 overflow: hidden;
2535 overflow: hidden;
2509 }
2536 }
2510
2537
2511 #login div.form div.fields div.field
2538 #login div.form div.fields div.field
2512 {
2539 {
2513 margin: 0;
2540 margin: 0;
2514 padding: 0 0 10px 0;
2541 padding: 0 0 10px 0;
2515 clear: both;
2542 clear: both;
2516 overflow: hidden;
2543 overflow: hidden;
2517 }
2544 }
2518
2545
2519 #login div.form div.fields div.field span.error-message
2546 #login div.form div.fields div.field span.error-message
2520 {
2547 {
2521 margin: 8px 0 0 0;
2548 margin: 8px 0 0 0;
2522 padding: 0;
2549 padding: 0;
2523 height: 1%;
2550 height: 1%;
2524 display: block;
2551 display: block;
2525 color: #FF0000;
2552 color: #FF0000;
2526 }
2553 }
2527
2554
2528 #login div.form div.fields div.field div.label
2555 #login div.form div.fields div.field div.label
2529 {
2556 {
2530 margin: 2px 10px 0 0;
2557 margin: 2px 10px 0 0;
2531 padding: 5px 0 0 5px;
2558 padding: 5px 0 0 5px;
2532 width: 173px;
2559 width: 173px;
2533 float: left;
2560 float: left;
2534 text-align: right;
2561 text-align: right;
2535 }
2562 }
2536
2563
2537 #login div.form div.fields div.field div.label label
2564 #login div.form div.fields div.field div.label label
2538 {
2565 {
2539 color: #000000;
2566 color: #000000;
2540 font-weight: bold;
2567 font-weight: bold;
2541 }
2568 }
2542
2569
2543 #login div.form div.fields div.field div.label span
2570 #login div.form div.fields div.field div.label span
2544 {
2571 {
2545 margin: 0;
2572 margin: 0;
2546 padding: 2px 0 0 0;
2573 padding: 2px 0 0 0;
2547 height: 1%;
2574 height: 1%;
2548 display: block;
2575 display: block;
2549 color: #363636;
2576 color: #363636;
2550 }
2577 }
2551
2578
2552 #login div.form div.fields div.field div.input
2579 #login div.form div.fields div.field div.input
2553 {
2580 {
2554 margin: 0;
2581 margin: 0;
2555 padding: 0;
2582 padding: 0;
2556 float: left;
2583 float: left;
2557 }
2584 }
2558
2585
2559 #login div.form div.fields div.field div.input input
2586 #login div.form div.fields div.field div.input input
2560 {
2587 {
2561 margin: 0;
2588 margin: 0;
2562 padding: 7px 7px 6px 7px;
2589 padding: 7px 7px 6px 7px;
2563 width: 176px;
2590 width: 176px;
2564 background: #FFFFFF;
2591 background: #FFFFFF;
2565 border-top: 1px solid #b3b3b3;
2592 border-top: 1px solid #b3b3b3;
2566 border-left: 1px solid #b3b3b3;
2593 border-left: 1px solid #b3b3b3;
2567 border-right: 1px solid #eaeaea;
2594 border-right: 1px solid #eaeaea;
2568 border-bottom: 1px solid #eaeaea;
2595 border-bottom: 1px solid #eaeaea;
2569 color: #000000;
2596 color: #000000;
2570 font-family: Lucida Grande, Verdana, Lucida Sans Regular, Lucida Sans Unicode, Arial, sans-serif;
2597 font-family: Lucida Grande, Verdana, Lucida Sans Regular, Lucida Sans Unicode, Arial, sans-serif;
2571 font-size: 11px;
2598 font-size: 11px;
2572 }
2599 }
2573
2600
2574 #login div.form div.fields div.field div.input input.error
2601 #login div.form div.fields div.field div.input input.error
2575 {
2602 {
2576 background: #FBE3E4;
2603 background: #FBE3E4;
2577 border-top: 1px solid #e1b2b3;
2604 border-top: 1px solid #e1b2b3;
2578 border-left: 1px solid #e1b2b3;
2605 border-left: 1px solid #e1b2b3;
2579 border-right: 1px solid #FBC2C4;
2606 border-right: 1px solid #FBC2C4;
2580 border-bottom: 1px solid #FBC2C4;
2607 border-bottom: 1px solid #FBC2C4;
2581 }
2608 }
2582
2609
2583 #login div.form div.fields div.field div.input input.success
2610 #login div.form div.fields div.field div.input input.success
2584 {
2611 {
2585 background: #E6EFC2;
2612 background: #E6EFC2;
2586 border-top: 1px solid #cebb98;
2613 border-top: 1px solid #cebb98;
2587 border-left: 1px solid #cebb98;
2614 border-left: 1px solid #cebb98;
2588 border-right: 1px solid #c6d880;
2615 border-right: 1px solid #c6d880;
2589 border-bottom: 1px solid #c6d880;
2616 border-bottom: 1px solid #c6d880;
2590 }
2617 }
2591
2618
2592 #login div.form div.fields div.field div.input div.link
2619 #login div.form div.fields div.field div.input div.link
2593 {
2620 {
2594 margin: 6px 0 0 0;
2621 margin: 6px 0 0 0;
2595 padding: 0;
2622 padding: 0;
2596 text-align: right;
2623 text-align: right;
2597 }
2624 }
2598
2625
2599 #login div.form div.fields div.field div.checkbox
2626 #login div.form div.fields div.field div.checkbox
2600 {
2627 {
2601 margin: 0 0 0 184px;
2628 margin: 0 0 0 184px;
2602 padding: 0;
2629 padding: 0;
2603 }
2630 }
2604
2631
2605 #login div.form div.fields div.field div.checkbox label
2632 #login div.form div.fields div.field div.checkbox label
2606 {
2633 {
2607 color: #565656;
2634 color: #565656;
2608 font-weight: bold;
2635 font-weight: bold;
2609 }
2636 }
2610
2637
2611 #login div.form div.fields div.buttons
2638 #login div.form div.fields div.buttons
2612 {
2639 {
2613 margin: 0;
2640 margin: 0;
2614 padding: 10px 0 0 0;
2641 padding: 10px 0 0 0;
2615 clear: both;
2642 clear: both;
2616 overflow: hidden;
2643 overflow: hidden;
2617 border-top: 1px solid #DDDDDD;
2644 border-top: 1px solid #DDDDDD;
2618 text-align: right;
2645 text-align: right;
2619 }
2646 }
2620
2647
2621 #login div.form div.fields div.buttons input
2648 #login div.form div.fields div.buttons input
2622 {
2649 {
2623 margin: 0;
2650 margin: 0;
2624 color: #000000;
2651 color: #000000;
2625 font-size: 1.0em;
2652 font-size: 1.0em;
2626 font-weight: bold;
2653 font-weight: bold;
2627 font-family: Verdana, Helvetica, Sans-Serif;
2654 font-family: Verdana, Helvetica, Sans-Serif;
2628 }
2655 }
2629
2656
2630 #login div.form div.fields div.buttons input.ui-state-default
2657 #login div.form div.fields div.buttons input.ui-state-default
2631 {
2658 {
2632 margin: 0;
2659 margin: 0;
2633 padding: 6px 12px 6px 12px;
2660 padding: 6px 12px 6px 12px;
2634 background: #e5e3e3 url("../images/button.png") repeat-x;
2661 background: #e5e3e3 url("../images/button.png") repeat-x;
2635 border-top: 1px solid #DDDDDD;
2662 border-top: 1px solid #DDDDDD;
2636 border-left: 1px solid #c6c6c6;
2663 border-left: 1px solid #c6c6c6;
2637 border-right: 1px solid #DDDDDD;
2664 border-right: 1px solid #DDDDDD;
2638 border-bottom: 1px solid #c6c6c6;
2665 border-bottom: 1px solid #c6c6c6;
2639 color: #515151;
2666 color: #515151;
2640 }
2667 }
2641
2668
2642 #login div.form div.fields div.buttons input.ui-state-hover
2669 #login div.form div.fields div.buttons input.ui-state-hover
2643 {
2670 {
2644 margin: 0;
2671 margin: 0;
2645 padding: 6px 12px 6px 12px;
2672 padding: 6px 12px 6px 12px;
2646 background: #b4b4b4 url("../images/button_selected.png") repeat-x;
2673 background: #b4b4b4 url("../images/button_selected.png") repeat-x;
2647 border-top: 1px solid #cccccc;
2674 border-top: 1px solid #cccccc;
2648 border-left: 1px solid #bebebe;
2675 border-left: 1px solid #bebebe;
2649 border-right: 1px solid #b1b1b1;
2676 border-right: 1px solid #b1b1b1;
2650 border-bottom: 1px solid #afafaf;
2677 border-bottom: 1px solid #afafaf;
2651 color: #515151;
2678 color: #515151;
2652 }
2679 }
2653
2680
2654 /* -----------------------------------------------------------
2681 /* -----------------------------------------------------------
2655 login -> links
2682 login -> links
2656 ----------------------------------------------------------- */
2683 ----------------------------------------------------------- */
2657
2684
2658 #login div.form div.links
2685 #login div.form div.links
2659 {
2686 {
2660 margin: 10px 0 0 0;
2687 margin: 10px 0 0 0;
2661 padding: 0 0 2px 0;
2688 padding: 0 0 2px 0;
2662 clear: both;
2689 clear: both;
2663 overflow: hidden;
2690 overflow: hidden;
2664 }
2691 }
2665
2692
2666 /* -----------------------------------------------------------
2693 /* -----------------------------------------------------------
2667 register
2694 register
2668 ----------------------------------------------------------- */
2695 ----------------------------------------------------------- */
2669
2696
2670 #register
2697 #register
2671 {
2698 {
2672 margin: 10% auto 0 auto;
2699 margin: 10% auto 0 auto;
2673 padding: 0;
2700 padding: 0;
2674 width: 420px;
2701 width: 420px;
2675 }
2702 }
2676
2703
2677 /* -----------------------------------------------------------
2704 /* -----------------------------------------------------------
2678 register -> colors
2705 register -> colors
2679 ----------------------------------------------------------- */
2706 ----------------------------------------------------------- */
2680
2707
2681 #register div.color
2708 #register div.color
2682 {
2709 {
2683 margin: 10px auto 0 auto;
2710 margin: 10px auto 0 auto;
2684 padding: 3px 3px 3px 0;
2711 padding: 3px 3px 3px 0;
2685 clear: both;
2712 clear: both;
2686 overflow: hidden;
2713 overflow: hidden;
2687 background: #FFFFFF;
2714 background: #FFFFFF;
2688 }
2715 }
2689
2716
2690 #register div.color a
2717 #register div.color a
2691 {
2718 {
2692 margin: 0 0 0 3px;
2719 margin: 0 0 0 3px;
2693 padding: 0;
2720 padding: 0;
2694 width: 20px;
2721 width: 20px;
2695 height: 20px;
2722 height: 20px;
2696 display: block;
2723 display: block;
2697 float: left;
2724 float: left;
2698 }
2725 }
2699
2726
2700 /* -----------------------------------------------------------
2727 /* -----------------------------------------------------------
2701 register -> title
2728 register -> title
2702 ----------------------------------------------------------- */
2729 ----------------------------------------------------------- */
2703
2730
2704 #register div.title
2731 #register div.title
2705 {
2732 {
2706 margin: 0 auto;
2733 margin: 0 auto;
2707 padding: 0;
2734 padding: 0;
2708 width: 420px;
2735 width: 420px;
2709 clear: both;
2736 clear: both;
2710 overflow: hidden;
2737 overflow: hidden;
2711 position: relative;
2738 position: relative;
2712 background: #003367 url("../images/colors/blue/header_inner.png") repeat-x;
2739 background: #003367 url("../images/colors/blue/header_inner.png") repeat-x;
2713 }
2740 }
2714
2741
2715 #register div.title h5
2742 #register div.title h5
2716 {
2743 {
2717 margin: 10px;
2744 margin: 10px;
2718 padding: 0;
2745 padding: 0;
2719 color: #ffffff;
2746 color: #ffffff;
2720 }
2747 }
2721
2748
2722 /* -----------------------------------------------------------
2749 /* -----------------------------------------------------------
2723 register -> inner
2750 register -> inner
2724 ----------------------------------------------------------- */
2751 ----------------------------------------------------------- */
2725 #register div.title div.corner
2752 #register div.title div.corner
2726 {
2753 {
2727 height: 6px;
2754 height: 6px;
2728 width: 6px;
2755 width: 6px;
2729 position: absolute;
2756 position: absolute;
2730 background: url("../images/colors/blue/login_corners.png") no-repeat;
2757 background: url("../images/colors/blue/login_corners.png") no-repeat;
2731 }
2758 }
2732
2759
2733 #register div.title div.tl
2760 #register div.title div.tl
2734 {
2761 {
2735 top: 0;
2762 top: 0;
2736 left: 0;
2763 left: 0;
2737 background-position: 0 0;
2764 background-position: 0 0;
2738 }
2765 }
2739
2766
2740 #register div.title div.tr
2767 #register div.title div.tr
2741 {
2768 {
2742 top: 0;
2769 top: 0;
2743 right: 0;
2770 right: 0;
2744 background-position: -6px 0;
2771 background-position: -6px 0;
2745
2772
2746 }
2773 }
2747 #register div.inner
2774 #register div.inner
2748 {
2775 {
2749 margin: 0 auto;
2776 margin: 0 auto;
2750 padding: 20px;
2777 padding: 20px;
2751 width: 380px;
2778 width: 380px;
2752 background: #FFFFFF;
2779 background: #FFFFFF;
2753 border-top: none;
2780 border-top: none;
2754 border-bottom: none;
2781 border-bottom: none;
2755 }
2782 }
2756
2783
2757 /* -----------------------------------------------------------
2784 /* -----------------------------------------------------------
2758 register -> form
2785 register -> form
2759 ----------------------------------------------------------- */
2786 ----------------------------------------------------------- */
2760
2787
2761 #register div.form
2788 #register div.form
2762 {
2789 {
2763 margin: 0;
2790 margin: 0;
2764 padding: 0;
2791 padding: 0;
2765 clear: both;
2792 clear: both;
2766 overflow: hidden;
2793 overflow: hidden;
2767 }
2794 }
2768
2795
2769 #register div.form div.fields
2796 #register div.form div.fields
2770 {
2797 {
2771 margin: 0;
2798 margin: 0;
2772 padding: 0;
2799 padding: 0;
2773 clear: both;
2800 clear: both;
2774 overflow: hidden;
2801 overflow: hidden;
2775 }
2802 }
2776
2803
2777 #register div.form div.fields div.field
2804 #register div.form div.fields div.field
2778 {
2805 {
2779 margin: 0;
2806 margin: 0;
2780 padding: 0 0 10px 0;
2807 padding: 0 0 10px 0;
2781 clear: both;
2808 clear: both;
2782 overflow: hidden;
2809 overflow: hidden;
2783 }
2810 }
2784
2811
2785 #register div.form div.fields div.field span.error-message
2812 #register div.form div.fields div.field span.error-message
2786 {
2813 {
2787 margin: 8px 0 0 0;
2814 margin: 8px 0 0 0;
2788 padding: 0;
2815 padding: 0;
2789 height: 1%;
2816 height: 1%;
2790 display: block;
2817 display: block;
2791 color: #FF0000;
2818 color: #FF0000;
2792 }
2819 }
2793
2820
2794 #register div.form div.fields div.field div.label
2821 #register div.form div.fields div.field div.label
2795 {
2822 {
2796 margin: 2px 10px 0 0;
2823 margin: 2px 10px 0 0;
2797 padding: 5px 0 0 5px;
2824 padding: 5px 0 0 5px;
2798 width: 100px;
2825 width: 100px;
2799 float: left;
2826 float: left;
2800 text-align: right;
2827 text-align: right;
2801 }
2828 }
2802
2829
2803 #register div.form div.fields div.field div.label label
2830 #register div.form div.fields div.field div.label label
2804 {
2831 {
2805 color: #000000;
2832 color: #000000;
2806 font-weight: bold;
2833 font-weight: bold;
2807 }
2834 }
2808
2835
2809 #register div.form div.fields div.field div.label span
2836 #register div.form div.fields div.field div.label span
2810 {
2837 {
2811 margin: 0;
2838 margin: 0;
2812 padding: 2px 0 0 0;
2839 padding: 2px 0 0 0;
2813 height: 1%;
2840 height: 1%;
2814 display: block;
2841 display: block;
2815 color: #363636;
2842 color: #363636;
2816 }
2843 }
2817
2844
2818 #register div.form div.fields div.field div.input
2845 #register div.form div.fields div.field div.input
2819 {
2846 {
2820 margin: 0;
2847 margin: 0;
2821 padding: 0;
2848 padding: 0;
2822 float: left;
2849 float: left;
2823 }
2850 }
2824
2851
2825 #register div.form div.fields div.field div.input input
2852 #register div.form div.fields div.field div.input input
2826 {
2853 {
2827 margin: 0;
2854 margin: 0;
2828 padding: 7px 7px 6px 7px;
2855 padding: 7px 7px 6px 7px;
2829 width: 245px;
2856 width: 245px;
2830 background: #FFFFFF;
2857 background: #FFFFFF;
2831 border-top: 1px solid #b3b3b3;
2858 border-top: 1px solid #b3b3b3;
2832 border-left: 1px solid #b3b3b3;
2859 border-left: 1px solid #b3b3b3;
2833 border-right: 1px solid #eaeaea;
2860 border-right: 1px solid #eaeaea;
2834 border-bottom: 1px solid #eaeaea;
2861 border-bottom: 1px solid #eaeaea;
2835 color: #000000;
2862 color: #000000;
2836 font-family: Lucida Grande, Verdana, Lucida Sans Regular, Lucida Sans Unicode, Arial, sans-serif;
2863 font-family: Lucida Grande, Verdana, Lucida Sans Regular, Lucida Sans Unicode, Arial, sans-serif;
2837 font-size: 11px;
2864 font-size: 11px;
2838 }
2865 }
2839
2866
2840 #register div.form div.fields div.field div.input input.error
2867 #register div.form div.fields div.field div.input input.error
2841 {
2868 {
2842 background: #FBE3E4;
2869 background: #FBE3E4;
2843 border-top: 1px solid #e1b2b3;
2870 border-top: 1px solid #e1b2b3;
2844 border-left: 1px solid #e1b2b3;
2871 border-left: 1px solid #e1b2b3;
2845 border-right: 1px solid #FBC2C4;
2872 border-right: 1px solid #FBC2C4;
2846 border-bottom: 1px solid #FBC2C4;
2873 border-bottom: 1px solid #FBC2C4;
2847 }
2874 }
2848
2875
2849 #register div.form div.fields div.field div.input input.success
2876 #register div.form div.fields div.field div.input input.success
2850 {
2877 {
2851 background: #E6EFC2;
2878 background: #E6EFC2;
2852 border-top: 1px solid #cebb98;
2879 border-top: 1px solid #cebb98;
2853 border-left: 1px solid #cebb98;
2880 border-left: 1px solid #cebb98;
2854 border-right: 1px solid #c6d880;
2881 border-right: 1px solid #c6d880;
2855 border-bottom: 1px solid #c6d880;
2882 border-bottom: 1px solid #c6d880;
2856 }
2883 }
2857
2884
2858 #register div.form div.fields div.field div.input div.link
2885 #register div.form div.fields div.field div.input div.link
2859 {
2886 {
2860 margin: 6px 0 0 0;
2887 margin: 6px 0 0 0;
2861 padding: 0;
2888 padding: 0;
2862 text-align: right;
2889 text-align: right;
2863 }
2890 }
2864
2891
2865 #register div.form div.fields div.field div.checkbox
2892 #register div.form div.fields div.field div.checkbox
2866 {
2893 {
2867 margin: 0 0 0 184px;
2894 margin: 0 0 0 184px;
2868 padding: 0;
2895 padding: 0;
2869 }
2896 }
2870
2897
2871 #register div.form div.fields div.field div.checkbox label
2898 #register div.form div.fields div.field div.checkbox label
2872 {
2899 {
2873 color: #565656;
2900 color: #565656;
2874 font-weight: bold;
2901 font-weight: bold;
2875 }
2902 }
2876
2903
2877 #register div.form div.fields div.buttons
2904 #register div.form div.fields div.buttons
2878 {
2905 {
2879 margin: 0;
2906 margin: 0;
2880 padding: 10px 0 0 97px;
2907 padding: 10px 0 0 114px;
2881 clear: both;
2908 clear: both;
2882 overflow: hidden;
2909 overflow: hidden;
2883 border-top: 1px solid #DDDDDD;
2910 border-top: 1px solid #DDDDDD;
2884 text-align: left;
2911 text-align: left;
2885 }
2912 }
2886
2913
2887 #register div.form div.fields div.buttons input
2914 #register div.form div.fields div.buttons input
2888 {
2915 {
2889 margin: 0;
2916 margin: 0;
2890 color: #000000;
2917 color: #000000;
2891 font-size: 1.0em;
2918 font-size: 1.0em;
2892 font-weight: bold;
2919 font-weight: bold;
2893 font-family: Verdana, Helvetica, Sans-Serif;
2920 font-family: Verdana, Helvetica, Sans-Serif;
2894 }
2921 }
2895
2922
2896 #register div.form div.fields div.buttons input.ui-state-default
2923 #register div.form div.fields div.buttons input.ui-state-default
2897 {
2924 {
2898 margin: 0;
2925 margin: 0;
2899 padding: 6px 12px 6px 12px;
2926 padding: 6px 12px 6px 12px;
2900 background: #e5e3e3 url("../images/button.png") repeat-x;
2927 background: #e5e3e3 url("../images/button.png") repeat-x;
2901 border-top: 1px solid #DDDDDD;
2928 border-top: 1px solid #DDDDDD;
2902 border-left: 1px solid #c6c6c6;
2929 border-left: 1px solid #c6c6c6;
2903 border-right: 1px solid #DDDDDD;
2930 border-right: 1px solid #DDDDDD;
2904 border-bottom: 1px solid #c6c6c6;
2931 border-bottom: 1px solid #c6c6c6;
2905 color: #515151;
2932 color: #515151;
2906 }
2933 }
2907 #register div.form div.fields div.buttons div.highlight input.ui-state-default
2934 #register div.form div.fields div.buttons div.highlight input.ui-state-default
2908 {
2935 {
2909 background:url("../images/colors/blue/button_highlight.png") repeat-x scroll 0 0 #4E85BB;
2936 background:url("../images/colors/blue/button_highlight.png") repeat-x scroll 0 0 #4E85BB;
2910 border-color:#5C91A4 #2B7089 #1A6480 #2A6F89;
2937 border-color:#5C91A4 #2B7089 #1A6480 #2A6F89;
2911 border-style:solid;
2938 border-style:solid;
2912 border-width:1px;
2939 border-width:1px;
2913 color:#FFFFFF;
2940 color:#FFFFFF;
2914 }
2941 }
2915
2942
2916
2943
2917
2944
2918 #register div.form div.fields div.buttons input.ui-state-hover
2945 #register div.form div.fields div.buttons input.ui-state-hover
2919 {
2946 {
2920 margin: 0;
2947 margin: 0;
2921 padding: 6px 12px 6px 12px;
2948 padding: 6px 12px 6px 12px;
2922 background: #b4b4b4 url("../images/button_selected.png") repeat-x;
2949 background: #b4b4b4 url("../images/button_selected.png") repeat-x;
2923 border-top: 1px solid #cccccc;
2950 border-top: 1px solid #cccccc;
2924 border-left: 1px solid #bebebe;
2951 border-left: 1px solid #bebebe;
2925 border-right: 1px solid #b1b1b1;
2952 border-right: 1px solid #b1b1b1;
2926 border-bottom: 1px solid #afafaf;
2953 border-bottom: 1px solid #afafaf;
2927 color: #515151;
2954 color: #515151;
2928 }
2955 }
2929
2956
2930 #register div.form div.activation_msg {
2957 #register div.form div.activation_msg {
2931 padding-top:4px;
2958 padding-top:4px;
2932 padding-bottom:4px;
2959 padding-bottom:4px;
2933
2960
2934 }
2961 }
2935
2962
2936 /* -----------------------------------------------------------
2963 /* -----------------------------------------------------------
2937 SUMMARY
2964 SUMMARY
2938 ----------------------------------------------------------- */
2965 ----------------------------------------------------------- */
2939
2966
2940 #clone_url{
2967 #clone_url{
2941 border: none;
2968 border: none;
2942 }
2969 }
2943 /* -----------------------------------------------------------
2970 /* -----------------------------------------------------------
2944 FILES
2971 FILES
2945 ----------------------------------------------------------- */
2972 ----------------------------------------------------------- */
2946
2973
2947 h3.files_location{
2974 h3.files_location{
2948 font-size: 1.8em;
2975 font-size: 1.8em;
2949 font-weight: bold;
2976 font-weight: bold;
2950 margin: 10px 0 !important;
2977 margin: 10px 0 !important;
2951 border-bottom: none !important;
2978 border-bottom: none !important;
2952 }
2979 }
2953
2980
2954 #files_data.dl{
2981 #files_data.dl{
2955
2982
2956
2983
2957 }
2984 }
2958 #files_data dl dt{
2985 #files_data dl dt{
2959 float:left;
2986 float:left;
2960 margin:0 !important;
2987 margin:0 !important;
2961 padding:5px;
2988 padding:5px;
2962 width:115px;
2989 width:115px;
2963 }
2990 }
2964 #files_data dl dd{
2991 #files_data dl dd{
2965 margin:0 !important;
2992 margin:0 !important;
2966 padding: 5px !important;
2993 padding: 5px !important;
2967 }
2994 }
2968
2995
2969
2996
2970 /* -----------------------------------------------------------
2997 /* -----------------------------------------------------------
2971 CHANGESETS
2998 CHANGESETS
2972 ----------------------------------------------------------- */
2999 ----------------------------------------------------------- */
2973 #changeset_content {
3000 #changeset_content {
2974 border:1px solid #CCCCCC;
3001 border:1px solid #CCCCCC;
2975 padding:5px;
3002 padding:5px;
2976 }
3003 }
2977
3004
2978 #changeset_content .container .wrapper {
3005 #changeset_content .container .wrapper {
2979 width: 600px;
3006 width: 600px;
2980 }
3007 }
2981
3008
2982 #changeset_content .container {
3009 #changeset_content .container {
2983 height: 120px;
3010 height: 120px;
2984 }
3011 }
2985
3012
2986 #changeset_content .container .left {
3013 #changeset_content .container .left {
2987 float: left;
3014 float: left;
2988 width: 70%;
3015 width: 70%;
2989 padding-left: 5px;
3016 padding-left: 5px;
2990 }
3017 }
2991
3018
2992 #changeset_content .container .right {
3019 #changeset_content .container .right {
2993 float: right;
3020 float: right;
2994 width: 25%;
3021 width: 25%;
2995 text-align: right;
3022 text-align: right;
2996 }
3023 }
2997
3024
2998 #changeset_content .container .left .date {
3025 #changeset_content .container .left .date {
2999 font-weight: bold;
3026 font-weight: bold;
3000 }
3027 }
3001
3028
3002 #changeset_content .container .left .author {
3029 #changeset_content .container .left .author {
3003
3030
3004 }
3031 }
3005
3032
3006 #changeset_content .container .left .message {
3033 #changeset_content .container .left .message {
3007 font-style: italic;
3034 font-style: italic;
3008 color: #556CB5;
3035 color: #556CB5;
3009 }
3036 }
3010
3037
3011 .cs_files {
3038 .cs_files {
3012
3039
3013 }
3040 }
3014
3041
3015 .cs_files .cs_added {
3042 .cs_files .cs_added {
3016 background: url("/images/icons/page_white_add.png") no-repeat scroll 3px;
3043 background: url("/images/icons/page_white_add.png") no-repeat scroll 3px;
3017 /*background-color:#BBFFBB;*/
3044 /*background-color:#BBFFBB;*/
3018 height: 16px;
3045 height: 16px;
3019 padding-left: 20px;
3046 padding-left: 20px;
3020 margin-top: 7px;
3047 margin-top: 7px;
3021 text-align: left;
3048 text-align: left;
3022 }
3049 }
3023
3050
3024 .cs_files .cs_changed {
3051 .cs_files .cs_changed {
3025 background: url("/images/icons/page_white_edit.png") no-repeat scroll
3052 background: url("/images/icons/page_white_edit.png") no-repeat scroll
3026 3px;
3053 3px;
3027 /*background-color: #FFDD88;*/
3054 /*background-color: #FFDD88;*/
3028 height: 16px;
3055 height: 16px;
3029 padding-left: 20px;
3056 padding-left: 20px;
3030 margin-top: 7px;
3057 margin-top: 7px;
3031 text-align: left;
3058 text-align: left;
3032 }
3059 }
3033
3060
3034 .cs_files .cs_removed {
3061 .cs_files .cs_removed {
3035 background: url("/images/icons/page_white_delete.png") no-repeat scroll
3062 background: url("/images/icons/page_white_delete.png") no-repeat scroll
3036 3px;
3063 3px;
3037 /*background-color: #FF8888;*/
3064 /*background-color: #FF8888;*/
3038 height: 16px;
3065 height: 16px;
3039 padding-left: 20px;
3066 padding-left: 20px;
3040 margin-top: 7px;
3067 margin-top: 7px;
3041 text-align: left;
3068 text-align: left;
3042 }
3069 }
3043
3070
3044 /* -----------------------------------------------------------
3071 /* -----------------------------------------------------------
3045 CHANGESETS - CANVAS
3072 CHANGESETS - CANVAS
3046 ----------------------------------------------------------- */
3073 ----------------------------------------------------------- */
3047
3074
3048 #graph {
3075 #graph {
3049 overflow: hidden;
3076 overflow: hidden;
3050 }
3077 }
3051
3078
3052 #graph_nodes {
3079 #graph_nodes {
3053 width: 160px;
3080 width: 160px;
3054 float: left;
3081 float: left;
3055 margin-left:-50px;
3082 margin-left:-50px;
3056 margin-top: 5px;
3083 margin-top: 5px;
3057 }
3084 }
3058
3085
3059 #graph_content {
3086 #graph_content {
3060 width: 800px;
3087 width: 800px;
3061 float: left;
3088 float: left;
3062 }
3089 }
3063
3090
3064 #graph_content .container_header {
3091 #graph_content .container_header {
3065 border: 1px solid #CCCCCC;
3092 border: 1px solid #CCCCCC;
3066 padding:10px;
3093 padding:10px;
3067 }
3094 }
3068
3095
3069 #graph_content .container .wrapper {
3096 #graph_content .container .wrapper {
3070 width: 600px;
3097 width: 600px;
3071 }
3098 }
3072
3099
3073 #graph_content .container {
3100 #graph_content .container {
3074 border-bottom: 1px solid #CCCCCC;
3101 border-bottom: 1px solid #CCCCCC;
3075 border-left: 1px solid #CCCCCC;
3102 border-left: 1px solid #CCCCCC;
3076 border-right: 1px solid #CCCCCC;
3103 border-right: 1px solid #CCCCCC;
3077 min-height: 90px;
3104 min-height: 90px;
3078 overflow: hidden;
3105 overflow: hidden;
3079 font-size:1.2em;
3106 font-size:1.2em;
3080 }
3107 }
3081
3108
3082 #graph_content .container .left {
3109 #graph_content .container .left {
3083 float: left;
3110 float: left;
3084 width: 70%;
3111 width: 70%;
3085 padding-left: 5px;
3112 padding-left: 5px;
3086 }
3113 }
3087
3114
3088 #graph_content .container .right {
3115 #graph_content .container .right {
3089 float: right;
3116 float: right;
3090 width: 25%;
3117 width: 25%;
3091 text-align: right;
3118 text-align: right;
3092 }
3119 }
3093
3120
3094 #graph_content .container .left .date {
3121 #graph_content .container .left .date {
3095 font-weight: bold;
3122 font-weight: bold;
3096 }
3123 }
3097
3124
3098 #graph_content .container .left .author {
3125 #graph_content .container .left .author {
3099
3126
3100 }
3127 }
3101
3128
3102 #graph_content .container .left .message {
3129 #graph_content .container .left .message {
3103 font-size: 100%;
3130 font-size: 100%;
3104 padding-top: 3px;
3131 padding-top: 3px;
3105 }
3132 }
3106
3133
3107 .right div {
3134 .right div {
3108 clear: both;
3135 clear: both;
3109 }
3136 }
3110
3137
3111 .right .changes .added,.changed,.removed {
3138 .right .changes .added,.changed,.removed {
3112 border: 1px solid #DDDDDD;
3139 border: 1px solid #DDDDDD;
3113 display: block;
3140 display: block;
3114 float: right;
3141 float: right;
3115 font-size: 0.75em;
3142 font-size: 0.75em;
3116 text-align: center;
3143 text-align: center;
3117 min-width: 15px;
3144 min-width: 15px;
3118 }
3145 }
3119
3146
3120 .right .changes .added {
3147 .right .changes .added {
3121 background: #BBFFBB;
3148 background: #BBFFBB;
3122 }
3149 }
3123
3150
3124 .right .changes .changed {
3151 .right .changes .changed {
3125 background: #FFDD88;
3152 background: #FFDD88;
3126 }
3153 }
3127
3154
3128 .right .changes .removed {
3155 .right .changes .removed {
3129 background: #FF8888;
3156 background: #FF8888;
3130 }
3157 }
3131
3158
3132 .right .merge {
3159 .right .merge {
3133 vertical-align: top;
3160 vertical-align: top;
3134 font-size: 60%;
3161 font-size: 60%;
3135 font-weight: bold;
3162 font-weight: bold;
3136 }
3163 }
3137
3164
3138 .right .merge img {
3165 .right .merge img {
3139 vertical-align: bottom;
3166 vertical-align: bottom;
3140 }
3167 }
3141
3168
3142 .right .parent {
3169 .right .parent {
3143 font-size: 90%;
3170 font-size: 90%;
3144 font-family: monospace;
3171 font-family: monospace;
3145 }
3172 }
3146
3173
3147
3174
3148
3175
3149 /* -----------------------------------------------------------
3176 /* -----------------------------------------------------------
3150 FILE BROWSER
3177 FILE BROWSER
3151 ----------------------------------------------------------- */
3178 ----------------------------------------------------------- */
3152 div.browserblock {
3179 div.browserblock {
3153 overflow: hidden;
3180 overflow: hidden;
3154 padding: 0px;
3181 padding: 0px;
3155 border: 1px solid #ccc;
3182 border: 1px solid #ccc;
3156 background: #f8f8f8;
3183 background: #f8f8f8;
3157 font-size: 100%;
3184 font-size: 100%;
3158 line-height: 100%;
3185 line-height: 100%;
3159 /* new */
3186 /* new */
3160 line-height: 125%;
3187 line-height: 125%;
3161 }
3188 }
3162
3189
3163 div.browserblock .browser-header {
3190 div.browserblock .browser-header {
3164 border-bottom: 1px solid #CCCCCC;
3191 border-bottom: 1px solid #CCCCCC;
3165 background: #FFFFFF;
3192 background: #FFFFFF;
3166 color: blue;
3193 color: blue;
3167 padding: 10px 0 10px 0;
3194 padding: 10px 0 10px 0;
3168 }
3195 }
3169
3196
3170 div.browserblock .browser-header span {
3197 div.browserblock .browser-header span {
3171 margin-left: 25px;
3198 margin-left: 25px;
3172 font-weight: bold;
3199 font-weight: bold;
3173 }
3200 }
3174
3201
3175 div.browserblock .browser-body {
3202 div.browserblock .browser-body {
3176 background: #EEEEEE;
3203 background: #EEEEEE;
3177 }
3204 }
3178
3205
3179 table.code-browser {
3206 table.code-browser {
3180 border-collapse: collapse;
3207 border-collapse: collapse;
3181 width: 100%;
3208 width: 100%;
3182 }
3209 }
3183
3210
3184 table.code-browser tr {
3211 table.code-browser tr {
3185 margin: 3px;
3212 margin: 3px;
3186 }
3213 }
3187
3214
3188 table.code-browser thead th {
3215 table.code-browser thead th {
3189 background-color: #EEEEEE;
3216 background-color: #EEEEEE;
3190 height: 20px;
3217 height: 20px;
3191 font-size: 1.1em;
3218 font-size: 1.1em;
3192 font-weight: bold;
3219 font-weight: bold;
3193 text-align: center;
3220 text-align: center;
3194 text-align: left;
3221 text-align: left;
3195 padding-left: 10px;
3222 padding-left: 10px;
3196 }
3223 }
3197
3224
3198 table.code-browser tbody tr {
3225 table.code-browser tbody tr {
3199
3226
3200 }
3227 }
3201
3228
3202 table.code-browser tbody td {
3229 table.code-browser tbody td {
3203 padding-left: 10px;
3230 padding-left: 10px;
3204 height: 20px;
3231 height: 20px;
3205 }
3232 }
3206 table.code-browser .browser-file {
3233 table.code-browser .browser-file {
3207 background: url("/images/icons/document_16.png") no-repeat scroll 3px;
3234 background: url("/images/icons/document_16.png") no-repeat scroll 3px;
3208 height: 16px;
3235 height: 16px;
3209 padding-left: 20px;
3236 padding-left: 20px;
3210 text-align: left;
3237 text-align: left;
3211 }
3238 }
3212
3239
3213 table.code-browser .browser-dir {
3240 table.code-browser .browser-dir {
3214 background: url("/images/icons/folder_16.png") no-repeat scroll 3px;
3241 background: url("/images/icons/folder_16.png") no-repeat scroll 3px;
3215 height: 16px;
3242 height: 16px;
3216 padding-left: 20px;
3243 padding-left: 20px;
3217 text-align: left;
3244 text-align: left;
3218 }
3245 }
3219
3246
3220 /* -----------------------------------------------------------
3247 /* -----------------------------------------------------------
3221 ADMIN - SETTINGS
3248 ADMIN - SETTINGS
3222 ----------------------------------------------------------- */
3249 ----------------------------------------------------------- */
3223 #path_unlock{
3250 #path_unlock{
3224 color: red;
3251 color: red;
3225 font-size: 1.2em;
3252 font-size: 1.2em;
3226 padding-left: 4px;
3253 padding-left: 4px;
3227 }
3254 }
3228
3255
3229 /* -----------------------------------------------------------
3256 /* -----------------------------------------------------------
3230 INFOBOX
3257 INFOBOX
3231 ----------------------------------------------------------- */
3258 ----------------------------------------------------------- */
3232 .info_box *{
3259 .info_box *{
3233 background:url("../../images/pager.png") repeat-x scroll 0 0 #EBEBEB;
3260 background:url("../../images/pager.png") repeat-x scroll 0 0 #EBEBEB;
3234 border-color:#DEDEDE #C4C4C4 #C4C4C4 #CFCFCF;
3261 border-color:#DEDEDE #C4C4C4 #C4C4C4 #CFCFCF;
3235 border-style:solid;
3262 border-style:solid;
3236 border-width:1px;
3263 border-width:1px;
3237 color:#4A4A4A;
3264 color:#4A4A4A;
3238 display:block;
3265 display:block;
3239 font-weight:bold;
3266 font-weight:bold;
3240 height:1%;
3267 height:1%;
3241 padding:4px 6px;
3268 padding:4px 6px;
3242 display: inline;
3269 display: inline;
3243 }
3270 }
3244 .info_box span{
3271 .info_box span{
3245 margin-left:3px;
3272 margin-left:3px;
3246 margin-right:3px;
3273 margin-right:3px;
3247 }
3274 }
3248 .info_box input#at_rev {
3275 .info_box input#at_rev {
3249 padding:1px 3px 3px 2px;
3276 padding:1px 3px 3px 2px;
3250 text-align:center;
3277 text-align:center;
3251 }
3278 }
3252 .info_box input#view {
3279 .info_box input#view {
3253 padding:0px 3px 2px 2px;
3280 padding:0px 3px 2px 2px;
3254 text-align:center;
3281 text-align:center;
3255 }
3282 }
3256 /* -----------------------------------------------------------
3283 /* -----------------------------------------------------------
3257 YUI TOOLTIP
3284 YUI TOOLTIP
3258 ----------------------------------------------------------- */
3285 ----------------------------------------------------------- */
3259 .yui-overlay,.yui-panel-container {
3286 .yui-overlay,.yui-panel-container {
3260 visibility: hidden;
3287 visibility: hidden;
3261 position: absolute;
3288 position: absolute;
3262 z-index: 2;
3289 z-index: 2;
3263 }
3290 }
3264
3291
3265 .yui-tt {
3292 .yui-tt {
3266 visibility: hidden;
3293 visibility: hidden;
3267 position: absolute;
3294 position: absolute;
3268 color: #666666;
3295 color: #666666;
3269 background-color: #FFFFFF;
3296 background-color: #FFFFFF;
3270 font-family: arial, helvetica, verdana, sans-serif;
3297 font-family: arial, helvetica, verdana, sans-serif;
3271 padding: 8px;
3298 padding: 8px;
3272 border: 2px solid #556CB5;
3299 border: 2px solid #556CB5;
3273 font: 100% sans-serif;
3300 font: 100% sans-serif;
3274 width: auto;
3301 width: auto;
3275 opacity: 1.0;
3302 opacity: 1.0;
3276 }
3303 }
3277
3304
3278 .yui-tt-shadow {
3305 .yui-tt-shadow {
3279 display: none;
3306 display: none;
3280 }
3307 }
3281
3308
3282 /* -----------------------------------------------------------
3309 /* -----------------------------------------------------------
3283 YUI AUTOCOMPLETE
3310 YUI AUTOCOMPLETE
3284 ----------------------------------------------------------- */
3311 ----------------------------------------------------------- */
3285
3312
3286 .ac{
3313 .ac{
3287 vertical-align: top;
3314 vertical-align: top;
3288
3315
3289 }
3316 }
3290 .ac .match {
3317 .ac .match {
3291 font-weight:bold;
3318 font-weight:bold;
3292 }
3319 }
3293
3320
3294 .ac .yui-ac {
3321 .ac .yui-ac {
3295 position: relative;
3322 position: relative;
3296 font-family: arial;
3323 font-family: arial;
3297 font-size: 100%;
3324 font-size: 100%;
3298 }
3325 }
3299
3326
3300 .ac .perm_ac{
3327 .ac .perm_ac{
3301 width:15em;
3328 width:15em;
3302 }
3329 }
3303 /* styles for input field */
3330 /* styles for input field */
3304 .ac .yui-ac-input {
3331 .ac .yui-ac-input {
3305 width: 100%;
3332 width: 100%;
3306 }
3333 }
3307
3334
3308 /* styles for results container */
3335 /* styles for results container */
3309 .ac .yui-ac-container {
3336 .ac .yui-ac-container {
3310 position: absolute;
3337 position: absolute;
3311 top: 1.6em;
3338 top: 1.6em;
3312 width: 100%;
3339 width: 100%;
3313 }
3340 }
3314
3341
3315 /* styles for header/body/footer wrapper within container */
3342 /* styles for header/body/footer wrapper within container */
3316 .ac .yui-ac-content {
3343 .ac .yui-ac-content {
3317 position: absolute;
3344 position: absolute;
3318 width: 100%;
3345 width: 100%;
3319 border: 1px solid #808080;
3346 border: 1px solid #808080;
3320 background: #fff;
3347 background: #fff;
3321 overflow: hidden;
3348 overflow: hidden;
3322 z-index: 9050;
3349 z-index: 9050;
3323 }
3350 }
3324
3351
3325 /* styles for container shadow */
3352 /* styles for container shadow */
3326 .ac .yui-ac-shadow {
3353 .ac .yui-ac-shadow {
3327 position: absolute;
3354 position: absolute;
3328 margin: .3em;
3355 margin: .3em;
3329 width: 100%;
3356 width: 100%;
3330 background: #000;
3357 background: #000;
3331 -moz-opacity: 0.10;
3358 -moz-opacity: 0.10;
3332 opacity: .10;
3359 opacity: .10;
3333 filter: alpha(opacity = 10);
3360 filter: alpha(opacity = 10);
3334 z-index: 9049;
3361 z-index: 9049;
3335 }
3362 }
3336
3363
3337 /* styles for results list */
3364 /* styles for results list */
3338 .ac .yui-ac-content ul {
3365 .ac .yui-ac-content ul {
3339 margin: 0;
3366 margin: 0;
3340 padding: 0;
3367 padding: 0;
3341 width: 100%;
3368 width: 100%;
3342 }
3369 }
3343
3370
3344 /* styles for result item */
3371 /* styles for result item */
3345 .ac .yui-ac-content li {
3372 .ac .yui-ac-content li {
3346 margin: 0;
3373 margin: 0;
3347 padding: 2px 5px;
3374 padding: 2px 5px;
3348 cursor: default;
3375 cursor: default;
3349 white-space: nowrap;
3376 white-space: nowrap;
3350 }
3377 }
3351
3378
3352 /* styles for prehighlighted result item */
3379 /* styles for prehighlighted result item */
3353 .ac .yui-ac-content li.yui-ac-prehighlight {
3380 .ac .yui-ac-content li.yui-ac-prehighlight {
3354 background: #B3D4FF;
3381 background: #B3D4FF;
3355 }
3382 }
3356
3383
3357 /* styles for highlighted result item */
3384 /* styles for highlighted result item */
3358 .ac .yui-ac-content li.yui-ac-highlight {
3385 .ac .yui-ac-content li.yui-ac-highlight {
3359 background: #556CB5;
3386 background: #556CB5;
3360 color: #FFF;
3387 color: #FFF;
3361 }
3388 }
3362
3389
3363
3390
3364 /* -----------------------------------------------------------
3391 /* -----------------------------------------------------------
3365 ACTION ICONS
3392 ACTION ICONS
3366 ----------------------------------------------------------- */
3393 ----------------------------------------------------------- */
3367 .add_icon {
3394 .add_icon {
3368 background: url("/images/icons/add.png") no-repeat scroll 3px ;
3395 background: url("/images/icons/add.png") no-repeat scroll 3px ;
3369 height: 16px;
3396 height: 16px;
3370 padding-left: 20px;
3397 padding-left: 20px;
3371 padding-top: 1px;
3398 padding-top: 1px;
3372 text-align: left;
3399 text-align: left;
3373 }
3400 }
3374
3401
3375 .edit_icon {
3402 .edit_icon {
3376 background: url("/images/icons/folder_edit.png") no-repeat scroll 3px;
3403 background: url("/images/icons/folder_edit.png") no-repeat scroll 3px;
3377 height: 16px;
3404 height: 16px;
3378 padding-left: 20px;
3405 padding-left: 20px;
3379 padding-top: 1px;
3406 padding-top: 1px;
3380 text-align: left;
3407 text-align: left;
3381 }
3408 }
3382
3409
3383 .delete_icon {
3410 .delete_icon {
3384 background: url("/images/icons/delete.png") no-repeat scroll 3px;
3411 background: url("/images/icons/delete.png") no-repeat scroll 3px;
3385 height: 16px;
3412 height: 16px;
3386 padding-left: 20px;
3413 padding-left: 20px;
3387 padding-top: 1px;
3414 padding-top: 1px;
3388 text-align: left;
3415 text-align: left;
3389 }
3416 }
3390
3417
3391 .rss_icon {
3418 .rss_icon {
3392 background: url("/images/icons/rss_16.png") no-repeat scroll 3px;
3419 background: url("/images/icons/rss_16.png") no-repeat scroll 3px;
3393 height: 16px;
3420 height: 16px;
3394 padding-left: 20px;
3421 padding-left: 20px;
3395 padding-top: 1px;
3422 padding-top: 1px;
3396 text-align: left;
3423 text-align: left;
3397 }
3424 }
3398
3425
3399 .atom_icon {
3426 .atom_icon {
3400 background: url("/images/icons/atom.png") no-repeat scroll 3px;
3427 background: url("/images/icons/atom.png") no-repeat scroll 3px;
3401 height: 16px;
3428 height: 16px;
3402 padding-left: 20px;
3429 padding-left: 20px;
3403 padding-top: 1px;
3430 padding-top: 1px;
3404 text-align: left;
3431 text-align: left;
3405 }
3432 }
3406
3433
3407 .archive_icon {
3434 .archive_icon {
3408 background: url("/images/icons/compress.png") no-repeat scroll 3px;
3435 background: url("/images/icons/compress.png") no-repeat scroll 3px;
3409 height: 16px;
3436 height: 16px;
3410 padding-left: 20px;
3437 padding-left: 20px;
3411 text-align: left;
3438 text-align: left;
3412 padding-top: 1px;
3439 padding-top: 1px;
3413 }
3440 }
3414
3441
3415 .action_button {
3442 .action_button {
3416 border: 0px;
3443 border: 0px;
3417 display: block;
3444 display: block;
3418 }
3445 }
3419
3446
3420 .action_button:hover {
3447 .action_button:hover {
3421 border: 0px;
3448 border: 0px;
3422 font-style: italic;
3449 font-style: italic;
3423 cursor: pointer;
3450 cursor: pointer;
3424 }
3451 }
3425
3452
3426 /* -----------------------------------------------------------
3453 /* -----------------------------------------------------------
3427 REPO SWITCHER
3454 REPO SWITCHER
3428 ----------------------------------------------------------- */
3455 ----------------------------------------------------------- */
3429
3456
3430 #switch_repos{
3457 #switch_repos{
3431 position: absolute;
3458 position: absolute;
3432 height: 25px;
3459 height: 25px;
3433 z-index: 1;
3460 z-index: 1;
3434 }
3461 }
3435 #switch_repos select{
3462 #switch_repos select{
3436 min-width:150px;
3463 min-width:150px;
3437 max-height: 250px;
3464 max-height: 250px;
3438 z-index: 1;
3465 z-index: 1;
3439 }
3466 }
3440 /* -----------------------------------------------------------
3467 /* -----------------------------------------------------------
3441 BREADCRUMBS
3468 BREADCRUMBS
3442 ----------------------------------------------------------- */
3469 ----------------------------------------------------------- */
3443
3470
3444 .breadcrumbs{
3471 .breadcrumbs{
3445 border:medium none;
3472 border:medium none;
3446 color:#FFFFFF;
3473 color:#FFFFFF;
3447 float:left;
3474 float:left;
3448 margin:0;
3475 margin:0;
3449 padding:11px 0 11px 10px;
3476 padding:11px 0 11px 10px;
3450 text-transform:uppercase;
3477 text-transform:uppercase;
3451 font-weight: bold;
3478 font-weight: bold;
3452 font-size: 14px;
3479 font-size: 14px;
3453 }
3480 }
3454 .breadcrumbs a{
3481 .breadcrumbs a{
3455 color: #FFFFFF;
3482 color: #FFFFFF;
3456 }
3483 }
3457
3484
3458
3485
3459 /* -----------------------------------------------------------
3486 /* -----------------------------------------------------------
3460 FLASH MSG
3487 FLASH MSG
3461 ----------------------------------------------------------- */
3488 ----------------------------------------------------------- */
3462 .flash_msg ul {
3489 .flash_msg ul {
3463 margin: 0;
3490 margin: 0;
3464 padding: 0px 0px 10px 0px;
3491 padding: 0px 0px 10px 0px;
3465 }
3492 }
3466
3493
3467 .error_msg {
3494 .error_msg {
3468 background-color: #FFCFCF;
3495 background-color: #FFCFCF;
3469 background-image: url("/images/icons/error_msg.png");
3496 background-image: url("/images/icons/error_msg.png");
3470 border: 1px solid #FF9595;
3497 border: 1px solid #FF9595;
3471 color: #CC3300;
3498 color: #CC3300;
3472 }
3499 }
3473
3500
3474 .warning_msg {
3501 .warning_msg {
3475 background-color: #FFFBCC;
3502 background-color: #FFFBCC;
3476 background-image: url("/images/icons/warning_msg.png");
3503 background-image: url("/images/icons/warning_msg.png");
3477 border: 1px solid #FFF35E;
3504 border: 1px solid #FFF35E;
3478 color: #C69E00;
3505 color: #C69E00;
3479 }
3506 }
3480
3507
3481 .success_msg {
3508 .success_msg {
3482 background-color: #D5FFCF;
3509 background-color: #D5FFCF;
3483 background-image: url("/images/icons/success_msg.png");
3510 background-image: url("/images/icons/success_msg.png");
3484 border: 1px solid #97FF88;
3511 border: 1px solid #97FF88;
3485 color: #009900;
3512 color: #009900;
3486 }
3513 }
3487
3514
3488 .notice_msg {
3515 .notice_msg {
3489 background-color: #DCE3FF;
3516 background-color: #DCE3FF;
3490 background-image: url("/images/icons/notice_msg.png");
3517 background-image: url("/images/icons/notice_msg.png");
3491 border: 1px solid #93A8FF;
3518 border: 1px solid #93A8FF;
3492 color: #556CB5;
3519 color: #556CB5;
3493 }
3520 }
3494
3521
3495 .success_msg,.error_msg,.notice_msg,.warning_msg {
3522 .success_msg,.error_msg,.notice_msg,.warning_msg {
3496 background-position: 10px center;
3523 background-position: 10px center;
3497 background-repeat: no-repeat;
3524 background-repeat: no-repeat;
3498 font-size: 12px;
3525 font-size: 12px;
3499 font-weight: bold;
3526 font-weight: bold;
3500 min-height: 14px;
3527 min-height: 14px;
3501 line-height: 14px;
3528 line-height: 14px;
3502 margin-bottom: 0px;
3529 margin-bottom: 0px;
3503 margin-top: 0px;
3530 margin-top: 0px;
3504 padding: 6px 10px 6px 40px;
3531 padding: 6px 10px 6px 40px;
3505 display: block;
3532 display: block;
3506 overflow: auto;
3533 overflow: auto;
3507 }
3534 }
3508
3535
3509 #msg_close {
3536 #msg_close {
3510 background: transparent url("icons/cross_grey_small.png") no-repeat
3537 background: transparent url("icons/cross_grey_small.png") no-repeat
3511 scroll 0 0;
3538 scroll 0 0;
3512 cursor: pointer;
3539 cursor: pointer;
3513 height: 16px;
3540 height: 16px;
3514 position: absolute;
3541 position: absolute;
3515 right: 5px;
3542 right: 5px;
3516 top: 5px;
3543 top: 5px;
3517 width: 16px;
3544 width: 16px;
3518 }
3545 }
3519 /* -----------------------------------------------------------
3546 /* -----------------------------------------------------------
3520 YUI FLOT
3547 YUI FLOT
3521 ----------------------------------------------------------- */
3548 ----------------------------------------------------------- */
3522
3549
3523 div#commit_history{
3550 div#commit_history{
3524 float: left;
3551 float: left;
3525 }
3552 }
3526 div#legend_data{
3553 div#legend_data{
3527 float:left;
3554 float:left;
3528
3555
3529 }
3556 }
3530 div#legend_container {
3557 div#legend_container {
3531 float: left;
3558 float: left;
3532 }
3559 }
3533
3560
3534 div#legend_container table,div#legend_choices table{
3561 div#legend_container table,div#legend_choices table{
3535 width:auto !important;
3562 width:auto !important;
3536 }
3563 }
3537
3564
3538 div#legend_container table td{
3565 div#legend_container table td{
3539 border: none !important;
3566 border: none !important;
3540 padding: 0px !important;
3567 padding: 0px !important;
3541 height: 20px !important;
3568 height: 20px !important;
3542 }
3569 }
3543
3570
3544 div#legend_choices table td{
3571 div#legend_choices table td{
3545 border: none !important;
3572 border: none !important;
3546 padding: 0px !important;
3573 padding: 0px !important;
3547 height: 20px !important;
3574 height: 20px !important;
3548 }
3575 }
3549
3576
3550 div#legend_choices{
3577 div#legend_choices{
3551 float:left;
3578 float:left;
3552 }
3579 }
3553
3580
3554 /* -----------------------------------------------------------
3581 /* -----------------------------------------------------------
3555 PERMISSIONS TABLE
3582 PERMISSIONS TABLE
3556 ----------------------------------------------------------- */
3583 ----------------------------------------------------------- */
3557 table#permissions_manage{
3584 table#permissions_manage{
3558 width: 0 !important;
3585 width: 0 !important;
3559
3586
3560 }
3587 }
3561 table#permissions_manage span.private_repo_msg{
3588 table#permissions_manage span.private_repo_msg{
3562 font-size: 0.8em;
3589 font-size: 0.8em;
3563 opacity:0.6;
3590 opacity:0.6;
3564
3591
3565 }
3592 }
3566 table#permissions_manage td.private_repo_msg{
3593 table#permissions_manage td.private_repo_msg{
3567 font-size: 0.8em;
3594 font-size: 0.8em;
3568
3595
3569 }
3596 }
3570 table#permissions_manage tr#add_perm_input td{
3597 table#permissions_manage tr#add_perm_input td{
3571 vertical-align:middle;
3598 vertical-align:middle;
3572
3599
3573 }
3600 }
3574
3601
3575 /* -----------------------------------------------------------
3602 /* -----------------------------------------------------------
3576 GRAVATARS
3603 GRAVATARS
3577 ----------------------------------------------------------- */
3604 ----------------------------------------------------------- */
3578 div.gravatar{
3605 div.gravatar{
3579 background-color:white;
3606 background-color:white;
3580 border:1px solid #D0D0D0;
3607 border:1px solid #D0D0D0;
3581 float:left;
3608 float:left;
3582 margin-right:0.7em;
3609 margin-right:0.7em;
3583 padding: 2px 2px 0px;
3610 padding: 2px 2px 0px;
3584 }
3611 }
3585
3612
3586 /* -----------------------------------------------------------
3613 /* -----------------------------------------------------------
3587 STYLING OF LAYOUT
3614 STYLING OF LAYOUT
3588 ----------------------------------------------------------- */
3615 ----------------------------------------------------------- */
3589
3616
3590
3617
3591 /* -----------------------------------------------------------
3618 /* -----------------------------------------------------------
3592 GLOBAL WIDTH
3619 GLOBAL WIDTH
3593 ----------------------------------------------------------- */
3620 ----------------------------------------------------------- */
3594 #header,#content,#footer{
3621 #header,#content,#footer{
3595 min-width: 1224px;
3622 min-width: 1224px;
3596 }
3623 }
3597
3624
3598 /* -----------------------------------------------------------
3625 /* -----------------------------------------------------------
3599 content
3626 content
3600 ----------------------------------------------------------- */
3627 ----------------------------------------------------------- */
3601
3628
3602 #content
3629 #content
3603 {
3630 {
3604 margin: 10px 30px 0 30px;
3631 margin: 10px 30px 0 30px;
3605 padding: 0;
3632 padding: 0;
3606 min-height: 100%;
3633 min-height: 100%;
3607 clear: both;
3634 clear: both;
3608 overflow: hidden;
3635 overflow: hidden;
3609 background: transparent;
3636 background: transparent;
3610 }
3637 }
3611
3638
3612 /* -----------------------------------------------------------
3639 /* -----------------------------------------------------------
3613 content -> right -> forms -> labels
3640 content -> right -> forms -> labels
3614 ----------------------------------------------------------- */
3641 ----------------------------------------------------------- */
3615
3642
3616 #content div.box div.form div.fields div.field div.label
3643 #content div.box div.form div.fields div.field div.label
3617 {
3644 {
3618 left: 80px;
3645 left: 80px;
3619 margin: 0;
3646 margin: 0;
3620 padding: 8px 0 0 5px;
3647 padding: 8px 0 0 5px;
3621 width: auto;
3648 width: auto;
3622 position: absolute;
3649 position: absolute;
3623 }
3650 }
3624
3651
3625 #content div.box-left div.form div.fields div.field div.label,
3652 #content div.box-left div.form div.fields div.field div.label,
3626 #content div.box-right div.form div.fields div.field div.label
3653 #content div.box-right div.form div.fields div.field div.label
3627 {
3654 {
3628 left: 0;
3655 left: 0;
3629 margin: 0;
3656 margin: 0;
3630 padding: 0 0 8px 0;
3657 padding: 0 0 8px 0;
3631 width: auto;
3658 width: auto;
3632 position: relative;
3659 position: relative;
3633 } No newline at end of file
3660 }
@@ -1,41 +1,41 b''
1 ## -*- coding: utf-8 -*-
1 ## -*- coding: utf-8 -*-
2 %if c.users_log:
2 %if c.users_log:
3 <table>
3 <table>
4 <tr>
4 <tr>
5 <th class="left">${_('Username')}</th>
5 <th class="left">${_('Username')}</th>
6 <th class="left">${_('Repository')}</th>
6 <th class="left">${_('Repository')}</th>
7 <th class="left">${_('Action')}</th>
7 <th class="left">${_('Action')}</th>
8 <th class="left">${_('Date')}</th>
8 <th class="left">${_('Date')}</th>
9 <th class="left">${_('From IP')}</th>
9 <th class="left">${_('From IP')}</th>
10 </tr>
10 </tr>
11
11
12 %for cnt,l in enumerate(c.users_log):
12 %for cnt,l in enumerate(c.users_log):
13 <tr class="parity${cnt%2}">
13 <tr class="parity${cnt%2}">
14 <td>${l.user.username}</td>
14 <td>${h.link_to(l.user.username,h.url('edit_user', id=l.user.user_id))}</td>
15 <td>${l.repository}</td>
15 <td>${h.link_to(l.repository,h.url('summary_home',repo_name=l.repository))}</td>
16 <td>${l.action}</td>
16 <td>${l.action}</td>
17 <td>${l.action_date}</td>
17 <td>${l.action_date}</td>
18 <td>${l.user_ip}</td>
18 <td>${l.user_ip}</td>
19 </tr>
19 </tr>
20 %endfor
20 %endfor
21 </table>
21 </table>
22
22
23 <script type="text/javascript">
23 <script type="text/javascript">
24 var data_div = 'user_log';
24 var data_div = 'user_log';
25 YAHOO.util.Event.onDOMReady(function(){
25 YAHOO.util.Event.onDOMReady(function(){
26 YAHOO.util.Event.addListener(YAHOO.util.Dom.getElementsByClassName('pager_link'),"click",function(){
26 YAHOO.util.Event.addListener(YAHOO.util.Dom.getElementsByClassName('pager_link'),"click",function(){
27 YAHOO.util.Dom.setStyle('shortlog_data','opacity','0.3');});});
27 YAHOO.util.Dom.setStyle('shortlog_data','opacity','0.3');});});
28 </script>
28 </script>
29
29
30
30
31 <div class="pagination-wh pagination-left">
31 <div class="pagination-wh pagination-left">
32 ${c.users_log.pager('$link_previous ~2~ $link_next',
32 ${c.users_log.pager('$link_previous ~2~ $link_next',
33 onclick="""YAHOO.util.Connect.asyncRequest('GET','$partial_url',{
33 onclick="""YAHOO.util.Connect.asyncRequest('GET','$partial_url',{
34 success:function(o){YAHOO.util.Dom.get(data_div).innerHTML=o.responseText;
34 success:function(o){YAHOO.util.Dom.get(data_div).innerHTML=o.responseText;
35 YAHOO.util.Event.addListener(YAHOO.util.Dom.getElementsByClassName('pager_link'),"click",function(){
35 YAHOO.util.Event.addListener(YAHOO.util.Dom.getElementsByClassName('pager_link'),"click",function(){
36 YAHOO.util.Dom.setStyle(data_div,'opacity','0.3');});
36 YAHOO.util.Dom.setStyle(data_div,'opacity','0.3');});
37 YAHOO.util.Dom.setStyle(data_div,'opacity','1');}},null); return false;""")}
37 YAHOO.util.Dom.setStyle(data_div,'opacity','1');}},null); return false;""")}
38 </div>
38 </div>
39 %else:
39 %else:
40 ${_('No actions yet')}
40 ${_('No actions yet')}
41 %endif
41 %endif
@@ -1,68 +1,68 b''
1 ## -*- coding: utf-8 -*-
1 ## -*- coding: utf-8 -*-
2 <%inherit file="/base/base.html"/>
2 <%inherit file="/base/base.html"/>
3
3
4 <%def name="title()">
4 <%def name="title()">
5 ${_('Permissions administration')}
5 ${_('Permissions administration')}
6 </%def>
6 </%def>
7
7
8 <%def name="breadcrumbs_links()">
8 <%def name="breadcrumbs_links()">
9 ${h.link_to(_('Admin'),h.url('admin_home'))}
9 ${h.link_to(_('Admin'),h.url('admin_home'))}
10 &raquo;
10 &raquo;
11 ${_('Permissions')}
11 ${_('Permissions')}
12 </%def>
12 </%def>
13
13
14 <%def name="page_nav()">
14 <%def name="page_nav()">
15 ${self.menu('admin')}
15 ${self.menu('admin')}
16 </%def>
16 </%def>
17
17
18 <%def name="main()">
18 <%def name="main()">
19 <div class="box">
19 <div class="box">
20 <!-- box / title -->
20 <!-- box / title -->
21 <div class="title">
21 <div class="title">
22 ${self.breadcrumbs()}
22 ${self.breadcrumbs()}
23 </div>
23 </div>
24 <h3>${_('Default permissions')}</h3>
24 <h3>${_('Default permissions')}</h3>
25 ${h.form(url('permission', id='default'),method='put')}
25 ${h.form(url('permission', id='default'),method='put')}
26 <div class="form">
26 <div class="form">
27 <!-- fields -->
27 <!-- fields -->
28 <div class="fields">
28 <div class="fields">
29
29
30 <div class="field">
30 <div class="field">
31 <div class="label">
31 <div class="label">
32 <label for="default_perm">${_('Default repository permission')}:</label>
32 <label for="default_perm">${_('Repository permission')}:</label>
33 </div>
33 </div>
34 <div class="select">
34 <div class="select">
35 ${h.select('default_perm','',c.perms_choices)}
35 ${h.select('default_perm','',c.perms_choices)}
36
36
37 ${h.checkbox('overwrite_default','true')}
37 ${h.checkbox('overwrite_default','true')}
38 <label for="overwrite_default">
38 <label for="overwrite_default">
39 <span class="tooltip"
39 <span class="tooltip"
40 tooltip_title="${h.tooltip(_('All default permissions on each repository will be reset to choosen permission, note that all custom default permission on repositories will be lost'))}">
40 tooltip_title="${h.tooltip(_('All default permissions on each repository will be reset to choosen permission, note that all custom default permission on repositories will be lost'))}">
41 ${_('overwrite existing settings')}</span> </label>
41 ${_('overwrite existing settings')}</span> </label>
42 </div>
42 </div>
43 </div>
43 </div>
44 <div class="field">
44 <div class="field">
45 <div class="label">
45 <div class="label">
46 <label for="default_register">${_('Registration')}:</label>
46 <label for="default_register">${_('Registration')}:</label>
47 </div>
47 </div>
48 <div class="select">
48 <div class="select">
49 ${h.select('default_register','',c.register_choices)}
49 ${h.select('default_register','',c.register_choices)}
50 </div>
50 </div>
51 </div>
51 </div>
52 <div class="field">
52 <div class="field">
53 <div class="label">
53 <div class="label">
54 <label for="default_create">${_('Allow repository creation')}:</label>
54 <label for="default_create">${_('Repository creation')}:</label>
55 </div>
55 </div>
56 <div class="select">
56 <div class="select">
57 ${h.select('default_create','',c.create_choices)}
57 ${h.select('default_create','',c.create_choices)}
58 </div>
58 </div>
59 </div>
59 </div>
60
60
61 <div class="buttons">
61 <div class="buttons">
62 ${h.submit('set','set',class_="ui-button ui-widget ui-state-default ui-corner-all")}
62 ${h.submit('set','set',class_="ui-button ui-widget ui-state-default ui-corner-all")}
63 </div>
63 </div>
64 </div>
64 </div>
65 </div>
65 </div>
66 ${h.end_form()}
66 ${h.end_form()}
67 </div>
67 </div>
68 </%def>
68 </%def>
@@ -1,145 +1,170 b''
1 ## -*- coding: utf-8 -*-
1 ## -*- coding: utf-8 -*-
2 <%inherit file="/base/base.html"/>
2 <%inherit file="/base/base.html"/>
3
3
4 <%def name="title()">
4 <%def name="title()">
5 ${_('Settings administration')}
5 ${_('Settings administration')}
6 </%def>
6 </%def>
7
7
8 <%def name="breadcrumbs_links()">
8 <%def name="breadcrumbs_links()">
9 ${h.link_to(_('Admin'),h.url('admin_home'))} &raquo; ${_('Settings')}
9 ${h.link_to(_('Admin'),h.url('admin_home'))} &raquo; ${_('Settings')}
10 </%def>
10 </%def>
11
11
12 <%def name="page_nav()">
12 <%def name="page_nav()">
13 ${self.menu('admin')}
13 ${self.menu('admin')}
14 </%def>
14 </%def>
15
15
16 <%def name="main()">
16 <%def name="main()">
17 <div class="box">
17 <div class="box">
18 <!-- box / title -->
18 <!-- box / title -->
19 <div class="title">
19 <div class="title">
20 ${self.breadcrumbs()}
20 ${self.breadcrumbs()}
21 </div>
21 </div>
22 <!-- end box / title -->
22 <!-- end box / title -->
23
23
24 <h3>${_('Remap and rescan repositories')}</h3>
24 <h3>${_('Remap and rescan repositories')}</h3>
25 ${h.form(url('admin_setting', setting_id='mapping'),method='put')}
25 ${h.form(url('admin_setting', setting_id='mapping'),method='put')}
26 <div class="form">
26 <div class="form">
27 <!-- fields -->
27 <!-- fields -->
28
28
29 <div class="fields">
29 <div class="fields">
30 <div class="field">
30 <div class="field">
31 <div class="label label-checkbox">
31 <div class="label label-checkbox">
32 <label for="destroy">${_('rescan option')}:</label>
32 <label for="destroy">${_('rescan option')}:</label>
33 </div>
33 </div>
34 <div class="checkboxes">
34 <div class="checkboxes">
35 <div class="checkbox">
35 <div class="checkbox">
36 ${h.checkbox('destroy',True)}
36 ${h.checkbox('destroy',True)}
37 <label for="checkbox-1">
37 <label for="checkbox-1">
38 <span class="tooltip" tooltip_title="${h.tooltip(_('In case a repository was deleted from filesystem and there are leftovers in the database check this option to scan obsolete data in database and remove it.'))}">
38 <span class="tooltip" tooltip_title="${h.tooltip(_('In case a repository was deleted from filesystem and there are leftovers in the database check this option to scan obsolete data in database and remove it.'))}">
39 ${_('destroy old data')}</span> </label>
39 ${_('destroy old data')}</span> </label>
40 </div>
40 </div>
41 </div>
41 </div>
42 </div>
42 </div>
43
43
44 <div class="buttons">
44 <div class="buttons">
45 ${h.submit('rescan','rescan repositories',class_="ui-button ui-widget ui-state-default ui-corner-all")}
45 ${h.submit('rescan','rescan repositories',class_="ui-button ui-widget ui-state-default ui-corner-all")}
46 </div>
46 </div>
47 </div>
47 </div>
48 </div>
48 </div>
49 ${h.end_form()}
49 ${h.end_form()}
50
50
51 <h3>${_('Whoosh indexing')}</h3>
52 ${h.form(url('admin_setting', setting_id='whoosh'),method='put')}
53 <div class="form">
54 <!-- fields -->
55
56 <div class="fields">
57 <div class="field">
58 <div class="label label-checkbox">
59 <label for="destroy">${_('index build option')}:</label>
60 </div>
61 <div class="checkboxes">
62 <div class="checkbox">
63 ${h.checkbox('full_index',True)}
64 <label for="checkbox-1">${_('build from scratch')}</label>
65 </div>
66 </div>
67 </div>
68
69 <div class="buttons">
70 ${h.submit('reindex','reindex',class_="ui-button ui-widget ui-state-default ui-corner-all")}
71 </div>
72 </div>
73 </div>
74 ${h.end_form()}
75
51 <h3>${_('Global application settings')}</h3>
76 <h3>${_('Global application settings')}</h3>
52 ${h.form(url('admin_setting', setting_id='global'),method='put')}
77 ${h.form(url('admin_setting', setting_id='global'),method='put')}
53 <div class="form">
78 <div class="form">
54 <!-- fields -->
79 <!-- fields -->
55
80
56 <div class="fields">
81 <div class="fields">
57
82
58 <div class="field">
83 <div class="field">
59 <div class="label">
84 <div class="label">
60 <label for="hg_app_title">${_('Application name')}:</label>
85 <label for="hg_app_title">${_('Application name')}:</label>
61 </div>
86 </div>
62 <div class="input">
87 <div class="input">
63 ${h.text('hg_app_title',size=30)}
88 ${h.text('hg_app_title',size=30)}
64 </div>
89 </div>
65 </div>
90 </div>
66
91
67 <div class="field">
92 <div class="field">
68 <div class="label">
93 <div class="label">
69 <label for="hg_app_realm">${_('Realm text')}:</label>
94 <label for="hg_app_realm">${_('Realm text')}:</label>
70 </div>
95 </div>
71 <div class="input">
96 <div class="input">
72 ${h.text('hg_app_realm',size=30)}
97 ${h.text('hg_app_realm',size=30)}
73 </div>
98 </div>
74 </div>
99 </div>
75
100
76 <div class="buttons">
101 <div class="buttons">
77 ${h.submit('save','save settings',class_="ui-button ui-widget ui-state-default ui-corner-all")}
102 ${h.submit('save','save settings',class_="ui-button ui-widget ui-state-default ui-corner-all")}
78 </div>
103 </div>
79 </div>
104 </div>
80 </div>
105 </div>
81 ${h.end_form()}
106 ${h.end_form()}
82
107
83 <h3>${_('Mercurial settings')}</h3>
108 <h3>${_('Mercurial settings')}</h3>
84 ${h.form(url('admin_setting', setting_id='mercurial'),method='put')}
109 ${h.form(url('admin_setting', setting_id='mercurial'),method='put')}
85 <div class="form">
110 <div class="form">
86 <!-- fields -->
111 <!-- fields -->
87
112
88 <div class="fields">
113 <div class="fields">
89
114
90 <div class="field">
115 <div class="field">
91 <div class="label label-checkbox">
116 <div class="label label-checkbox">
92 <label for="web_push_ssl">${_('Web')}:</label>
117 <label for="web_push_ssl">${_('Web')}:</label>
93 </div>
118 </div>
94 <div class="checkboxes">
119 <div class="checkboxes">
95 <div class="checkbox">
120 <div class="checkbox">
96 ${h.checkbox('web_push_ssl','true')}
121 ${h.checkbox('web_push_ssl','true')}
97 <label for="web_push_ssl">${_('require ssl for pushing')}</label>
122 <label for="web_push_ssl">${_('require ssl for pushing')}</label>
98 </div>
123 </div>
99 </div>
124 </div>
100 </div>
125 </div>
101
126
102 <div class="field">
127 <div class="field">
103 <div class="label label-checkbox">
128 <div class="label label-checkbox">
104 <label for="web_push_ssl">${_('Hooks')}:</label>
129 <label for="web_push_ssl">${_('Hooks')}:</label>
105 </div>
130 </div>
106 <div class="checkboxes">
131 <div class="checkboxes">
107 <div class="checkbox">
132 <div class="checkbox">
108 ${h.checkbox('hooks_changegroup_update','True')}
133 ${h.checkbox('hooks_changegroup_update','True')}
109 <label for="hooks_changegroup_update">${_('Update repository after push (hg update)')}</label>
134 <label for="hooks_changegroup_update">${_('Update repository after push (hg update)')}</label>
110 </div>
135 </div>
111 <div class="checkbox">
136 <div class="checkbox">
112 ${h.checkbox('hooks_changegroup_repo_size','True')}
137 ${h.checkbox('hooks_changegroup_repo_size','True')}
113 <label for="hooks_changegroup_repo_size">${_('Show repository size after push')}</label>
138 <label for="hooks_changegroup_repo_size">${_('Show repository size after push')}</label>
114 </div>
139 </div>
115 </div>
140 </div>
116 </div>
141 </div>
117
142
118 <div class="field">
143 <div class="field">
119 <div class="label">
144 <div class="label">
120 <label for="paths_root_path">${_('Repositories location')}:</label>
145 <label for="paths_root_path">${_('Repositories location')}:</label>
121 </div>
146 </div>
122 <div class="input">
147 <div class="input">
123 ${h.text('paths_root_path',size=30,readonly="readonly")}
148 ${h.text('paths_root_path',size=30,readonly="readonly")}
124 <span id="path_unlock" class="tooltip"
149 <span id="path_unlock" class="tooltip"
125 tooltip_title="${h.tooltip(_('This a crucial application setting. If You really sure you need to change this, you must restart application in order to make this settings take effect. Click this label to unlock.'))}">
150 tooltip_title="${h.tooltip(_('This a crucial application setting. If You really sure you need to change this, you must restart application in order to make this settings take effect. Click this label to unlock.'))}">
126 ${_('unlock')}</span>
151 ${_('unlock')}</span>
127 </div>
152 </div>
128 </div>
153 </div>
129
154
130 <div class="buttons">
155 <div class="buttons">
131 ${h.submit('save','save settings',class_="ui-button ui-widget ui-state-default ui-corner-all")}
156 ${h.submit('save','save settings',class_="ui-button ui-widget ui-state-default ui-corner-all")}
132 </div>
157 </div>
133 </div>
158 </div>
134 </div>
159 </div>
135 ${h.end_form()}
160 ${h.end_form()}
136
161
137 <script type="text/javascript">
162 <script type="text/javascript">
138 YAHOO.util.Event.onDOMReady(function(){
163 YAHOO.util.Event.onDOMReady(function(){
139 YAHOO.util.Event.addListener('path_unlock','click',function(){
164 YAHOO.util.Event.addListener('path_unlock','click',function(){
140 YAHOO.util.Dom.get('paths_root_path').removeAttribute('readonly');
165 YAHOO.util.Dom.get('paths_root_path').removeAttribute('readonly');
141 });
166 });
142 });
167 });
143 </script>
168 </script>
144 </div>
169 </div>
145 </%def>
170 </%def>
@@ -1,242 +1,247 b''
1 ## -*- coding: utf-8 -*-
1 ## -*- coding: utf-8 -*-
2 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
2 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
3 <html xmlns="http://www.w3.org/1999/xhtml" id="mainhtml">
3 <html xmlns="http://www.w3.org/1999/xhtml" id="mainhtml">
4 <head>
4 <head>
5 <title>${next.title()}</title>
5 <title>${next.title()}</title>
6 <link rel="icon" href="/images/hgicon.png" type="image/png" />
6 <link rel="icon" href="/images/hgicon.png" type="image/png" />
7 <meta http-equiv="Content-Type" content="text/html;charset=utf-8" />
7 <meta http-equiv="Content-Type" content="text/html;charset=utf-8" />
8 <meta name="robots" content="index, nofollow"/>
8 <meta name="robots" content="index, nofollow"/>
9 <!-- stylesheets -->
9 <!-- stylesheets -->
10 ${self.css()}
10 ${self.css()}
11 <!-- scripts -->
11 <!-- scripts -->
12 ${self.js()}
12 ${self.js()}
13 </head>
13 </head>
14 <body>
14 <body>
15 <!-- header -->
15 <!-- header -->
16 <div id="header">
16 <div id="header">
17 <!-- user -->
17 <!-- user -->
18 <ul id="logged-user">
18 <ul id="logged-user">
19 <li class="first">
19 <li class="first">
20 <div class="gravatar">
20 <div class="gravatar">
21 <img alt="gravatar" src="${h.gravatar_url(c.hg_app_user.email,24)}" />
21 <img alt="gravatar" src="${h.gravatar_url(c.hg_app_user.email,24)}" />
22 </div>
22 </div>
23 <div class="account">
23 <div class="account">
24 ${h.link_to('%s %s'%(c.hg_app_user.name,c.hg_app_user.lastname),h.url('admin_settings_my_account'))}<br/>
24 ${h.link_to('%s %s'%(c.hg_app_user.name,c.hg_app_user.lastname),h.url('admin_settings_my_account'))}<br/>
25 ${h.link_to(c.hg_app_user.username,h.url('admin_settings_my_account'))}
25 ${h.link_to(c.hg_app_user.username,h.url('admin_settings_my_account'))}
26 </div>
26 </div>
27 </li>
27 </li>
28 <li class="last highlight">${h.link_to(u'Logout',h.url('logout_home'))}</li>
28 <li class="last highlight">${h.link_to(u'Logout',h.url('logout_home'))}</li>
29 </ul>
29 </ul>
30 <!-- end user -->
30 <!-- end user -->
31 <div id="header-inner">
31 <div id="header-inner">
32 <div id="home">
32 <div id="home">
33 <a href="${h.url('hg_home')}"></a>
33 <a href="${h.url('hg_home')}"></a>
34 </div>
34 </div>
35 <!-- logo -->
35 <!-- logo -->
36 <div id="logo">
36 <div id="logo">
37 <h1><a href="${h.url('hg_home')}">${c.hg_app_name}</a></h1>
37 <h1><a href="${h.url('hg_home')}">${c.hg_app_name}</a></h1>
38 </div>
38 </div>
39 <!-- end logo -->
39 <!-- end logo -->
40 <!-- quick menu -->
40 <!-- quick menu -->
41 ${self.page_nav()}
41 ${self.page_nav()}
42 <!-- end quick -->
42 <!-- end quick -->
43 <div class="corner tl"></div>
43 <div class="corner tl"></div>
44 <div class="corner tr"></div>
44 <div class="corner tr"></div>
45 </div>
45 </div>
46 </div>
46 </div>
47 <!-- end header -->
47 <!-- end header -->
48
48
49 <!-- CONTENT -->
49 <!-- CONTENT -->
50 <div id="content">
50 <div id="content">
51 <div class="flash_msg">
51 <div class="flash_msg">
52 <% messages = h.flash.pop_messages() %>
52 <% messages = h.flash.pop_messages() %>
53 % if messages:
53 % if messages:
54 <ul id="flash-messages">
54 <ul id="flash-messages">
55 % for message in messages:
55 % for message in messages:
56 <li class="${message.category}_msg">${message}</li>
56 <li class="${message.category}_msg">${message}</li>
57 % endfor
57 % endfor
58 </ul>
58 </ul>
59 % endif
59 % endif
60 </div>
60 </div>
61 <div id="main">
61 <div id="main">
62 ${next.main()}
62 ${next.main()}
63 </div>
63 </div>
64 </div>
64 </div>
65 <!-- END CONTENT -->
65 <!-- END CONTENT -->
66
66
67 <!-- footer -->
67 <!-- footer -->
68 <div id="footer">
68 <div id="footer">
69 <p>Hg App ${c.hg_app_version} &copy; 2010 by Marcin Kuzminski</p>
69 <p>Hg App ${c.hg_app_version} &copy; 2010 by Marcin Kuzminski</p>
70 <script type="text/javascript">${h.tooltip.activate()}</script>
70 <script type="text/javascript">${h.tooltip.activate()}</script>
71 </div>
71 </div>
72 <!-- end footer -->
72 <!-- end footer -->
73 </body>
73 </body>
74
74
75 </html>
75 </html>
76
76
77 ### MAKO DEFS ###
77 ### MAKO DEFS ###
78 <%def name="page_nav()">
78 <%def name="page_nav()">
79 ${self.menu()}
79 ${self.menu()}
80 </%def>
80 </%def>
81
81
82 <%def name="menu(current=None)">
82 <%def name="menu(current=None)">
83 <%
83 <%
84 def is_current(selected):
84 def is_current(selected):
85 if selected == current:
85 if selected == current:
86 return h.literal('class="current"')
86 return h.literal('class="current"')
87 %>
87 %>
88 %if current not in ['home','admin']:
88 %if current not in ['home','admin']:
89 ##REGULAR MENU
89 ##REGULAR MENU
90 <ul id="quick">
90 <ul id="quick">
91 <!-- repo switcher -->
91 <!-- repo switcher -->
92 <li>
92 <li>
93 <a id="repo_switcher" title="${_('Switch repository')}" href="#">
93 <a id="repo_switcher" title="${_('Switch repository')}" href="#">
94 <span class="icon">
94 <span class="icon">
95 <img src="/images/icons/database.png" alt="${_('Products')}" />
95 <img src="/images/icons/database.png" alt="${_('Products')}" />
96 </span>
96 </span>
97 <span>&darr;</span>
97 <span>&darr;</span>
98 </a>
98 </a>
99 <ul class="repo_switcher">
99 <ul class="repo_switcher">
100 %for repo in c.repo_switcher_list:
100 %for repo,private in c.repo_switcher_list:
101 <li>${h.link_to(repo,h.url('summary_home',repo_name=repo))}</li>
101 %if private:
102 <li>${h.link_to(repo,h.url('summary_home',repo_name=repo),class_="private_repo")}</li>
103 %else:
104 <li>${h.link_to(repo,h.url('summary_home',repo_name=repo),class_="public_repo")}</li>
105 %endif
102 %endfor
106 %endfor
103 </ul>
107 </ul>
104 </li>
108 </li>
105
109
106 <li ${is_current('summary')}>
110 <li ${is_current('summary')}>
107 <a title="${_('Summary')}" href="${h.url('summary_home',repo_name=c.repo_name)}">
111 <a title="${_('Summary')}" href="${h.url('summary_home',repo_name=c.repo_name)}">
108 <span class="icon">
112 <span class="icon">
109 <img src="/images/icons/clipboard_16.png" alt="${_('Summary')}" />
113 <img src="/images/icons/clipboard_16.png" alt="${_('Summary')}" />
110 </span>
114 </span>
111 <span>${_('Summary')}</span>
115 <span>${_('Summary')}</span>
112 </a>
116 </a>
113 </li>
117 </li>
114 <li ${is_current('shortlog')}>
118 <li ${is_current('shortlog')}>
115 <a title="${_('Shortlog')}" href="${h.url('shortlog_home',repo_name=c.repo_name)}">
119 <a title="${_('Shortlog')}" href="${h.url('shortlog_home',repo_name=c.repo_name)}">
116 <span class="icon">
120 <span class="icon">
117 <img src="/images/icons/application_double.png" alt="${_('Shortlog')}" />
121 <img src="/images/icons/application_double.png" alt="${_('Shortlog')}" />
118 </span>
122 </span>
119 <span>${_('Shortlog')}</span>
123 <span>${_('Shortlog')}</span>
120 </a>
124 </a>
121 </li>
125 </li>
122 <li ${is_current('changelog')}>
126 <li ${is_current('changelog')}>
123 <a title="${_('Changelog')}" href="${h.url('changelog_home',repo_name=c.repo_name)}">
127 <a title="${_('Changelog')}" href="${h.url('changelog_home',repo_name=c.repo_name)}">
124 <span class="icon">
128 <span class="icon">
125 <img src="/images/icons/time.png" alt="${_('Changelog')}" />
129 <img src="/images/icons/time.png" alt="${_('Changelog')}" />
126 </span>
130 </span>
127 <span>${_('Changelog')}</span>
131 <span>${_('Changelog')}</span>
128 </a>
132 </a>
129 </li>
133 </li>
130
134
131 <li ${is_current('switch_to')}>
135 <li ${is_current('switch_to')}>
132 <a title="${_('Switch to')}" href="#">
136 <a title="${_('Switch to')}" href="#">
133 <span class="icon">
137 <span class="icon">
134 <img src="/images/icons/arrow_switch.png" alt="${_('Switch to')}" />
138 <img src="/images/icons/arrow_switch.png" alt="${_('Switch to')}" />
135 </span>
139 </span>
136 <span>${_('Switch to')}</span>
140 <span>${_('Switch to')}</span>
137 </a>
141 </a>
138 <ul>
142 <ul>
139 <li>
143 <li>
140 ${h.link_to(_('branches'),h.url('branches_home',repo_name=c.repo_name),class_='branches childs')}
144 ${h.link_to(_('branches'),h.url('branches_home',repo_name=c.repo_name),class_='branches childs')}
141 <ul>
145 <ul>
142 %for cnt,branch in enumerate(c.repository_branches.items()):
146 %for cnt,branch in enumerate(c.repository_branches.items()):
143 <li>${h.link_to('%s - %s' % (branch[0],branch[1]),h.url('files_home',repo_name=c.repo_name,revision=branch[1]))}</li>
147 <li>${h.link_to('%s - %s' % (branch[0],branch[1]),h.url('files_home',repo_name=c.repo_name,revision=branch[1]))}</li>
144 %endfor
148 %endfor
145 </ul>
149 </ul>
146 </li>
150 </li>
147 <li>
151 <li>
148 ${h.link_to(_('tags'),h.url('tags_home',repo_name=c.repo_name),class_='tags childs')}
152 ${h.link_to(_('tags'),h.url('tags_home',repo_name=c.repo_name),class_='tags childs')}
149 <ul>
153 <ul>
150 %for cnt,tag in enumerate(c.repository_tags.items()):
154 %for cnt,tag in enumerate(c.repository_tags.items()):
151 <li>${h.link_to('%s - %s' % (tag[0],tag[1]),h.url('files_home',repo_name=c.repo_name,revision=tag[1]))}</li>
155 <li>${h.link_to('%s - %s' % (tag[0],tag[1]),h.url('files_home',repo_name=c.repo_name,revision=tag[1]))}</li>
152 %endfor
156 %endfor
153 </ul>
157 </ul>
154 </li>
158 </li>
155 </ul>
159 </ul>
156 </li>
160 </li>
157 <li ${is_current('files')}>
161 <li ${is_current('files')}>
158 <a title="${_('Files')}" href="${h.url('files_home',repo_name=c.repo_name)}">
162 <a title="${_('Files')}" href="${h.url('files_home',repo_name=c.repo_name)}">
159 <span class="icon">
163 <span class="icon">
160 <img src="/images/icons/file.png" alt="${_('Files')}" />
164 <img src="/images/icons/file.png" alt="${_('Files')}" />
161 </span>
165 </span>
162 <span>${_('Files')}</span>
166 <span>${_('Files')}</span>
163 </a>
167 </a>
164 </li>
168 </li>
165 %if h.HasRepoPermissionAll('repository.admin')(c.repo_name):
169 %if h.HasRepoPermissionAll('repository.admin')(c.repo_name):
166 <li ${is_current('settings')}>
170 <li ${is_current('settings')}>
167 <a title="${_('Settings')}" href="${h.url('repo_settings_home',repo_name=c.repo_name)}">
171 <a title="${_('Settings')}" href="${h.url('repo_settings_home',repo_name=c.repo_name)}">
168 <span class="icon">
172 <span class="icon">
169 <img src="/images/icons/cog_edit.png" alt="${_('Settings')}" />
173 <img src="/images/icons/cog_edit.png" alt="${_('Settings')}" />
170 </span>
174 </span>
171 <span>${_('Settings')}</span>
175 <span>${_('Settings')}</span>
172 </a>
176 </a>
173 </li>
177 </li>
174 %endif
178 %endif
175 </ul>
179 </ul>
176 %else:
180 %else:
177 ##ROOT MENU
181 ##ROOT MENU
178 <ul id="quick">
182 <ul id="quick">
179 <li>
183 <li>
180 <a title="${_('Home')}" href="${h.url('hg_home')}">
184 <a title="${_('Home')}" href="${h.url('hg_home')}">
181 <span class="icon">
185 <span class="icon">
182 <img src="/images/icons/home_16.png" alt="${_('Home')}" />
186 <img src="/images/icons/home_16.png" alt="${_('Home')}" />
183 </span>
187 </span>
184 <span>${_('Home')}</span>
188 <span>${_('Home')}</span>
185 </a>
189 </a>
186 </li>
190 </li>
187
191
188 <li>
192 <li>
189 <a title="${_('Search')}" href="${h.url('search')}">
193 <a title="${_('Search')}" href="${h.url('search')}">
190 <span class="icon">
194 <span class="icon">
191 <img src="/images/icons/search_16.png" alt="${_('Search')}" />
195 <img src="/images/icons/search_16.png" alt="${_('Search')}" />
192 </span>
196 </span>
193 <span>${_('Search')}</span>
197 <span>${_('Search')}</span>
194 </a>
198 </a>
195 </li>
199 </li>
196
200
197 %if h.HasPermissionAll('hg.admin')('access admin main page'):
201 %if h.HasPermissionAll('hg.admin')('access admin main page'):
198 <li ${is_current('admin')}>
202 <li ${is_current('admin')}>
199 <a title="${_('Admin')}" href="${h.url('admin_home')}">
203 <a title="${_('Admin')}" href="${h.url('admin_home')}">
200 <span class="icon">
204 <span class="icon">
201 <img src="/images/icons/cog_edit.png" alt="${_('Admin')}" />
205 <img src="/images/icons/cog_edit.png" alt="${_('Admin')}" />
202 </span>
206 </span>
203 <span>${_('Admin')}</span>
207 <span>${_('Admin')}</span>
204 </a>
208 </a>
205 <ul>
209 <ul>
210 <li>${h.link_to(_('journal'),h.url('admin_home'),class_='journal')}</li>
206 <li>${h.link_to(_('repositories'),h.url('repos'),class_='repos')}</li>
211 <li>${h.link_to(_('repositories'),h.url('repos'),class_='repos')}</li>
207 <li>${h.link_to(_('users'),h.url('users'),class_='users')}</li>
212 <li>${h.link_to(_('users'),h.url('users'),class_='users')}</li>
208 <li>${h.link_to(_('permissions'),h.url('edit_permission',id='default'),class_='permissions')}</li>
213 <li>${h.link_to(_('permissions'),h.url('edit_permission',id='default'),class_='permissions')}</li>
209 <li class="last">${h.link_to(_('settings'),h.url('admin_settings'),class_='settings')}</li>
214 <li class="last">${h.link_to(_('settings'),h.url('admin_settings'),class_='settings')}</li>
210 </ul>
215 </ul>
211 </li>
216 </li>
212 %endif
217 %endif
213
218
214 </ul>
219 </ul>
215 %endif
220 %endif
216 </%def>
221 </%def>
217
222
218
223
219 <%def name="css()">
224 <%def name="css()">
220 <link rel="stylesheet" type="text/css" href="/css/reset.css" />
225 <link rel="stylesheet" type="text/css" href="/css/reset.css" />
221 <link rel="stylesheet" type="text/css" href="/css/style.css" media="screen" />
226 <link rel="stylesheet" type="text/css" href="/css/style.css" media="screen" />
222 <link id="color" rel="stylesheet" type="text/css" href="/css/colors/blue.css" />
227 <link id="color" rel="stylesheet" type="text/css" href="/css/colors/blue.css" />
223 <link rel="stylesheet" type="text/css" href="/css/pygments.css" />
228 <link rel="stylesheet" type="text/css" href="/css/pygments.css" />
224 <link rel="stylesheet" type="text/css" href="/css/diff.css" />
229 <link rel="stylesheet" type="text/css" href="/css/diff.css" />
225 </%def>
230 </%def>
226
231
227 <%def name="js()">
232 <%def name="js()">
228 ##<script type="text/javascript" src="/js/yui/utilities/utilities.js"></script>
233 ##<script type="text/javascript" src="/js/yui/utilities/utilities.js"></script>
229 ##<script type="text/javascript" src="/js/yui/container/container.js"></script>
234 ##<script type="text/javascript" src="/js/yui/container/container.js"></script>
230 ##<script type="text/javascript" src="/js/yui/datasource/datasource.js"></script>
235 ##<script type="text/javascript" src="/js/yui/datasource/datasource.js"></script>
231 ##<script type="text/javascript" src="/js/yui/autocomplete/autocomplete.js"></script>
236 ##<script type="text/javascript" src="/js/yui/autocomplete/autocomplete.js"></script>
232
237
233 <script type="text/javascript" src="/js/yui2.js"></script>
238 <script type="text/javascript" src="/js/yui2.js"></script>
234 <!--[if IE]><script language="javascript" type="text/javascript" src="/js/excanvas.min.js"></script><![endif]-->
239 <!--[if IE]><script language="javascript" type="text/javascript" src="/js/excanvas.min.js"></script><![endif]-->
235 <script type="text/javascript" src="/js/yui.flot.js"></script>
240 <script type="text/javascript" src="/js/yui.flot.js"></script>
236 </%def>
241 </%def>
237
242
238 <%def name="breadcrumbs()">
243 <%def name="breadcrumbs()">
239 <div class="breadcrumbs">
244 <div class="breadcrumbs">
240 ${self.breadcrumbs_links()}
245 ${self.breadcrumbs_links()}
241 </div>
246 </div>
242 </%def> No newline at end of file
247 </%def>
@@ -1,52 +1,61 b''
1 <%inherit file="/base/base.html"/>
1 <%inherit file="/base/base.html"/>
2
2
3 <%def name="title()">
3 <%def name="title()">
4 ${_('File annotate')}
4 ${_('File annotate')}
5 </%def>
5 </%def>
6
6
7 <%def name="breadcrumbs_links()">
7 <%def name="breadcrumbs_links()">
8 ${h.link_to(u'Home',h.url('/'))}
8 ${h.link_to(u'Home',h.url('/'))}
9 &raquo;
9 &raquo;
10 ${h.link_to(c.repo_name,h.url('summary_home',repo_name=c.repo_name))}
10 ${h.link_to(c.repo_name,h.url('summary_home',repo_name=c.repo_name))}
11 &raquo;
11 &raquo;
12 ${_('annotate')} @ R${c.rev_nr}:${c.cur_rev}
12 ${_('annotate')} @ R${c.rev_nr}:${c.cur_rev}
13 </%def>
13 </%def>
14
14
15 <%def name="page_nav()">
15 <%def name="page_nav()">
16 ${self.menu('files')}
16 ${self.menu('files')}
17 </%def>
17 </%def>
18 <%def name="main()">
18 <%def name="main()">
19 <div class="box">
19 <div class="box">
20 <!-- box / title -->
20 <!-- box / title -->
21 <div class="title">
21 <div class="title">
22 ${self.breadcrumbs()}
22 ${self.breadcrumbs()}
23 </div>
23 </div>
24 <div class="table">
24 <div class="table">
25 <div id="files_data">
25 <div id="files_data">
26 <h2>${_('Location')}: ${h.files_breadcrumbs(c.repo_name,c.cur_rev,c.file.path)}</h2>
26 <h3 class="files_location">${_('Location')}: ${h.files_breadcrumbs(c.repo_name,c.cur_rev,c.file.path)}</h3>
27 <dl class="overview">
27 <dl class="overview">
28 <dt>${_('Last revision')}</dt>
28 <dt>${_('Last revision')}</dt>
29 <dd>${h.link_to("r%s:%s" % (c.file.last_changeset.revision,c.file.last_changeset._short),
29 <dd>${h.link_to("r%s:%s" % (c.file.last_changeset.revision,c.file.last_changeset._short),
30 h.url('files_annotate_home',repo_name=c.repo_name,revision=c.file.last_changeset._short,f_path=c.f_path))} </dd>
30 h.url('files_annotate_home',repo_name=c.repo_name,revision=c.file.last_changeset._short,f_path=c.f_path))} </dd>
31 <dt>${_('Size')}</dt>
31 <dt>${_('Size')}</dt>
32 <dd>${h.format_byte_size(c.file.size,binary=True)}</dd>
32 <dd>${h.format_byte_size(c.file.size,binary=True)}</dd>
33 <dt>${_('Mimetype')}</dt>
34 <dd>${c.file.mimetype}</dd>
33 <dt>${_('Options')}</dt>
35 <dt>${_('Options')}</dt>
34 <dd>${h.link_to(_('show source'),
36 <dd>${h.link_to(_('show source'),
35 h.url('files_home',repo_name=c.repo_name,revision=c.cur_rev,f_path=c.f_path))}
37 h.url('files_home',repo_name=c.repo_name,revision=c.cur_rev,f_path=c.f_path))}
38 / ${h.link_to(_('show as raw'),
39 h.url('files_raw_home',repo_name=c.repo_name,revision=c.cur_rev,f_path=c.f_path))}
36 / ${h.link_to(_('download as raw'),
40 / ${h.link_to(_('download as raw'),
37 h.url('files_raw_home',repo_name=c.repo_name,revision=c.cur_rev,f_path=c.f_path))}
41 h.url('files_rawfile_home',repo_name=c.repo_name,revision=c.cur_rev,f_path=c.f_path))}
38 </dd>
42 </dd>
39 </dl>
43 </dl>
40 <div id="body" class="codeblock">
44 <div id="body" class="codeblock">
41 <div class="code-header">
45 <div class="code-header">
42 <div class="revision">${c.file.name}@r${c.file.last_changeset.revision}:${c.file.last_changeset._short}</div>
46 <div class="revision">${c.file.name}@r${c.file.last_changeset.revision}:${c.file.last_changeset._short}</div>
43 <div class="commit">"${c.file_msg}"</div>
47 <div class="commit">"${c.file_msg}"</div>
44 </div>
48 </div>
45 <div class="code-body">
49 <div class="code-body">
46 ${h.pygmentize_annotation(c.file,linenos=True,anchorlinenos=True,lineanchors='S',cssclass="code-highlight")}
50 % if c.file.size < c.file_size_limit:
51 ${h.pygmentize_annotation(c.file,linenos=True,anchorlinenos=True,lineanchors='S',cssclass="code-highlight")}
52 %else:
53 ${_('File is to big to display')} ${h.link_to(_('show as raw'),
54 h.url('files_raw_home',repo_name=c.repo_name,revision=c.cur_rev,f_path=c.f_path))}
55 %endif
47 </div>
56 </div>
48 </div>
57 </div>
49 </div>
58 </div>
50 </div>
59 </div>
51 </div>
60 </div>
52 </%def> No newline at end of file
61 </%def>
@@ -1,71 +1,78 b''
1 <%def name="file_class(node)">
1 <%def name="file_class(node)">
2 %if node.is_file():
2 %if node.is_file():
3 <%return "browser-file" %>
3 <%return "browser-file" %>
4 %else:
4 %else:
5 <%return "browser-dir"%>
5 <%return "browser-dir"%>
6 %endif
6 %endif
7 </%def>
7 </%def>
8 <div id="body" class="browserblock">
8 <div id="body" class="browserblock">
9 <div class="browser-header">
9 <div class="browser-header">
10 ${h.form(h.url.current())}
10 ${h.form(h.url.current())}
11 <div class="info_box">
11 <div class="info_box">
12 <span >${_('view')}@rev</span>
12 <span >${_('view')}@rev</span>
13 <a href="${c.url_prev}">&laquo;</a>
13 <a href="${c.url_prev}">&laquo;</a>
14 ${h.text('at_rev',value=c.rev_nr,size=3)}
14 ${h.text('at_rev',value=c.rev_nr,size=3)}
15 <a href="${c.url_next}">&raquo;</a>
15 <a href="${c.url_next}">&raquo;</a>
16 ${h.submit('view','view')}
16 ${h.submit('view','view')}
17 </div>
17 </div>
18 ${h.end_form()}
18 ${h.end_form()}
19 </div>
19 </div>
20 <div class="browser-body">
20 <div class="browser-body">
21 <table class="code-browser">
21 <table class="code-browser">
22 <thead>
22 <thead>
23 <tr>
23 <tr>
24 <th>${_('Name')}</th>
24 <th>${_('Name')}</th>
25 <th>${_('Size')}</th>
25 <th>${_('Size')}</th>
26 <th>${_('Mimetype')}</th>
26 <th>${_('Revision')}</th>
27 <th>${_('Revision')}</th>
27 <th>${_('Last modified')}</th>
28 <th>${_('Last modified')}</th>
28 <th>${_('Last commiter')}</th>
29 <th>${_('Last commiter')}</th>
29 </tr>
30 </tr>
30 </thead>
31 </thead>
31 <tr class="parity0">
32
32 <td>
33 % if c.files_list.parent:
33 % if c.files_list.parent:
34 <tr class="parity0">
34 ${h.link_to('..',h.url('files_home',repo_name=c.repo_name,revision=c.cur_rev,f_path=c.files_list.parent.path),class_="browser-dir")}
35 <td>
35 %endif
36 ${h.link_to('..',h.url('files_home',repo_name=c.repo_name,revision=c.cur_rev,f_path=c.files_list.parent.path),class_="browser-dir")}
36 </td>
37 </td>
37 <td></td>
38 <td></td>
38 <td></td>
39 <td></td>
39 <td></td>
40 <td></td>
40 <td></td>
41 <td></td>
41 </tr>
42 <td></td>
43 </tr>
44 %endif
45
42 %for cnt,node in enumerate(c.files_list,1):
46 %for cnt,node in enumerate(c.files_list,1):
43 <tr class="parity${cnt%2}">
47 <tr class="parity${cnt%2}">
44 <td>
48 <td>
45 ${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))}
49 ${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))}
46 </td>
50 </td>
47 <td>
51 <td>
48 %if node.is_file():
52 ${h.format_byte_size(node.size,binary=True)}
49 ${h.format_byte_size(node.size,binary=True)}
53 </td>
50 %endif
54 <td>
55 %if node.is_file():
56 ${node.mimetype}
57 %endif
51 </td>
58 </td>
52 <td>
59 <td>
53 %if node.is_file():
60 %if node.is_file():
54 ${node.last_changeset.revision}
61 ${node.last_changeset.revision}
55 %endif
62 %endif
56 </td>
63 </td>
57 <td>
64 <td>
58 %if node.is_file():
65 %if node.is_file():
59 ${h.age(node.last_changeset._ctx.date())} - ${node.last_changeset.date}
66 ${h.age(node.last_changeset._ctx.date())} - ${node.last_changeset.date}
60 %endif
67 %endif
61 </td>
68 </td>
62 <td>
69 <td>
63 %if node.is_file():
70 %if node.is_file():
64 ${node.last_changeset.author}
71 ${node.last_changeset.author}
65 %endif
72 %endif
66 </td>
73 </td>
67 </tr>
74 </tr>
68 %endfor
75 %endfor
69 </table>
76 </table>
70 </div>
77 </div>
71 </div> No newline at end of file
78 </div>
@@ -1,48 +1,57 b''
1 <dl>
1 <dl>
2 <dt>${_('Last revision')}</dt>
2 <dt>${_('Last revision')}</dt>
3 <dd>
3 <dd>
4 ${h.link_to("r%s:%s" % (c.files_list.last_changeset.revision,c.files_list.last_changeset._short),
4 ${h.link_to("r%s:%s" % (c.files_list.last_changeset.revision,c.files_list.last_changeset._short),
5 h.url('files_home',repo_name=c.repo_name,revision=c.files_list.last_changeset._short,f_path=c.f_path))}
5 h.url('files_home',repo_name=c.repo_name,revision=c.files_list.last_changeset._short,f_path=c.f_path))}
6 </dd>
6 </dd>
7 <dt>${_('Size')}</dt>
7 <dt>${_('Size')}</dt>
8 <dd>${h.format_byte_size(c.files_list.size,binary=True)}</dd>
8 <dd>${h.format_byte_size(c.files_list.size,binary=True)}</dd>
9 <dt>${_('Mimetype')}</dt>
10 <dd>${c.files_list.mimetype}</dd>
9 <dt>${_('Options')}</dt>
11 <dt>${_('Options')}</dt>
10 <dd>${h.link_to(_('show annotation'),
12 <dd>${h.link_to(_('show annotation'),
11 h.url('files_annotate_home',repo_name=c.repo_name,revision=c.cur_rev,f_path=c.f_path))}
13 h.url('files_annotate_home',repo_name=c.repo_name,revision=c.cur_rev,f_path=c.f_path))}
14 / ${h.link_to(_('show as raw'),
15 h.url('files_raw_home',repo_name=c.repo_name,revision=c.cur_rev,f_path=c.f_path))}
12 / ${h.link_to(_('download as raw'),
16 / ${h.link_to(_('download as raw'),
13 h.url('files_raw_home',repo_name=c.repo_name,revision=c.cur_rev,f_path=c.f_path))}
17 h.url('files_rawfile_home',repo_name=c.repo_name,revision=c.cur_rev,f_path=c.f_path))}
14 </dd>
18 </dd>
15 <dt>${_('History')}</dt>
19 <dt>${_('History')}</dt>
16 <dd>
20 <dd>
17 <div>
21 <div>
18 ${h.form(h.url('files_diff_home',repo_name=c.repo_name,f_path=c.f_path),method='get')}
22 ${h.form(h.url('files_diff_home',repo_name=c.repo_name,f_path=c.f_path),method='get')}
19 ${h.hidden('diff2',c.files_list.last_changeset._short)}
23 ${h.hidden('diff2',c.files_list.last_changeset._short)}
20 ${h.select('diff1',c.files_list.last_changeset._short,c.file_history)}
24 ${h.select('diff1',c.files_list.last_changeset._short,c.file_history)}
21 ${h.submit('diff','diff to revision',class_="ui-button ui-widget ui-state-default ui-corner-all")}
25 ${h.submit('diff','diff to revision',class_="ui-button ui-widget ui-state-default ui-corner-all")}
22 ${h.submit('show_rev','show at revision',class_="ui-button ui-widget ui-state-default ui-corner-all")}
26 ${h.submit('show_rev','show at revision',class_="ui-button ui-widget ui-state-default ui-corner-all")}
23 ${h.end_form()}
27 ${h.end_form()}
24 </div>
28 </div>
25 </dd>
29 </dd>
26 </dl>
30 </dl>
27
31
28
32
29 <div id="body" class="codeblock">
33 <div id="body" class="codeblock">
30 <div class="code-header">
34 <div class="code-header">
31 <div class="revision">${c.files_list.name}@r${c.files_list.last_changeset.revision}:${c.files_list.last_changeset._short}</div>
35 <div class="revision">${c.files_list.name}@r${c.files_list.last_changeset.revision}:${c.files_list.last_changeset._short}</div>
32 <div class="commit">"${c.files_list.last_changeset.message}"</div>
36 <div class="commit">"${c.files_list.last_changeset.message}"</div>
33 </div>
37 </div>
34 <div class="code-body">
38 <div class="code-body">
35 ${h.pygmentize(c.files_list,linenos=True,anchorlinenos=True,lineanchors='S',cssclass="code-highlight")}
39 % if c.files_list.size < c.file_size_limit:
40 ${h.pygmentize(c.files_list,linenos=True,anchorlinenos=True,lineanchors='S',cssclass="code-highlight")}
41 %else:
42 ${_('File is to big to display')} ${h.link_to(_('show as raw'),
43 h.url('files_raw_home',repo_name=c.repo_name,revision=c.cur_rev,f_path=c.f_path))}
44 %endif
36 </div>
45 </div>
37 </div>
46 </div>
38
47
39 <script type="text/javascript">
48 <script type="text/javascript">
40 YAHOO.util.Event.onDOMReady(function(){
49 YAHOO.util.Event.onDOMReady(function(){
41 YAHOO.util.Event.addListener('show_rev','click',function(e){
50 YAHOO.util.Event.addListener('show_rev','click',function(e){
42 YAHOO.util.Event.preventDefault(e);
51 YAHOO.util.Event.preventDefault(e);
43 var cs = YAHOO.util.Dom.get('diff1').value;
52 var cs = YAHOO.util.Dom.get('diff1').value;
44 var url = "${h.url('files_home',repo_name=c.repo_name,revision='__CS__',f_path=c.f_path)}".replace('__CS__',cs);
53 var url = "${h.url('files_home',repo_name=c.repo_name,revision='__CS__',f_path=c.f_path)}".replace('__CS__',cs);
45 window.location = url;
54 window.location = url;
46 });
55 });
47 });
56 });
48 </script> No newline at end of file
57 </script>
@@ -1,78 +1,78 b''
1 ## -*- coding: utf-8 -*-
1 ## -*- coding: utf-8 -*-
2 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
2 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
3 <html xmlns="http://www.w3.org/1999/xhtml" id="mainhtml">
3 <html xmlns="http://www.w3.org/1999/xhtml" id="mainhtml">
4 <head>
4 <head>
5 <title>${_('Sign In to hg-app')}</title>
5 <title>${_('Sign In to hg-app')}</title>
6 <meta http-equiv="Content-Type" content="text/html;charset=utf-8" />
6 <meta http-equiv="Content-Type" content="text/html;charset=utf-8" />
7 <link rel="icon" href="/images/hgicon.png" type="image/png" />
7 <link rel="icon" href="/images/hgicon.png" type="image/png" />
8 <meta name="robots" content="index, nofollow"/>
8 <meta name="robots" content="index, nofollow"/>
9
9
10 <!-- stylesheets -->
10 <!-- stylesheets -->
11 <link rel="stylesheet" type="text/css" href="/css/reset.css" />
11 <link rel="stylesheet" type="text/css" href="/css/reset.css" />
12 <link rel="stylesheet" type="text/css" href="/css/style.css" media="screen" />
12 <link rel="stylesheet" type="text/css" href="/css/style.css" media="screen" />
13 <link id="color" rel="stylesheet" type="text/css" href="/css/colors/blue.css" />
13 <link id="color" rel="stylesheet" type="text/css" href="/css/colors/blue.css" />
14
14
15 <!-- scripts -->
15 <!-- scripts -->
16
16
17 </head>
17 </head>
18 <body>
18 <body>
19 <div id="login">
19 <div id="login">
20 <!-- login -->
20 <!-- login -->
21 <div class="title">
21 <div class="title">
22 <h5>${_('Sign In to hg-app')}</h5>
22 <h5>${_('Sign In to hg-app')}</h5>
23 <div class="corner tl"></div>
23 <div class="corner tl"></div>
24 <div class="corner tr"></div>
24 <div class="corner tr"></div>
25 </div>
25 </div>
26 <div class="inner">
26 <div class="inner">
27 ${h.form(h.url.current(came_from=c.came_from))}
27 ${h.form(h.url.current(came_from=c.came_from))}
28 <div class="form">
28 <div class="form">
29 <!-- fields -->
29 <!-- fields -->
30
30
31 <div class="fields">
31 <div class="fields">
32 <div class="field">
32 <div class="field">
33 <div class="label">
33 <div class="label">
34 <label for="username">${_('Username')}:</label>
34 <label for="username">${_('Username')}:</label>
35 </div>
35 </div>
36 <div class="input">
36 <div class="input">
37 ${h.text('username',class_='focus',size=40)}
37 ${h.text('username',class_='focus',size=40)}
38 </div>
38 </div>
39
39
40 </div>
40 </div>
41 <div class="field">
41 <div class="field">
42 <div class="label">
42 <div class="label">
43 <label for="password">${_('Password')}:</label>
43 <label for="password">${_('Password')}:</label>
44 </div>
44 </div>
45 <div class="input">
45 <div class="input">
46 ${h.password('password',class_='focus',size=40)}
46 ${h.password('password',class_='focus',size=40)}
47 </div>
47 </div>
48
48
49 </div>
49 </div>
50 ##<div class="field">
50 ##<div class="field">
51 ## <div class="checkbox">
51 ## <div class="checkbox">
52 ## <input type="checkbox" id="remember" name="remember" />
52 ## <input type="checkbox" id="remember" name="remember" />
53 ## <label for="remember">Remember me</label>
53 ## <label for="remember">Remember me</label>
54 ## </div>
54 ## </div>
55 ##</div>
55 ##</div>
56 <div class="buttons">
56 <div class="buttons">
57 ${h.submit('sign_in','Sign In',class_="ui-button ui-widget ui-state-default ui-corner-all")}
57 ${h.submit('sign_in','Sign In',class_="ui-button ui-widget ui-state-default ui-corner-all")}
58 </div>
58 </div>
59 </div>
59 </div>
60 <!-- end fields -->
60 <!-- end fields -->
61 <!-- links -->
61 <!-- links -->
62 <div class="links">
62 <div class="links">
63 ${h.link_to(_('Forgot your password ?'),h.url('#'))}
63 ${h.link_to(_('Forgot your password ?'),h.url('reset_password'))}
64 %if h.HasPermissionAny('hg.admin', 'hg.register.auto_activate', 'hg.register.manual_activate')():
64 %if h.HasPermissionAny('hg.admin', 'hg.register.auto_activate', 'hg.register.manual_activate')():
65 /
65 /
66 ${h.link_to(_("Don't have an account ?"),h.url('register'))}
66 ${h.link_to(_("Don't have an account ?"),h.url('register'))}
67 %endif
67 %endif
68 </div>
68 </div>
69
69
70 <!-- end links -->
70 <!-- end links -->
71 </div>
71 </div>
72 ${h.end_form()}
72 ${h.end_form()}
73 </div>
73 </div>
74 <!-- end login -->
74 <!-- end login -->
75 </div>
75 </div>
76 </body>
76 </body>
77 </html>
77 </html>
78
78
@@ -1,69 +1,71 b''
1 ## -*- coding: utf-8 -*-
1 ## -*- coding: utf-8 -*-
2 <%inherit file="/base/base.html"/>
2 <%inherit file="/base/base.html"/>
3 <%def name="title()">
3 <%def name="title()">
4 ${_('Search')}: ${c.cur_query}
4 ${_('Search')}: ${c.cur_query}
5 </%def>
5 </%def>
6 <%def name="breadcrumbs()">
6 <%def name="breadcrumbs()">
7 ${c.hg_app_name}
7 ${c.hg_app_name}
8 </%def>
8 </%def>
9 <%def name="page_nav()">
9 <%def name="page_nav()">
10 ${self.menu('home')}
10 ${self.menu('home')}
11 </%def>
11 </%def>
12 <%def name="main()">
12 <%def name="main()">
13
13
14 <div class="box">
14 <div class="box">
15 <!-- box / title -->
15 <!-- box / title -->
16 <div class="title">
16 <div class="title">
17 <h5>${_('Search')}</h5>
17 <h5>${_('Search')}</h5>
18 </div>
18 </div>
19 <!-- end box / title -->
19 <!-- end box / title -->
20 ${h.form('search',method='get')}
20 ${h.form('search',method='get')}
21 <div class="form">
21 <div class="form">
22 <div class="fields">
22 <div class="fields">
23
23
24 <div class="field ">
24 <div class="field ">
25 <div class="label">
25 <div class="label">
26 <label for="q">${_('Search:')}</label>
26 <label for="q">${_('Search:')}</label>
27 </div>
27 </div>
28 <div class="input">
28 <div class="input">
29 ${h.text('q',c.cur_query,class_="small")}
29 ${h.text('q',c.cur_query,class_="small")}
30 <div class="button highlight">
30 <div class="button highlight">
31 <input type="submit" value="${_('Search')}" class="ui-button ui-widget ui-state-default ui-corner-all"/>
31 <input type="submit" value="${_('Search')}" class="ui-button ui-widget ui-state-default ui-corner-all"/>
32 </div>
32 </div>
33 <div style="font-weight: bold;clear:both;padding: 5px">${c.runtime}</div>
33 <div style="font-weight: bold;clear:both;padding: 5px">${c.runtime}</div>
34 </div>
34 </div>
35 </div>
35 </div>
36 </div>
36 </div>
37 </div>
37 </div>
38 ${h.end_form()}
38 ${h.end_form()}
39
39
40 %for cnt,sr in enumerate(c.formated_results):
40 %for cnt,sr in enumerate(c.formated_results):
41 %if h.HasRepoPermissionAny('repository.write','repository.read','repository.admin')(sr['repository'],'search results check'):
41 %if h.HasRepoPermissionAny('repository.write','repository.read','repository.admin')(sr['repository'],'search results check'):
42 <div class="table">
42 <div class="table">
43 <div id="body${cnt}" class="codeblock">
43 <div id="body${cnt}" class="codeblock">
44 <div class="code-header">
44 <div class="code-header">
45 <div class="revision">${h.link_to(h.literal('%s &raquo; %s' % (sr['repository'],sr['f_path'])),
45 <div class="revision">${h.link_to(h.literal('%s &raquo; %s' % (sr['repository'],sr['f_path'])),
46 h.url('files_home',repo_name=sr['repository'],revision='tip',f_path=sr['f_path']))}</div>
46 h.url('files_home',repo_name=sr['repository'],revision='tip',f_path=sr['f_path']))}</div>
47 </div>
47 </div>
48 <div class="code-body">
48 <div class="code-body">
49 <pre>${h.literal(sr['content_short'])}</pre>
49 <pre>${h.literal(sr['content_short_hl'])}</pre>
50 </div>
50 </div>
51 </div>
51 </div>
52 </div>
52 </div>
53 %else:
53 %else:
54 %if cnt == 0:
54 %if cnt == 0:
55 <div class="table">
55 <div class="table">
56 <div id="body${cnt}" class="codeblock">
56 <div id="body${cnt}" class="codeblock">
57 <div class="error">${_('Permission denied')}</div>
57 <div class="error">${_('Permission denied')}</div>
58 </div>
58 </div>
59 </div>
59 </div>
60 %endif
60 %endif
61
61
62 %endif
62 %endif
63 %endfor
63 %endfor
64
64 %if c.cur_query:
65
65 <div class="pagination-wh pagination-left">
66
66 ${c.formated_results.pager('$link_previous ~2~ $link_next')}
67 </div>
68 %endif
67 </div>
69 </div>
68
70
69 </%def>
71 </%def>
@@ -1,63 +1,63 b''
1 ## -*- coding: utf-8 -*-
1 ## -*- coding: utf-8 -*-
2 % if c.repo_changesets:
2 % if c.repo_changesets:
3 <table>
3 <table>
4 <tr>
4 <tr>
5 <th class="left">${_('date')}</th>
5 <th class="left">${_('date')}</th>
6 <th class="left">${_('author')}</th>
6 <th class="left">${_('author')}</th>
7 <th class="left">${_('revision')}</th>
7 <th class="left">${_('revision')}</th>
8 <th class="left">${_('commit message')}</th>
8 <th class="left">${_('commit message')}</th>
9 <th class="left">${_('branch')}</th>
9 <th class="left">${_('branch')}</th>
10 <th class="left">${_('tags')}</th>
10 <th class="left">${_('tags')}</th>
11 <th class="left">${_('links')}</th>
11 <th class="left">${_('links')}</th>
12
12
13 </tr>
13 </tr>
14 %for cnt,cs in enumerate(c.repo_changesets):
14 %for cnt,cs in enumerate(c.repo_changesets):
15 <tr class="parity${cnt%2}">
15 <tr class="parity${cnt%2}">
16 <td>${h.age(cs._ctx.date())}</td>
16 <td>${h.age(cs._ctx.date())} - ${h.rfc822date_notz(cs._ctx.date())} </td>
17 <td title="${cs.author}">${h.person(cs.author)}</td>
17 <td title="${cs.author}">${h.person(cs.author)}</td>
18 <td>r${cs.revision}:${cs.raw_id}</td>
18 <td>r${cs.revision}:${cs.raw_id}</td>
19 <td>
19 <td>
20 ${h.link_to(h.truncate(cs.message,60),
20 ${h.link_to(h.truncate(cs.message,60),
21 h.url('changeset_home',repo_name=c.repo_name,revision=cs._short),
21 h.url('changeset_home',repo_name=c.repo_name,revision=cs._short),
22 title=cs.message)}
22 title=cs.message)}
23 </td>
23 </td>
24 <td>
24 <td>
25 <span class="logtags">
25 <span class="logtags">
26 <span class="branchtag">${cs.branch}</span>
26 <span class="branchtag">${cs.branch}</span>
27 </span>
27 </span>
28 </td>
28 </td>
29 <td>
29 <td>
30 <span class="logtags">
30 <span class="logtags">
31 %for tag in cs.tags:
31 %for tag in cs.tags:
32 <span class="tagtag">${tag}</span>
32 <span class="tagtag">${tag}</span>
33 %endfor
33 %endfor
34 </span>
34 </span>
35 </td>
35 </td>
36 <td class="nowrap">
36 <td class="nowrap">
37 ${h.link_to(_('changeset'),h.url('changeset_home',repo_name=c.repo_name,revision=cs.raw_id))}
37 ${h.link_to(_('changeset'),h.url('changeset_home',repo_name=c.repo_name,revision=cs.raw_id))}
38 |
38 |
39 ${h.link_to(_('files'),h.url('files_home',repo_name=c.repo_name,revision=cs.raw_id))}
39 ${h.link_to(_('files'),h.url('files_home',repo_name=c.repo_name,revision=cs.raw_id))}
40 </td>
40 </td>
41 </tr>
41 </tr>
42 %endfor
42 %endfor
43
43
44 </table>
44 </table>
45
45
46 <script type="text/javascript">
46 <script type="text/javascript">
47 var data_div = 'shortlog_data';
47 var data_div = 'shortlog_data';
48 YAHOO.util.Event.onDOMReady(function(){
48 YAHOO.util.Event.onDOMReady(function(){
49 YAHOO.util.Event.addListener(YAHOO.util.Dom.getElementsByClassName('pager_link'),"click",function(){
49 YAHOO.util.Event.addListener(YAHOO.util.Dom.getElementsByClassName('pager_link'),"click",function(){
50 YAHOO.util.Dom.setStyle('shortlog_data','opacity','0.3');});});
50 YAHOO.util.Dom.setStyle('shortlog_data','opacity','0.3');});});
51 </script>
51 </script>
52
52
53 <div class="pagination-wh pagination-left">
53 <div class="pagination-wh pagination-left">
54 ${c.repo_changesets.pager('$link_previous ~2~ $link_next',
54 ${c.repo_changesets.pager('$link_previous ~2~ $link_next',
55 onclick="""YAHOO.util.Connect.asyncRequest('GET','$partial_url',{
55 onclick="""YAHOO.util.Connect.asyncRequest('GET','$partial_url',{
56 success:function(o){YAHOO.util.Dom.get(data_div).innerHTML=o.responseText;
56 success:function(o){YAHOO.util.Dom.get(data_div).innerHTML=o.responseText;
57 YAHOO.util.Event.addListener(YAHOO.util.Dom.getElementsByClassName('pager_link'),"click",function(){
57 YAHOO.util.Event.addListener(YAHOO.util.Dom.getElementsByClassName('pager_link'),"click",function(){
58 YAHOO.util.Dom.setStyle(data_div,'opacity','0.3');});
58 YAHOO.util.Dom.setStyle(data_div,'opacity','0.3');});
59 YAHOO.util.Dom.setStyle(data_div,'opacity','1');}},null); return false;""")}
59 YAHOO.util.Dom.setStyle(data_div,'opacity','1');}},null); return false;""")}
60 </div>
60 </div>
61 %else:
61 %else:
62 ${_('There are no commits yet')}
62 ${_('There are no commits yet')}
63 %endif
63 %endif
@@ -1,301 +1,508 b''
1 <%inherit file="/base/base.html"/>
1 <%inherit file="/base/base.html"/>
2
2
3 <%def name="title()">
3 <%def name="title()">
4 ${_('Mercurial Repository Overview')}
4 ${_('Mercurial Repository Overview')}
5 </%def>
5 </%def>
6
6
7 <%def name="breadcrumbs_links()">
7 <%def name="breadcrumbs_links()">
8 ${h.link_to(u'Home',h.url('/'))}
8 ${h.link_to(u'Home',h.url('/'))}
9 &raquo;
9 &raquo;
10 ${h.link_to(c.repo_name,h.url('summary_home',repo_name=c.repo_name))}
10 ${h.link_to(c.repo_name,h.url('summary_home',repo_name=c.repo_name))}
11 &raquo;
11 &raquo;
12 ${_('summary')}
12 ${_('summary')}
13 </%def>
13 </%def>
14
14
15 <%def name="page_nav()">
15 <%def name="page_nav()">
16 ${self.menu('summary')}
16 ${self.menu('summary')}
17 </%def>
17 </%def>
18
18
19 <%def name="main()">
19 <%def name="main()">
20 <script type="text/javascript">
20 <script type="text/javascript">
21 var E = YAHOO.util.Event;
21 var E = YAHOO.util.Event;
22 var D = YAHOO.util.Dom;
22 var D = YAHOO.util.Dom;
23
23
24 E.onDOMReady(function(e){
24 E.onDOMReady(function(e){
25 id = 'clone_url';
25 id = 'clone_url';
26 E.addListener(id,'click',function(e){
26 E.addListener(id,'click',function(e){
27 D.get('clone_url').select();
27 D.get('clone_url').select();
28 })
28 })
29 })
29 })
30 </script>
30 </script>
31 <div class="box box-left">
31 <div class="box box-left">
32 <!-- box / title -->
32 <!-- box / title -->
33 <div class="title">
33 <div class="title">
34 ${self.breadcrumbs()}
34 ${self.breadcrumbs()}
35 </div>
35 </div>
36 <!-- end box / title -->
36 <!-- end box / title -->
37 <div class="form">
37 <div class="form">
38 <div class="fields">
38 <div class="fields">
39
39
40 <div class="field">
40 <div class="field">
41 <div class="label">
41 <div class="label">
42 <label>${_('Name')}:</label>
42 <label>${_('Name')}:</label>
43 </div>
43 </div>
44 <div class="input-short">
44 <div class="input-short">
45 <span style="font-size: 1.6em;font-weight: bold">${c.repo_info.name}</span>
45 <span style="font-size: 1.6em;font-weight: bold">${c.repo_info.name}</span>
46 </div>
46 </div>
47 </div>
47 </div>
48
48
49
49
50 <div class="field">
50 <div class="field">
51 <div class="label">
51 <div class="label">
52 <label>${_('Description')}:</label>
52 <label>${_('Description')}:</label>
53 </div>
53 </div>
54 <div class="input-short">
54 <div class="input-short">
55 ${c.repo_info.description}
55 ${c.repo_info.description}
56 </div>
56 </div>
57 </div>
57 </div>
58
58
59
59
60 <div class="field">
60 <div class="field">
61 <div class="label">
61 <div class="label">
62 <label>${_('Contact')}:</label>
62 <label>${_('Contact')}:</label>
63 </div>
63 </div>
64 <div class="input-short">
64 <div class="input-short">
65 <div class="gravatar">
65 <div class="gravatar">
66 <img alt="gravatar" src="${h.gravatar_url(c.repo_info.dbrepo.user.email)}"/>
66 <img alt="gravatar" src="${h.gravatar_url(c.repo_info.dbrepo.user.email)}"/>
67 </div>
67 </div>
68 ${_('Username')}: ${c.repo_info.dbrepo.user.username}<br/>
68 ${_('Username')}: ${c.repo_info.dbrepo.user.username}<br/>
69 ${_('Name')}: ${c.repo_info.dbrepo.user.name} ${c.repo_info.dbrepo.user.lastname}<br/>
69 ${_('Name')}: ${c.repo_info.dbrepo.user.name} ${c.repo_info.dbrepo.user.lastname}<br/>
70 ${_('Email')}: <a href="mailto:${c.repo_info.dbrepo.user.email}">${c.repo_info.dbrepo.user.email}</a>
70 ${_('Email')}: <a href="mailto:${c.repo_info.dbrepo.user.email}">${c.repo_info.dbrepo.user.email}</a>
71 </div>
71 </div>
72 </div>
72 </div>
73
73
74 <div class="field">
74 <div class="field">
75 <div class="label">
75 <div class="label">
76 <label>${_('Last change')}:</label>
76 <label>${_('Last change')}:</label>
77 </div>
77 </div>
78 <div class="input-short">
78 <div class="input-short">
79 ${h.age(c.repo_info.last_change)} - ${h.rfc822date(c.repo_info.last_change)}
79 ${h.age(c.repo_info.last_change)} - ${h.rfc822date(c.repo_info.last_change)}
80 ${_('by')} ${h.get_changeset_safe(c.repo_info,'tip').author}
81
80 </div>
82 </div>
81 </div>
83 </div>
82
84
83 <div class="field">
85 <div class="field">
84 <div class="label">
86 <div class="label">
85 <label>${_('Clone url')}:</label>
87 <label>${_('Clone url')}:</label>
86 </div>
88 </div>
87 <div class="input-short">
89 <div class="input-short">
88 <input type="text" id="clone_url" readonly="readonly" value="hg clone ${c.clone_repo_url}" size="70"/>
90 <input type="text" id="clone_url" readonly="readonly" value="hg clone ${c.clone_repo_url}" size="70"/>
89 </div>
91 </div>
90 </div>
92 </div>
91
93
92 <div class="field">
94 <div class="field">
93 <div class="label">
95 <div class="label">
94 <label>${_('Download')}:</label>
96 <label>${_('Download')}:</label>
95 </div>
97 </div>
96 <div class="input-short">
98 <div class="input-short">
97 %for cnt,archive in enumerate(c.repo_info._get_archives()):
99 %for cnt,archive in enumerate(c.repo_info._get_archives()):
98 %if cnt >=1:
100 %if cnt >=1:
99 |
101 |
100 %endif
102 %endif
101 ${h.link_to(c.repo_info.name+'.'+archive['type'],
103 ${h.link_to(c.repo_info.name+'.'+archive['type'],
102 h.url('files_archive_home',repo_name=c.repo_info.name,
104 h.url('files_archive_home',repo_name=c.repo_info.name,
103 revision='tip',fileformat=archive['extension']),class_="archive_icon")}
105 revision='tip',fileformat=archive['extension']),class_="archive_icon")}
104 %endfor
106 %endfor
105 </div>
107 </div>
106 </div>
108 </div>
107
109
108 <div class="field">
110 <div class="field">
109 <div class="label">
111 <div class="label">
110 <label>${_('Feeds')}:</label>
112 <label>${_('Feeds')}:</label>
111 </div>
113 </div>
112 <div class="input-short">
114 <div class="input-short">
113 ${h.link_to(_('RSS'),h.url('rss_feed_home',repo_name=c.repo_info.name),class_='rss_icon')}
115 ${h.link_to(_('RSS'),h.url('rss_feed_home',repo_name=c.repo_info.name),class_='rss_icon')}
114 ${h.link_to(_('Atom'),h.url('atom_feed_home',repo_name=c.repo_info.name),class_='atom_icon')}
116 ${h.link_to(_('Atom'),h.url('atom_feed_home',repo_name=c.repo_info.name),class_='atom_icon')}
115 </div>
117 </div>
116 </div>
118 </div>
117 </div>
119 </div>
118 </div>
120 </div>
119 </div>
121 </div>
120
122
121 <div class="box box-right" style="min-height:455px">
123 <div class="box box-right" style="min-height:455px">
122 <!-- box / title -->
124 <!-- box / title -->
123 <div class="title">
125 <div class="title">
124 <h5>${_('Last month commit activity')}</h5>
126 <h5>${_('Commit activity by day / author')}</h5>
125 </div>
127 </div>
126
128
127 <div class="table">
129 <div class="table">
128 <div id="commit_history" style="width:560px;height:300px;float:left"></div>
130 <div id="commit_history" style="width:560px;height:300px;float:left"></div>
129 <div id="legend_data">
131 <div style="clear: both;height: 10px"></div>
132 <div id="overview" style="width:560px;height:100px;float:left"></div>
133
134 <div id="legend_data" style="clear:both;margin-top:10px;">
130 <div id="legend_container"></div>
135 <div id="legend_container"></div>
131 <div id="legend_choices">
136 <div id="legend_choices">
132 <table id="legend_choices_tables" style="font-size:smaller;color:#545454"></table>
137 <table id="legend_choices_tables" style="font-size:smaller;color:#545454"></table>
133 </div>
138 </div>
134 </div>
139 </div>
135 <script type="text/javascript">
140 <script type="text/javascript">
136
141 /**
137 (function () {
142 * Plots summary graph
138 var datasets = {${c.commit_data|n}};
143 *
139 var i = 0;
144 * @class SummaryPlot
145 * @param {from} initial from for detailed graph
146 * @param {to} initial to for detailed graph
147 * @param {dataset}
148 * @param {overview_dataset}
149 */
150 function SummaryPlot(from,to,dataset,overview_dataset) {
151 var initial_ranges = {
152 "xaxis":{
153 "from":from,
154 "to":to,
155 },
156 };
157 var dataset = dataset;
158 var overview_dataset = [overview_dataset];
140 var choiceContainer = YAHOO.util.Dom.get("legend_choices");
159 var choiceContainer = YAHOO.util.Dom.get("legend_choices");
141 var choiceContainerTable = YAHOO.util.Dom.get("legend_choices_tables");
160 var choiceContainerTable = YAHOO.util.Dom.get("legend_choices_tables");
142 for(var key in datasets) {
161 var plotContainer = YAHOO.util.Dom.get('commit_history');
143 datasets[key].color = i;
162 var overviewContainer = YAHOO.util.Dom.get('overview');
144 i++;
145 choiceContainerTable.innerHTML += '<tr><td>'+
146 '<input type="checkbox" name="' + key +'" checked="checked" />'
147 +datasets[key].label+
148 '</td></tr>';
149 };
150
163
151
164 var plot_options = {
152 function plotAccordingToChoices() {
165 bars: {show:true,align:'center',lineWidth:4},
153 var data = [];
166 legend: {show:true, container:"legend_container"},
154
167 points: {show:true,radius:0,fill:false},
155 var inputs = choiceContainer.getElementsByTagName("input");
168 yaxis: {tickDecimals:0,},
156 for(var i=0; i<inputs.length; i++) {
169 xaxis: {
157 var key = inputs[i].name;
170 mode: "time",
158 if (key && datasets[key]){
171 timeformat: "%d/%m",
159 if(!inputs[i].checked){
172 min:from,
160 data.push({label:key,data:[[0,1],]});
173 max:to,
161 }
174 },
162 else{
175 grid: {
163 data.push(datasets[key]);
176 hoverable: true,
164 }
177 clickable: true,
165
178 autoHighlight:true,
166 }
179 color: "#999"
167
180 },
168 };
181 //selection: {mode: "x"}
169
182 };
170 if (data.length > 0){
183 var overview_options = {
184 legend:{show:false},
185 bars: {show:true,barWidth: 2,},
186 shadowSize: 0,
187 xaxis: {mode: "time", timeformat: "%d/%m/%y",},
188 yaxis: {ticks: 3, min: 0,},
189 grid: {color: "#999",},
190 selection: {mode: "x"}
191 };
171
192
172 var plot = YAHOO.widget.Flot("commit_history", data,
193 /**
173 { bars: { show: true, align:'center',lineWidth:4 },
194 *get dummy data needed in few places
174 points: { show: true, radius:0,fill:true },
195 */
175 legend:{show:true, container:"legend_container"},
196 function getDummyData(label){
176 selection: { mode: "xy" },
197 return {"label":label,
177 yaxis: {tickDecimals:0},
198 "data":[{"time":0,
178 xaxis: { mode: "time", timeformat: "%d",tickSize:[1, "day"],min:${c.ts_min},max:${c.ts_max} },
199 "commits":0,
179 grid: { hoverable: true, clickable: true,autoHighlight:true },
200 "added":0,
180 });
201 "changed":0,
181
202 "removed":0,
182 function showTooltip(x, y, contents) {
203 }],
183 var div=document.getElementById('tooltip');
204 "schema":["commits"],
184 if(!div) {
205 "color":'#ffffff',
185 div = document.createElement('div');
206 }
186 div.id="tooltip";
207 }
187 div.style.position="absolute";
208
188 div.style.border='1px solid #fdd';
209 /**
189 div.style.padding='2px';
210 * generate checkboxes accordindly to data
190 div.style.backgroundColor='#fee';
211 * @param keys
191 document.body.appendChild(div);
212 * @returns
192 }
213 */
193 YAHOO.util.Dom.setStyle(div, 'opacity', 0);
214 function generateCheckboxes(data) {
194 div.innerHTML = contents;
215 //append checkboxes
195 div.style.top=(y + 5) + "px";
216 var i = 0;
196 div.style.left=(x + 5) + "px";
217 choiceContainerTable.innerHTML = '';
197
218 for(var pos in data) {
198 var anim = new YAHOO.util.Anim(div, {opacity: {to: 0.8}}, 0.2);
219
199 anim.animate();
220 data[pos].color = i;
221 i++;
222 if(data[pos].label != ''){
223 choiceContainerTable.innerHTML += '<tr><td>'+
224 '<input type="checkbox" name="' + data[pos].label +'" checked="checked" />'
225 +data[pos].label+
226 '</td></tr>';
227 }
228 }
229 }
230
231 /**
232 * ToolTip show
233 */
234 function showTooltip(x, y, contents) {
235 var div=document.getElementById('tooltip');
236 if(!div) {
237 div = document.createElement('div');
238 div.id="tooltip";
239 div.style.position="absolute";
240 div.style.border='1px solid #fdd';
241 div.style.padding='2px';
242 div.style.backgroundColor='#fee';
243 document.body.appendChild(div);
244 }
245 YAHOO.util.Dom.setStyle(div, 'opacity', 0);
246 div.innerHTML = contents;
247 div.style.top=(y + 5) + "px";
248 div.style.left=(x + 5) + "px";
249
250 var anim = new YAHOO.util.Anim(div, {opacity: {to: 0.8}}, 0.2);
251 anim.animate();
252 }
253
254 /**
255 * This function will detect if selected period has some changesets for this user
256 if it does this data is then pushed for displaying
257 Additionally it will only display users that are selected by the checkbox
258 */
259 function getDataAccordingToRanges(ranges) {
260
261 var data = [];
262 var keys = [];
263 for(var key in dataset){
264 var push = false;
265 //method1 slow !!
266 ///*
267 for(var ds in dataset[key].data){
268 commit_data = dataset[key].data[ds];
269 //console.log(key);
270 //console.log(new Date(commit_data.time*1000));
271 //console.log(new Date(ranges.xaxis.from*1000));
272 //console.log(new Date(ranges.xaxis.to*1000));
273 if (commit_data.time >= ranges.xaxis.from && commit_data.time <= ranges.xaxis.to){
274 push = true;
275 break;
276 }
200 }
277 }
278 //*/
279 /*//method2 sorted commit data !!!
280 var first_commit = dataset[key].data[0].time;
281 var last_commit = dataset[key].data[dataset[key].data.length-1].time;
282
283 console.log(first_commit);
284 console.log(last_commit);
285
286 if (first_commit >= ranges.xaxis.from && last_commit <= ranges.xaxis.to){
287 push = true;
288 }
289 */
290 if(push){
291 data.push(dataset[key]);
292 }
293 }
294 if(data.length >= 1){
295 return data;
296 }
297 else{
298 //just return dummy data for graph to plot itself
299 return [getDummyData('')];
300 }
301
302 }
303
304 /**
305 * redraw using new checkbox data
306 */
307 function plotchoiced(e,args){
308 var cur_data = args[0];
309 var cur_ranges = args[1];
310
311 var new_data = [];
312 var inputs = choiceContainer.getElementsByTagName("input");
201
313
202 var previousPoint = null;
314 //show only checked labels
203 plot.subscribe("plothover", function (o) {
315 for(var i=0; i<inputs.length; i++) {
204 var pos = o.pos;
316 var checkbox_key = inputs[i].name;
205 var item = o.item;
317
206
318 if(inputs[i].checked){
207 //YAHOO.util.Dom.get("x").innerHTML = pos.x.toFixed(2);
319 for(var d in cur_data){
208 //YAHOO.util.Dom.get("y").innerHTML = pos.y.toFixed(2);
320 if(cur_data[d].label == checkbox_key){
209 if (item) {
321 new_data.push(cur_data[d]);
210 if (previousPoint != item.datapoint) {
322 }
211 previousPoint = item.datapoint;
323 }
212
324 }
213 var tooltip = YAHOO.util.Dom.get("tooltip");
325 else{
214 if(tooltip) {
326 //push dummy data to not hide the label
215 tooltip.parentNode.removeChild(tooltip);
327 new_data.push(getDummyData(checkbox_key));
216 }
328 }
217 var x = item.datapoint.x.toFixed(2);
329 }
218 var y = item.datapoint.y.toFixed(2);
330
219
331 var new_options = YAHOO.lang.merge(plot_options, {
220 if (!item.series.label){
332 xaxis: {
221 item.series.label = 'commits';
333 min: cur_ranges.xaxis.from,
222 }
334 max: cur_ranges.xaxis.to,
223 var d = new Date(x*1000);
335 mode:"time",
224 var fd = d.getFullYear()+'-'+(d.getMonth()+1)+'-'+d.getDate();
336 timeformat: "%d/%m",
225 var nr_commits = parseInt(y);
337 }
226
338 });
227 var cur_data = datasets[item.series.label].data[item.dataIndex];
339 if (!new_data){
228 var added = cur_data.added;
340 new_data = [[0,1]];
229 var changed = cur_data.changed;
341 }
230 var removed = cur_data.removed;
342 // do the zooming
231
343 plot = YAHOO.widget.Flot(plotContainer, new_data, new_options);
232 var nr_commits_suffix = " ${_('commits')} ";
344
233 var added_suffix = " ${_('files added')} ";
345 plot.subscribe("plotselected", plotselected);
234 var changed_suffix = " ${_('files changed')} ";
346
235 var removed_suffix = " ${_('files removed')} ";
347 //resubscribe plothover
348 plot.subscribe("plothover", plothover);
349
350 // don't fire event on the overview to prevent eternal loop
351 overview.setSelection(cur_ranges, true);
352
353 }
354
355 /**
356 * plot only selected items from overview
357 * @param ranges
358 * @returns
359 */
360 function plotselected(ranges,cur_data) {
361 //updates the data for new plot
362 data = getDataAccordingToRanges(ranges);
363 generateCheckboxes(data);
364
365 var new_options = YAHOO.lang.merge(plot_options, {
366 xaxis: {
367 min: ranges.xaxis.from,
368 max: ranges.xaxis.to,
369 mode:"time",
370 timeformat: "%d/%m",
371 }
372 });
373 // do the zooming
374 plot = YAHOO.widget.Flot(plotContainer, data, new_options);
375
376 plot.subscribe("plotselected", plotselected);
377
378 //resubscribe plothover
379 plot.subscribe("plothover", plothover);
380
381 // don't fire event on the overview to prevent eternal loop
382 overview.setSelection(ranges, true);
383
384 //resubscribe choiced
385 YAHOO.util.Event.on(choiceContainer.getElementsByTagName("input"), "click", plotchoiced, [data, ranges]);
386 }
387
388 var previousPoint = null;
236
389
237
390 function plothover(o) {
238 if(nr_commits == 1){nr_commits_suffix = " ${_('commit')} ";}
391 var pos = o.pos;
239 if(added==1){added_suffix=" ${_('file added')} ";}
392 var item = o.item;
240 if(changed==1){changed_suffix=" ${_('file changed')} ";}
393
241 if(removed==1){removed_suffix=" ${_('file removed')} ";}
394 //YAHOO.util.Dom.get("x").innerHTML = pos.x.toFixed(2);
242
395 //YAHOO.util.Dom.get("y").innerHTML = pos.y.toFixed(2);
243 showTooltip(item.pageX, item.pageY, item.series.label + " on " + fd
396 if (item) {
244 +'<br/>'+
397 if (previousPoint != item.datapoint) {
245 nr_commits + nr_commits_suffix+'<br/>'+
398 previousPoint = item.datapoint;
246 added + added_suffix +'<br/>'+
399
247 changed + changed_suffix + '<br/>'+
400 var tooltip = YAHOO.util.Dom.get("tooltip");
248 removed + removed_suffix + '<br/>');
401 if(tooltip) {
249 }
402 tooltip.parentNode.removeChild(tooltip);
403 }
404 var x = item.datapoint.x.toFixed(2);
405 var y = item.datapoint.y.toFixed(2);
406
407 if (!item.series.label){
408 item.series.label = 'commits';
250 }
409 }
251 else {
410 var d = new Date(x*1000);
252 var tooltip = YAHOO.util.Dom.get("tooltip");
411 var fd = d.toDateString()
253
412 var nr_commits = parseInt(y);
254 if(tooltip) {
413
255 tooltip.parentNode.removeChild(tooltip);
414 var cur_data = dataset[item.series.label].data[item.dataIndex];
256 }
415 var added = cur_data.added;
257 previousPoint = null;
416 var changed = cur_data.changed;
258 }
417 var removed = cur_data.removed;
259 });
418
419 var nr_commits_suffix = " ${_('commits')} ";
420 var added_suffix = " ${_('files added')} ";
421 var changed_suffix = " ${_('files changed')} ";
422 var removed_suffix = " ${_('files removed')} ";
260
423
261 }
424
425 if(nr_commits == 1){nr_commits_suffix = " ${_('commit')} ";}
426 if(added==1){added_suffix=" ${_('file added')} ";}
427 if(changed==1){changed_suffix=" ${_('file changed')} ";}
428 if(removed==1){removed_suffix=" ${_('file removed')} ";}
429
430 showTooltip(item.pageX, item.pageY, item.series.label + " on " + fd
431 +'<br/>'+
432 nr_commits + nr_commits_suffix+'<br/>'+
433 added + added_suffix +'<br/>'+
434 changed + changed_suffix + '<br/>'+
435 removed + removed_suffix + '<br/>');
436 }
437 }
438 else {
439 var tooltip = YAHOO.util.Dom.get("tooltip");
440
441 if(tooltip) {
442 tooltip.parentNode.removeChild(tooltip);
443 }
444 previousPoint = null;
445 }
262 }
446 }
447
448 /**
449 * MAIN EXECUTION
450 */
451
452 var data = getDataAccordingToRanges(initial_ranges);
453 generateCheckboxes(data);
454
455 //main plot
456 var plot = YAHOO.widget.Flot(plotContainer,data,plot_options);
457
458 //overview
459 var overview = YAHOO.widget.Flot(overviewContainer, overview_dataset, overview_options);
460
461 //show initial selection on overview
462 overview.setSelection(initial_ranges);
463
464 plot.subscribe("plotselected", plotselected);
465
466 overview.subscribe("plotselected", function (ranges) {
467 plot.setSelection(ranges);
468 });
469
470 plot.subscribe("plothover", plothover);
263
471
264 YAHOO.util.Event.on(choiceContainer.getElementsByTagName("input"), "click", plotAccordingToChoices);
472 YAHOO.util.Event.on(choiceContainer.getElementsByTagName("input"), "click", plotchoiced, [data, initial_ranges]);
265
473 }
266 plotAccordingToChoices();
474 SummaryPlot(${c.ts_min},${c.ts_max},${c.commit_data|n},${c.overview_data|n});
267 })();
475 </script>
268 </script>
269
476
270 </div>
477 </div>
271 </div>
478 </div>
272
479
273 <div class="box">
480 <div class="box">
274 <div class="title">
481 <div class="title">
275 <div class="breadcrumbs">${h.link_to(_('Last ten changes'),h.url('changelog_home',repo_name=c.repo_name))}</div>
482 <div class="breadcrumbs">${h.link_to(_('Last ten changes'),h.url('changelog_home',repo_name=c.repo_name))}</div>
276 </div>
483 </div>
277 <div class="table">
484 <div class="table">
278 <%include file='../shortlog/shortlog_data.html'/>
485 <%include file='../shortlog/shortlog_data.html'/>
279 ${h.link_to(_('show more'),h.url('changelog_home',repo_name=c.repo_name))}
486 ${h.link_to(_('show more'),h.url('changelog_home',repo_name=c.repo_name))}
280 </div>
487 </div>
281 </div>
488 </div>
282 <div class="box">
489 <div class="box">
283 <div class="title">
490 <div class="title">
284 <div class="breadcrumbs">${h.link_to(_('Last ten tags'),h.url('tags_home',repo_name=c.repo_name))}</div>
491 <div class="breadcrumbs">${h.link_to(_('Last ten tags'),h.url('tags_home',repo_name=c.repo_name))}</div>
285 </div>
492 </div>
286 <div class="table">
493 <div class="table">
287 <%include file='../tags/tags_data.html'/>
494 <%include file='../tags/tags_data.html'/>
288 ${h.link_to(_('show more'),h.url('tags_home',repo_name=c.repo_name))}
495 ${h.link_to(_('show more'),h.url('tags_home',repo_name=c.repo_name))}
289 </div>
496 </div>
290 </div>
497 </div>
291 <div class="box">
498 <div class="box">
292 <div class="title">
499 <div class="title">
293 <div class="breadcrumbs">${h.link_to(_('Last ten branches'),h.url('branches_home',repo_name=c.repo_name))}</div>
500 <div class="breadcrumbs">${h.link_to(_('Last ten branches'),h.url('branches_home',repo_name=c.repo_name))}</div>
294 </div>
501 </div>
295 <div class="table">
502 <div class="table">
296 <%include file='../branches/branches_data.html'/>
503 <%include file='../branches/branches_data.html'/>
297 ${h.link_to(_('show more'),h.url('branches_home',repo_name=c.repo_name))}
504 ${h.link_to(_('show more'),h.url('branches_home',repo_name=c.repo_name))}
298 </div>
505 </div>
299 </div>
506 </div>
300
507
301 </%def> No newline at end of file
508 </%def>
@@ -1,45 +1,51 b''
1 """Pylons application test package
1 """Pylons application test package
2
2
3 This package assumes the Pylons environment is already loaded, such as
3 This package assumes the Pylons environment is already loaded, such as
4 when this script is imported from the `nosetests --with-pylons=test.ini`
4 when this script is imported from the `nosetests --with-pylons=test.ini`
5 command.
5 command.
6
6
7 This module initializes the application via ``websetup`` (`paster
7 This module initializes the application via ``websetup`` (`paster
8 setup-app`) and provides the base testing objects.
8 setup-app`) and provides the base testing objects.
9 """
9 """
10 from unittest import TestCase
10 from unittest import TestCase
11
11
12 from paste.deploy import loadapp
12 from paste.deploy import loadapp
13 from paste.script.appinstall import SetupCommand
13 from paste.script.appinstall import SetupCommand
14 from pylons import config, url
14 from pylons import config, url
15 from routes.util import URLGenerator
15 from routes.util import URLGenerator
16 from webtest import TestApp
16 from webtest import TestApp
17 import os
17 import os
18 from pylons_app.model import meta
18 from pylons_app.model import meta
19 import logging
20
21
22 log = logging.getLogger(__name__)
23
19 import pylons.test
24 import pylons.test
20
25
21 __all__ = ['environ', 'url', 'TestController']
26 __all__ = ['environ', 'url', 'TestController']
22
27
23 # Invoke websetup with the current config file
28 # Invoke websetup with the current config file
24 SetupCommand('setup-app').run([pylons.test.pylonsapp.config['__file__']])
29 #SetupCommand('setup-app').run([config_file])
30
25
31
26 environ = {}
32 environ = {}
27
33
28 class TestController(TestCase):
34 class TestController(TestCase):
29
35
30 def __init__(self, *args, **kwargs):
36 def __init__(self, *args, **kwargs):
31 wsgiapp = pylons.test.pylonsapp
37 wsgiapp = pylons.test.pylonsapp
32 config = wsgiapp.config
38 config = wsgiapp.config
33 self.app = TestApp(wsgiapp)
39 self.app = TestApp(wsgiapp)
34 url._push_object(URLGenerator(config['routes.map'], environ))
40 url._push_object(URLGenerator(config['routes.map'], environ))
35 self.sa = meta.Session
41 self.sa = meta.Session
42
36 TestCase.__init__(self, *args, **kwargs)
43 TestCase.__init__(self, *args, **kwargs)
37
38
44
39 def log_user(self):
45 def log_user(self, username='test_admin', password='test'):
40 response = self.app.post(url(controller='login', action='index'),
46 response = self.app.post(url(controller='login', action='index'),
41 {'username':'test_admin',
47 {'username':username,
42 'password':'test'})
48 'password':password})
43 assert response.status == '302 Found', 'Wrong response code from login got %s' % response.status
49 assert response.status == '302 Found', 'Wrong response code from login got %s' % response.status
44 assert response.session['hg_app_user'].username == 'test_admin', 'wrong logged in user'
50 assert response.session['hg_app_user'].username == 'test_admin', 'wrong logged in user'
45 return response.follow() No newline at end of file
51 return response.follow()
@@ -1,7 +1,9 b''
1 from pylons_app.tests import *
1 from pylons_app.tests import *
2
2
3 class TestAdminController(TestController):
3 class TestAdminController(TestController):
4
4
5 def test_index(self):
5 def test_index(self):
6 self.log_user()
6 response = self.app.get(url(controller='admin/admin', action='index'))
7 response = self.app.get(url(controller='admin/admin', action='index'))
8 assert 'Admin dashboard - journal' in response.body,'No proper title in dashboard'
7 # Test response...
9 # Test response...
@@ -1,43 +1,116 b''
1 from pylons_app.tests import *
1 from pylons_app.tests import *
2 from pylons_app.model.db import User
2
3
3 class TestSettingsController(TestController):
4 class TestSettingsController(TestController):
4
5
5 def test_index(self):
6 def test_index(self):
6 response = self.app.get(url('admin_settings'))
7 response = self.app.get(url('admin_settings'))
7 # Test response...
8 # Test response...
8
9
9 def test_index_as_xml(self):
10 def test_index_as_xml(self):
10 response = self.app.get(url('formatted_admin_settings', format='xml'))
11 response = self.app.get(url('formatted_admin_settings', format='xml'))
11
12
12 def test_create(self):
13 def test_create(self):
13 response = self.app.post(url('admin_settings'))
14 response = self.app.post(url('admin_settings'))
14
15
15 def test_new(self):
16 def test_new(self):
16 response = self.app.get(url('admin_new_setting'))
17 response = self.app.get(url('admin_new_setting'))
17
18
18 def test_new_as_xml(self):
19 def test_new_as_xml(self):
19 response = self.app.get(url('formatted_admin_new_setting', format='xml'))
20 response = self.app.get(url('formatted_admin_new_setting', format='xml'))
20
21
21 def test_update(self):
22 def test_update(self):
22 response = self.app.put(url('admin_setting', setting_id=1))
23 response = self.app.put(url('admin_setting', setting_id=1))
23
24
24 def test_update_browser_fakeout(self):
25 def test_update_browser_fakeout(self):
25 response = self.app.post(url('admin_setting', setting_id=1), params=dict(_method='put'))
26 response = self.app.post(url('admin_setting', setting_id=1), params=dict(_method='put'))
26
27
27 def test_delete(self):
28 def test_delete(self):
28 response = self.app.delete(url('admin_setting', setting_id=1))
29 response = self.app.delete(url('admin_setting', setting_id=1))
29
30
30 def test_delete_browser_fakeout(self):
31 def test_delete_browser_fakeout(self):
31 response = self.app.post(url('admin_setting', setting_id=1), params=dict(_method='delete'))
32 response = self.app.post(url('admin_setting', setting_id=1), params=dict(_method='delete'))
32
33
33 def test_show(self):
34 def test_show(self):
34 response = self.app.get(url('admin_setting', setting_id=1))
35 response = self.app.get(url('admin_setting', setting_id=1))
35
36
36 def test_show_as_xml(self):
37 def test_show_as_xml(self):
37 response = self.app.get(url('formatted_admin_setting', setting_id=1, format='xml'))
38 response = self.app.get(url('formatted_admin_setting', setting_id=1, format='xml'))
38
39
39 def test_edit(self):
40 def test_edit(self):
40 response = self.app.get(url('admin_edit_setting', setting_id=1))
41 response = self.app.get(url('admin_edit_setting', setting_id=1))
41
42
42 def test_edit_as_xml(self):
43 def test_edit_as_xml(self):
43 response = self.app.get(url('formatted_admin_edit_setting', setting_id=1, format='xml'))
44 response = self.app.get(url('formatted_admin_edit_setting', setting_id=1, format='xml'))
45
46 def test_my_account(self):
47 self.log_user()
48 response = self.app.get(url('admin_settings_my_account'))
49 print response
50 assert 'value="test_admin' in response.body
51
52
53
54 def test_my_account_update(self):
55 self.log_user()
56 new_email = 'new@mail.pl'
57 response = self.app.post(url('admin_settings_my_account_update'), params=dict(
58 _method='put',
59 username='test_admin',
60 new_password='test',
61 password='',
62 name='NewName',
63 lastname='NewLastname',
64 email=new_email,))
65 response.follow()
66 print response
67
68 print 'x' * 100
69 print response.session
70 assert 'Your account was updated succesfully' in response.session['flash'][0][1], 'no flash message about success of change'
71 user = self.sa.query(User).filter(User.username == 'test_admin').one()
72 assert user.email == new_email , 'incorrect user email after update got %s vs %s' % (user.email, new_email)
73
74 def test_my_account_update_own_email_ok(self):
75 self.log_user()
76
77 new_email = 'new@mail.pl'
78 response = self.app.post(url('admin_settings_my_account_update'), params=dict(
79 _method='put',
80 username='test_admin',
81 new_password='test',
82 name='NewName',
83 lastname='NewLastname',
84 email=new_email,))
85 print response
86
87 def test_my_account_update_err_email_exists(self):
88 self.log_user()
89
90 new_email = 'test_regular@mail.com'#already exisitn email
91 response = self.app.post(url('admin_settings_my_account_update'), params=dict(
92 _method='put',
93 username='test_admin',
94 new_password='test',
95 name='NewName',
96 lastname='NewLastname',
97 email=new_email,))
98 print response
99
100 assert 'That e-mail address is already taken' in response.body, 'Missing error message about existing email'
101
102
103 def test_my_account_update_err(self):
104 self.log_user()
105
106 new_email = 'newmail.pl'
107 response = self.app.post(url('admin_settings_my_account_update'), params=dict(
108 _method='put',
109 username='test_regular2',
110 new_password='test',
111 name='NewName',
112 lastname='NewLastname',
113 email=new_email,))
114 print response
115 assert 'An email address must contain a single @' in response.body, 'Missing error message about wrong email'
116 assert 'This username already exists' in response.body, 'Missing error message about existing user'
@@ -1,7 +1,8 b''
1 from pylons_app.tests import *
1 from pylons_app.tests import *
2
2
3 class TestBranchesController(TestController):
3 class TestBranchesController(TestController):
4
4
5 def test_index(self):
5 def test_index(self):
6 self.log_user()
6 response = self.app.get(url(controller='branches', action='index',repo_name='vcs_test'))
7 response = self.app.get(url(controller='branches', action='index',repo_name='vcs_test'))
7 # Test response...
8 # Test response...
@@ -1,7 +1,8 b''
1 from pylons_app.tests import *
1 from pylons_app.tests import *
2
2
3 class TestChangelogController(TestController):
3 class TestChangelogController(TestController):
4
4
5 def test_index(self):
5 def test_index(self):
6 self.log_user()
6 response = self.app.get(url(controller='changelog', action='index',repo_name='vcs_test'))
7 response = self.app.get(url(controller='changelog', action='index',repo_name='vcs_test'))
7 # Test response...
8 # Test response...
@@ -1,13 +1,15 b''
1 from pylons_app.tests import *
1 from pylons_app.tests import *
2
2
3 class TestFeedController(TestController):
3 class TestFeedController(TestController):
4
4
5 def test_rss(self):
5 def test_rss(self):
6 self.log_user()
6 response = self.app.get(url(controller='feed', action='rss',
7 response = self.app.get(url(controller='feed', action='rss',
7 repo_name='vcs_test'))
8 repo_name='vcs_test'))
8 # Test response...
9 # Test response...
9
10
10 def test_atom(self):
11 def test_atom(self):
12 self.log_user()
11 response = self.app.get(url(controller='feed', action='atom',
13 response = self.app.get(url(controller='feed', action='atom',
12 repo_name='vcs_test'))
14 repo_name='vcs_test'))
13 # Test response... No newline at end of file
15 # Test response...
@@ -1,10 +1,11 b''
1 from pylons_app.tests import *
1 from pylons_app.tests import *
2
2
3 class TestFilesController(TestController):
3 class TestFilesController(TestController):
4
4
5 def test_index(self):
5 def test_index(self):
6 self.log_user()
6 response = self.app.get(url(controller='files', action='index',
7 response = self.app.get(url(controller='files', action='index',
7 repo_name='vcs_test',
8 repo_name='vcs_test',
8 revision='tip',
9 revision='tip',
9 f_path='/'))
10 f_path='/'))
10 # Test response...
11 # Test response...
@@ -1,111 +1,139 b''
1 from pylons_app.tests import *
1 from pylons_app.tests import *
2 from pylons_app.model.db import User
2 from pylons_app.model.db import User
3 from pylons_app.lib.auth import check_password
3 from pylons_app.lib.auth import check_password
4
4
5
5
6 class TestLoginController(TestController):
6 class TestLoginController(TestController):
7
7
8 def test_index(self):
8 def test_index(self):
9 response = self.app.get(url(controller='login', action='index'))
9 response = self.app.get(url(controller='login', action='index'))
10 assert response.status == '200 OK', 'Wrong response from login page got %s' % response.status
10 assert response.status == '200 OK', 'Wrong response from login page got %s' % response.status
11 # Test response...
11 # Test response...
12
12
13 def test_login_admin_ok(self):
13 def test_login_admin_ok(self):
14 response = self.app.post(url(controller='login', action='index'),
14 response = self.app.post(url(controller='login', action='index'),
15 {'username':'test_admin',
15 {'username':'test_admin',
16 'password':'test'})
16 'password':'test'})
17 assert response.status == '302 Found', 'Wrong response code from login got %s' % response.status
17 assert response.status == '302 Found', 'Wrong response code from login got %s' % response.status
18 assert response.session['hg_app_user'].username == 'test_admin', 'wrong logged in user'
18 assert response.session['hg_app_user'].username == 'test_admin', 'wrong logged in user'
19 response = response.follow()
19 response = response.follow()
20 assert 'auto description for vcs_test' in response.body
20 assert 'auto description for vcs_test' in response.body
21
21
22 def test_login_regular_ok(self):
22 def test_login_regular_ok(self):
23 response = self.app.post(url(controller='login', action='index'),
23 response = self.app.post(url(controller='login', action='index'),
24 {'username':'test_regular',
24 {'username':'test_regular',
25 'password':'test'})
25 'password':'test'})
26 assert response.status == '302 Found', 'Wrong response code from login got %s' % response.status
26 assert response.status == '302 Found', 'Wrong response code from login got %s' % response.status
27 assert response.session['hg_app_user'].username == 'test_regular', 'wrong logged in user'
27 assert response.session['hg_app_user'].username == 'test_regular', 'wrong logged in user'
28 response = response.follow()
28 response = response.follow()
29 assert 'auto description for vcs_test' in response.body
29 assert 'auto description for vcs_test' in response.body
30 assert '<a title="Admin" href="/_admin">' not in response.body
30 assert '<a title="Admin" href="/_admin">' not in response.body
31
31
32 def test_login_ok_came_from(self):
32 def test_login_ok_came_from(self):
33 test_came_from = '/_admin/users'
33 test_came_from = '/_admin/users'
34 response = self.app.post(url(controller='login', action='index', came_from=test_came_from),
34 response = self.app.post(url(controller='login', action='index', came_from=test_came_from),
35 {'username':'test_admin',
35 {'username':'test_admin',
36 'password':'test'})
36 'password':'test'})
37 assert response.status == '302 Found', 'Wrong response code from came from redirection'
37 assert response.status == '302 Found', 'Wrong response code from came from redirection'
38 response = response.follow()
38 response = response.follow()
39
39
40 assert response.status == '200 OK', 'Wrong response from login page got %s' % response.status
40 assert response.status == '200 OK', 'Wrong response from login page got %s' % response.status
41 assert 'Users administration' in response.body, 'No proper title in response'
41 assert 'Users administration' in response.body, 'No proper title in response'
42
42
43
43
44 def test_login_wrong(self):
44 def test_login_wrong(self):
45 response = self.app.post(url(controller='login', action='index'),
45 response = self.app.post(url(controller='login', action='index'),
46 {'username':'error',
46 {'username':'error',
47 'password':'test'})
47 'password':'test'})
48 assert response.status == '200 OK', 'Wrong response from login page'
48 assert response.status == '200 OK', 'Wrong response from login page'
49
49
50 assert 'invalid user name' in response.body, 'No error username message in response'
50 assert 'invalid user name' in response.body, 'No error username message in response'
51 assert 'invalid password' in response.body, 'No error password message in response'
51 assert 'invalid password' in response.body, 'No error password message in response'
52
52
53
53
54 def test_register(self):
54 def test_register(self):
55 response = self.app.get(url(controller='login', action='register'))
55 response = self.app.get(url(controller='login', action='register'))
56 assert 'Sign Up to hg-app' in response.body, 'wrong page for user registration'
56 assert 'Sign Up to hg-app' in response.body, 'wrong page for user registration'
57
57
58 def test_register_err_same_username(self):
58 def test_register_err_same_username(self):
59 response = self.app.post(url(controller='login', action='register'),
59 response = self.app.post(url(controller='login', action='register'),
60 {'username':'test_admin',
60 {'username':'test_admin',
61 'password':'test',
61 'password':'test',
62 'email':'goodmail@domain.com',
62 'email':'goodmail@domain.com',
63 'name':'test',
63 'name':'test',
64 'lastname':'test'})
64 'lastname':'test'})
65
65
66 assert response.status == '200 OK', 'Wrong response from register page got %s' % response.status
66 assert response.status == '200 OK', 'Wrong response from register page got %s' % response.status
67 assert 'This username already exists' in response.body
67 assert 'This username already exists' in response.body
68
68
69 def test_register_err_wrong_data(self):
69 def test_register_err_wrong_data(self):
70 response = self.app.post(url(controller='login', action='register'),
70 response = self.app.post(url(controller='login', action='register'),
71 {'username':'xs',
71 {'username':'xs',
72 'password':'',
72 'password':'',
73 'email':'goodmailm',
73 'email':'goodmailm',
74 'name':'test',
74 'name':'test',
75 'lastname':'test'})
75 'lastname':'test'})
76
76
77 assert response.status == '200 OK', 'Wrong response from register page got %s' % response.status
77 assert response.status == '200 OK', 'Wrong response from register page got %s' % response.status
78 assert 'An email address must contain a single @' in response.body
78 assert 'An email address must contain a single @' in response.body
79 assert 'Enter a value 3 characters long or more' in response.body
79 assert 'Enter a value 3 characters long or more' in response.body
80 assert 'Please enter a value<' in response.body
80 assert 'Please enter a value<' in response.body
81
81
82
82
83
83
84 def test_register_ok(self):
84 def test_register_ok(self):
85 username = 'test_regular2'
85 username = 'test_regular4'
86 password = 'qweqwe'
86 password = 'qweqwe'
87 email = 'goodmail@mail.com'
87 email = 'marcin@test.com'
88 name = 'testname'
88 name = 'testname'
89 lastname = 'testlastname'
89 lastname = 'testlastname'
90
90
91 response = self.app.post(url(controller='login', action='register'),
91 response = self.app.post(url(controller='login', action='register'),
92 {'username':username,
92 {'username':username,
93 'password':password,
93 'password':password,
94 'email':email,
94 'email':email,
95 'name':name,
95 'name':name,
96 'lastname':lastname})
96 'lastname':lastname})
97
97 print response.body
98 assert response.status == '302 Found', 'Wrong response from register page got %s' % response.status
98 assert response.status == '302 Found', 'Wrong response from register page got %s' % response.status
99 assert 'You have successfully registered into hg-app' in response.session['flash'][0], 'No flash message about user registration'
99
100
100 ret = self.sa.query(User).filter(User.username == 'test_regular2').one()
101 ret = self.sa.query(User).filter(User.username == 'test_regular4').one()
101 assert ret.username == username , 'field mismatch %s %s' % (ret.username, username)
102 assert ret.username == username , 'field mismatch %s %s' % (ret.username, username)
102 assert check_password(password,ret.password) == True , 'password mismatch'
103 assert check_password(password, ret.password) == True , 'password mismatch'
103 assert ret.email == email , 'field mismatch %s %s' % (ret.email, email)
104 assert ret.email == email , 'field mismatch %s %s' % (ret.email, email)
104 assert ret.name == name , 'field mismatch %s %s' % (ret.name, name)
105 assert ret.name == name , 'field mismatch %s %s' % (ret.name, name)
105 assert ret.lastname == lastname , 'field mismatch %s %s' % (ret.lastname, lastname)
106 assert ret.lastname == lastname , 'field mismatch %s %s' % (ret.lastname, lastname)
106
107
107
108
109 def test_forgot_password_wrong_mail(self):
110 response = self.app.post(url(controller='login', action='password_reset'),
111 {'email':'marcin@wrongmail.org', })
112
113 assert "That e-mail address doesn't exist" in response.body, 'Missing error message about wrong email'
114
115 def test_forgot_password(self):
116 response = self.app.get(url(controller='login', action='password_reset'))
117 assert response.status == '200 OK', 'Wrong response from login page got %s' % response.status
118
119 username = 'test_password_reset_1'
120 password = 'qweqwe'
121 email = 'marcin@python-works.com'
122 name = 'passwd'
123 lastname = 'reset'
124
125 response = self.app.post(url(controller='login', action='register'),
126 {'username':username,
127 'password':password,
128 'email':email,
129 'name':name,
130 'lastname':lastname})
131 #register new user for email test
132 response = self.app.post(url(controller='login', action='password_reset'),
133 {'email':email, })
134 print response.session['flash']
135 assert 'You have successfully registered into hg-app' in response.session['flash'][0], 'No flash message about user registration'
136 assert 'Your new password was sent' in response.session['flash'][1], 'No flash message about password reset'
108
137
109
138
110
139
111
@@ -1,29 +1,38 b''
1 from pylons_app.tests import *
1 from pylons_app.tests import *
2 from pylons_app.lib.indexers import IDX_LOCATION
2 from pylons_app.lib.indexers import IDX_LOCATION
3 import os
3 import os
4 from nose.plugins.skip import SkipTest
4 from nose.plugins.skip import SkipTest
5
5
6 class TestSearchController(TestController):
6 class TestSearchController(TestController):
7
7
8 def test_index(self):
8 def test_index(self):
9 self.log_user()
9 self.log_user()
10 response = self.app.get(url(controller='search', action='index'))
10 response = self.app.get(url(controller='search', action='index'))
11 print response.body
11 print response.body
12 assert 'class="small" id="q" name="q" type="text"' in response.body,'Search box content error'
12 assert 'class="small" id="q" name="q" type="text"' in response.body, 'Search box content error'
13 # Test response...
13 # Test response...
14
14
15 def test_empty_search(self):
15 def test_empty_search(self):
16
16
17 if os.path.isdir(IDX_LOCATION):
17 if os.path.isdir(IDX_LOCATION):
18 raise SkipTest('skipped due to existing index')
18 raise SkipTest('skipped due to existing index')
19 else:
19 else:
20 self.log_user()
20 self.log_user()
21 response = self.app.get(url(controller='search', action='index'),{'q':'vcs_test'})
21 response = self.app.get(url(controller='search', action='index'), {'q':'vcs_test'})
22 assert 'There is no index to search in. Please run whoosh indexer' in response.body,'No error message about empty index'
22 assert 'There is no index to search in. Please run whoosh indexer' in response.body, 'No error message about empty index'
23
23
24 def test_normal_search(self):
24 def test_normal_search(self):
25 self.log_user()
25 self.log_user()
26 response = self.app.get(url(controller='search', action='index'),{'q':'def+repo'})
26 response = self.app.get(url(controller='search', action='index'), {'q':'def repo'})
27 print response.body
27 print response.body
28 assert '9 results' in response.body,'no message about proper search results'
28 assert '10 results' in response.body, 'no message about proper search results'
29 assert 'Permission denied' not in response.body, 'Wrong permissions settings for that repo and user'
29
30
31
32 def test_repo_search(self):
33 self.log_user()
34 response = self.app.get(url(controller='search', action='index'), {'q':'repository:vcs_test def test'})
35 print response.body
36 assert '4 results' in response.body, 'no message about proper search results'
37 assert 'Permission denied' not in response.body, 'Wrong permissions settings for that repo and user'
38
@@ -1,8 +1,9 b''
1 from pylons_app.tests import *
1 from pylons_app.tests import *
2
2
3 class TestSettingsController(TestController):
3 class TestSettingsController(TestController):
4
4
5 def test_index(self):
5 def test_index(self):
6 self.log_user()
6 response = self.app.get(url(controller='settings', action='index',
7 response = self.app.get(url(controller='settings', action='index',
7 repo_name='vcs_test'))
8 repo_name='vcs_test'))
8 # Test response...
9 # Test response...
@@ -1,7 +1,8 b''
1 from pylons_app.tests import *
1 from pylons_app.tests import *
2
2
3 class TestShortlogController(TestController):
3 class TestShortlogController(TestController):
4
4
5 def test_index(self):
5 def test_index(self):
6 self.log_user()
6 response = self.app.get(url(controller='shortlog', action='index',repo_name='vcs_test'))
7 response = self.app.get(url(controller='shortlog', action='index',repo_name='vcs_test'))
7 # Test response...
8 # Test response...
@@ -1,7 +1,8 b''
1 from pylons_app.tests import *
1 from pylons_app.tests import *
2
2
3 class TestSummaryController(TestController):
3 class TestSummaryController(TestController):
4
4
5 def test_index(self):
5 def test_index(self):
6 self.log_user()
6 response = self.app.get(url(controller='summary', action='index',repo_name='vcs_test'))
7 response = self.app.get(url(controller='summary', action='index',repo_name='vcs_test'))
7 # Test response...
8 # Test response...
@@ -1,7 +1,8 b''
1 from pylons_app.tests import *
1 from pylons_app.tests import *
2
2
3 class TestTagsController(TestController):
3 class TestTagsController(TestController):
4
4
5 def test_index(self):
5 def test_index(self):
6 self.log_user()
6 response = self.app.get(url(controller='tags', action='index',repo_name='vcs_test'))
7 response = self.app.get(url(controller='tags', action='index',repo_name='vcs_test'))
7 # Test response...
8 # Test response...
@@ -1,47 +1,32 b''
1 """Setup the pylons_app application"""
1 """Setup the pylons_app application"""
2
2
3 from os.path import dirname as dn, join as jn
3 from os.path import dirname as dn
4 from pylons_app.config.environment import load_environment
4 from pylons_app.config.environment import load_environment
5 from pylons_app.lib.db_manage import DbManage
5 from pylons_app.lib.db_manage import DbManage
6 import datetime
7 from time import mktime
8 import logging
6 import logging
9 import os
7 import os
10 import sys
8 import sys
11 import tarfile
12
9
13 log = logging.getLogger(__name__)
10 log = logging.getLogger(__name__)
14
11
15 ROOT = dn(dn(os.path.realpath(__file__)))
12 ROOT = dn(dn(os.path.realpath(__file__)))
16 sys.path.append(ROOT)
13 sys.path.append(ROOT)
17
14
15
18 def setup_app(command, conf, vars):
16 def setup_app(command, conf, vars):
19 """Place any commands to setup pylons_app here"""
17 """Place any commands to setup pylons_app here"""
20 log_sql = True
18 log_sql = True
21 tests = False
19 tests = False
22
20 REPO_TEST_PATH = None
23 dbname = os.path.split(conf['sqlalchemy.db1.url'])[-1]
24 filename = os.path.split(conf.filename)[-1]
25
21
26 if filename == 'tests.ini':
22 dbname = os.path.split(conf['sqlalchemy.db1.url'])[-1]
27 uniq_suffix = str(int(mktime(datetime.datetime.now().timetuple())))
28 REPO_TEST_PATH = '/tmp/hg_app_test_%s' % uniq_suffix
29
30 if not os.path.isdir(REPO_TEST_PATH):
31 os.mkdir(REPO_TEST_PATH)
32 cur_dir = dn(os.path.abspath(__file__))
33 tar = tarfile.open(jn(cur_dir,'tests',"vcs_test.tar.gz"))
34 tar.extractall(REPO_TEST_PATH)
35 tar.close()
36
37 tests = True
38
23
39 dbmanage = DbManage(log_sql, dbname, tests)
24 dbmanage = DbManage(log_sql, dbname, tests)
40 dbmanage.create_tables(override=True)
25 dbmanage.create_tables(override=True)
41 dbmanage.config_prompt(REPO_TEST_PATH)
26 dbmanage.config_prompt(REPO_TEST_PATH)
42 dbmanage.create_default_user()
27 dbmanage.create_default_user()
43 dbmanage.admin_prompt()
28 dbmanage.admin_prompt()
44 dbmanage.create_permissions()
29 dbmanage.create_permissions()
45 dbmanage.populate_default_permissions()
30 dbmanage.populate_default_permissions()
46 load_environment(conf.global_conf, conf.local_conf, initial=True)
31 load_environment(conf.global_conf, conf.local_conf, initial=True)
47
32
@@ -1,34 +1,34 b''
1 [egg_info]
1 [egg_info]
2 tag_build = dev
2 tag_build = dev
3 tag_svn_revision = true
3 tag_svn_revision = true
4
4
5 [easy_install]
5 [easy_install]
6 find_links = http://www.pylonshq.com/download/
6 find_links = http://www.pylonshq.com/download/
7
7
8 [nosetests]
8 [nosetests]
9 verbose=True
9 verbose=True
10 verbosity=2
10 verbosity=2
11 with-pylons=tests.ini
11 with-pylons=test.ini
12 detailed-errors=1
12 detailed-errors=1
13
13
14 # Babel configuration
14 # Babel configuration
15 [compile_catalog]
15 [compile_catalog]
16 domain = pylons_app
16 domain = pylons_app
17 directory = pylons_app/i18n
17 directory = pylons_app/i18n
18 statistics = true
18 statistics = true
19
19
20 [extract_messages]
20 [extract_messages]
21 add_comments = TRANSLATORS:
21 add_comments = TRANSLATORS:
22 output_file = pylons_app/i18n/pylons_app.pot
22 output_file = pylons_app/i18n/pylons_app.pot
23 width = 80
23 width = 80
24
24
25 [init_catalog]
25 [init_catalog]
26 domain = pylons_app
26 domain = pylons_app
27 input_file = pylons_app/i18n/pylons_app.pot
27 input_file = pylons_app/i18n/pylons_app.pot
28 output_dir = pylons_app/i18n
28 output_dir = pylons_app/i18n
29
29
30 [update_catalog]
30 [update_catalog]
31 domain = pylons_app
31 domain = pylons_app
32 input_file = pylons_app/i18n/pylons_app.pot
32 input_file = pylons_app/i18n/pylons_app.pot
33 output_dir = pylons_app/i18n
33 output_dir = pylons_app/i18n
34 previous = true
34 previous = true
@@ -1,48 +1,49 b''
1 from pylons_app import get_version
1 from pylons_app import get_version
2 try:
2 try:
3 from setuptools import setup, find_packages
3 from setuptools import setup, find_packages
4 except ImportError:
4 except ImportError:
5 from ez_setup import use_setuptools
5 from ez_setup import use_setuptools
6 use_setuptools()
6 use_setuptools()
7 from setuptools import setup, find_packages
7 from setuptools import setup, find_packages
8
8
9 setup(
9 setup(
10 name='HgApp-%s'%get_version(),
10 name='HgApp-%s' % get_version(),
11 version=get_version(),
11 version=get_version(),
12 description='Mercurial repository serving and browsing app',
12 description='Mercurial repository serving and browsing app',
13 keywords='mercurial web hgwebdir replacement serving hgweb',
13 keywords='mercurial web hgwebdir replacement serving hgweb',
14 license='BSD',
14 license='BSD',
15 author='marcin kuzminski',
15 author='marcin kuzminski',
16 author_email='marcin@python-works.com',
16 author_email='marcin@python-works.com',
17 url='http://hg.python-works.com',
17 url='http://hg.python-works.com',
18 install_requires=[
18 install_requires=[
19 "Pylons>=1.0.0",
19 "Pylons>=1.0.0",
20 "SQLAlchemy>=0.6",
20 "SQLAlchemy>=0.6",
21 "babel",
21 "babel",
22 "Mako>=0.3.2",
22 "Mako>=0.3.2",
23 "vcs>=0.1.4",
23 "vcs>=0.1.5",
24 "pygments>=1.3.0",
24 "pygments>=1.3.0",
25 "mercurial>=1.6",
25 "mercurial>=1.6",
26 "pysqlite",
26 "pysqlite",
27 "whoosh==1.0.0b10",
27 "whoosh==1.0.0b17",
28 "py-bcrypt",
28 "py-bcrypt",
29 "celery",
29 ],
30 ],
30 setup_requires=["PasteScript>=1.6.3"],
31 setup_requires=["PasteScript>=1.6.3"],
31 packages=find_packages(exclude=['ez_setup']),
32 packages=find_packages(exclude=['ez_setup']),
32 include_package_data=True,
33 include_package_data=True,
33 test_suite='nose.collector',
34 test_suite='nose.collector',
34 package_data={'pylons_app': ['i18n/*/LC_MESSAGES/*.mo']},
35 package_data={'pylons_app': ['i18n/*/LC_MESSAGES/*.mo']},
35 message_extractors={'pylons_app': [
36 message_extractors={'pylons_app': [
36 ('**.py', 'python', None),
37 ('**.py', 'python', None),
37 ('templates/**.mako', 'mako', {'input_encoding': 'utf-8'}),
38 ('templates/**.mako', 'mako', {'input_encoding': 'utf-8'}),
38 ('public/**', 'ignore', None)]},
39 ('public/**', 'ignore', None)]},
39 zip_safe=False,
40 zip_safe=False,
40 paster_plugins=['PasteScript', 'Pylons'],
41 paster_plugins=['PasteScript', 'Pylons'],
41 entry_points="""
42 entry_points="""
42 [paste.app_factory]
43 [paste.app_factory]
43 main = pylons_app.config.middleware:make_app
44 main = pylons_app.config.middleware:make_app
44
45
45 [paste.app_install]
46 [paste.app_install]
46 main = pylons.util:PylonsInstaller
47 main = pylons.util:PylonsInstaller
47 """,
48 """,
48 )
49 )
@@ -1,155 +1,160 b''
1 ################################################################################
1 ################################################################################
2 ################################################################################
2 ################################################################################
3 # pylons_app - Pylons environment configuration #
3 # hg-app - Pylons environment configuration #
4 # #
4 # #
5 # The %(here)s variable will be replaced with the parent directory of this file#
5 # The %(here)s variable will be replaced with the parent directory of this file#
6 ################################################################################
6 ################################################################################
7
7
8 [DEFAULT]
8 [DEFAULT]
9 debug = true
9 debug = true
10 ############################################
10 ################################################################################
11 ## Uncomment and replace with the address ##
11 ## Uncomment and replace with the address which should receive ##
12 ## which should receive any error reports ##
12 ## any error reports after application crash ##
13 ############################################
13 ## Additionally those settings will be used by hg-app mailing system ##
14 ################################################################################
14 #email_to = admin@localhost
15 #email_to = admin@localhost
16 #error_email_from = paste_error@localhost
17 #app_email_from = hg-app-noreply@localhost
18 #error_message =
19
15 #smtp_server = mail.server.com
20 #smtp_server = mail.server.com
16 #error_email_from = paste_error@localhost
17 #smtp_username =
21 #smtp_username =
18 #smtp_password =
22 #smtp_password =
19 #error_message = 'mercurial crash !'
23 #smtp_port =
24 #smtp_use_tls = false
20
25
21 [server:main]
26 [server:main]
22 ##nr of threads to spawn
27 ##nr of threads to spawn
23 threadpool_workers = 5
28 threadpool_workers = 5
24
29
25 ##max request before
30 ##max request before thread respawn
26 threadpool_max_requests = 2
31 threadpool_max_requests = 2
27
32
28 ##option to use threads of process
33 ##option to use threads of process
29 use_threadpool = true
34 use_threadpool = true
30
35
31 use = egg:Paste#http
36 use = egg:Paste#http
32 host = 127.0.0.1
37 host = 127.0.0.1
33 port = 5000
38 port = 5000
34
39
35 [app:main]
40 [app:main]
36 use = egg:pylons_app
41 use = egg:pylons_app
37 full_stack = true
42 full_stack = true
38 static_files = true
43 static_files = true
39 lang=en
44 lang=en
40 cache_dir = %(here)s/data
45 cache_dir = %(here)s/data
41
46
42 ####################################
47 ####################################
43 ### BEAKER CACHE ####
48 ### BEAKER CACHE ####
44 ####################################
49 ####################################
45 beaker.cache.data_dir=/%(here)s/data/cache/data
50 beaker.cache.data_dir=/%(here)s/data/cache/data
46 beaker.cache.lock_dir=/%(here)s/data/cache/lock
51 beaker.cache.lock_dir=/%(here)s/data/cache/lock
47 beaker.cache.regions=super_short_term,short_term,long_term
52 beaker.cache.regions=super_short_term,short_term,long_term
48 beaker.cache.long_term.type=memory
53 beaker.cache.long_term.type=memory
49 beaker.cache.long_term.expire=36000
54 beaker.cache.long_term.expire=36000
50 beaker.cache.short_term.type=memory
55 beaker.cache.short_term.type=memory
51 beaker.cache.short_term.expire=60
56 beaker.cache.short_term.expire=60
52 beaker.cache.super_short_term.type=memory
57 beaker.cache.super_short_term.type=memory
53 beaker.cache.super_short_term.expire=10
58 beaker.cache.super_short_term.expire=10
54
59
55 ####################################
60 ####################################
56 ### BEAKER SESSION ####
61 ### BEAKER SESSION ####
57 ####################################
62 ####################################
58 ## Type of storage used for the session, current types are
63 ## Type of storage used for the session, current types are
59 ## “dbm”, “file”, “memcached”, “database”, and “memory”.
64 ## "dbm", "file", "memcached", "database", and "memory".
60 ## The storage uses the Container API
65 ## The storage uses the Container API
61 ##that is also used by the cache system.
66 ##that is also used by the cache system.
62 beaker.session.type = file
67 beaker.session.type = file
63
68
64 beaker.session.key = hg-app
69 beaker.session.key = hg-app
65 beaker.session.secret = g654dcno0-9873jhgfreyu
70 beaker.session.secret = g654dcno0-9873jhgfreyu
66 beaker.session.timeout = 36000
71 beaker.session.timeout = 36000
67
72
68 ##auto save the session to not to use .save()
73 ##auto save the session to not to use .save()
69 beaker.session.auto = False
74 beaker.session.auto = False
70
75
71 ##true exire at browser close
76 ##true exire at browser close
72 #beaker.session.cookie_expires = 3600
77 #beaker.session.cookie_expires = 3600
73
78
74
79
75 ################################################################################
80 ################################################################################
76 ## WARNING: *THE LINE BELOW MUST BE UNCOMMENTED ON A PRODUCTION ENVIRONMENT* ##
81 ## WARNING: *THE LINE BELOW MUST BE UNCOMMENTED ON A PRODUCTION ENVIRONMENT* ##
77 ## Debug mode will enable the interactive debugging tool, allowing ANYONE to ##
82 ## Debug mode will enable the interactive debugging tool, allowing ANYONE to ##
78 ## execute malicious code after an exception is raised. ##
83 ## execute malicious code after an exception is raised. ##
79 ################################################################################
84 ################################################################################
80 #set debug = false
85 #set debug = false
81
86
82 ##################################
87 ##################################
83 ### LOGVIEW CONFIG ###
88 ### LOGVIEW CONFIG ###
84 ##################################
89 ##################################
85 logview.sqlalchemy = #faa
90 logview.sqlalchemy = #faa
86 logview.pylons.templating = #bfb
91 logview.pylons.templating = #bfb
87 logview.pylons.util = #eee
92 logview.pylons.util = #eee
88
93
89 #########################################################
94 #########################################################
90 ### DB CONFIGS - EACH DB WILL HAVE IT'S OWN CONFIG ###
95 ### DB CONFIGS - EACH DB WILL HAVE IT'S OWN CONFIG ###
91 #########################################################
96 #########################################################
92 sqlalchemy.db1.url = sqlite:///%(here)s/test.db
97 sqlalchemy.db1.url = sqlite:///%(here)s/test.db
93 #sqlalchemy.db1.echo = False
98 #sqlalchemy.db1.echo = False
94 #sqlalchemy.db1.pool_recycle = 3600
99 #sqlalchemy.db1.pool_recycle = 3600
95 sqlalchemy.convert_unicode = true
100 sqlalchemy.convert_unicode = true
96
101
97 ################################
102 ################################
98 ### LOGGING CONFIGURATION ####
103 ### LOGGING CONFIGURATION ####
99 ################################
104 ################################
100 [loggers]
105 [loggers]
101 keys = root, routes, pylons_app, sqlalchemy
106 keys = root, routes, pylons_app, sqlalchemy
102
107
103 [handlers]
108 [handlers]
104 keys = console
109 keys = console
105
110
106 [formatters]
111 [formatters]
107 keys = generic,color_formatter
112 keys = generic,color_formatter
108
113
109 #############
114 #############
110 ## LOGGERS ##
115 ## LOGGERS ##
111 #############
116 #############
112 [logger_root]
117 [logger_root]
113 level = ERROR
118 level = ERROR
114 handlers = console
119 handlers = console
115
120
116 [logger_routes]
121 [logger_routes]
117 level = ERROR
122 level = ERROR
118 handlers = console
123 handlers = console
119 qualname = routes.middleware
124 qualname = routes.middleware
120 # "level = DEBUG" logs the route matched and routing variables.
125 # "level = DEBUG" logs the route matched and routing variables.
121
126
122 [logger_pylons_app]
127 [logger_pylons_app]
123 level = ERROR
128 level = ERROR
124 handlers = console
129 handlers = console
125 qualname = pylons_app
130 qualname = pylons_app
126 propagate = 0
131 propagate = 0
127
132
128 [logger_sqlalchemy]
133 [logger_sqlalchemy]
129 level = ERROR
134 level = ERROR
130 handlers = console
135 handlers = console
131 qualname = sqlalchemy.engine
136 qualname = sqlalchemy.engine
132 propagate = 0
137 propagate = 0
133
138
134 ##############
139 ##############
135 ## HANDLERS ##
140 ## HANDLERS ##
136 ##############
141 ##############
137
142
138 [handler_console]
143 [handler_console]
139 class = StreamHandler
144 class = StreamHandler
140 args = (sys.stderr,)
145 args = (sys.stderr,)
141 level = NOTSET
146 level = NOTSET
142 formatter = color_formatter
147 formatter = color_formatter
143
148
144 ################
149 ################
145 ## FORMATTERS ##
150 ## FORMATTERS ##
146 ################
151 ################
147
152
148 [formatter_generic]
153 [formatter_generic]
149 format = %(asctime)s.%(msecs)03d %(levelname)-5.5s [%(name)s] %(message)s
154 format = %(asctime)s.%(msecs)03d %(levelname)-5.5s [%(name)s] %(message)s
150 datefmt = %Y-%m-%d %H:%M:%S
155 datefmt = %Y-%m-%d %H:%M:%S
151
156
152 [formatter_color_formatter]
157 [formatter_color_formatter]
153 class=pylons_app.lib.colored_formatter.ColorFormatter
158 class=pylons_app.lib.colored_formatter.ColorFormatter
154 format= %(asctime)s.%(msecs)03d %(levelname)-5.5s [%(name)s] %(message)s
159 format= %(asctime)s.%(msecs)03d %(levelname)-5.5s [%(name)s] %(message)s
155 datefmt = %Y-%m-%d %H:%M:%S No newline at end of file
160 datefmt = %Y-%m-%d %H:%M:%S
General Comments 0
You need to be logged in to leave comments. Login now