##// END OF EJS Templates
Added gravatars into permissions view and permissions autocomplete
marcink -
r2142:a21eab6f beta
parent child Browse files
Show More
@@ -27,6 +27,7 b' news'
27 27 solving issues with Mercurial and LDAP
28 28 - #402 removed group prefix from repository name when listing repositories
29 29 inside a group
30 - added gravatars into permission view and permissions autocomplete
30 31
31 32 fixes
32 33 +++++
@@ -284,7 +284,6 b' class ReposController(BaseController):'
284 284
285 285 :param repo_name:
286 286 """
287
288 287 try:
289 288 RepoModel().revoke_user_permission(repo=repo_name,
290 289 user=request.POST['user_id'])
@@ -29,7 +29,7 b' import traceback'
29 29 from datetime import datetime
30 30
31 31 from rhodecode.lib.vcs.backends import get_backend
32
32 from rhodecode.lib.compat import json
33 33 from rhodecode.lib.utils2 import LazyProperty, safe_str, safe_unicode
34 34 from rhodecode.lib.caching_query import FromCache
35 35 from rhodecode.lib.hooks import log_create_repository
@@ -37,6 +37,7 b' from rhodecode.lib.hooks import log_crea'
37 37 from rhodecode.model import BaseModel
38 38 from rhodecode.model.db import Repository, UserRepoToPerm, User, Permission, \
39 39 Statistics, UsersGroup, UsersGroupRepoToPerm, RhodeCodeUi, RepoGroup
40 from rhodecode.lib import helpers as h
40 41
41 42
42 43 log = logging.getLogger(__name__)
@@ -94,25 +95,28 b' class RepoModel(BaseModel):'
94 95 return repo.scalar()
95 96
96 97 def get_users_js(self):
97
98 98 users = self.sa.query(User).filter(User.active == True).all()
99 u_tmpl = '''{id:%s, fname:"%s", lname:"%s", nname:"%s"},'''
100 users_array = '[%s]' % '\n'.join([u_tmpl % (u.user_id, u.name,
101 u.lastname, u.username)
102 for u in users])
103 return users_array
99 return json.dumps([
100 {
101 'id': u.user_id,
102 'fname': u.name,
103 'lname': u.lastname,
104 'nname': u.username,
105 'gravatar_lnk': h.gravatar_url(u.email, 14)
106 } for u in users]
107 )
104 108
105 109 def get_users_groups_js(self):
106 110 users_groups = self.sa.query(UsersGroup)\
107 111 .filter(UsersGroup.users_group_active == True).all()
108 112
109 g_tmpl = '''{id:%s, grname:"%s",grmembers:"%s"},'''
110
111 users_groups_array = '[%s]' % '\n'.join([g_tmpl % \
112 (gr.users_group_id, gr.users_group_name,
113 len(gr.members))
114 for gr in users_groups])
115 return users_groups_array
113 return json.dumps([
114 {
115 'id': gr.users_group_id,
116 'grname': gr.users_group_name,
117 'grmembers': len(gr.members),
118 } for gr in users_groups]
119 )
116 120
117 121 def _get_defaults(self, repo_name):
118 122 """
@@ -345,6 +349,7 b' class RepoModel(BaseModel):'
345 349 :param repo: Instance of Repository, repository_id, or repository name
346 350 :param user: Instance of User, user_id or username
347 351 """
352
348 353 user = self.__get_user(user)
349 354 repo = self.__get_repo(repo)
350 355
@@ -4195,7 +4195,14 b' form.comment-inline-form {'
4195 4195 color: #ffffff;
4196 4196 }
4197 4197
4198
4198 .perm-gravatar{
4199 vertical-align:middle;
4200 padding:2px;
4201 }
4202 .perm-gravatar-ac{
4203 vertical-align:middle;
4204 padding:2px;
4205 }
4199 4206
4200 4207 /*****************************************************************************
4201 4208 DIFFS CSS
@@ -609,6 +609,178 b' var deleteNotification = function(url, n'
609 609 };
610 610
611 611
612 /** MEMBERS AUTOCOMPLETE WIDGET **/
613
614 var MembersAutoComplete = function (users_list, groups_list, group_lbl, members_lbl) {
615 var myUsers = users_list;
616 var myGroups = groups_list;
617
618 // Define a custom search function for the DataSource of users
619 var matchUsers = function (sQuery) {
620 // Case insensitive matching
621 var query = sQuery.toLowerCase();
622 var i = 0;
623 var l = myUsers.length;
624 var matches = [];
625
626 // Match against each name of each contact
627 for (; i < l; i++) {
628 contact = myUsers[i];
629 if ((contact.fname.toLowerCase().indexOf(query) > -1) || (contact.lname.toLowerCase().indexOf(query) > -1) || (contact.nname && (contact.nname.toLowerCase().indexOf(query) > -1))) {
630 matches[matches.length] = contact;
631 }
632 }
633 return matches;
634 };
635
636 // Define a custom search function for the DataSource of usersGroups
637 var matchGroups = function (sQuery) {
638 // Case insensitive matching
639 var query = sQuery.toLowerCase();
640 var i = 0;
641 var l = myGroups.length;
642 var matches = [];
643
644 // Match against each name of each contact
645 for (; i < l; i++) {
646 matched_group = myGroups[i];
647 if (matched_group.grname.toLowerCase().indexOf(query) > -1) {
648 matches[matches.length] = matched_group;
649 }
650 }
651 return matches;
652 };
653
654 //match all
655 var matchAll = function (sQuery) {
656 u = matchUsers(sQuery);
657 g = matchGroups(sQuery);
658 return u.concat(g);
659 };
660
661 // DataScheme for members
662 var memberDS = new YAHOO.util.FunctionDataSource(matchAll);
663 memberDS.responseSchema = {
664 fields: ["id", "fname", "lname", "nname", "grname", "grmembers", "gravatar_lnk"]
665 };
666
667 // DataScheme for owner
668 var ownerDS = new YAHOO.util.FunctionDataSource(matchUsers);
669 ownerDS.responseSchema = {
670 fields: ["id", "fname", "lname", "nname", "gravatar_lnk"]
671 };
672
673 // Instantiate AutoComplete for perms
674 var membersAC = new YAHOO.widget.AutoComplete("perm_new_member_name", "perm_container", memberDS);
675 membersAC.useShadow = false;
676 membersAC.resultTypeList = false;
677
678 // Instantiate AutoComplete for owner
679 var ownerAC = new YAHOO.widget.AutoComplete("user", "owner_container", ownerDS);
680 ownerAC.useShadow = false;
681 ownerAC.resultTypeList = false;
682
683
684 // Helper highlight function for the formatter
685 var highlightMatch = function (full, snippet, matchindex) {
686 return full.substring(0, matchindex)
687 + "<span class='match'>"
688 + full.substr(matchindex, snippet.length)
689 + "</span>" + full.substring(matchindex + snippet.length);
690 };
691
692 // Custom formatter to highlight the matching letters
693 var custom_formatter = function (oResultData, sQuery, sResultMatch) {
694 var query = sQuery.toLowerCase();
695 var _gravatar = function(res, em, group){
696 if (group !== undefined){
697 em = '/images/icons/group.png'
698 }
699 tmpl = '<img class="perm-gravatar-ac" src="{0}"/>{1}'
700 return tmpl.format(em,res)
701 }
702 // group
703 if (oResultData.grname != undefined) {
704 var grname = oResultData.grname;
705 var grmembers = oResultData.grmembers;
706 var grnameMatchIndex = grname.toLowerCase().indexOf(query);
707 var grprefix = "{0}: ".format(group_lbl);
708 var grsuffix = " (" + grmembers + " )";
709 var grsuffix = " ({0} {1})".format(grmembers, members_lbl);
710
711 if (grnameMatchIndex > -1) {
712 return _gravatar(grprefix + highlightMatch(grname, query, grnameMatchIndex) + grsuffix,null,true);
713 }
714 return _gravatar(grprefix + oResultData.grname + grsuffix, null,true);
715 // Users
716 } else if (oResultData.fname != undefined) {
717 var fname = oResultData.fname,
718 lname = oResultData.lname,
719 nname = oResultData.nname || "",
720 // Guard against null value
721 fnameMatchIndex = fname.toLowerCase().indexOf(query),
722 lnameMatchIndex = lname.toLowerCase().indexOf(query),
723 nnameMatchIndex = nname.toLowerCase().indexOf(query),
724 displayfname, displaylname, displaynname;
725
726 if (fnameMatchIndex > -1) {
727 displayfname = highlightMatch(fname, query, fnameMatchIndex);
728 } else {
729 displayfname = fname;
730 }
731
732 if (lnameMatchIndex > -1) {
733 displaylname = highlightMatch(lname, query, lnameMatchIndex);
734 } else {
735 displaylname = lname;
736 }
737
738 if (nnameMatchIndex > -1) {
739 displaynname = "(" + highlightMatch(nname, query, nnameMatchIndex) + ")";
740 } else {
741 displaynname = nname ? "(" + nname + ")" : "";
742 }
743
744 return _gravatar(displayfname + " " + displaylname + " " + displaynname, oResultData.gravatar_lnk);
745 } else {
746 return '';
747 }
748 };
749 membersAC.formatResult = custom_formatter;
750 ownerAC.formatResult = custom_formatter;
751
752 var myHandler = function (sType, aArgs) {
753
754 var myAC = aArgs[0]; // reference back to the AC instance
755 var elLI = aArgs[1]; // reference to the selected LI element
756 var oData = aArgs[2]; // object literal of selected item's result data
757 //fill the autocomplete with value
758 if (oData.nname != undefined) {
759 //users
760 myAC.getInputEl().value = oData.nname;
761 YUD.get('perm_new_member_type').value = 'user';
762 } else {
763 //groups
764 myAC.getInputEl().value = oData.grname;
765 YUD.get('perm_new_member_type').value = 'users_group';
766 }
767 };
768
769 membersAC.itemSelectEvent.subscribe(myHandler);
770 if(ownerAC.itemSelectEvent){
771 ownerAC.itemSelectEvent.subscribe(myHandler);
772 }
773
774 return {
775 memberDS: memberDS,
776 ownerDS: ownerDS,
777 membersAC: membersAC,
778 ownerAC: ownerAC,
779 };
780 }
781
782
783
612 784 /**
613 785 * QUICK REPO MENU
614 786 */
@@ -25,7 +25,7 b''
25 25 <td>${h.radio('u_perm_%s' % r2p.user.username,'repository.write')}</td>
26 26 <td>${h.radio('u_perm_%s' % r2p.user.username,'repository.admin')}</td>
27 27 <td style="white-space: nowrap;">
28 <img style="vertical-align:bottom" src="${h.url('/images/icons/user.png')}"/>${r2p.user.username}
28 <img class="perm-gravatar" src="${h.gravatar_url(r2p.user.email,14)}"/>${r2p.user.username}
29 29 </td>
30 30 <td>
31 31 %if r2p.user.username !='default':
@@ -46,7 +46,7 b''
46 46 <td>${h.radio('g_perm_%s' % g2p.users_group.users_group_name,'repository.write')}</td>
47 47 <td>${h.radio('g_perm_%s' % g2p.users_group.users_group_name,'repository.admin')}</td>
48 48 <td style="white-space: nowrap;">
49 <img style="vertical-align:bottom" src="${h.url('/images/icons/group.png')}"/>${g2p.users_group.users_group_name}
49 <img class="perm-gravatar" src="${h.url('/images/icons/group.png')}"/>${g2p.users_group.users_group_name}
50 50 </td>
51 51 <td>
52 52 <span class="delete_icon action_button" onclick="ajaxActionUsersGroup(${g2p.users_group.users_group_id},'${'id%s'%id(g2p.users_group.users_group_name)}')">
@@ -117,165 +117,12 b' YUE.onDOMReady(function () {'
117 117 YUD.setStyle('add_perm', 'opacity', '0.6');
118 118 YUD.setStyle('add_perm', 'cursor', 'default');
119 119 });
120 MembersAutoComplete(
121 ${c.users_array|n},
122 ${c.users_groups_array|n},
123 "${_('Group')}",
124 "${_('members')}"
125 );
120 126 });
121 127
122 YAHOO.example.FnMultipleFields = function () {
123 var myUsers = ${c.users_array|n};
124 var myGroups = ${c.users_groups_array|n};
125
126 // Define a custom search function for the DataSource of users
127 var matchUsers = function (sQuery) {
128 // Case insensitive matching
129 var query = sQuery.toLowerCase();
130 var i = 0;
131 var l = myUsers.length;
132 var matches = [];
133
134 // Match against each name of each contact
135 for (; i < l; i++) {
136 contact = myUsers[i];
137 if ((contact.fname.toLowerCase().indexOf(query) > -1) || (contact.lname.toLowerCase().indexOf(query) > -1) || (contact.nname && (contact.nname.toLowerCase().indexOf(query) > -1))) {
138 matches[matches.length] = contact;
139 }
140 }
141 return matches;
142 };
143
144 // Define a custom search function for the DataSource of usersGroups
145 var matchGroups = function (sQuery) {
146 // Case insensitive matching
147 var query = sQuery.toLowerCase();
148 var i = 0;
149 var l = myGroups.length;
150 var matches = [];
151
152 // Match against each name of each contact
153 for (; i < l; i++) {
154 matched_group = myGroups[i];
155 if (matched_group.grname.toLowerCase().indexOf(query) > -1) {
156 matches[matches.length] = matched_group;
157 }
158 }
159 return matches;
160 };
161
162 //match all
163 var matchAll = function (sQuery) {
164 u = matchUsers(sQuery);
165 g = matchGroups(sQuery);
166 return u.concat(g);
167 };
168
169 // DataScheme for members
170 var memberDS = new YAHOO.util.FunctionDataSource(matchAll);
171 memberDS.responseSchema = {
172 fields: ["id", "fname", "lname", "nname", "grname", "grmembers"]
173 };
174
175 // DataScheme for owner
176 var ownerDS = new YAHOO.util.FunctionDataSource(matchUsers);
177 ownerDS.responseSchema = {
178 fields: ["id", "fname", "lname", "nname"]
179 };
180
181 // Instantiate AutoComplete for perms
182 var membersAC = new YAHOO.widget.AutoComplete("perm_new_member_name", "perm_container", memberDS);
183 membersAC.useShadow = false;
184 membersAC.resultTypeList = false;
185
186 // Instantiate AutoComplete for owner
187 var ownerAC = new YAHOO.widget.AutoComplete("user", "owner_container", ownerDS);
188 ownerAC.useShadow = false;
189 ownerAC.resultTypeList = false;
190
191
192 // Helper highlight function for the formatter
193 var highlightMatch = function (full, snippet, matchindex) {
194 return full.substring(0, matchindex) + "<span class='match'>" + full.substr(matchindex, snippet.length) + "</span>" + full.substring(matchindex + snippet.length);
195 };
196
197 // Custom formatter to highlight the matching letters
198 var custom_formatter = function (oResultData, sQuery, sResultMatch) {
199 var query = sQuery.toLowerCase();
200
201 if (oResultData.grname != undefined) {
202 var grname = oResultData.grname;
203 var grmembers = oResultData.grmembers;
204 var grnameMatchIndex = grname.toLowerCase().indexOf(query);
205 var grprefix = "${_('Group')}: ";
206 var grsuffix = " (" + grmembers + " ${_('members')})";
207
208 if (grnameMatchIndex > -1) {
209 return grprefix + highlightMatch(grname, query, grnameMatchIndex) + grsuffix;
210 }
211
212 return grprefix + oResultData.grname + grsuffix;
213 } else if (oResultData.fname != undefined) {
214
215 var fname = oResultData.fname,
216 lname = oResultData.lname,
217 nname = oResultData.nname || "",
218 // Guard against null value
219 fnameMatchIndex = fname.toLowerCase().indexOf(query),
220 lnameMatchIndex = lname.toLowerCase().indexOf(query),
221 nnameMatchIndex = nname.toLowerCase().indexOf(query),
222 displayfname, displaylname, displaynname;
223
224 if (fnameMatchIndex > -1) {
225 displayfname = highlightMatch(fname, query, fnameMatchIndex);
226 } else {
227 displayfname = fname;
228 }
229
230 if (lnameMatchIndex > -1) {
231 displaylname = highlightMatch(lname, query, lnameMatchIndex);
232 } else {
233 displaylname = lname;
234 }
235
236 if (nnameMatchIndex > -1) {
237 displaynname = "(" + highlightMatch(nname, query, nnameMatchIndex) + ")";
238 } else {
239 displaynname = nname ? "(" + nname + ")" : "";
240 }
241
242 return displayfname + " " + displaylname + " " + displaynname;
243 } else {
244 return '';
245 }
246 };
247 membersAC.formatResult = custom_formatter;
248 ownerAC.formatResult = custom_formatter;
249
250 var myHandler = function (sType, aArgs) {
251
252 var myAC = aArgs[0]; // reference back to the AC instance
253 var elLI = aArgs[1]; // reference to the selected LI element
254 var oData = aArgs[2]; // object literal of selected item's result data
255 //fill the autocomplete with value
256 if (oData.nname != undefined) {
257 //users
258 myAC.getInputEl().value = oData.nname;
259 YUD.get('perm_new_member_type').value = 'user';
260 } else {
261 //groups
262 myAC.getInputEl().value = oData.grname;
263 YUD.get('perm_new_member_type').value = 'users_group';
264 }
265
266 };
267
268 membersAC.itemSelectEvent.subscribe(myHandler);
269 if(ownerAC.itemSelectEvent){
270 ownerAC.itemSelectEvent.subscribe(myHandler);
271 }
272
273 return {
274 memberDS: memberDS,
275 ownerDS: ownerDS,
276 membersAC: membersAC,
277 ownerAC: ownerAC,
278 };
279 }();
280
281 128 </script>
@@ -15,7 +15,7 b''
15 15 <td>${h.radio('u_perm_%s' % r2p.user.username,'group.write')}</td>
16 16 <td>${h.radio('u_perm_%s' % r2p.user.username,'group.admin')}</td>
17 17 <td style="white-space: nowrap;">
18 <img style="vertical-align:bottom" src="${h.url('/images/icons/user.png')}"/>${r2p.user.username}
18 <img class="perm-gravatar" src="${h.gravatar_url(r2p.user.email,14)}"/>${r2p.user.username}
19 19 </td>
20 20 <td>
21 21 %if r2p.user.username !='default':
@@ -35,7 +35,7 b''
35 35 <td>${h.radio('g_perm_%s' % g2p.users_group.users_group_name,'group.write')}</td>
36 36 <td>${h.radio('g_perm_%s' % g2p.users_group.users_group_name,'group.admin')}</td>
37 37 <td style="white-space: nowrap;">
38 <img style="vertical-align:bottom" src="${h.url('/images/icons/group.png')}"/>${g2p.users_group.users_group_name}
38 <img class="perm-gravatar" src="${h.url('/images/icons/group.png')}"/>${g2p.users_group.users_group_name}
39 39 </td>
40 40 <td>
41 41 <span class="delete_icon action_button" onclick="ajaxActionUsersGroup(${g2p.users_group.users_group_id},'${'id%s'%id(g2p.users_group.users_group_name)}')">
@@ -106,165 +106,12 b' YUE.onDOMReady(function () {'
106 106 YUD.setStyle('add_perm', 'opacity', '0.6');
107 107 YUD.setStyle('add_perm', 'cursor', 'default');
108 108 });
109 MembersAutoComplete(
110 ${c.users_array|n},
111 ${c.users_groups_array|n},
112 "${_('Group')}",
113 "${_('members')}"
114 );
109 115 });
110 116
111 YAHOO.example.FnMultipleFields = function () {
112 var myUsers = ${c.users_array|n};
113 var myGroups = ${c.users_groups_array|n};
114
115 // Define a custom search function for the DataSource of users
116 var matchUsers = function (sQuery) {
117 // Case insensitive matching
118 var query = sQuery.toLowerCase();
119 var i = 0;
120 var l = myUsers.length;
121 var matches = [];
122
123 // Match against each name of each contact
124 for (; i < l; i++) {
125 contact = myUsers[i];
126 if ((contact.fname.toLowerCase().indexOf(query) > -1) || (contact.lname.toLowerCase().indexOf(query) > -1) || (contact.nname && (contact.nname.toLowerCase().indexOf(query) > -1))) {
127 matches[matches.length] = contact;
128 }
129 }
130 return matches;
131 };
132
133 // Define a custom search function for the DataSource of usersGroups
134 var matchGroups = function (sQuery) {
135 // Case insensitive matching
136 var query = sQuery.toLowerCase();
137 var i = 0;
138 var l = myGroups.length;
139 var matches = [];
140
141 // Match against each name of each contact
142 for (; i < l; i++) {
143 matched_group = myGroups[i];
144 if (matched_group.grname.toLowerCase().indexOf(query) > -1) {
145 matches[matches.length] = matched_group;
146 }
147 }
148 return matches;
149 };
150
151 //match all
152 var matchAll = function (sQuery) {
153 u = matchUsers(sQuery);
154 g = matchGroups(sQuery);
155 return u.concat(g);
156 };
157
158 // DataScheme for members
159 var memberDS = new YAHOO.util.FunctionDataSource(matchAll);
160 memberDS.responseSchema = {
161 fields: ["id", "fname", "lname", "nname", "grname", "grmembers"]
162 };
163
164 // DataScheme for owner
165 var ownerDS = new YAHOO.util.FunctionDataSource(matchUsers);
166 ownerDS.responseSchema = {
167 fields: ["id", "fname", "lname", "nname"]
168 };
169
170 // Instantiate AutoComplete for perms
171 var membersAC = new YAHOO.widget.AutoComplete("perm_new_member_name", "perm_container", memberDS);
172 membersAC.useShadow = false;
173 membersAC.resultTypeList = false;
174
175 // Instantiate AutoComplete for owner
176 var ownerAC = new YAHOO.widget.AutoComplete("user", "owner_container", ownerDS);
177 ownerAC.useShadow = false;
178 ownerAC.resultTypeList = false;
179
180
181 // Helper highlight function for the formatter
182 var highlightMatch = function (full, snippet, matchindex) {
183 return full.substring(0, matchindex) + "<span class='match'>" + full.substr(matchindex, snippet.length) + "</span>" + full.substring(matchindex + snippet.length);
184 };
185
186 // Custom formatter to highlight the matching letters
187 var custom_formatter = function (oResultData, sQuery, sResultMatch) {
188 var query = sQuery.toLowerCase();
189
190 if (oResultData.grname != undefined) {
191 var grname = oResultData.grname;
192 var grmembers = oResultData.grmembers;
193 var grnameMatchIndex = grname.toLowerCase().indexOf(query);
194 var grprefix = "${_('Group')}: ";
195 var grsuffix = " (" + grmembers + " ${_('members')})";
196
197 if (grnameMatchIndex > -1) {
198 return grprefix + highlightMatch(grname, query, grnameMatchIndex) + grsuffix;
199 }
200
201 return grprefix + oResultData.grname + grsuffix;
202 } else if (oResultData.fname != undefined) {
203
204 var fname = oResultData.fname,
205 lname = oResultData.lname,
206 nname = oResultData.nname || "",
207 // Guard against null value
208 fnameMatchIndex = fname.toLowerCase().indexOf(query),
209 lnameMatchIndex = lname.toLowerCase().indexOf(query),
210 nnameMatchIndex = nname.toLowerCase().indexOf(query),
211 displayfname, displaylname, displaynname;
212
213 if (fnameMatchIndex > -1) {
214 displayfname = highlightMatch(fname, query, fnameMatchIndex);
215 } else {
216 displayfname = fname;
217 }
218
219 if (lnameMatchIndex > -1) {
220 displaylname = highlightMatch(lname, query, lnameMatchIndex);
221 } else {
222 displaylname = lname;
223 }
224
225 if (nnameMatchIndex > -1) {
226 displaynname = "(" + highlightMatch(nname, query, nnameMatchIndex) + ")";
227 } else {
228 displaynname = nname ? "(" + nname + ")" : "";
229 }
230
231 return displayfname + " " + displaylname + " " + displaynname;
232 } else {
233 return '';
234 }
235 };
236 membersAC.formatResult = custom_formatter;
237 ownerAC.formatResult = custom_formatter;
238
239 var myHandler = function (sType, aArgs) {
240
241 var myAC = aArgs[0]; // reference back to the AC instance
242 var elLI = aArgs[1]; // reference to the selected LI element
243 var oData = aArgs[2]; // object literal of selected item's result data
244 //fill the autocomplete with value
245 if (oData.nname != undefined) {
246 //users
247 myAC.getInputEl().value = oData.nname;
248 YUD.get('perm_new_member_type').value = 'user';
249 } else {
250 //groups
251 myAC.getInputEl().value = oData.grname;
252 YUD.get('perm_new_member_type').value = 'users_group';
253 }
254
255 };
256
257 membersAC.itemSelectEvent.subscribe(myHandler);
258 if(ownerAC.itemSelectEvent){
259 ownerAC.itemSelectEvent.subscribe(myHandler);
260 }
261
262 return {
263 memberDS: memberDS,
264 ownerDS: ownerDS,
265 membersAC: membersAC,
266 ownerAC: ownerAC,
267 };
268 }();
269
270 117 </script>
General Comments 0
You need to be logged in to leave comments. Login now