view website/issues/html/issue.item.html @ 8044:f9eaaa63fda2

build: update website build to sync built files Sourceforge only supports python 2.7. Newer version of sphinx are required to build docs and they don't work with 2.7. Set up rsync targets that: 1) copy html build directory to sourceforge target directory (dev_docs, production and user home directory) 2) backup existing sourceforge target directory re-sync so it can be served without any missing files. The Makefile now check to see if .orig or *~ files are present in the html build tree. It lists the garbage file and fails if so. Also inserts a .htaccess into the tree to prevent access to: .buildinfo file docs_backup-* files *.orig *~ The first one is a build artifact from newer version of sphinx. The second is the backup directory created with all the original files before a rsync from the local system is done to sourceforge. The backup directory is timestamped with the time of its sync. The last two are probably redundant since make html will fail if they exist. To rollback a sync: move the target directory to a new name. move the backup directory (in the renamed target directory) to the old target directory name. I added the --delete flag to remove files missing from the html directory. Using the -no-times flags will create all new files with the current directory. Using the --backup, --backup-dir flags backs up all replaced/deleted files to backup-dir. The --exclude flag preserves the backup directories on the sourceforge side. Without --exclude the -delete flag would remove these backup-dir's. Note that --delete-exclude must not be used otherwise the backup directories will be deleted.
author John Rouillard <rouilj@ieee.org>
date Wed, 26 Jun 2024 19:11:35 -0400
parents 12c5ddf865b1
children 63390dcfcfe9
line wrap: on
line source

<tal:block metal:use-macro="templates/page/macros/icing">
<title metal:fill-slot="head_title">
<tal:block condition="context/id" i18n:translate=""
 >Issue <span tal:replace="context/id" i18n:name="id"
 />: <span tal:replace="context/title" i18n:name="title"
 /> - <span tal:replace="config/TRACKER_NAME" i18n:name="tracker"
/></tal:block>
<tal:block condition="not:context/id" i18n:translate=""
 >New Issue - <span tal:replace="config/TRACKER_NAME" i18n:name="tracker"
/></tal:block>
</title>
<tal:block metal:fill-slot="body_title">
 <span tal:condition="python: not (context.id or context.is_edit_ok())"
  tal:omit-tag="python:1" i18n:translate="">New Issue</span>
 <span tal:condition="python: not context.id and context.is_edit_ok()"
  tal:omit-tag="python:1" i18n:translate="">New Issue Editing</span>
 <span tal:condition="python: context.id and not context.is_edit_ok()"
  tal:omit-tag="python:1" i18n:translate="">Issue <tal:x
  replace="context/id" i18n:name="id" /></span>
 <span tal:condition="python: context.id and context.is_edit_ok()"
  tal:omit-tag="python:1" i18n:translate="">Issue<tal:x
  replace="context/id" i18n:name="id" /> Editing</span>
</tal:block>

<td class="content" metal:fill-slot="content">

<p tal:condition="python:not (context.is_view_ok()
 or request.user.hasRole('Anonymous'))" i18n:translate="">
 You are not allowed to view this page.</p>

<p tal:condition="python:not context.is_view_ok()
 and request.user.hasRole('Anonymous')" i18n:translate="">
 Please login with your username and password.</p>

<div tal:condition="context/is_view_ok">

<form method="POST" name="itemSynopsis"
      onSubmit="return submit_once()" enctype="multipart/form-data"
      tal:attributes="action context/designator">

<fieldset><legend>classification</legend>
<table class="form">
<tr>
 <th class="required"><label for="title" i18n:translate="">Title:</label></th>
 <td colspan="3" tal:condition="context/title/is_edit_ok"
     tal:content="structure python:context.title.field(id='title', size=60)">title</td>
 <td colspan="3" tal:condition="not:context/title/is_edit_ok">
  <span tal:content="structure context/title/plain"/>
  <input type="hidden" name="title" tal:attributes="value context/title">
 </td>
</tr>

<tr>
 <th class="required" i18n:translate="">
   <span tal:replace="structure python:db.issue_type.classhelp('id,name,description',label='Type')" />:
 </th>
 <td tal:content="structure context/type/menu">type</td>
 <th i18n:translate="">
   <span tal:replace="structure python:db.severity.classhelp('id,name,description',label='Severity')" />:
 </th>
 <td tal:content="structure context/severity/menu">components</td>
</tr>

<tr>
 <th i18n:translate="">
   <span tal:replace="structure python:db.component.classhelp('id,name,description',label='Components')" />:
 </th>
 <td tal:content="structure context/components/menu">components</td>
 <th i18n:translate="">
   <span tal:replace="structure python:db.version.classhelp('id,name,description',label='Versions')" />:
 </th>
 <td tal:content="structure context/versions/menu">versions</td>
</tr>
</table>
</fieldset>

<fieldset><legend>process</legend>
<table class="form">
<tr tal:condition="context/id">
 <th i18n:translate="">
   <span tal:replace="structure python:db.status.classhelp('id,name,description',label='Status')" />:
 </th>
 <td tal:content="structure context/status/menu">status</td>
 <th><label for="resolution" i18n:translate="">Resolution:</label></th>
 <td tal:content="structure python:context.resolution.menu(html_kwargs={'id':'resolution'})">resolution</td>
</tr>

<tr tal:condition="context/id">
 <th> <label for="dependencies" i18n:translate="">Dependencies</label>
  <span tal:condition="context/dependencies/is_edit_ok"
        tal:replace="structure python:db.issue.classhelp('id,title', filter='status=0,1', property='dependencies')" />
 </th>
 <td>
  <span tal:replace="structure python:context.dependencies.field(id='dependencies',showid=1,size=20)" />
  <span tal:condition="context/dependencies" tal:repeat="d python:context.dependencies.sorted('creation')">
   <br/>View: <a tal:attributes="href string:issue${d/id}" tal:content="d/id"></a>
  </span>
 </td>
 <th>
  <tal:block><label for="superseder" i18n:translate="">Superseder</label></tal:block>:
  <span tal:condition="context/superseder/is_edit_ok"
        tal:replace="structure python:db.issue.classhelp('id,title', filter='status=0,1', property='superseder')" />
 </th>
 <td>
  <span tal:replace="structure python:context.superseder.field(id='superseder', showid=1, size=20)" />
 <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> -->
   <br><span i18n:translate="">View</span>:
     <a tal:content="context/superseder/id"
        tal:attributes="href string:issue${context/superseder/id}; title context/superseder/title;"></a> 
  </span> 
 </td>
 </tr>
 <tr>
 <th><label for="assignee" i18n:translate="">Assigned To</label>:</th>
 <td tal:condition="context/status/is_edit_ok">
  <select id="assignee" name="assignee">
   <option value="-1">nobody</option>
   <tal:block tal:repeat="userdata python:db._db.user.filter_sql('select id,_username from _user where _roles like \'%Developer%\' order by _username')">
    <option tal:attributes="value python:userdata[0]; 
                            selected python:str(userdata[0]) == context.assignee._value"
            tal:content="python:userdata[1]"></option>
   </tal:block>
  </select>
 </td>
 <td tal:condition="not:context/assignee/is_edit_ok">
  <span tal:replace="structure context/assignee/plain" />
 </td>
 <th><label for="nosy" i18n:translate="">Nosy List</label>:
  <span tal:condition="context/nosy/is_edit_ok"
        tal:replace="structure python:db.user.classhelp('username,realname,address', property='nosy')" />
 </th>
 <td>
  <span tal:replace="structure python:context.nosy.field(id='nosy')" />
 </td>
</tr>
<tr>
 <th>
   <span tal:replace="structure python:db.priority.classhelp('id,name,description',label='Priority')" />:
 </th>
 <td tal:content="structure context/priority/menu">priority</td>
 <th><label for="keywords" i18n:translate="">Keywords</label>:</th>
 <td tal:content="structure python:context['keywords'].menu(height=5,html_kwargs={'id': 'keywords'})">keywords</td>


</tr>
<tr tal:condition="context/is_edit_ok">
 <th><label for="@note" i18n:translate="">Comment</label>:</th>
 <td colspan="3">
  <textarea tal:content="request/form/@note/value | default"
            id="@note" name="@note" wrap="hard" rows="10" cols="72"></textarea>
 </td>
</tr>

<tr tal:condition="context/is_edit_ok">
 <th><label for="file-1@content" i18n:translate="">File</label>:</th>
 <td>
   <input type="hidden" name="@link@files" value="file-1">
   <input type="file" id="file-1@content" name="file-1@content" size="40">
 </td>
  <th><label for="file-1@description" i18n:translate="">File Description</label>:</th>
 <td colspan=3><input type="text" class="fileDesc" id="file-1@description" name="file-1@description" size="40"></td>
</tr>
<tr tal:condition="context/is_edit_ok">
  <td colspan=4>
   <textarea readonly id="DropZone" aria-labeledby="DropZone">
     paste images or drag and drop files here....
   </textarea>
  </td>
</tr>
</table>
</fieldset>
<table class="form">
<tr tal:condition="context/is_edit_ok">
 <td>
  &nbsp;
  <input type="hidden" name="@template" value="item">
  <input type="hidden" name="@required" value="title">
 </td>
 <td colspan=3>
  <span tal:replace="structure context/submit">submit button</span>
  <a tal:condition="context/id" tal:attributes="href context/copy_url"
   i18n:translate="">Make a copy</a>
 </td>
</tr>
</table>
</form>

<!-- drag and drop code -->
              <script tal:attributes="nonce request/client/client_nonce">
                /* multiple file drops cause issues with redefined
                   file-X@content issues. input multiple assumes
                   it can start numbering from 1 for each of the
                   multiple files. However numbering here does the
                   same leading to duplicate file-2@content.

                   layout needs some work, alignnment of new file
                   input's isn't great.

                   Need a way to delete or reset file inputs so file
                   assigned to them isn't uploaded. Clicking on button
                   in chrome and then canceling unsets the file. But this
                   sequence does nothing in firefox.

                   Pasting always uses image.<type> can't name file.
                   Need to query user during paste for name/description.
                */

                let newInput=null;
                let NextInputNum = 100; /* file input 1 is hardcoded in form.
                     It is a multiple file input control. To
                     prevent collision, we start dynamic file controls at
                     file-100@content. 100 is larger than we expect
                     the number of files uploaded using file input 1.*/

                let target = document.getElementById('DropZone');
                target.style.display = "block";
                let body = document.body;
                let fileInput = document.getElementById('file-1@content');
		let fileDesc = document.getElementById('file-1@description');

		function make_clear_fileInput_closure(input) {
		  return function(ev) { input.value = "";
		  ev.preventDefault();}
		  }


                function make_new_clear_button() {
		  newClearInput=document.createElement('button');
		  newClearInput.textContent = "X";
		  newClearInput.setAttribute("aria-label",
		    "Clear next file input.");
		  newClearInput.setAttribute("title",
		    "Clear next file input.");
		  newClearInput.classList.add("clearButton");
		  return newClearInput;
		}

                function make_new_file_input() {
                  newInput=document.createElement('input');
                  newInput.type="file";
                  newInput.id="file-" + NextInputNum +"@content";
                  newInput.name=newInput.id;
                  return newInput;
		}

                function add_file_input () {
                 // Only allow one change listener on newest input.
                 fileInput.removeEventListener('change',
                          add_file_input,
                          false);
		  newClearInput = fileInput.insertAdjacentElement(
		                                   'beforebegin',
		                                   make_new_clear_button());

		  // add change handler to file clear button
                  newClearInput.addEventListener('click',
			make_clear_fileInput_closure(fileInput),
			false);

		  /* Insert break so next input is on new line */
		  br = fileInput.insertAdjacentElement('afterend',
					document.createElement('br'));

                  /* create new file input to get next dragged file */
                  /* <input type="file" name="file-2@content"> for 2,
                     3, 4, ... */
                  fileInput = br.insertAdjacentElement('afterend',
                                                    make_new_file_input());

                  // add change hander to newest file input
                  fileInput.addEventListener('change',
                        add_file_input, // create new input for more files
                        false);

                  /* link file-N to list of files on issue.
                     also link to msg-1 */
                  addLink=document.createElement('input');
                  addLink.type="hidden";
                  addLink.id="@link@file=file-" + NextInputNum;
                  addLink.name="@link@files"
                  addLink.value="file-" + NextInputNum;
                  fileInput.insertAdjacentElement('afterend', addLink);

                  addLink=document.createElement('input');
                  addLink.type="hidden";
                  addLink.id="msg-1@link@files=file-" + NextInputNum;
                  addLink.name="msg-1@link@files"
                  addLink.value="file-" + NextInputNum
                  fileInput.insertAdjacentElement('afterend', addLink);

		  /* break line before description field to prevent
		   wrapping multiple descriptions onto one line when
		   zoomed out or large display.*/
		  br = fileDesc.insertAdjacentElement('afterend',
				       document.createElement('br'));
                  fileDesc=document.createElement('input');
                  fileDesc.type="text";
                  fileDesc.id="file-" + NextInputNum + "@description";
                  fileDesc.name=fileDesc.id
                  fileDesc.size = 40
		  fileDesc.classList.add("fileDesc");
                  fileDesc=br.insertAdjacentElement('afterend', fileDesc);

                  NextInputNum = NextInputNum+1;
                }

                function MarkDropZone(e, active) {
                    active == true ? e.style.backgroundColor = "goldenrod" :
                    e.style.backgroundColor = "";
                }
                fileInput.addEventListener('change',
                         add_file_input, // create new input for more files
                         false);

                target.addEventListener('dragover', (e) => {
                    e.preventDefault();
                    body.classList.add('dragging');
                });

                target.addEventListener('dragenter', (e) => {
                    e.preventDefault();
                    MarkDropZone(target, true);
                });


                target.addEventListener('dragleave', (e) => {
                    e.preventDefault();
                    MarkDropZone(target, false);
                });

                target.addEventListener('dragleave', () => {
                    body.classList.remove('dragging');
                });

                target.addEventListener('drop', (e) => {
                    body.classList.remove('dragging');
                    MarkDropZone(target, false);

		    // Only allow single file drop unless
		    // fileInput name is @file that can support
		    // multiple file drop and file drop is multiple.
		    if (( fileInput.name != "@file" ||
			  ! fileInput.hasAttribute('multiple')) &&
			  e.dataTransfer.files.length != 1 ) {
			alert("File input can only accept one file.")
			e.preventDefault();
			return
		    }
                    // set file input files to the dragged files
                    fileInput.files = e.dataTransfer.files;

                    add_file_input(); // create new input for more files
                    // run last otherwise firefox empties e.dataTransfer
                    e.preventDefault();
                });

                target.addEventListener('mouseover', (e) => {
                    e.preventDefault();
                    MarkDropZone(target, true);
                });

                target.addEventListener('mouseout', (e) => {
                    e.preventDefault();
                    MarkDropZone(target, false);
                });

                target.addEventListener('paste', (event) => {
                    // https://mobiarch.wordpress.com/2013/09/25/upload-image-by-copy-and-paste/
                    // add paste event listener to the page

                    // https://stackoverflow.com/questions/50427513/
                    // html-paste-clipboard-image-to-file-input
                    if ( event.clipboardData.files.length == 0) {
                       // if not file data alert
                       alert("No image found for pasting");
                       event.preventDefault();
                       return;
                    }
                    fileInput.files = event.clipboardData.files;

                    /* Possible enhancement if file check fails.
                       iterate over all items 0 ...:
                           event.clipboardData.items.length
                       look at all items[i].kind for 'string' and
                       items[i].type looking for a text/plain item. If
                       found,
                         event.clipboardData.items[1].getAsString(
                           callback_fcn(s))

                       where callback function that creates a new
                       dataTransfer object with a file and insert the
                       content s and assigns it to the input.

                       https://gist.github.com/guest271314/7eac2c21911f5e40f489\33ac78e518bd
                    */
                    add_file_input(); // create new input for more files
                    // do not paste contents to dropzone
                    event.preventDefault();
                }, false);
              </script>
              <style tal:attributes="nonce request/client/client_nonce">
                #FileArea button.clearButton ~ input[type=file] {display:inline-block;}
                #DropZone {     /* don't display dropzone by default.
                                   Displayed as block by javascript. */
                             display:none;
	                     width: 100%;
                                /* override textarea inset */
                             border-style: dashed;
                             padding: 3ex 0; /* larger dropzone */
                             /* add space below inputs */
                             margin-block-start: 1em;
                                /* lighter color */
                             background: rgba(255,255,255,0.4);
                          }
		input[id$=\@content], input.fileDesc {margin-block-end: 0.5em}
              </style>

<p tal:condition="context/id" i18n:translate="">
 Created on <b><tal:x replace="python:context.creation.pretty('%Y-%m-%d %H:%M')" i18n:name="creation" /></b>
 by <b><tal:x replace="context/creator" i18n:name="creator" /></b>,
 last changed <b><tal:x replace="python:context.activity.pretty('%Y-%m-%d %H:%M')" i18n:name="activity" /></b>
 by <b><tal:x replace="context/actor" i18n:name="actor" /></b>.
</p>

<table class="files" tal:condition="context/files">
 <tr><th colspan="5" class="header" i18n:translate="">Files</th></tr>
 <tr>
  <th i18n:translate="">File name</th>
  <th i18n:translate="">Uploaded</th>
  <th i18n:translate="">Description</th>
  <th i18n:translate="">Edit</th>
  <th i18n:translate="">Remove</th>
 </tr>
 <tr tal:repeat="file python:context.files.sorted('creation')">
  <td>
   <a tal:attributes="href file/download_url"
      tal:content="file/name">dld link</a>
  </td>
  <td>
   <span tal:content="file/creator">creator's name</span>,
   <span tal:content="python:file.creation.pretty('%Y-%m-%d %H:%M')">creation date</span>
  </td>
  <td tal:content="file/description" />
  <td><a tal:condition="file/is_edit_ok"
          tal:attributes="href string:file${file/id}">edit</a>
  </td>
  <td>
   <form style="padding:0" method="POST" tal:condition="file/is_edit_ok"
         tal:attributes="action string:issue${context/id}">
    <input type="hidden" name="@remove@files" tal:attributes="value file/id">
    <input type="hidden" name="@action" value="edit">
    <input type="submit" value="remove" i18n:attributes="value">
   </form>
  </td>
 </tr>
</table>

<table class="messages" tal:condition="context/messages">
 <tr><th colspan="4" class="header" i18n:translate="">Messages</th></tr>
 <tal:block tal:repeat="msg context/messages">
  <tr>
   <th><a tal:attributes="href string:msg${msg/id}"
    i18n:translate="">msg<tal:x replace="msg/id" i18n:name="id" /></a></th>
   <th i18n:translate="">Author: <tal:x replace="python:msg.author.realname.plain()"
       i18n:name="author" /> (<tal:x replace="msg/author"/>)</th>
   <th i18n:translate="">Date: <tal:x replace="python:msg.date.pretty('%Y-%m-%d %H:%M')"
       i18n:name="date" /></th>
   <th>
    <form style="padding:0" method="POST" tal:condition="msg/is_edit_ok"
          tal:attributes="action string:issue${context/id}">
     <input type="hidden" name="@remove@messages" tal:attributes="value msg/id">
     <input type="hidden" name="@action" value="edit">
     <input type="submit" value="remove" i18n:attributes="value">
    </form>
   </th>
  </tr>
  <tr>
   <td colspan="4" class="content">
    <pre tal:condition="python:msg.content.is_view_ok()"
         tal:content="structure python:utils.localReplace(msg.content.hyperlinked())">content</pre>
   </td>
  </tr>
 </tal:block>
</table>

<tal:block tal:condition="context/id" tal:replace="structure context/history" />

</div>

</td>

</tal:block>
<!-- SHA: ad841842c0da5f9d1a7f69a1e0c847a549b75bf2 -->

Roundup Issue Tracker: http://roundup-tracker.org/