Anonymous avatar Anonymous committed 6d528fb

Merge based on changes between /tracker/vendor/roundup/1.1.2 and
/tracker/vendor/roundup/current (roundup 1.2.1).

Needs roundup 1.2.1 to run.

Comments (0)

Files changed (17)

detectors/userauditor.py

 # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 # SOFTWARE.
 #
-#$Id: userauditor.py,v 1.2 2003/11/11 22:25:37 richard Exp $
+#$Id: userauditor.py,v 1.3 2006/09/18 03:24:38 tobias-herp Exp $
 
 def audit_user_fields(db, cl, nodeid, newvalues):
     ''' Make sure user properties are valid.
     if newvalues.has_key('address') and ' ' in newvalues['address']:
         raise ValueError, 'Email address must not contain spaces'
 
-    if newvalues.has_key('roles'):
+    if newvalues.has_key('roles') and newvalues['roles']:
         roles = [x.lower().strip() for x in newvalues['roles'].split(',')]
         for rolename in roles:
             if not db.security.role.has_key(rolename):

html/_generic.help-empty.html

+<html>
+  <head>
+    <title>Empty page (no search performed yet)</title>
+  </head>
+  <body>
+    <p i18n:translate="">Please specify your search parameters!</p>
+  </body>
+</html>

html/_generic.help-list.html

+<!-- $Id: _generic.help-list.html,v 1.1 2006/09/18 00:03:02 tobias-herp Exp $ vim: sw=2 ts=8 et
+--><html tal:define="vok context/is_view_ok">
+  <head>
+    <title>Search result for user helper</title>
+    <link rel="stylesheet" type="text/css" href="@@file/style.css" />
+    <script language="Javascript" type="text/javascript"
+        tal:content="structure string:<!--
+        // this is the name of the field in the original form that we're working on
+        form  = parent.opener.document.${request/form/form/value};
+        field  = '${request/form/property/value}';
+    //-->"></script>
+    <script src="@@file/help_controls.js" type="text/javascript"></script>
+<script type="text/javascript"><!--
+var text_field = parent.submit.document.frm_help.text_preview;
+//--></script>
+  </head>
+  <body>
+    <pre tal:content="request/env/QUERY_STRING" tal:condition=false />
+
+  <p tal:condition="not:vok" i18n:translate="">You are not
+  allowed to view this page.</p>
+
+  <tal:if condition="context/is_view_ok">
+  <tal:def define="batch request/batch;">
+  <form name=dummyform>
+    <table width="100%"
+      tal:define="template string:help-list"
+      metal:use-macro="templates/help/macros/batch_navi"
+      >
+      <tr class="navigation">
+       <th>
+        <a href="#">&lt;&lt; previous</a>
+       </th>
+       <th i18n:translate="">1..25 out of 50
+       </th>
+       <th>
+        <a href="#">next &gt;&gt;</a>
+       </th>
+      </tr>
+     </table>
+
+  <form name=dummyform>
+  <table class="classhelp"
+    tal:define="
+       props python:request.form['properties'].value.split(',');
+       legend templates/help/macros/legend;
+    "><thead>
+      <tr metal:use-macro="legend">
+         <th>&nbsp;<b>x</b></th>
+         <th tal:repeat="prop props" tal:content="prop" i18n:translate=""></th>
+       </tr>
+     </thead>
+     <tfoot tal:condition=true>
+       <tr metal:use-macro="legend" />
+     </tfoot>
+     <tbody>
+       <tr tal:repeat="item batch">
+         <tal:block tal:define="attr python:item[props[0]]" >
+           <td>
+             <input name="check"
+             onclick="switch_val(text_field, this);" type="checkbox"
+             tal:attributes="value attr; id string:id_$attr" />
+             </td>
+             <td tal:repeat="prop props">
+                 <label class="classhelp-label"
+                        tal:attributes="for string:id_$attr"
+                        tal:content="structure python:item[prop]"></label>
+             </td>
+           </tal:block>
+         </tr>
+       </tbody>
+     </table>
+   </form>
+     </tal:def>
+     </tal:if>
+     
+     <pre tal:content=request tal:condition=false />
+     <script type="text/javascript"><!--
+       parent.submit.document.frm_help.cb_listpresent.checked=true;
+       reviseList_framed(document.dummyform, text_field)
+     //--></script>
+  </body>
+</html>

html/_generic.help-search.html

+<html>
+  <head>
+    <title>Frame for search input fields</title>
+  </head>
+  <body>
+    <p i18n:translate="">Generic template
+    <span tal:replace="request/template" i18n:name="template">help-search</span>
+    or version for class
+    <span tal:replace="request/classname" i18n:name="classname">user</span>
+    is not yet implemented</p>
+  </body>
+</html>
+

html/_generic.help-submit.html

+<html>
+  <head>
+      <link rel="stylesheet" type="text/css" href="@@file/style.css" />
+      <meta http-equiv="Content-Type"
+       tal:attributes="content string:text/html;; charset=${request/client/charset}" />
+      <tal:block tal:condition="python:request.form.has_key('property')">
+      <title>Generic submit page for framed helper windows</title>
+      <script language="Javascript" type="text/javascript"
+          tal:content="structure string:<!--
+// this is the name of the field in the original form that we're working on
+form  = parent.opener.document.${request/form/form/value};
+callingform=form
+field  = '${request/form/property/value}';
+var listform = null
+function listPresent() {
+  return document.frm_help.cb_listpresent.checked
+}
+function getListForm() {
+  if (listPresent()) {
+    return parent.list.document.forms.dummyform
+  } else {
+    return null
+  }
+}
+
+
+function checkListForm() {
+  // global listform
+  if (listform != null)
+    if (parent.list.document.dummyform) {
+      listform = parent.list.document.dummyform
+      alert(listform)
+    }
+
+  var bol= listform != null
+  alert('checkListForm: bol='+bol)
+  return bol
+}
+//-->">
+      </script>
+      <script src="@@file/help_controls.js" type="text/javascript"></script>
+      </tal:block>
+  </head>
+ <body class="body" onload="parent.focus();" id="submit">
+ <pre tal:content="request/env/QUERY_STRING" tal:condition=false />
+ <form name="frm_help"
+       tal:define="batch request/batch;
+       props python:request.form['properties'].value.split(',')"
+       class="help-submit"
+       id="classhelp-controls">
+     <div style="width:100%;text-align:left;margin-bottom:0.2em">
+       <input type="text" name="text_preview" size="24" class="preview"
+       onchange="f=getListForm();if(f){ reviseList_framed(f, this)};"
+       />
+     </div>
+     <input type=checkbox name="cb_listpresent" readonly="readonly" style="display:none">
+     <input type="button" id="btn_cancel"
+            value=" Cancel " onclick="parent.close();return false;"
+            i18n:attributes="value" />
+     <input type="reset" id="btn_reset"
+     onclick="text_field.value=original_field;f=getListForm();if (f) {reviseList_framed(f, this)};return false"
+            />
+     <input type="submit" id="btn_apply" class="apply"
+            value=" Apply " onclick="callingform[field].value=text_field.value; parent.close();"
+            i18n:attributes="value" />
+ </form>
+ <script type="text/javascript"><!--
+var text_field = document.frm_help.text_preview;
+original_field=form[field].value;
+text_field.value=original_field;
+//--></script>
+ </body>
+</html>
+<!--
+Macros for framed help windows
+-->
+
+<!-- legend for helper search results -->
+<thead>
+<tr metal:define-macro="legend">
+  <th><b>x</b></th>
+  <th tal:repeat="prop props" tal:content="prop" i18n:translate=""></th>
+</tr>
+</thead>
+
+<table width="100%"
+  metal:define-macro="batch_navi"
+  tal:define="prev batch/previous;
+  next batch/next;
+  "
+  tal:condition="python:prev or next">
+  <tr class="navigation">
+   <th width="30%">
+    <a tal:condition="prev"
+       tal:attributes="href python:request.indexargs_url(request.classname, {'@template':'help-list', 'property': request.form['property'].value, 'properties': request.form['properties'].value, 'form': request.form['form'].value, '@startwith':prev.first, '@pagesize':prev.size})"
+       i18n:translate="" >&lt;&lt; previous</a>
+    &nbsp;
+   </th>
+   <th i18n:translate="" width="40%"><span tal:replace="batch/start" i18n:name="start"
+    />..<span tal:replace="python: batch.start + batch.length -1" i18n:name="end"
+    /> out of <span tal:replace="batch/sequence_length" i18n:name="total"
+    />
+   </th>
+   <th width="30%">
+    <a tal:condition="next"
+       tal:attributes="href python:request.indexargs_url(request.classname, {'@template':'help-list', 'property': request.form['property'].value, 'properties': request.form['properties'].value, 'form': request.form['form'].value, '@startwith':next.first, '@pagesize':next.size})"
+       i18n:translate="" >next &gt;&gt;</a>
+    &nbsp;
+   </th>
+  </tr>
+ </table>

html/help_controls.js

 function determineList() {
      // generate a comma-separated list of the checked items
      var list = new String('');
- 
+
      // either a checkbox object or an array of checkboxes
      var check = document.frm_help.check;
- 
+
      if ((check.length == undefined) && (check.checked != undefined)) {
          // only one checkbox on page
          if (check.checked) {
              }
          }
      }
-     return list;  
+     return list;
+}
+
+/**
+ * update the field in the opening window;
+ * the text_field variable must be set in the calling page
+ */
+function updateOpener() {
+  // write back to opener window
+  if (document.frm_help.check==undefined) { return; }
+  form[field].value = text_field.value;
 }
 
 function updateList() {
   }
 }
 
+function reviseList_framed(form, textfield) {
+  // update the checkboxes based on the preview field
+  // alert('reviseList_framed')
+  // alert(form)
+  if (form.check==undefined)
+      return;
+  // alert(textfield)
+  var to_check;
+  var list = textfield.value.split(",");
+  if (form.check.length==undefined) {
+      check = form.check;
+      to_check = false;
+      for (val in list) {
+          if (check.value==trim(list[val])) {
+              to_check = true;
+              break;
+          }
+      }
+      check.checked = to_check;
+  } else {
+    for (box=0; box < form.check.length; box++) {
+      check = form.check[box];
+      to_check = false;
+      for (val in list) {
+          if (check.value==trim(list[val])) {
+              to_check = true;
+              break;
+          }
+      }
+      check.checked = to_check;
+    }
+  }
+}
+
 function reviseList(vals) {
   // update the checkboxes based on the preview field
   if (document.frm_help.check==undefined) { return; }
 function selectField(name) {
     for(i=0; i < document.forms.length; ++i) {
       var obj = document.forms[i].elements[name];
-      if (obj && obj.focus){obj.focus();} 
+      if (obj && obj.focus){obj.focus();}
       if (obj && obj.select){obj.select();}
     }
 }
 
+function checkRequiredFields(fields)
+{
+    var bonk='';
+    var res='';
+    var argv = checkRequiredFields.arguments;
+    var argc = argv.length;
+    var input = '';
+    var val='';
+
+    for (var i=0; i < argc; i++) {
+        fi = argv[i];
+        input = document.getElementById(fi);
+        if (input) {
+            val = input.value
+            if (val == '' || val == '-1' || val == -1) {
+                if (res == '') {
+                    res = fi;
+                    bonk = input;
+                } else {
+                    res += ', '+fi;
+                }
+            }
+        } else {
+            alert('Field with id='+fi+' not found!')
+        }
+    }
+    if (res == '') {
+        return submit_once();
+    } else {
+        alert('Missing value here ('+res+')!');
+        if (window.event && window.event.returnvalue) {
+            event.returnValue = 0;    // work-around for IE
+        }
+        bonk.focus();
+        return false;
+    }
+}
+
+/**
+ * seeks the given value (2nd argument)
+ * in the value of the given input element (1st argument),
+ * which is considered a list of values, separated by commas
+ */
+function has_value(input, val)
+{
+    var actval = input.value
+    var arr = feld.value.split(',');
+    var max = arr.length;
+    for (i=0;i<max;i++) {
+        if (trim(arr[i]) == val) {
+            return true
+        }
+    }
+    return false
+}
+
+/**
+ * Switch Value:
+ * change the value of the given input field (might be of type text or hidden),
+ * adding or removing the value of the given checkbox field (might be a radio
+ * button as well)
+ *
+ * This function doesn't care whether or not the checkboxes of all values of
+ * interest are present; but of course it doesn't have total control of the
+ * text field.
+ */
+function switch_val(text, check)
+{
+    var switched_val = check.value
+    var arr = text.value.split(',')
+    var max = arr.length
+    if (check.checked) {
+        for (i=0; i<max; i++) {
+            if (trim(arr[i]) == switched_val) {
+                return
+            }
+        }
+	if (text.value)
+            text.value = text.value+','+switched_val
+	else
+            text.value = switched_val
+    } else {
+        var neu = ''
+	var changed = false
+        for (i=0; i<max; i++) {
+            if (trim(arr[i]) == switched_val) {
+                changed=true
+            } else {
+                neu = neu+','+trim(arr[i])
+            }
+        }
+        if (changed) {
+            text.value = neu.substr(1)
+        }
+    }
+}
+
+/**
+ * append the given value (2nd argument) to an input field
+ * (1st argument) which contains comma-separated values;
+ * see --> remove_val()
+ *
+ * This will work nicely even for batched lists
+ */
+function append_val(name, val)
+{
+    var feld = document.itemSynopsis[name];
+    var actval = feld.value;
+    if (actval == '') {
+        feld.value = val
+    } else {
+        var arr = feld.value.split(',');
+        var max = arr.length;
+        for (i=0;i<max;i++) {
+            if (trim(arr[i]) == val) {
+                return
+            }
+        }
+        feld.value = actval+','+val
+    }
+}
+
+/**
+ * remove the given value (2nd argument) from the comma-separated values
+ * of the given input element (1st argument); see --> append_val()
+ */
+function remove_val(name, val)
+{
+    var feld = document.itemSynopsis[name];
+    var actval = feld.value;
+    var changed=false;
+    if (actval == '') {
+	return
+    } else {
+        var arr = feld.value.split(',');
+        var max = arr.length;
+        var neu = ''
+        for (i=0;i<max;i++) {
+            if (trim(arr[i]) == val) {
+                changed=true
+            } else {
+                neu = neu+','+trim(arr[i])
+            }
+        }
+        if (changed) {
+            feld.value = neu.substr(1)
+        }
+    }
+}
+
+/**
+ * give the focus to the element given by id
+ */
+function focus2id(name)
+{
+    document.getElementById(name).focus();
+}
  whatever. It's a good idea to have the issues on the front page though
 -->
 <span tal:replace="structure python:db.issue.renderWith('index',
-    sort=('-', 'activity'), group=('+', 'priority'), filter=['status'],
+    sort=[('-', 'activity')], group=[('+', 'priority')], filter=['status'],
     columns=['id','activity','title','creator','assignedto', 'status'],
     filterspec={'status':['1','2']})" />
 <!-- SHA: 1fdaa3238149b4381d19a45a81deb6075de7d0e9 -->

html/issue.index.html

-<!-- dollarId: issue.index,v 1.2 2001/07/29 04:07:37 richard Exp dollar-->
+<!-- $Id: issue.index.html,v 1.25 2006/09/18 00:03:02 tobias-herp Exp $ -->
 <tal:block metal:use-macro="templates/page/macros/icing">
-<title metal:fill-slot="head_title" i18n:translate="" >
-	List of issues - 
-	<span tal:condition="request/dispname"
-          tal:replace="python:' %s - '%request.dispname" />
-	<span tal:replace="config/TRACKER_NAME" i18n:name="tracker" />
-	</title>
-<span metal:fill-slot="body_title" tal:omit-tag="python:1"
-	i18n:translate="">List of issues 
-	<span tal:condition="request/dispname"
-          tal:replace="python:' - %s' % request.dispname" />
+<title metal:fill-slot="head_title" >
+  <span tal:omit-tag="true" i18n:translate="" >List of issues</span>
+  <span tal:condition="request/dispname"
+   tal:replace="python:' - %s '%request.dispname"
+  /> - <span tal:replace="config/TRACKER_NAME" />
+</title>
+<span metal:fill-slot="body_title" tal:omit-tag="true">
+  <span tal:omit-tag="true" i18n:translate="" >List of issues</span>
+  <span tal:condition="request/dispname"
+   tal:replace="python:' - %s' % request.dispname" />
 </span>
 <td class="content" metal:fill-slot="content">
 
    <th tal:condition="request/show/creator" i18n:translate="">Creator</th>
    <th tal:condition="request/show/assignedto" i18n:translate="">Assigned&nbsp;To</th>
   </tr>
- <tal:block tal:repeat="i batch">
-  <tr tal:define="group python:request.group[1]"
-      tal:condition="python:group and batch.propchanged(group)">
-   <th tal:attributes="colspan python:len(request.columns)"
-       tal:content="python:str(i[group]) or '(no %s set)'%group" class="group">
+ <tal:block tal:repeat="i batch" condition=true>
+  <tr tal:define="group python:[r[1] for r in request.group]"
+      tal:condition="python:group and batch.propchanged(*group)">
+   <th tal:attributes="colspan python:len(request.columns)" class="group">
+    <tal:block tal:repeat="g group">
+     <tal:block tal:content="python:str(i[g]) or '(no %s set)'%g"/>
+    </tal:block>
    </th>
   </tr>
 
 <form method="get" class="index-controls"
     tal:attributes="action request/classname">
 
- <table class="form">
-  <tr tal:condition="batch">
-   <th i18n:translate="">Sort on:</th>
+ <table class="form" tal:define="n_sort python:2">
+  <tal:block tal:repeat="n python:range(n_sort)" tal:condition="batch">
+  <tr tal:define="key python:len(request.sort)>n and request.sort[n]">
+   <th>
+    <tal:block tal:condition="not:n" i18n:translate="">Sort on:</tal:block>
+   </th>
    <td>
-    <select name="@sort">
+    <select tal:attributes="name python:'@sort%d'%n">
      <option value="" i18n:translate="">- nothing -</option>
      <option tal:repeat="col context/properties"
              tal:attributes="value col/_name;
-                             selected python:col._name == request.sort[1]"
+                             selected python:key and col._name == key[1]"
              tal:content="col/_name"
              i18n:translate="">column</option>
     </select>
    </td>
    <th i18n:translate="">Descending:</th>
    <td><input type="checkbox" name="@sortdir"
-              tal:attributes="checked python:request.sort[0] == '-'"/>
+              tal:attributes="checked python:key and key[0] == '-'">
    </td>
   </tr>
-  <tr>
-   <th i18n:translate="">Group on:</th>
+  </tal:block>
+  <tal:block tal:repeat="n python:range(n_sort)" tal:condition="batch">
+  <tr tal:define="key python:len(request.group)>n and request.group[n]">
+   <th>
+    <tal:block tal:condition="not:n" i18n:translate="">Group on:</tal:block>
+   </th>
    <td>
-    <select name="@group">
+    <select tal:attributes="name python:'@group%d'%n">
      <option value="" i18n:translate="">- nothing -</option>
      <option tal:repeat="col context/properties"
              tal:attributes="value col/_name;
-                             selected python:col._name == request.group[1]"
+                             selected python:key and col._name == key[1]"
              tal:content="col/_name"
              i18n:translate="">column</option>
     </select>
    </td>
    <th i18n:translate="">Descending:</th>
    <td><input type="checkbox" name="@groupdir"
-              tal:attributes="checked python:request.group[0] == '-'"/>
+              tal:attributes="checked python:key and key[0] == '-'">
    </td>
   </tr>
+  </tal:block>
   <tr><td colspan="4">
               <input type="submit" value="Redisplay" i18n:attributes="value"/>
               <tal:block tal:replace="structure
 
 </td>
 </tal:block>
-<!-- SHA: 8a7f0580114904917683796f76dde4da8cc80d3b -->

html/issue.item.html

  <td>
   <span tal:replace="structure python:context.superseder.field(showid=1, size=20)" />
   <span tal:condition="context/is_edit_ok" tal:replace="structure python:db.issue.classhelp('id,title', property='superseder')" />
-  <span tal:condition="context/superseder" tal:repeat="sup context/superseder">
-   <br><span i18n:translate="">View: <a i18n:name="link" tal:content="sup/id"
-     tal:attributes="href string:issue${sup/id};
-                     title sup/title;"></a></span>
+  <span tal:condition="context/superseder">
+   <br><span i18n:translate="">View:</span>
+     <a tal:repeat="sup context/superseder"
+        tal:content="python:sup['id'] + ', '*(not repeat['sup'].end)"
+        tal:attributes="href string:issue${sup/id};
+                        title sup/title;"></a>
   </span>
  </td>
  <th i18n:translate="">Nosy List</th>

html/issue.search.html

 
 <form method="get" name="itemSynopsis"
       tal:attributes="action request/classname">
-
+      
 <table class="form" tal:define="
    cols python:request.columns or 'id activity title status assignedto'.split();
-   sort_on python:request.sort[1] or 'activity';
-   group_on python:request.group[1] or 'priority';
+   sort_on python:request.sort and request.sort[0] or nothing;
+   sort_desc python:sort_on and sort_on[0] == '-';
+   sort_on python:(sort_on and sort_on[1]) or 'activity';
+   group_on python:request.group and request.group[0] or nothing;
+   group_desc python:group_on and group_on[0] == '-';
+   group_on python:(group_on and group_on[1]) or 'priority';
 
    search_input templates/page/macros/search_input;
+   search_date templates/page/macros/search_date;
    column_input templates/page/macros/column_input;
    sort_input templates/page/macros/sort_input;
    group_input templates/page/macros/group_input;
    search_select templates/page/macros/search_select;
+   search_select_translated templates/page/macros/search_select_translated;
    search_multiselect templates/page/macros/search_multiselect;">
 
 <tr>
 
 <tr tal:define="name string:creation">
   <th i18n:translate="">Creation Date:</th>
-  <td metal:use-macro="search_input"></td>
+  <td metal:use-macro="search_date"></td>
   <td metal:use-macro="column_input"></td>
   <td metal:use-macro="sort_input"></td>
   <td metal:use-macro="group_input"></td>
 
 <tr tal:define="name string:activity">
   <th i18n:translate="">Activity:</th>
-  <td metal:use-macro="search_input"></td>
+  <td metal:use-macro="search_date"></td>
   <td metal:use-macro="column_input"></td>
   <td metal:use-macro="sort_input"></td>
   <td>&nbsp;</td>
                 db_klass string:priority;
                 db_content string:name;">
   <th i18n:translate="">Priority:</th>
-  <td metal:use-macro="search_select">
+  <td metal:use-macro="search_select_translated">
     <option metal:fill-slot="extra_options" value="-1" i18n:translate=""
             tal:attributes="selected python:value == '-1'">not selected</option>
   </td>
                 db_klass string:status;
                 db_content string:name;">
   <th i18n:translate="">Status:</th>
-  <td metal:use-macro="search_select">
+  <td metal:use-macro="search_select_translated">
     <tal:block metal:fill-slot="extra_options">
       <option value="-1,1,2" i18n:translate=""
               tal:attributes="selected python:value == '-1,1,2'">not closed</option>
 <tr>
 <th i18n:translate="">Sort Descending:</th>
 <td><input type="checkbox" name="@sortdir"
-           tal:attributes="checked python:request.sort[0] == '-' or request.sort[0] is None"/>
+           tal:attributes="checked sort_desc">
 </td>
 </tr>
 
 <tr>
 <th i18n:translate="">Group Descending:</th>
 <td><input type="checkbox" name="@groupdir"
-           tal:attributes="checked python:request.group[0] == '-'"/>
+           tal:attributes="checked group_desc">
 </td>
 </tr>
 
  tal:attributes="content string:text/html;; charset=${request/client/charset}" />
 <script tal:replace="structure request/base_javascript">
 </script>
+<metal:x define-slot="more-javascript" />
 
 </head>
 <body>
 
 See issue.search.html in the classic template for examples.
 -->
+
+<!-- creates a th and a label: -->
+<th metal:define-macro="th_label"
+    tal:define="required required | python:[]"
+    tal:attributes="class python:(name in required) and 'required' or nothing">
+  <label tal:attributes="for name" tal:content="label" i18n:translate="">text</label>
+	<metal:x define-slot="behind_the_label" />
+</th>
+
 <td metal:define-macro="search_input">
   <input tal:attributes="value python:request.form.getvalue(name) or nothing;
-                         name name"/>
+                         name name;
+                         id name">
+</td>
+
+<td metal:define-macro="search_date">
+  <input tal:attributes="value python:request.form.getvalue(name) or nothing;
+                         name name;
+                         id name">
+  <a class="classhelp"
+	 tal:attributes="href python:'''javascript:help_window('issue?@template=calendar&property=%s&form=itemSynopsis', 300, 200)'''%name">(cal)</a>
 </td>
 
 <td metal:define-macro="search_popup">
     string (eg. "id,title" or "id,name,description") as well as name
   -->
   <input tal:attributes="value python:request.form.getvalue(name) or nothing;
-                         name name"/>
+                         name name;
+                         id name">
   <span tal:replace="structure python:db.issue.classhelp(columns,
                                       property=name)" />
 </td>
 
 <td metal:define-macro="search_select">
-  <select tal:attributes="name name"
+  <select tal:attributes="name name; id name"
           tal:define="value python:request.form.getvalue(name)">
     <option value="" i18n:translate="">don't care</option>
-    <tal:block metal:define-slot="extra_options"></tal:block>
-    <option value="" i18n:translate="">------------</option>
+    <metal:slot define-slot="extra_options" />
+    <option value="" i18n:translate="" disabled="disabled">------------</option>
     <option tal:repeat="s python:db[db_klass].list()"
             tal:attributes="value s/id; selected python:value == s.id"
             tal:content="python:s[db_content]"></option>
   </select>
 </td>
 
+<!-- like search_select, but translates the further values.
+Could extend it (METAL 1.1 attribute "extend-macro")
+-->
+<td metal:define-macro="search_select_translated">
+  <select tal:attributes="name name; id name"
+          tal:define="value python:request.form.getvalue(name)">
+    <option value="" i18n:translate="">don't care</option>
+    <metal:slot define-slot="extra_options" />
+    <option value="" i18n:translate="" disabled="disabled">------------</option>
+    <option tal:repeat="s python:db[db_klass].list()"
+            tal:attributes="value s/id; selected python:value == s.id"
+						tal:content="python:s[db_content]"
+						i18n:translate=""></option>
+  </select>
+</td>
+
+<!-- currently, there is no convenient API to get a list of all roles -->
+<td metal:define-macro="search_select_roles"
+	  tal:define="onchange onchange | nothing">
+  <select name=roles id=roles tal:attributes="onchange onchange">
+    <option value="" i18n:translate="">don't care</option>
+    <option value="" i18n:translate="" disabled="disabled">------------</option>
+    <option value="User">User</option>
+    <option value="Admin">Admin</option>
+    <option value="Anonymous">Anonymous</option>
+  </select>
+</td>
+
 <td metal:define-macro="search_multiselect">
   <input tal:attributes="value python:request.form.getvalue(name) or nothing;
-                         name name"/>
+                         name name;
+                         id name">
   <span tal:replace="structure python:db[db_klass].classhelp(db_content,
                                         property=name, width='600')" />
 </td>
          tal:attributes="value name;
                          checked python:name == group_on"/>
 </td>
+
+<!--
+The following macros are intended for user editing.
+
+The invoking context must define a "name" variable which names the
+property being searched; the "edit_ok" variable tells whether the
+current user is allowed to edit.
+
+See user.item.html in the classic template for examples.
+-->
+<script metal:define-macro="user_utils" type="text/javascript" src="@@file/user_utils.js"></script>
+
+<!-- src: value will be re-used for other input fields -->
+<input metal:define-macro="user_src_input"
+    type="text" tal:attributes="onblur python:edit_ok and 'split_name(this)';
+    id name; name name; value value; readonly not:edit_ok"
+    value="heinz.kunz">
+<!-- normal: no re-using -->
+<input metal:define-macro="user_normal_input" type="text"
+    tal:attributes="id name; name name; value value; readonly not:edit_ok"
+    value="heinz">
+<!-- password: type; no initial value -->
+    <input metal:define-macro="user_pw_input" type="password"
+    tal:attributes="id name; name name; readonly not:edit_ok" value="">
+    <input metal:define-macro="user_confirm_input" type="password"
+    tal:attributes="id name; name string:@confirm@$name; readonly not:edit_ok" value="">
+
 /* style for search forms */
 ul.search-checkboxes {
     display: inline;
-    padding: none;
+    padding: 0;
     list-style: none;
 }
 ul.search-checkboxes > li {
   font-weight: bold;
   text-align: left;
 }
-/* SHA: a055019fd4171164d8b3651575c38a33ef90511a */
+
+input[type="text"]:focus,
+input[type="checkbox"]:focus,
+input[type="radio"]:focus,
+input[type="password"]:focus,
+textarea:focus, select:focus {
+  background-color: #ffffc0;
+}
+
+

html/user.help-search.html

+<html
+  tal:define="form request/form/form/value;
+  field request/form/property/value"
+  >
+  <head>
+    <title>Search input for user helper</title>
+    <script language="Javascript" type="text/javascript"
+        tal:content="structure string:<!--
+        // this is the name of the field in the original form that we're working on
+        form  = parent.opener.document.${form};
+        field  = '${field}';
+        //-->">
+    </script>
+    <script type="text/javascript" src="@@file/help_controls.js"></script>
+    <link rel="stylesheet" type="text/css" href="@@file/style.css" />
+  </head>
+  <body onload="parent.submit.url='...'"
+    tal:define="
+qs request/env/QUERY_STRING;
+qs python:'&'.join([a for a in qs.split('&') if not a.startswith('@template=')])"
+>
+    <pre tal:content="request/env/QUERY_STRING" tal:condition=false />
+    <form method="GET" name="itemSynopsis"
+      target="list"
+      tal:attributes="action request/classname"
+      tal:define="
+      property request/form/property/value;
+   cols python:request.columns or 'id username address realname roles'.split();
+   sort_on request/sort | nothing;
+   sort_desc python:sort_on and request.sort[0][0] == '-';
+   sort_on python:sort_on and request.sort[0][1] or 'lastname';
+
+   search_input templates/page/macros/search_input;
+   search_select templates/page/macros/search_select;
+   search_select_roles templates/page/macros/search_select_roles;
+   required python:[];
+   th_label templates/page/macros/th_label;
+   ">
+   <input type="hidden" name="@template" value="help-list">
+   <input type="hidden" name="property" value="" tal:attributes="value property">
+   <input type="hidden" name="form" value="" tal:attributes="value request/form/form/value">
+   <table>
+<tr tal:define="name string:username; label string:Username:">
+  <th metal:use-macro="th_label">Name</th>
+  <td metal:use-macro="search_input"><input type=text></td>
+</tr>
+
+<tr tal:define="name string:phone; label string:Phone number">
+  <th metal:use-macro="th_label">Phone</th>
+  <td metal:use-macro="search_input"><input type=text></td>
+</tr>
+
+<tr tal:define="name string:roles;
+                onchange string:this.form.submit();
+                label string:Rollen:"
+                >
+  <th metal:use-macro="th_label">role</th>
+  <td metal:use-macro="search_select_roles">
+    <select>
+      <option value="">jokester</option>
+    </select>
+  </td>
+</tr>
+
+<tr>
+  <td>&nbsp;</td>
+  <td>
+    <input type="hidden" name="@action" value="search">
+    <input type="submit" value="Search" i18n:attributes="value">
+    <input type="reset">
+    <input type="hidden" value="username,realname,phone,organisation,roles" name="properties">
+    <input type="text" name="@pagesize" id="sp-pagesize" value="25" size="2">
+    <label for="sp-pagesize" i18n:translate="">Pagesize</label>
+  </td>
+</tr>
+
+   </table>
+
+</form>
+<pre tal:content="request" tal:condition=false />
+<script type="text/javascript"><!--
+  focus2id('username');
+//--></script>
+  </body>
+</html>

html/user.help.html

+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+<html tal:define="property request/form/property/value;
+qs request/env/QUERY_STRING;
+qs python:'&'.join([a for a in qs.split('&') if not a.startswith('@template=')]);
+form request/form/form/value;
+field request/form/property/value">
+  <head>
+      <link rel="stylesheet" type="text/css" href="@@file/style.css" />
+      <meta http-equiv="Content-Type"
+       tal:attributes="content string:text/html;; charset=${request/client/charset}" />
+      <tal:block tal:condition="python:request.form.has_key('property')">
+      <title><tal:x i18n:translate=""><tal:x i18n:name="property"
+       tal:content="property" i18n:translate="" /> help - <span i18n:name="tracker"
+	       tal:replace="config/TRACKER_NAME" /></tal:x></title>
+      <script language="Javascript" type="text/javascript"
+	      tal:condition=false
+          tal:content="structure string:<!--
+          // this is the name of the field in the original form that we're working on
+          form  = window.opener.document.${form};
+          field  = '${field}';
+          //-->">
+      </script>
+      <script src="@@file/help_controls.js"
+     tal:condition=false type="text/javascript"><!--
+      //--></script>
+      </tal:block>
+  </head>
+<frameset rows="123,*,62">
+  <frame src="#" tal:attributes="src string:?@template=help-search&${qs}" name="search">
+  <!-- for search results: help-list -->
+  <frame
+  tal:attributes="src string:?@template=help-empty&${qs}"
+  name="list">
+  <frame
+  tal:attributes="src string:?@template=help-submit&${qs}"
+  name="submit">
+  <!-- -->
+</frameset>
+<noframes>
+  <body>
+<p i18n:translate="">
+Your browser is not capable of using frames; you should be redirected immediately,
+or visit <a href="#" tal:attributes="href string:?${qs}&template=help-noframes"
+i18n:name="link">this link</a>.
+</p>
+</body>
+</noframes>
+
+</html>

html/user.item.html

 <!-- dollarId: user.item,v 1.7 2002/08/16 04:29:04 richard Exp dollar-->
-<tal:block metal:use-macro="templates/page/macros/icing">
+<tal:doc metal:use-macro="templates/page/macros/icing"
+define="edit_ok context/is_edit_ok"
+>
 <title metal:fill-slot="head_title">
-<tal:block condition="context/id" i18n:translate=""
+<tal:if condition="context/id" i18n:translate=""
  >User <span tal:replace="context/id" i18n:name="id"
  />: <span tal:replace="context/username" i18n:name="title"
  /> - <span tal:replace="config/TRACKER_NAME" i18n:name="tracker"
-/></tal:block>
-<tal:block condition="not:context/id" i18n:translate=""
+/></tal:if>
+<tal:if condition="not:context/id" i18n:translate=""
  >New User - <span tal:replace="config/TRACKER_NAME" i18n:name="tracker"
-/></tal:block>
+/></tal:if>
 </title>
-<tal:block metal:fill-slot="body_title">
- <span tal:condition="python: not (context.id or context.is_edit_ok())"
+<metal:slot fill-slot="more-javascript">
+<script metal:use-macro="templates/page/macros/user_utils"></script>
+<script type="text/javascript" src="@@file/help_controls.js"></script>
+</metal:slot>
+<tal:block metal:fill-slot="body_title"
+  define="edit_ok context/is_edit_ok">
+ <span tal:condition="python: not (context.id or edit_ok)"
   tal:omit-tag="python:1" i18n:translate="">New User</span>
- <span tal:condition="python: not context.id and context.is_edit_ok()"
+ <span tal:condition="python: not context.id and edit_ok"
   tal:omit-tag="python:1" i18n:translate="">New User Editing</span>
- <span tal:condition="python: context.id and not context.is_edit_ok()"
+ <span tal:condition="python: context.id and not edit_ok"
   tal:omit-tag="python:1" i18n:translate="">User<tal:x
   replace="context/id" i18n:name="id" /></span>
- <span tal:condition="python: context.id and context.is_edit_ok()"
+ <span tal:condition="python: context.id and edit_ok"
   tal:omit-tag="python:1" i18n:translate="">User<tal:x
   replace="context/id" i18n:name="id" /> Editing</span>
 </tal:block>
 
 <div tal:condition="context/is_view_ok">
 
-<form method="POST" onSubmit="return submit_once()"
+<form method="POST"
+      tal:define="required python:'username address'.split()"
       enctype="multipart/form-data"
-      tal:attributes="action context/designator">
-
-<table class="form">
- <tr>
-  <th i18n:translate="">Name</th>
-  <td tal:content="structure context/realname/field">realname</td>
+      tal:attributes="action context/designator;
+      onSubmit python:'return checkRequiredFields(\'%s\')'%'\', \''.join(required);
+      ">
+<table class="form" tal:define="
+  th_label templates/page/macros/th_label;
+  src_input templates/page/macros/user_src_input;
+  normal_input templates/page/macros/user_normal_input;
+  pw_input templates/page/macros/user_pw_input;
+  confirm_input templates/page/macros/user_confirm_input;
+  edit_ok context/is_edit_ok;
+  ">
+ <tr tal:define="name string:realname; label string:Name; value context/realname; edit_ok edit_ok">
+  <th metal:use-macro="th_label">Name</th>
+  <td><input name="realname" metal:use-macro="src_input"></td>
  </tr>
- <tr>
-  <th class="required" i18n:translate="">Login Name</th>
-  <td tal:content="structure context/username/field">username</td>
+ <tr tal:define="name string:username; label string:Login Name; value context/username">
+   <th metal:use-macro="th_label">Login Name</th>
+   <td><input metal:use-macro="src_input"></td>
  </tr>
- <tr tal:condition="context/is_edit_ok">
-  <th i18n:translate="">Login Password</th>
-  <td tal:content="structure context/password/field">password</td>
+ <tal:if condition="edit_ok">
+ <tr tal:define="name string:password; label string:Login Password">
+  <th metal:use-macro="th_label">Login Password</th>
+  <td><input metal:use-macro="pw_input" type="password"></td>
  </tr>
- <tr tal:condition="context/is_edit_ok">
-  <th i18n:translate="">Confirm Password</th>
-  <td tal:content="structure context/password/confirm">password</td>
+ <tr tal:define="name string:password; label string:Confirm Password">
+  <th metal:use-macro="th_label">Confirm Password</th>
+  <td><input metal:use-macro="confirm_input" type="password"></td>
  </tr>
- <tr tal:condition="python:request.user.hasPermission('Web Roles')">
-  <th i18n:translate="">Roles</th>
-  <td>
-   <input tal:condition="context/id"
-          tal:replace="structure context/roles/field">
-   <input name="roles" tal:condition="not:context/id"
-          tal:attributes="value db/config/NEW_WEB_USER_ROLES">
+ </tal:if>
+ <tal:if condition="python:request.user.hasPermission('Web Roles')">
+ <tr tal:define="name string:roles; label string:Roles;">
+  <th><label for="roles" i18n:translate="">Roles</label></th>
+  <td tal:define="gips context/id">
+    <tal:subif condition=gips define="value context/roles">
+      <input metal:use-macro="normal_input">
+    </tal:subif>
+    <tal:subif condition="not:gips" define="value db/config/NEW_WEB_USER_ROLES">
+      <input metal:use-macro="normal_input">
+    </tal:subif>
    <tal:block i18n:translate="">(to give the user more than one role,
     enter a comma,separated,list)</tal:block>
   </td>
  </tr>
- <tr>
-  <th i18n:translate="">Phone</th>
-  <td tal:content="structure context/phone/field">phone</td>
+ </tal:if>
+
+ <tr tal:define="name string:phone; label string:Phone; value context/phone">
+  <th metal:use-macro="th_label">Phone</th>
+  <td><input name="phone" metal:use-macro="normal_input"></td>
  </tr>
- <tr>
-  <th i18n:translate="">Organisation</th>
-  <td tal:content="structure context/organisation/field">organisation</td>
+
+ <tr tal:define="name string:organisation; label string:Organisation; value context/organisation">
+  <th metal:use-macro="th_label">Organisation</th>
+  <td><input name="organisation" metal:use-macro="normal_input"></td>
  </tr>
- <tr>
-  <th i18n:translate="">Timezone</th>
-  <td>
-   <input tal:replace="structure context/timezone/field">
-   <tal:block i18n:translate="">(this is a numeric hour offset, the default is
+
+ <tr tal:condition="python:edit_ok or context.timezone"
+     tal:define="name string:timezone; label string:Timezone; value context/timezone">
+  <th metal:use-macro="th_label">Timezone</th>
+  <td><input name="timezone" metal:use-macro="normal_input">
+   <tal:block tal:condition="edit_ok" i18n:translate="">(this is a numeric hour offset, the default is
     <span tal:replace="db/config/DEFAULT_TIMEZONE" i18n:name="zone"
     />)</tal:block>
   </td>
  </tr>
- <tr>
-  <th class="required" i18n:translate="">E-mail address</th>
-  <td tal:define="mailto context/address/field">
-   <a tal:condition="not:context/is_edit_ok"
-    tal:attributes="href string:mailto:${mailto}" tal:content="mailto"
-   /><span tal:condition="context/is_edit_ok" tal:replace="structure mailto" />
+
+ <tr tal:define="name string:address; label string:E-mail address; value context/address">
+  <th metal:use-macro="th_label">E-mail address</th>
+  <td tal:define="mailto python:context.address.field(id='address');
+	  mklink python:mailto and not edit_ok">
+      <a href="mailto:calvin@the-z.org"
+		  tal:attributes="href string:mailto:$value"
+		  tal:content="value"
+          tal:condition="python:mklink">calvin@the-z.org</a>
+      <tal:if condition=edit_ok>
+      <input metal:use-macro="src_input" value="calvin@the-z.org">
+      </tal:if>
+      &nbsp;
   </td>
  </tr>
+
  <tr>
-  <th i18n:translate="">Alternate E-mail addresses<br>One address per line</th>
-  <td tal:content="structure context/alternate_addresses/multiline">alternate_addresses</td>
+  <th><label for="alternate_addresses" i18n:translate="">Alternate E-mail addresses<br>One address per line</label></th>
+  <td>
+    <textarea rows=5 cols=40 tal:replace="structure context/alternate_addresses/multiline">nobody@nowhere.org
+anybody@everywhere.net
+(alternate_addresses)
+    </textarea>
+  </td>
  </tr>
 
- <tr tal:condition="context/is_edit_ok">
+ <tr tal:condition="edit_ok">
   <td>
    &nbsp;
    <input type="hidden" name="@template" value="item">
-   <input type="hidden" name="@required" value="username,address">
+   <input type="hidden" name="@required" value="username,address"
+          tal:attributes="value python:','.join(required)">
   </td>
-  <td tal:content="structure context/submit">submit button here</td>
+  <td><input type="submit" value="save" tal:replace="structure context/submit"><!--submit button here-->
+    <input type=reset>
+  </td>
  </tr>
 </table>
 </form>
 
 </td>
 
-</tal:block>
+</tal:doc>

html/user_utils.js

+// User Editing Utilities
+
+/**
+ * for new users:
+ * Depending on the input field which calls it, takes the value
+ * and dispatches it to certain other input fields:
+ *
+ * address
+ *  +-> username
+ *  |    `-> realname
+ *  `-> organisation
+ */
+function split_name(that) {
+    var raw = that.value
+    var val = trim(raw)
+    if (val == '') {
+        return
+    }
+    var username=''
+    var realname=''
+    var address=''
+    switch (that.name) {
+        case 'address':
+            address=val
+            break
+        case 'username':
+            username=val
+            break
+        case 'realname':
+            realname=val
+            break
+        default:
+            alert('Ooops - unknown name field '+that.name+'!')
+            return
+    }
+    var the_form = that.form;
+
+    function field_empty(name) {
+        return the_form[name].value == ''
+    }
+    
+    // no break statements - on purpose!
+    switch (that.name) {
+        case 'address':
+            var split1 = address.split('@')
+            if (field_empty('username')) {
+                username = split1[0]
+                the_form.username.value = username
+            }
+            if (field_empty('organisation')) {
+                the_form.organisation.value = default_organisation(split1[1])
+            }
+        case 'username':
+            if (field_empty('realname')) {
+                realname = Cap(username.split('.').join(' '))
+                the_form.realname.value = realname
+            }
+        case 'realname':
+            if (field_empty('username')) {
+                username = Cap(realname.replace(' ', '.'))
+                the_form.username.value = username
+            }
+            if (the_form.firstname && the_form.lastname) {
+                var split2 = realname.split(' ')
+                var firstname='', lastname=''
+                firstname = split2[0]
+                lastname = split2.slice(1).join(' ')
+                if (field_empty('firstname')) {
+                    the_form.firstname.value = firstname
+                }
+                if (field_empty('lastname')) {
+                    the_form.lastname.value = lastname
+                }
+            }
+    }
+}
+
+function SubCap(str) {
+    switch (str) {
+        case 'de': case 'do': case 'da':
+        case 'du': case 'von':
+            return str;
+    }
+    if (str.toLowerCase().slice(0,2) == 'mc') {
+        return 'Mc'+str.slice(2,3).toUpperCase()+str.slice(3).toLowerCase()
+    }
+    return str.slice(0,1).toUpperCase()+str.slice(1).toLowerCase()
+}
+
+function Cap(str) {
+    var liz = str.split(' ')
+    for (var i=0; i<liz.length; i++) {
+        liz[i] = SubCap(liz[i])
+    }
+    return liz.join(' ')
+}
+
+/**
+ * Takes a domain name (behind the @ part of an email address)
+ * Customise this to handle the mail domains you're interested in 
+ */
+function default_organisation(orga) {
+    switch (orga.toLowerCase()) {
+        case 'gmx':
+        case 'yahoo':
+            return ''
+        default:
+            return orga
+    }
+}
+
Tip: Filter by directory path e.g. /media app.js to search for public/media/app.js.
Tip: Use camelCasing e.g. ProjME to search for ProjectModifiedEvent.java.
Tip: Filter by extension type e.g. /repo .js to search for all .js files in the /repo directory.
Tip: Separate your search with spaces e.g. /ssh pom.xml to search for src/ssh/pom.xml.
Tip: Use ↑ and ↓ arrow keys to navigate and return to view the file.
Tip: You can also navigate files with Ctrl+j (next) and Ctrl+k (previous) and view the file with Ctrl+o.
Tip: You can also navigate files with Alt+j (next) and Alt+k (previous) and view the file with Alt+o.