changeset 298:15e96b5a2685

autocomplete for repository managment of users
author Marcin Kuzminski <marcin@python-works.com>
date Sat, 26 Jun 2010 18:42:13 +0200
parents a074dec6ee40
children d303aacb3349
files pylons_app/controllers/repos.py pylons_app/model/repo_model.py pylons_app/public/css/monoblue_custom.css pylons_app/templates/admin/repos/repo_edit.html pylons_app/templates/base/base.html pylons_app/templates/login.html
diffstat 6 files changed, 487 insertions(+), 247 deletions(-) [+]
line wrap: on
line diff
--- a/pylons_app/controllers/repos.py	Fri Jun 25 21:53:13 2010 +0200
+++ b/pylons_app/controllers/repos.py	Sat Jun 26 18:42:13 2010 +0200
@@ -111,6 +111,7 @@
                            
         except formencode.Invalid as errors:
             c.repo_info = repo_model.get(id)
+            c.users_array = repo_model.get_users_js()
             errors.value.update({'user':c.repo_info.user.username})
             c.form_errors = errors.error_dict
             return htmlfill.render(
@@ -169,6 +170,8 @@
         defaults = c.repo_info.__dict__
         defaults.update({'user':c.repo_info.user.username})
         
+        c.users_array = repo_model.get_users_js()
+        
         for p in c.repo_info.repo2perm:
             defaults.update({'perm_%s' % p.user.username: 
                              p.permission.permission_name})
--- a/pylons_app/model/repo_model.py	Fri Jun 25 21:53:13 2010 +0200
+++ b/pylons_app/model/repo_model.py	Sat Jun 26 18:42:13 2010 +0200
@@ -41,6 +41,15 @@
     def get(self, id):
         return self.sa.query(Repository).get(id)
         
+    def get_users_js(self):
+        
+        users = self.sa.query(User).all()
+        u_tmpl = '''{id:%s, fname:"%s", lname:"%s", nname:"%s"},'''
+        users_array = '[%s];' % '\n'.join([u_tmpl % (u.user_id, u.name,
+                                                    u.lastname, u.username) 
+                                        for u in users])
+        return users_array        
+        
     
     def update(self, repo_id, form_data):
         try:
--- a/pylons_app/public/css/monoblue_custom.css	Fri Jun 25 21:53:13 2010 +0200
+++ b/pylons_app/public/css/monoblue_custom.css	Sat Jun 26 18:42:13 2010 +0200
@@ -1,17 +1,20 @@
 /*** Initial Settings ***/
-#mainhtml{
+#mainhtml {
 	margin: 15px 50px;
 	background: #DBD4C6;
 	font-family: sans-serif;
 }
-#mainhtml .breadcrumbs a:HOVER{
-	text-decoration:  underline;
+
+#mainhtml .breadcrumbs a:HOVER {
+	text-decoration: underline;
 }
+
 a {
 	color: #556CB5;
 	text-decoration: none;
 }
-a:HOVER{
+
+a:HOVER {
 	text-decoration: underline;
 }
 
@@ -19,172 +22,257 @@
 
 /*** ***/
 .table_disp {
-    border-left: 0px solid #666666;
-    border-bottom: 1px solid #666666;
-    border-right: 1px solid #666666;
-    padding: 0px;
-    margin: 0px;
-    border-spacing: 0px;    
+	border-left: 0px solid #666666;
+	border-bottom: 1px solid #666666;
+	border-right: 1px solid #666666;
+	padding: 0px;
+	margin: 0px;
+	border-spacing: 0px;
 }
 
 .table_disp .header {
-    border-top: 1px solid #666666;
-    background-color: #556CB5;
-    font-weight: bold;
-    color: white;
-    vertical-align: middle;
-    padding: 3px 5px;
-    text-align: left;
-    font-size: 0.9em;
+	border-top: 1px solid #666666;
+	background-color: #556CB5;
+	font-weight: bold;
+	color: white;
+	vertical-align: middle;
+	padding: 3px 5px;
+	text-align: left;
+	font-size: 0.9em;
 }
 
 .table_disp .header td {
-    padding: 4px;
-    vertical-align: middle;
-    border-top: 1px solid #AAAAAA;
-    border-bottom: 2px solid #666666;
+	padding: 4px;
+	vertical-align: middle;
+	border-top: 1px solid #AAAAAA;
+	border-bottom: 2px solid #666666;
 }
+
 .table_disp td {
-    border-left: 1px solid #AAAAAA;
-    padding-left: 4px;
-    padding-right: 4px;
+	border-left: 1px solid #AAAAAA;
+	padding-left: 4px;
+	padding-right: 4px;
 }
 
 table tr.parity0:hover,table tr.parity1:hover {
-    background: #D5E1E6;
+	background: #D5E1E6;
 }
 
 table tr.parity0 {
-    background: #EAEAE9;
+	background: #EAEAE9;
 }
 
 table tr.parity1 {
-    background: #FFFFFF;
+	background: #FFFFFF;
+}
+
+/*** ***/ 
+
+/** COMMON SETTINGS **/
+.add_icon {
+	background: url("/images/icons/add.png") no-repeat scroll 3px;
+	height: 16px;
+	padding-left: 20px;
+	padding-top: 0px;
+	text-align: left;
+}
+
+.edit_icon {
+	background: url("/images/icons/folder_edit.png") no-repeat scroll 3px;
+	height: 16px;
+	padding-left: 20px;
+	padding-top: 0px;
+	text-align: left;
 }
 
-
-/*** ***/
+.delete_icon {
+	background: url("/images/icons/delete.png") no-repeat scroll 3px;
+	height: 16px;
+	padding-left: 20px;
+	padding-top: 0px;
+	text-align: left;
+}
 
-/** common settings **/
-.add_icon{
-    background: url("/images/icons/add.png") no-repeat scroll 3px;
-    height: 16px;
-    padding-left: 20px;
-    padding-top: 0px;
-    text-align: left;
-
-}
-.edit_icon{
-    background: url("/images/icons/folder_edit.png") no-repeat scroll 3px;
-    height: 16px;
-    padding-left: 20px;
-    padding-top: 0px;
-    text-align: left;
+.action_button {
+	border: 0px;
+	display: block;
 }
 
-.delete_icon{
-    background: url("/images/icons/delete.png") no-repeat scroll 3px;
-    height: 16px;
-    padding-left: 20px;
-    padding-top: 0px;
-    text-align: left;
-
+.action_button:hover {
+	border: 0px;
+	font-style: italic;
+	cursor: pointer;
 }
 
-.action_button{
-    border:0px;
-    display: block;
+.flash_msg ul {
+	margin: 0;
+	padding: 25px 0px 0px 0px;
 }
-.action_button:hover{
-    border:0px;
-    font-style:italic;
-    cursor: pointer;
+
+.error_msg {
+	background-color: #FFCFCF;
+	background-image: url("/images/icons/error_msg.png");
+	border: 1px solid #FF9595;
+	color: #CC3300;
 }
 
-.flash_msg ul{
-	margin:0;
-	padding:25px 0px 0px 0px;
-	
-}
-.error_msg {
-	background-color:#FFCFCF;
-	background-image: url("/images/icons/error_msg.png");
-	border:1px solid #FF9595;
-	color:#CC3300;
-}
 .warning_msg {
-	background-color:#FFFBCC;
+	background-color: #FFFBCC;
 	background-image: url("/images/icons/warning_msg.png");
-	border:1px solid #FFF35E;
-	color:#C69E00;
+	border: 1px solid #FFF35E;
+	color: #C69E00;
 }
+
 .success_msg {
-	background-color:#D5FFCF;
+	background-color: #D5FFCF;
 	background-image: url("/images/icons/success_msg.png");
-	border:1px solid #97FF88;
-	color:#009900;
+	border: 1px solid #97FF88;
+	color: #009900;
 }
+
 .notice_msg {
-	background-color:#DCE3FF;
+	background-color: #DCE3FF;
 	background-image: url("/images/icons/notice_msg.png");
-	border:1px solid #93A8FF;
-	color:#556CB5;
+	border: 1px solid #93A8FF;
+	color: #556CB5;
 }
 
-.success_msg, .error_msg, .notice_msg, .warning_msg{
-	background-position:10px center;
-	background-repeat:no-repeat;
-	font-size:12px;
-	font-weight:bold;
-	min-height:14px;
-	line-height:14px;
-	margin-bottom:0px;
-	margin-top:0px;
-	padding:3px 10px 3px 40px;
-	display:block;
+.success_msg,.error_msg,.notice_msg,.warning_msg {
+	background-position: 10px center;
+	background-repeat: no-repeat;
+	font-size: 12px;
+	font-weight: bold;
+	min-height: 14px;
+	line-height: 14px;
+	margin-bottom: 0px;
+	margin-top: 0px;
+	padding: 3px 10px 3px 40px;
+	display: block;
 	overflow: auto;
-}        
+}
 
 #msg_close {
-	background:transparent url("icons/cross_grey_small.png")	no-repeat scroll 0 0;
-	cursor:pointer;
-	height:16px;
-	position:absolute;
-	right:5px;
-	top:5px;
-	width:16px;
+	background: transparent url("icons/cross_grey_small.png") no-repeat
+		scroll 0 0;
+	cursor: pointer;
+	height: 16px;
+	position: absolute;
+	right: 5px;
+	top: 5px;
+	width: 16px;
 }
 
-.error-message{
-	color:#CC3300;
+.error-message {
+	color: #CC3300;
 }
-/**** Tooltip ****/
-.yui-overlay,
-.yui-panel-container {
-    visibility:hidden;
-    position:absolute;
-    z-index: 2;
+
+/**** TOOLTIP ****/
+.yui-overlay,.yui-panel-container {
+	visibility: hidden;
+	position: absolute;
+	z-index: 2;
 }
 
 .yui-tt {
-    visibility:hidden;
-    position:absolute;
-    color:#666666;
-    background-color:#FFFFFF;
-    font-family:arial,helvetica,verdana,sans-serif;
-    padding:8px;
-    border:2px solid #556CB5;
-    font:100% sans-serif;
-    width:auto;
-    opacity:1.0;
+	visibility: hidden;
+	position: absolute;
+	color: #666666;
+	background-color: #FFFFFF;
+	font-family: arial, helvetica, verdana, sans-serif;
+	padding: 8px;
+	border: 2px solid #556CB5;
+	font: 100% sans-serif;
+	width: auto;
+	opacity: 1.0;
 }
 
 .yui-tt-shadow {
-    display: none;
+	display: none;
+}
+
+/** END TOOLTIP **/ 
+
+/** AUTOCOMPLETE **/ 
+
+.ac{
+	vertical-align: top;
+
+}
+.ac .match {
+    font-weight:bold;
+}
+
+.ac .yui-ac {
+	position: relative;
+	font-family: arial;
+	font-size: 100%;
+}
+
+.ac #perm_ac{
+	width:15em;
+}
+/* styles for input field */
+.ac .yui-ac-input {
+	position: absolute;
+	width: 100%;
+}
+
+/* styles for results container */
+.ac .yui-ac-container {
+	position: absolute;
+	top: 1.6em;
+	width: 100%;
 }
-/** end of Tooltip **/ 
+
+/* styles for header/body/footer wrapper within container */
+.ac .yui-ac-content {
+	position: absolute;
+	width: 100%;
+	border: 1px solid #808080;
+	background: #fff;
+	overflow: hidden;
+	z-index: 9050;
+}
+
+/* styles for container shadow */
+.ac .yui-ac-shadow {
+	position: absolute;
+	margin: .3em;
+	width: 100%;
+	background: #000;
+	-moz-opacity: 0.10;
+	opacity: .10;
+	filter: alpha(opacity = 10);
+	z-index: 9049;
+}
 
+/* styles for results list */
+.ac .yui-ac-content ul {
+	margin: 0;
+	padding: 0;
+	width: 100%;
+}
 
+/* styles for result item */
+.ac .yui-ac-content li {
+	margin: 0;
+	padding: 2px 5px;
+	cursor: default;
+	white-space: nowrap;
+}
+
+/* styles for prehighlighted result item */
+.ac .yui-ac-content li.yui-ac-prehighlight {
+	background: #B3D4FF;
+}
+
+/* styles for highlighted result item */
+.ac .yui-ac-content li.yui-ac-highlight {
+	background: #556CB5;
+	color: #FFF;
+}
+
+/** END AUTOCOMPLETE **/
 div#main {
 	padding: 5px;
 }
@@ -273,9 +361,11 @@
 	padding-right: 5px;
 	padding-left: 5px;
 }
+
 ul.page-nav li.current a {
 	color: #556CB5;
 }
+
 ul.page-nav li a {
 	height: 24px;
 	color: #FFF;
@@ -285,9 +375,11 @@
 	text-decoration: none;
 	font-weight: bold;
 }
+
 ul.page-nav li.logout a {
 	color: #FDAC9D;
 }
+
 ul.page-nav li a:hover {
 	background: #FFF;
 	color: #556CB5;
@@ -301,34 +393,36 @@
 ul.submenu li {
 	margin: 0 10px 0 0;
 	font-size: 0.9em;
-	font-weight:bold;
+	font-weight: bold;
 	display: inline;
 }
+
 ul.submenu .repos {
-    background: url("/images/icons/folder_edit.png") no-repeat scroll 3px;
-    height: 16px;
-    padding-left: 20px;
-    padding-top: 0px;
-    text-align: left;
-   
+	background: url("/images/icons/folder_edit.png") no-repeat scroll 3px;
+	height: 16px;
+	padding-left: 20px;
+	padding-top: 0px;
+	text-align: left;
 }
+
 ul.submenu .users {
-    background: url("/images/icons/user_edit.png") no-repeat scroll 3px;
-    height: 16px;
-    padding-left: 20px;
-    padding-top: 0px;
-    text-align: left;
+	background: url("/images/icons/user_edit.png") no-repeat scroll 3px;
+	height: 16px;
+	padding-left: 20px;
+	padding-top: 0px;
+	text-align: left;
 }
+
 ul.submenu .permissions {
-    background: url("/images/icons/folder_key.png") no-repeat scroll 3px;
-    height: 16px;
-    padding-left: 20px;
-    padding-top: 0px;
-    text-align: left;
+	background: url("/images/icons/folder_key.png") no-repeat scroll 3px;
+	height: 16px;
+	padding-left: 20px;
+	padding-top: 0px;
+	text-align: left;
 }
 
 ul.submenu .current_submenu {
-    border-bottom: 2px solid #556CB5;
+	border-bottom: 2px solid #556CB5;
 }
 
 h2 {
@@ -340,7 +434,7 @@
 	font-size: 1.2em;
 	border-top: dotted 1px #D5E1E6;
 	font-weight: bold;
-	color:#556CB5;
+	color: #556CB5;
 }
 
 h2.no-link {
@@ -525,12 +619,11 @@
 	width: 150px;
 }
 
-#clone_url{
-    border: 0px;
+#clone_url {
+	border: 0px;
 }
-/** end of summary **/ 
 
-/** chagelog **/
+/** end of summary **/ /** chagelog **/
 h3.changelog {
 	margin: 20px 0 5px 30px;
 	padding: 0 0 2px;
@@ -568,207 +661,221 @@
 	font-size: 1.1em;
 }
 
-/** end of changelog **/ 
-
-/** file **/
+/** end of changelog **/ /** file **/
 p.files {
 	margin: 0 0 0 20px;
 	font-size: 2.0em;
 	font-weight: bold;
 }
 
-/** end of file **/ 
-
-/** changeset **/
-#changeset_content{
-	width:60%;
-	float:left;
+/** end of file **/ /** changeset **/
+#changeset_content {
+	width: 60%;
+	float: left;
 }
 
-#changeset_content .container .wrapper{
+#changeset_content .container .wrapper {
 	width: 600px;
 }
-#changeset_content .container{
-	border:1px solid #CCCCCC;
-	height:120px;
+
+#changeset_content .container {
+	border: 1px solid #CCCCCC;
+	height: 120px;
 }
 
-#changeset_content .container .left{
-	float:left;
+#changeset_content .container .left {
+	float: left;
 	width: 70%;
 	padding-left: 5px;
 }
 
-#changeset_content .container .right{
-	float:right;
+#changeset_content .container .right {
+	float: right;
 	width: 25%;
 	text-align: right;
 }
 
-#changeset_content .container .left .date{
-	font-weight:bold;
+#changeset_content .container .left .date {
+	font-weight: bold;
 }
-#changeset_content .container .left .author{
+
+#changeset_content .container .left .author {
 	
 }
-#changeset_content .container .left .message{
+
+#changeset_content .container .left .message {
 	font-style: italic;
 	color: #556CB5;
 }
 
-.cs_files{
+.cs_files {
 	width: 60%;
 }
 
-.cs_files .cs_added{
+.cs_files .cs_added {
 	background: url("/images/icons/page_white_add.png") no-repeat scroll 3px;
 	/*background-color:#BBFFBB;*/
 	height: 16px;
 	padding-left: 20px;
 	margin-top: 7px;
-	text-align: left;	
+	text-align: left;
 }
-.cs_files .cs_changed{
-	background: url("/images/icons/page_white_edit.png") no-repeat scroll 3px;
+
+.cs_files .cs_changed {
+	background: url("/images/icons/page_white_edit.png") no-repeat scroll
+		3px;
 	/*background-color: #FFDD88;*/
 	height: 16px;
 	padding-left: 20px;
 	margin-top: 7px;
-	text-align: left;	
+	text-align: left;
 }
-.cs_files .cs_removed{
-	background: url("/images/icons/page_white_delete.png") no-repeat scroll 3px;
+
+.cs_files .cs_removed {
+	background: url("/images/icons/page_white_delete.png") no-repeat scroll
+		3px;
 	/*background-color: #FF8888;*/
 	height: 16px;
 	padding-left: 20px;
 	margin-top: 7px;
-	text-align: left;	
+	text-align: left;
+}
+
+/** end of changeset **/ /** canvas **/
+#graph_nodes {
+	margin-top: 8px;
+}
+
+#graph {
+	overflow: hidden;
 }
 
-/** end of changeset **/ 
-
-/** canvas **/
-#graph_nodes{
-	margin-top:8px;
-}
-#graph{
-	overflow: hidden;
-
-}
-#graph_nodes{
-	width:160px;
-	float:left;
+#graph_nodes {
+	width: 160px;
+	float: left;
 }
 
-#graph_content{
-	width:800px;
-	float:left;
+#graph_content {
+	width: 800px;
+	float: left;
 }
-#graph_content .container_header{
-	border:1px solid #CCCCCC;
-	height:30px;
+
+#graph_content .container_header {
+	border: 1px solid #CCCCCC;
+	height: 30px;
 	background: #EEEEEE;
 }
 
-
-#graph_content .container .wrapper{
+#graph_content .container .wrapper {
 	width: 600px;
 }
-#graph_content .container{
+
+#graph_content .container {
 	border-bottom: 1px solid #CCCCCC;
 	border-left: 1px solid #CCCCCC;
 	border-right: 1px solid #CCCCCC;
-	height:120px;
+	height: 120px;
 }
 
-#graph_content .container .left{
-	float:left;
+#graph_content .container .left {
+	float: left;
 	width: 70%;
 	padding-left: 5px;
 }
 
-#graph_content .container .right{
-	float:right;
+#graph_content .container .right {
+	float: right;
 	width: 25%;
 	text-align: right;
 }
-#graph_content .container .left .date{
-	font-weight:bold;
+
+#graph_content .container .left .date {
+	font-weight: bold;
 }
-#graph_content .container .left .author{
+
+#graph_content .container .left .author {
 	
 }
-#graph_content .container .left .message{
+
+#graph_content .container .left .message {
 	font-size: 80%;
 }
 
-.right div{
+.right div {
 	clear: both;
 }
-.right .changes .added,.changed,.removed{
-	border:1px solid #DDDDDD;
-	display:block;
-	float:right;
-	font-size:0.75em;
-	text-align:center;
-	min-width:15px;
+
+.right .changes .added,.changed,.removed {
+	border: 1px solid #DDDDDD;
+	display: block;
+	float: right;
+	font-size: 0.75em;
+	text-align: center;
+	min-width: 15px;
 }
-.right .changes .added{
-	background:#BBFFBB; 
+
+.right .changes .added {
+	background: #BBFFBB;
 }
-.right .changes .changed{
+
+.right .changes .changed {
 	background: #FFDD88;
 }
-.right .changes .removed{
+
+.right .changes .removed {
 	background: #FF8888;
 }
 
-.right .merge{
+.right .merge {
 	vertical-align: top;
 	font-size: 60%;
 	font-weight: bold;
 }
-.right .merge img{
-	vertical-align: bottom;	
+
+.right .merge img {
+	vertical-align: bottom;
 }
 
-.right .parent{
+.right .parent {
 	font-size: 90%;
 	font-family: monospace;
 }
-/** end of canvas **/
 
-/* FILE BROWSER */
+/** end of canvas **/ /* FILE BROWSER */
 div.browserblock {
-    overflow: hidden;
-    padding: 0px;
-    border: 1px solid #ccc;
-    background: #f8f8f8;
-    font-size: 100%;
-    line-height: 100%;
-    /* new */
-    line-height: 125%;
+	overflow: hidden;
+	padding: 0px;
+	border: 1px solid #ccc;
+	background: #f8f8f8;
+	font-size: 100%;
+	line-height: 100%;
+	/* new */
+	line-height: 125%;
 }
-div.browserblock .browser-header{
+
+div.browserblock .browser-header {
 	border-bottom: 1px solid #CCCCCC;
 	background: #EEEEEE;
-	color:blue;
-	padding:10px 0 10px 0;
+	color: blue;
+	padding: 10px 0 10px 0;
 }
-div.browserblock .browser-header span{
-	margin-left:25px;
+
+div.browserblock .browser-header span {
+	margin-left: 25px;
 	font-weight: bold;
 }
-div.browserblock .browser-body{
+
+div.browserblock .browser-body {
 	background: #EEEEEE;
 }
 
 table.code-browser {
-	border-collapse:collapse;
+	border-collapse: collapse;
 	width: 100%;
 }
-table.code-browser tr{
-	margin:3px;	
+
+table.code-browser tr {
+	margin: 3px;
 }
 
 table.code-browser thead th {
@@ -780,13 +887,13 @@
 	text-align: left;
 	padding-left: 10px;
 }
+
 table.code-browser tbody tr {
 	
 }
 
 table.code-browser tbody td {
-	
-	padding-left:10px;
+	padding-left: 10px;
 	height: 20px;
 }
 
@@ -811,7 +918,8 @@
 	padding-top: 0px;
 	text-align: left;
 }
-.archive_logo{
+
+.archive_logo {
 	background: url("/images/icons/compress.png") no-repeat scroll 3px;
 	height: 16px;
 	padding-left: 20px;
--- a/pylons_app/templates/admin/repos/repo_edit.html	Fri Jun 25 21:53:13 2010 +0200
+++ b/pylons_app/templates/admin/repos/repo_edit.html	Sat Jun 26 18:42:13 2010 +0200
@@ -35,7 +35,12 @@
         	</tr>
         	<tr>
         		<td>${_('Owner')}</td>
-        		<td>${h.text('user')}</td>
+				<td class='ac'>
+					<div id="perm_ac">
+						${h.text('user',class_='yui-ac-input')}
+						<div id="owner_container"></div>
+					</div>
+				</td>        		
         		<td>${self.get_form_error('user')}</td>
         	</tr>
         	<tr>
@@ -59,10 +64,7 @@
 	        					<td>${r2p.user.username}</td>
 	        				</tr>
 						%endfor
-
-						
 						<%
-							
 							if not hasattr(c,'form_errors'):
 								d = 'display:none;'
 							else:
@@ -74,7 +76,12 @@
         					<td>${h.radio('perm_new_user','repository.read')}</td>
         					<td>${h.radio('perm_new_user','repository.write')}</td>
         					<td>${h.radio('perm_new_user','repository.admin')}</td>
-        					<td>${h.text('perm_new_user_name',size=10)}</td>
+        					<td class='ac'>
+        						<div id="perm_ac">
+        							${h.text('perm_new_user_name',class_='yui-ac-input')}
+									<div id="perm_container"></div>
+        						</div>
+        					</td>
         					<td>${self.get_form_error('perm_new_user_name')}</td>     					
         				</tr>
         				<tr>
@@ -105,5 +112,114 @@
 				});
             });
         </script>
+		<script type="text/javascript">    
+		YAHOO.example.FnMultipleFields = function(){
+		    var myContacts = ${c.users_array|n}
+		    
+		    // Define a custom search function for the DataSource
+		    var matchNames = function(sQuery) {
+		        // Case insensitive matching
+		        var query = sQuery.toLowerCase(),
+		            contact,
+		            i=0,
+		            l=myContacts.length,
+		            matches = [];
+		        
+		        // Match against each name of each contact
+		        for(; i<l; i++) {
+		            contact = myContacts[i];
+		            if((contact.fname.toLowerCase().indexOf(query) > -1) ||
+		                (contact.lname.toLowerCase().indexOf(query) > -1) ||
+		                (contact.nname && (contact.nname.toLowerCase().indexOf(query) > -1))) {
+		                matches[matches.length] = contact;
+		            }
+		        }
+		
+		        return matches;
+		    };
+		
+		    // Use a FunctionDataSource
+		    var oDS = new YAHOO.util.FunctionDataSource(matchNames);
+		    oDS.responseSchema = {
+		        fields: ["id", "fname", "lname", "nname"]
+		    }
+		
+		    // Instantiate AutoComplete for perms
+		    var oAC_perms = new YAHOO.widget.AutoComplete("perm_new_user_name", "perm_container", oDS);
+		    oAC_perms.useShadow = false;
+		    oAC_perms.resultTypeList = false;
+		    
+		    // Instantiate AutoComplete for owner
+		 	var oAC_owner = new YAHOO.widget.AutoComplete("user", "owner_container", oDS);
+		 	oAC_owner.useShadow = false;
+		 	oAC_owner.resultTypeList = false;
+		    
+		    
+		    // Custom formatter to highlight the matching letters
+		    var custom_formatter = function(oResultData, sQuery, sResultMatch) {
+		        var query = sQuery.toLowerCase(),
+		            fname = oResultData.fname,
+		            lname = oResultData.lname,
+		            nname = oResultData.nname || "", // Guard against null value
+		            query = sQuery.toLowerCase(),
+		            fnameMatchIndex = fname.toLowerCase().indexOf(query),
+		            lnameMatchIndex = lname.toLowerCase().indexOf(query),
+		            nnameMatchIndex = nname.toLowerCase().indexOf(query),
+		            displayfname, displaylname, displaynname;
+		            
+		        if(fnameMatchIndex > -1) {
+		            displayfname = highlightMatch(fname, query, fnameMatchIndex);
+		        }
+		        else {
+		            displayfname = fname;
+		        }
+		
+		        if(lnameMatchIndex > -1) {
+		            displaylname = highlightMatch(lname, query, lnameMatchIndex);
+		        }
+		        else {
+		            displaylname = lname;
+		        }
+		
+		        if(nnameMatchIndex > -1) {
+		            displaynname = "(" + highlightMatch(nname, query, nnameMatchIndex) + ")";
+		        }
+		        else {
+		            displaynname = nname ? "(" + nname + ")" : "";
+		        }
+		
+		        return displayfname + " " + displaylname + " " + displaynname;
+		        
+		    };
+		    oAC_perms.formatResult = custom_formatter; 
+		    oAC_owner.formatResult = custom_formatter;
+		    			    
+		    // Helper function for the formatter
+		    var highlightMatch = function(full, snippet, matchindex) {
+		        return full.substring(0, matchindex) + 
+		                "<span class='match'>" + 
+		                full.substr(matchindex, snippet.length) + 
+		                "</span>" +
+		                full.substring(matchindex + snippet.length);
+		    };
+		
+		    var myHandler = function(sType, aArgs) {
+		        var myAC = aArgs[0]; // reference back to the AC instance
+		        var elLI = aArgs[1]; // reference to the selected LI element
+		        var oData = aArgs[2]; // object literal of selected item's result data
+		        myAC.getInputEl().value = oData.nname;
+		    };
+
+		    oAC_perms.itemSelectEvent.subscribe(myHandler);
+		    oAC_owner.itemSelectEvent.subscribe(myHandler);
+		    
+		    return {
+		        oDS: oDS,
+		        oAC_perms: oAC_perms,
+		        oAC_owner: oAC_owner, 
+		    };
+		}();
+		    
+		</script>        
     </div>
 </%def>   
--- a/pylons_app/templates/base/base.html	Fri Jun 25 21:53:13 2010 +0200
+++ b/pylons_app/templates/base/base.html	Sat Jun 26 18:42:13 2010 +0200
@@ -106,6 +106,7 @@
 	            <li ${is_current('branches')}>${h.link_to(_('branches'),h.url('branches_home',repo_name=c.repo_name))}</li>
 	            <li ${is_current('tags')}>${h.link_to(_('tags'),h.url('tags_home',repo_name=c.repo_name))}</li>
 	            <li ${is_current('files')}>${h.link_to(_('files'),h.url('files_home',repo_name=c.repo_name))}</li>
+				<li>${h.link_to(_('settings'),h.url('edit_repo',id=c.repo_name))}</li>	        
 	        </ul>
 		%else:
 		##Root menu
@@ -142,6 +143,8 @@
 <%def name="js()">
 <script type="text/javascript" src="/js/yui/utilities/utilities.js"></script>
 <script type="text/javascript" src="/js/yui/container/container-min.js"></script>
+<script type="text/javascript" src="/js/yui/datasource/datasource-min.js"></script>
+<script type="text/javascript" src="/js/yui/autocomplete/autocomplete-min.js"></script>
 </%def>
 
 <!-- DEFINITION OF FORM ERROR FETCHER -->
--- a/pylons_app/templates/login.html	Fri Jun 25 21:53:13 2010 +0200
+++ b/pylons_app/templates/login.html	Sat Jun 26 18:42:13 2010 +0200
@@ -7,12 +7,13 @@
 	${c.repos_prefix} Mercurial Repositories
 </%def>
 <%def name="page_nav()">
-	${self.menu('home')}
+	&nbsp;
+	</div>
 </%def>
 <%def name="main()">
         <div>
         <br />
-        <h2>${_('Login')}</h2>
+        <h2>${_('Login to hg app')}</h2>
         ${h.form(h.url.current())}
         <table>
             <tr>