Changeset - 15e96b5a2685
[Not reviewed]
default
0 6 0
Marcin Kuzminski - 15 years ago 2010-06-26 18:42:13
marcin@python-works.com
autocomplete for repository managment of users
6 files changed with 276 insertions and 36 deletions:
0 comments (0 inline, 0 general)
pylons_app/controllers/repos.py
Show inline comments
 
@@ -111,6 +111,7 @@ class ReposController(BaseController):
 
                           
 
        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 @@ class ReposController(BaseController):
 
        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})
pylons_app/model/repo_model.py
Show inline comments
 
@@ -41,6 +41,15 @@ class RepoModel(object):
 
    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:
pylons_app/public/css/monoblue_custom.css
Show inline comments
 
@@ -4,13 +4,16 @@
 
	background: #DBD4C6;
 
	font-family: sans-serif;
 
}
 

	
 
#mainhtml .breadcrumbs a:HOVER{
 
	text-decoration:  underline;
 
}
 

	
 
a {
 
	color: #556CB5;
 
	text-decoration: none;
 
}
 

	
 
a:HOVER{
 
	text-decoration: underline;
 
}
 
@@ -44,6 +47,7 @@ a:HOVER{
 
    border-top: 1px solid #AAAAAA;
 
    border-bottom: 2px solid #666666;
 
}
 

	
 
.table_disp td {
 
    border-left: 1px solid #AAAAAA;
 
    padding-left: 4px;
 
@@ -62,18 +66,17 @@ table tr.parity1 {
 
    background: #FFFFFF;
 
}
 

	
 

	
 
/*** ***/
 

	
 
/** common settings **/
 
/** 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;
 
@@ -88,13 +91,13 @@ table tr.parity1 {
 
    padding-left: 20px;
 
    padding-top: 0px;
 
    text-align: left;
 

	
 
}
 

	
 
.action_button{
 
    border:0px;
 
    display: block;
 
}
 

	
 
.action_button:hover{
 
    border:0px;
 
    font-style:italic;
 
@@ -104,26 +107,29 @@ table tr.parity1 {
 
.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-image: url("/images/icons/warning_msg.png");
 
	border:1px solid #FFF35E;
 
	color:#C69E00;
 
}
 

	
 
.success_msg {
 
	background-color:#D5FFCF;
 
	background-image: url("/images/icons/success_msg.png");
 
	border:1px solid #97FF88;
 
	color:#009900;
 
}
 

	
 
.notice_msg {
 
	background-color:#DCE3FF;
 
	background-image: url("/images/icons/notice_msg.png");
 
@@ -146,7 +152,8 @@ table tr.parity1 {
 
}        
 

	
 
#msg_close {
 
	background:transparent url("icons/cross_grey_small.png")	no-repeat scroll 0 0;
 
	background: transparent url("icons/cross_grey_small.png") no-repeat
 
		scroll 0 0;
 
	cursor:pointer;
 
	height:16px;
 
	position:absolute;
 
@@ -158,9 +165,9 @@ table tr.parity1 {
 
.error-message{
 
	color:#CC3300;
 
}
 
/**** Tooltip ****/
 
.yui-overlay,
 
.yui-panel-container {
 

	
 
/**** TOOLTIP ****/
 
.yui-overlay,.yui-panel-container {
 
    visibility:hidden;
 
    position:absolute;
 
    z-index: 2;
 
@@ -182,9 +189,90 @@ table tr.parity1 {
 
.yui-tt-shadow {
 
    display: none;
 
}
 
/** end of Tooltip **/ 
 

	
 
/** 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%;
 
}
 

	
 
/* 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 @@ ul.page-nav li.current {
 
	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 @@ ul.page-nav li a {
 
	text-decoration: none;
 
	font-weight: bold;
 
}
 

	
 
ul.page-nav li.logout a {
 
	color: #FDAC9D;
 
}
 

	
 
ul.page-nav li a:hover {
 
	background: #FFF;
 
	color: #556CB5;
 
@@ -304,14 +396,15 @@ ul.submenu li {
 
	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;
 
}
 
   
 
}
 
ul.submenu .users {
 
    background: url("/images/icons/user_edit.png") no-repeat scroll 3px;
 
    height: 16px;
 
@@ -319,6 +412,7 @@ ul.submenu .users {
 
    padding-top: 0px;
 
    text-align: left;
 
}
 

	
 
ul.submenu .permissions {
 
    background: url("/images/icons/folder_key.png") no-repeat scroll 3px;
 
    height: 16px;
 
@@ -528,9 +622,8 @@ dl.overview dt {
 
#clone_url{
 
    border: 0px;
 
}
 
/** end of summary **/ 
 

	
 
/** chagelog **/
 
/** end of summary **/ /** chagelog **/
 
h3.changelog {
 
	margin: 20px 0 5px 30px;
 
	padding: 0 0 2px;
 
@@ -568,18 +661,14 @@ ul.changelog-entry li.description {
 
	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 **/
 
/** end of file **/ /** changeset **/
 
#changeset_content{
 
	width:60%;
 
	float:left;
 
@@ -588,6 +677,7 @@ p.files {
 
#changeset_content .container .wrapper{
 
	width: 600px;
 
}
 

	
 
#changeset_content .container{
 
	border:1px solid #CCCCCC;
 
	height:120px;
 
@@ -608,9 +698,11 @@ p.files {
 
#changeset_content .container .left .date{
 
	font-weight:bold;
 
}
 

	
 
#changeset_content .container .left .author{
 
	
 
}
 

	
 
#changeset_content .container .left .message{
 
	font-style: italic;
 
	color: #556CB5;
 
@@ -628,16 +720,20 @@ p.files {
 
	margin-top: 7px;
 
	text-align: left;	
 
}
 

	
 
.cs_files .cs_changed{
 
	background: url("/images/icons/page_white_edit.png") no-repeat scroll 3px;
 
	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;	
 
}
 

	
 
.cs_files .cs_removed{
 
	background: url("/images/icons/page_white_delete.png") no-repeat scroll 3px;
 
	background: url("/images/icons/page_white_delete.png") no-repeat scroll
 
		3px;
 
	/*background-color: #FF8888;*/
 
	height: 16px;
 
	padding-left: 20px;
 
@@ -645,16 +741,15 @@ p.files {
 
	text-align: left;	
 
}
 

	
 
/** end of changeset **/ 
 

	
 
/** canvas **/
 
/** end of changeset **/ /** canvas **/
 
#graph_nodes{
 
	margin-top:8px;
 
}
 

	
 
#graph{
 
	overflow: hidden;
 
}
 

	
 
}
 
#graph_nodes{
 
	width:160px;
 
	float:left;
 
@@ -664,16 +759,17 @@ p.files {
 
	width:800px;
 
	float:left;
 
}
 

	
 
#graph_content .container_header{
 
	border:1px solid #CCCCCC;
 
	height:30px;
 
	background: #EEEEEE;
 
}
 

	
 

	
 
#graph_content .container .wrapper{
 
	width: 600px;
 
}
 

	
 
#graph_content .container{
 
	border-bottom: 1px solid #CCCCCC;
 
	border-left: 1px solid #CCCCCC;
 
@@ -692,12 +788,15 @@ p.files {
 
	width: 25%;
 
	text-align: right;
 
}
 

	
 
#graph_content .container .left .date{
 
	font-weight:bold;
 
}
 

	
 
#graph_content .container .left .author{
 
	
 
}
 

	
 
#graph_content .container .left .message{
 
	font-size: 80%;
 
}
 
@@ -705,6 +804,7 @@ p.files {
 
.right div{
 
	clear: both;
 
}
 

	
 
.right .changes .added,.changed,.removed{
 
	border:1px solid #DDDDDD;
 
	display:block;
 
@@ -713,12 +813,15 @@ p.files {
 
	text-align:center;
 
	min-width:15px;
 
}
 

	
 
.right .changes .added{
 
	background:#BBFFBB; 
 
}
 

	
 
.right .changes .changed{
 
	background: #FFDD88;
 
}
 

	
 
.right .changes .removed{
 
	background: #FF8888;
 
}
 
@@ -728,6 +831,7 @@ p.files {
 
	font-size: 60%;
 
	font-weight: bold;
 
}
 

	
 
.right .merge img{
 
	vertical-align: bottom;	
 
}
 
@@ -736,9 +840,8 @@ p.files {
 
	font-size: 90%;
 
	font-family: monospace;
 
}
 
/** end of canvas **/
 

	
 
/* FILE BROWSER */
 
/** end of canvas **/ /* FILE BROWSER */
 
div.browserblock {
 
    overflow: hidden;
 
    padding: 0px;
 
@@ -749,16 +852,19 @@ div.browserblock {
 
    /* new */
 
    line-height: 125%;
 
}
 

	
 
div.browserblock .browser-header{
 
	border-bottom: 1px solid #CCCCCC;
 
	background: #EEEEEE;
 
	color:blue;
 
	padding:10px 0 10px 0;
 
}
 

	
 
div.browserblock .browser-header span{
 
	margin-left:25px;
 
	font-weight: bold;
 
}
 

	
 
div.browserblock .browser-body{
 
	background: #EEEEEE;
 
}
 
@@ -767,6 +873,7 @@ table.code-browser {
 
	border-collapse:collapse;
 
	width: 100%;
 
}
 

	
 
table.code-browser tr{
 
	margin:3px;	
 
}
 
@@ -780,12 +887,12 @@ table.code-browser thead th {
 
	text-align: left;
 
	padding-left: 10px;
 
}
 

	
 
table.code-browser tbody tr {
 
	
 
}
 

	
 
table.code-browser tbody td {
 
	
 
	padding-left:10px;
 
	height: 20px;
 
}
 
@@ -811,6 +918,7 @@ table.code-browser tbody td {
 
	padding-top: 0px;
 
	text-align: left;
 
}
 

	
 
.archive_logo{
 
	background: url("/images/icons/compress.png") no-repeat scroll 3px;
 
	height: 16px;
pylons_app/templates/admin/repos/repo_edit.html
Show inline comments
 
@@ -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>   
pylons_app/templates/base/base.html
Show inline comments
 
@@ -106,6 +106,7 @@ def is_current(selected):
 
	            <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 is_current(selected):
 
<%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 -->
pylons_app/templates/login.html
Show inline comments
 
@@ -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>
0 comments (0 inline, 0 general)