Mercurial > p > roundup > code
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> <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 -->
