From f7a762a5c6096faaa99da1fb849403bf6ba419ae Mon Sep 17 00:00:00 2001 From: Jim Allman Date: Wed, 18 Mar 2015 16:13:20 -0400 Subject: [PATCH 001/310] Fix broken display of single OTU mapping result. Use the more standard `A` (vs. `STRONG`) element for a single OTU mapping result. This gives us the correct color from Bootstrap's CSS, which expects any matching element to have an `href` attribute. Fixes #619. --- curator/views/study/edit.html | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/curator/views/study/edit.html b/curator/views/study/edit.html index 46282bbc0..a50a8d0be 100644 --- a/curator/views/study/edit.html +++ b/curator/views/study/edit.html @@ -1234,11 +1234,12 @@

Add a file to this study

- PROPOSED LABEL + href="#" + onclick="return false;" + >PROPOSED LABEL {{ # show available choices for mapping this OTU }} From 76a5bb45aa83112bb7087f861e425657df324790 Mon Sep 17 00:00:00 2001 From: Jim Allman Date: Tue, 19 May 2015 16:13:25 -0400 Subject: [PATCH 002/310] Show error message supporting study fails to load. --- webapp/static/js/treeview.js | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/webapp/static/js/treeview.js b/webapp/static/js/treeview.js index 35a64ee81..b7d9ccdb7 100644 --- a/webapp/static/js/treeview.js +++ b/webapp/static/js/treeview.js @@ -1180,6 +1180,7 @@ function showObjectProperties( objInfo, options ) { var supportingStudyInfo = { }; // don't repeat studies under 'Supported by', but gather trees for each *then* generate output // if we're still waiting on fetched study info, add a message to the properties window var waitingForStudyInfo = false; + var studiesWithErrors = []; dValues = String(aSection.displayedProperties[dLabel]).split(','); for (i = 0; i < dValues.length; i++) { @@ -1197,11 +1198,11 @@ function showObjectProperties( objInfo, options ) { moreInfo = metaMap[ rawVal ]; } + // adapt to various forms of meta-map key + metaMapValues = parseMetaMapKey( rawVal ); if (typeof moreInfo === 'object' && 'sourceDetails' in moreInfo) { // Study details, fetched via AJAX as needed - // adapt to various forms of meta-map key - metaMapValues = parseMetaMapKey( rawVal ); if (!(metaMapValues.studyID in supportingStudyInfo)) { // add this study now, plus an empty trees collection supportingStudyInfo[ metaMapValues.studyID ] = $.extend({ supportingTrees: {} }, moreInfo.sourceDetails); @@ -1216,10 +1217,12 @@ function showObjectProperties( objInfo, options ) { console.error(" "+ p2 +" = "+ moreInfo[p2]); } waitingForStudyInfo = true; + studiesWithErrors.push( metaMapValues.studyID ); } else { // when in doubt, just show the raw value console.error("! Expecting to find moreInfo and a study (dLabel="+ dLabel +", rawVal="+ rawVal +")"); waitingForStudyInfo = true; + studiesWithErrors.push( metaMapValues.studyID ); } } // Now that we've gathered all trees for all studies, show organized results by study, with taxonomy LAST if found @@ -1228,7 +1231,11 @@ function showObjectProperties( objInfo, options ) { $details.find('dt.loading-supporting-studies').remove(); if (waitingForStudyInfo) { - $details.append('
Loading supporting studies...
'); + if (studiesWithErrors) { + $details.append('
ERROR loading supporting study: '+ studiesWithErrors.join(',') +'
'); + } else { + $details.append('
Loading supporting studies...
'); + } } else { // we have all the details, try to show supporting studies for (studyID in supportingStudyInfo) { From b743d03c7acdf6a11a6e22f9d8e4beca0066b7e4 Mon Sep 17 00:00:00 2001 From: Karen Cranston Date: Fri, 7 Aug 2015 15:04:52 -0400 Subject: [PATCH 003/310] start of modifications to comment form --- webapp/controllers/plugin_localcomments.py | 131 +++++++++++---------- 1 file changed, 66 insertions(+), 65 deletions(-) diff --git a/webapp/controllers/plugin_localcomments.py b/webapp/controllers/plugin_localcomments.py index da612b2d7..c70f2182f 100644 --- a/webapp/controllers/plugin_localcomments.py +++ b/webapp/controllers/plugin_localcomments.py @@ -19,7 +19,7 @@ def SUL(*a,**b): return UL(*[u for u in a if u],**b) script=SCRIPT(""" var action = null; var formhtml = null; -function delete_all_forms() { +function delete_all_forms() { jQuery('div.plugin_localcomments div.reply').each(function() { if ($(this).closest('.issue').length === 0) { // this is the space for new topics, with a separate link @@ -31,8 +31,8 @@ def SUL(*a,**b): return UL(*[u for u in a if u],**b) $(this).find('a.reply').unbind('click').click(function() { $formHolder = $(this).parent(); delete_all_forms(); - $formHolder.html(formhtml); - capture_form(); + $formHolder.html(formhtml); + capture_form(); return false; }); }); @@ -69,22 +69,22 @@ def SUL(*a,**b): return UL(*[u for u in a if u],**b) var $form = jQuery(this).closest('form'); // validate form fields - var $visitorNameField = $form.find('input[name="visitor_name"]'); + var $visitorNameField = $form.find('input[name="visitor_name"]'); if ($visitorNameField.is(':visible') && ($.trim($visitorNameField.val()) === '')) { alert("Please enter your name (and preferably an email address) so we can stay in touch."); return false; } - var $fbTypeField = $form.find('select[name="feedback_type"]'); + var $fbTypeField = $form.find('select[name="feedback_type"]'); if ($fbTypeField.is(':visible') && ($.trim($fbTypeField.val()) === '')) { alert("Please choose a feedback type for this topic."); return false; } - var $titleField = $form.find('input[name="issue_title"]'); + var $titleField = $form.find('input[name="issue_title"]'); if ($titleField.is(':visible') && ($.trim($titleField.val()) === '')) { alert("Please give this topic a title."); return false; } - var $bodyField = $form.find('textarea[name="body"]'); + var $bodyField = $form.find('textarea[name="body"]'); if ($.trim($bodyField.val()) === '') { alert("Please enter some text for this "+ (isThreadStarter ? 'issue' : 'comment') +"."); return false; @@ -94,7 +94,7 @@ def SUL(*a,**b): return UL(*[u for u in a if u],**b) { ////$'thread_parent_id': form.find('input[name="thread_parent_id"]').val(), 'issue_or_comment': (isThreadStarter ? 'issue' : 'comment'), - 'thread_parent_id': threadParentID, ///$form.parent().prev().attr('id').split('r')[1], + 'thread_parent_id': threadParentID, ///$form.parent().prev().attr('id').split('r')[1], 'synthtree_id': $form.find('input[name="synthtree_id"]').val(), 'synthtree_node_id': $form.find('input[name="synthtree_node_id"]').val(), 'sourcetree_id': $form.find('input[name="sourcetree_id"]').val(), @@ -109,8 +109,8 @@ def SUL(*a,**b): return UL(*[u for u in a if u],**b) 'visitor_name': $form.find('input[name="visitor_name"]').val(), 'visitor_email': $form.find('input[name="visitor_email"]').val() }, - function(data,r){ - if(data) { + function(data,r){ + if(data) { var $refreshArea; if (isThreadStarter) { $refreshArea = $form.parent().nextAll('ul'); @@ -120,10 +120,10 @@ def SUL(*a,**b): return UL(*[u for u in a if u],**b) $refreshArea = $form.parent().prevAll('ul'); // add the new comment (LI) to the end of the list $refreshArea.append(data); - } + } $form.find('textarea[name="body"]').val(''); //$form.find('input[name="thread_parent_id"]').val('0'); - plugin_localcomments_init(); + plugin_localcomments_init(); delete_all_forms(); } }, @@ -166,7 +166,7 @@ def SUL(*a,**b): return UL(*[u for u in a if u],**b) }); } jQuery(document).ready(function() { - action = jQuery('div.plugin_localcomments form').attr('action'); + action = jQuery('div.plugin_localcomments form').attr('action'); formhtml = jQuery('div.plugin_localcomments form').parent().html(); delete_all_forms(); // important! creates .reply buttons before init() below plugin_localcomments_init(); @@ -182,7 +182,7 @@ def sqlform(): # comments = db().select(db.plugin_localcomments_comment.ALL, orderby=~db.plugin_localcomments_comment.created_on) # =~ is DESCENDING ORDER form = SQLFORM(db.plugin_localcomments_comment) return dict(form=form) - + def show_type_icon(type): iconClass = "icon-comment" if type == 'Error in phylogeny': @@ -218,18 +218,18 @@ def grid(): deletable=False, # we'll flip the hidden flag, but not truly delete..? orderby=~db.plugin_localcomments_comment.created_on, - fields=[ - #db.plugin_localcomments_comment.id, - db.plugin_localcomments_comment.feedback_type, - db.plugin_localcomments_comment.body, - db.plugin_localcomments_comment.url, - db.plugin_localcomments_comment.ottol_id, - db.plugin_localcomments_comment.synthtree_id, + fields=[ + #db.plugin_localcomments_comment.id, + db.plugin_localcomments_comment.feedback_type, + db.plugin_localcomments_comment.body, + db.plugin_localcomments_comment.url, + db.plugin_localcomments_comment.ottol_id, + db.plugin_localcomments_comment.synthtree_id, db.plugin_localcomments_comment.synthtree_node_id, db.plugin_localcomments_comment.created_on, db.plugin_localcomments_comment.intended_scope, ], - headers = { + headers = { # NOTE the funky key format used here 'plugin_localcomments_comment.feedback_type' : 'Type', } @@ -242,12 +242,12 @@ def grid(): ) return locals() - + def smartgrid(): # comments = db().select(db.plugin_localcomments_comment.ALL, orderby=~db.plugin_localcomments_comment.created_on) # =~ is DESCENDING ORDER grid = SQLFORM.smartgrid(db.plugin_localcomments_comment) return locals() - + def index(): # this is a tricky function that does simple display, handles POSTed comments, moderation, etc. @@ -265,7 +265,7 @@ def index(): filter = request.vars['filter'] # if anonymous user submitted identifying information, remember it - visitor_name = request.vars['visitor_name'] + visitor_name = request.vars['visitor_name'] if visitor_name: session['visitor_name'] = visitor_name visitor_email = request.vars['visitor_email'] @@ -334,7 +334,7 @@ def node(comment): current_user_id = auth.user and auth.user.github_login or None # Cook up some reasonably strong regular expressions to detect bare - # URLs and wrap them in hyperlinks. Adapted from + # URLs and wrap them in hyperlinks. Adapted from # http://stackoverflow.com/questions/1071191/detect-urls-in-a-string-and-wrap-with-a-href-tag link_regex = re.compile( r''' (?x)( # verbose identify URLs within text @@ -415,9 +415,9 @@ def node(comment): # build useful links for some footer fields if auth.user: author_link = '[{0}]({1})'.format(auth.user.name, auth.user.github_url) - elif visitor_name and visitor_email: + elif visitor_name and visitor_email: author_link = '[{0}](mailto:{1})'.format(visitor_name, visitor_email) - elif visitor_name: + elif visitor_name: # no email provided author_link = visitor_name elif visitor_email: @@ -441,7 +441,7 @@ def node(comment): # expand hidden link url_link = '[{0}]({1}{2})'.format(url, request.get('env').get('http_origin'), url) - # add full metadata for an issue + # add full metadata for an issue footer = build_comment_metadata_footer(metadata={ "Author": author_link, "Upvotes": 0, @@ -478,14 +478,14 @@ def node(comment): "body": "{0}\n{1}".format(msg_body, footer) } new_msg = add_or_update_comment(msg_data, parent_issue_id=thread_parent_id) - return node(new_msg) + return node(new_msg) # retrieve related comments, based on the chosen filter print("retrieving local comments using this filter:") print(filter) if filter == 'synthtree_id,synthtree_node_id': comments = get_local_comments({ - "Synthetic tree id": synthtree_id, + "Synthetic tree id": synthtree_id, "Synthetic tree node id": synthtree_node_id}) elif filter == 'sourcetree_id': comments = get_local_comments({"Source tree id(s)": sourcetree_id}) @@ -494,7 +494,7 @@ def node(comment): else: # fall back to url comments = get_local_comments({"URL": url}) - #pprint(comments) + #pprint(comments) for comment in comments: #thread[comment.thread_parent_id] = thread.get(comment.thread_parent_id,[])+[comment] @@ -506,25 +506,26 @@ def node(comment): '' if auth.user_id else T(' or '), '' if auth.user_id else INPUT(_type='text',_id='visitor_name',_name='visitor_name',_value=session.get('visitor_name',''),_placeholder="Enter your name"), '' if auth.user_id else T(' '), - '' if auth.user_id else INPUT(_type='text',_id='visitor_email',_name='visitor_email',_value=session.get('visitor_email',''),_placeholder="Your email (visible on GitHub)"), + '' if auth.user_id else INPUT(_type='text',_id='visitor_email',_name='visitor_email',_value=session.get('visitor_email',''),_placeholder="Your email (will be public)"), '' if auth.user_id else BR(), SELECT( OPTION('What kind of feedback is this?', _value=''), OPTION('General comment', _value='General comment'), - OPTION('Reporting an error in phylogeny', _value='Error in phylogeny'), + OPTION('Feedback on tree of life', _value='Error in phylogeny'), OPTION('Bug report (website behavior)', _value='Bug report'), OPTION('New feature request', _value='Feature request'), _name='feedback_type',value='',_style='width: auto;'), T(' '), SELECT( + OPTION('suggest tree for incorporation', _value='Re: suggest tree'), OPTION('about node placement in the synthetic tree', _value='Re: synthetic tree'), OPTION('about node placement in the source tree', _value='Re: source tree'), OPTION('about taxon data in OTT', _value='Re: OTT taxon'), OPTION('general feedback (none of the above)', _value=''), _name='intended_scope',value='',_style='width: auto;'), LABEL(INPUT(_type='checkbox',_name=T('claimed_expertise')), T(' I claim expertise in this area'),_style='float: right;',_class='expertise-option'), - INPUT(_type='text',_id='issue_title',_name='issue_title',_value='',_placeholder="Give this topic a title"), # should appear for proper issues only - TEXTAREA(_name='body',_placeholder="Add more to this topic, using Markdown (click 'Markdown help' below to learn more)."), + INPUT(_type='text',_id='issue_title',_name='issue_title',_value='',_placeholder="Short title"), # should appear for proper issues only + TEXTAREA(_name='body',_placeholder="Add more detail; optionally using Markdown for formatting (click 'Markdown help' below to learn more)."), INPUT(_type='hidden',_name='synthtree_id',_value=synthtree_id), INPUT(_type='hidden',_name='synthtree_node_id',_value=synthtree_node_id), INPUT(_type='hidden',_name='sourcetree_id',_value=sourcetree_id), @@ -536,7 +537,7 @@ def node(comment): SPAN(' | ',_style='margin-right: 6px'), A(T('Markdown help'),_href='https://help.github.com/articles/markdown-basics', _target='_blank',_style='margin-right: 10px'), - INPUT(_type='submit',_value=T('Post'),_class='btn btn-small',_style=''), + INPUT(_type='submit',_value=T('Post'),_class='btn btn-small',_style=''), _class='msg-footer'), _method='post',_action=URL(r=request,args=[])),_class='reply'), SUL(*[node(comment) for comment in threads]),_class='plugin_localcomments') @@ -572,14 +573,14 @@ def add_or_update_issue(msg_data, issue_id=None): if issue_id: # edit an existing issue via the GitHub API url = '{0}/repos/OpenTreeOfLife/feedback/issues/{1}'.format(GH_BASE_URL) - resp = requests.patch( url, + resp = requests.patch( url, headers=GH_POST_HEADERS, data=json.dumps(msg_data) ) else: # create a new issue url = '{0}/repos/OpenTreeOfLife/feedback/issues'.format(GH_BASE_URL) - resp = requests.post( url, + resp = requests.post( url, headers=GH_POST_HEADERS, data=json.dumps(msg_data) ) @@ -598,7 +599,7 @@ def add_or_update_comment(msg_data, comment_id=None, parent_issue_id=None ): url = '{0}/repos/OpenTreeOfLife/feedback/issues/comments/{1}'.format(GH_BASE_URL, comment_id) print('URL for editing an existing comment:') print(url) - resp = requests.patch( url, + resp = requests.patch( url, headers=GH_POST_HEADERS, data=json.dumps(msg_data) ) @@ -607,7 +608,7 @@ def add_or_update_comment(msg_data, comment_id=None, parent_issue_id=None ): url = '{0}/repos/OpenTreeOfLife/feedback/issues/{1}/comments'.format(GH_BASE_URL, parent_issue_id) print('URL for adding a new comment:') print(url) - resp = requests.post( url, + resp = requests.post( url, headers=GH_POST_HEADERS, data=json.dumps(msg_data) ) @@ -624,7 +625,7 @@ def close_issue(issue_id): url = '{0}/repos/OpenTreeOfLife/feedback/issues/{1}'.format(GH_BASE_URL, issue_id) print('URL for closing an existing issue:') print(url) - resp = requests.patch( url, + resp = requests.patch( url, headers=GH_POST_HEADERS, data=json.dumps({"state":"closed"}) ) @@ -641,7 +642,7 @@ def delete_comment(comment_id): url = '{0}/repos/OpenTreeOfLife/feedback/issues/comments/{1}'.format(GH_BASE_URL, comment_id) print('URL for deleting an existing comment:') print(url) - resp = requests.delete( url, + resp = requests.delete( url, headers=GH_GET_HEADERS ) ##pprint(resp) @@ -675,16 +676,16 @@ def clear_matching_cache_keys(key_pattern): pprint("===") pprint(" %d items removed" % (item_count_before - item_count_after,)) -@cache(key=build_localcomments_key(request), - time_expire=60*5, +@cache(key=build_localcomments_key(request), + time_expire=60*5, cache_model=cache.ram) def get_local_comments(location={}): - # Use the Search API to get all comments for this location. + # Use the Search API to get all comments for this location. # See https://developer.github.com/v3/search/#search-issues # build and encode search text (location "filter") print('>> its cache key would be:') print(build_localcomments_key(request)) - search_text = '' + search_text = '' for k,v in location.items(): search_text = '{0}"{1} | {2} " '.format( search_text, k, v ) search_text = urllib.quote_plus(search_text.encode('utf-8'), safe='~') @@ -711,7 +712,7 @@ def get_local_comments(location={}): def clear_local_comments(): # Examine the JSON payload (now in request.vars) to see if we can clear - # only the affected localcomments. If not, play it safe and clear all + # only the affected localcomments. If not, play it safe and clear all # comments in the cache. trigger_action = request.vars.get('action', None) print(">>> clear_local_comments(): trigger_action is [%s]" % trigger_action) @@ -732,7 +733,7 @@ def clear_local_comments(): # the side of clobbering, since reloading is no big deal, but we definitely # don't want to show stale comments from the cache. if metadata['URL']: - # Extract a root-relative URL from markdown strings like + # Extract a root-relative URL from markdown strings like # "[devtree.opentreeoflife.org/opentree/argus/otol.draft.22@132](http://devtree.opentreeoflife.org/opentree/argus/otol.draft.22@132)" markdown_url = metadata['URL'] print('markdown_url:') @@ -761,7 +762,7 @@ def clear_local_comments(): clear_matching_cache_keys("^localcomments:") -# Build and parse metadata for comments (stored as markdown in GitHub). +# Build and parse metadata for comments (stored as markdown in GitHub). # The full footer is used for a thread starter (GitHub issue), while replies # (appended GitHub comments) use an abbreviated version. @@ -769,31 +770,31 @@ def clear_local_comments(): ================================================ Metadata | Do not edit below this line :------------|:---------- -Author | %(Author)s -Upvotes | %(Upvotes)d -URL | %(URL)s -Target node label | %(Target node label)s -Synthetic tree id | %(Synthetic tree id)s -Synthetic tree node id | %(Synthetic tree node id)s -Source tree id(s) | %(Source tree id)s -Open Tree Taxonomy id | %(Open Tree Taxonomy id)s -Intended scope | %(Intended scope)s +Author | %(Author)s +Upvotes | %(Upvotes)d +URL | %(URL)s +Target node label | %(Target node label)s +Synthetic tree id | %(Synthetic tree id)s +Synthetic tree node id | %(Synthetic tree node id)s +Source tree id(s) | %(Source tree id)s +Open Tree Taxonomy id | %(Open Tree Taxonomy id)s +Intended scope | %(Intended scope)s """ reply_footer = """ ================================================ Metadata | Do not edit below this line :------------|:---------- -Author | %(Author)s -Upvotes | %(Upvotes)s +Author | %(Author)s +Upvotes | %(Upvotes)s """ # TODO: Restore the expertise flag to both footers? -# Claimed Expertise | %(Claimed Expertise)s +# Claimed Expertise | %(Claimed Expertise)s # TODO: Move 'Feedback type' from labels to footer? -# Feedback type | %(Feedback type)s +# Feedback type | %(Feedback type)s def build_comment_metadata_footer(metadata={}): - # build full footer (for starter) or abbreviated (for replies), + # build full footer (for starter) or abbreviated (for replies), # and return the string if 'Intended scope' in metadata: # it's a thread starter (a proper GitHub issue) @@ -833,7 +834,7 @@ def get_visible_comment_body(comment_body): break visible_lines.append(line) return '\n'.join(visible_lines) - + # Time-zone converstion from UTC to local time (needed for GitHub date-strings), # adapted from code found here: http://stackoverflow.com/a/13287083 import calendar From bc6332b8ad9fe61963856a0761f3787d658a12d2 Mon Sep 17 00:00:00 2001 From: Jim Allman Date: Wed, 12 Aug 2015 16:43:10 -0400 Subject: [PATCH 004/310] Start refactoring to simpler feedback types. This includes specific widgets for e.g. suggesting a tree. We'll need to add validation logic for these. --- webapp/controllers/plugin_localcomments.py | 77 ++++++++++++++----- webapp/models/plugin_localcomments.py | 15 ++-- .../plugin_localcomments/moderation.html | 35 +++++---- 3 files changed, 84 insertions(+), 43 deletions(-) diff --git a/webapp/controllers/plugin_localcomments.py b/webapp/controllers/plugin_localcomments.py index c70f2182f..90c5474ef 100644 --- a/webapp/controllers/plugin_localcomments.py +++ b/webapp/controllers/plugin_localcomments.py @@ -54,17 +54,49 @@ def SUL(*a,**b): return UL(*[u for u in a if u],**b) if (isThreadStarter) { //jQuery('div.plugin_localcomments select[name=feedback_type] option:eq(1)').text( 'General comment' ); jQuery('div.plugin_localcomments select[name=feedback_type]').show(); - jQuery('div.plugin_localcomments select[name=intended_scope]').show(); + //jQuery('div.plugin_localcomments select[name=intended_scope]').show(); jQuery('div.plugin_localcomments select[name=issue_title]').show(); + + // hide/show form widgets based on the chosen feedback type + console.log("SETTING UP SELECT"); + console.log("exists? "+ jQuery('div.plugin_localcomments select[name=feedback_type]').length); + jQuery('div.plugin_localcomments select[name=feedback_type]') + .unbind('change') + .change(function(){ + console.log("SELECT CHANGED"); + var $select = $(this); + switch($select.val()) { + case '': + // hide all UI until they choose a feedback type + jQuery('div.plugin_localcomments [name=doi]').hide(); + jQuery('div.plugin_localcomments [name=tree_id]').hide(); + jQuery('div.plugin_localcomments [name=body]').hide(); + break; + case 'Tree suggestion': + // show fields for paper and tree + jQuery('div.plugin_localcomments [name=doi]').show(); + jQuery('div.plugin_localcomments [name=tree_id]').show(); + jQuery('div.plugin_localcomments [name=body]').show(); + break; + default: + jQuery('div.plugin_localcomments [name=doi]').hide(); + jQuery('div.plugin_localcomments [name=tree_id]').hide(); + jQuery('div.plugin_localcomments [name=body]').show(); + } + }); + jQuery('div.plugin_localcomments select[name=feedback_type]').change(); + } else { - //jQuery('div.plugin_localcomments select[name=feedback_type] option:eq(1)').text( 'Reply or general comment' ); + // jQuery('div.plugin_localcomments select[name=feedback_type] option:eq(1)').text( 'Reply or general comment' ); jQuery('div.plugin_localcomments select[name=feedback_type]').hide(); - jQuery('div.plugin_localcomments select[name=intended_scope]').hide(); + // jQuery('div.plugin_localcomments select[name=intended_scope]').hide(); jQuery('div.plugin_localcomments select[name=issue_title]').hide(); } // always hide expertise checkbox and surrounding label (not currently needed) jQuery('div.plugin_localcomments label.expertise-option').hide(); + + jQuery('div.plugin_localcomments :submit').unbind('click').click(function(){ var $form = jQuery(this).closest('form'); @@ -104,7 +136,7 @@ def SUL(*a,**b): return UL(*[u for u in a if u],**b) 'title': $form.find('input[name="issue_title"]').val(), 'body': $form.find('textarea[name="body"]').val(), 'feedback_type': $form.find('select[name="feedback_type"]').val(), - 'intended_scope': $form.find('select[name="intended_scope"]').val(), + // 'intended_scope': $form.find('select[name="intended_scope"]').val(), 'claimed_expertise': $form.find(':checkbox[name="claimed_expertise"]').is(':checked'), 'visitor_name': $form.find('input[name="visitor_name"]').val(), 'visitor_email': $form.find('input[name="visitor_email"]').val() @@ -134,6 +166,7 @@ def SUL(*a,**b): return UL(*[u for u in a if u],**b) } function plugin_localcomments_init() { + // bind client-side widgets to get desired behavior jQuery('div.plugin_localcomments .toggle').unbind('click').click(function(){ var $toggle = $(this); var $parentIssue = $toggle.closest('li.issue'); @@ -146,6 +179,7 @@ def SUL(*a,**b): return UL(*[u for u in a if u],**b) } return false; }); + jQuery('div.plugin_localcomments .delete').unbind('click').click(function(){ delete_all_forms(); var $commentDiv = jQuery(this).closest('.msg-wrapper'); @@ -197,8 +231,8 @@ def show_type_icon(type): @auth.requires_membership(role='editor') def grid(): - db.plugin_localcomments_comment.intended_scope.readable = True - db.plugin_localcomments_comment.intended_scope.represent = lambda scope, row: scope and scope.capitalize() or XML(T('—')) + #db.plugin_localcomments_comment.intended_scope.readable = True + #db.plugin_localcomments_comment.intended_scope.represent = lambda scope, row: scope and scope.capitalize() or XML(T('—')) db.plugin_localcomments_comment.feedback_type.represent = lambda row, value: show_type_icon(value) grid = SQLFORM.grid( db.plugin_localcomments_comment, @@ -227,7 +261,7 @@ def grid(): db.plugin_localcomments_comment.synthtree_id, db.plugin_localcomments_comment.synthtree_node_id, db.plugin_localcomments_comment.created_on, - db.plugin_localcomments_comment.intended_scope, + #db.plugin_localcomments_comment.intended_scope, ], headers = { # NOTE the funky key format used here @@ -276,7 +310,7 @@ def index(): thread_parent_id = request.vars['thread_parent_id'] # can be None comment_id = request.vars['comment_id'] # used for some operations (eg, delete) feedback_type = request.vars['feedback_type'] # used for new comments - intended_scope = request.vars['intended_scope'] # used for new comments + #intended_scope = request.vars['intended_scope'] # used for new comments issue_title = request.vars['title'] # used for new issues (threads) claims_expertise = request.vars['claimed_expertise'] # used for new comments threads = [ ] @@ -451,7 +485,7 @@ def node(comment): "Synthetic tree node id": synthtree_node_id, "Source tree id": sourcetree_id, "Open Tree Taxonomy id": ottol_id, - "Intended scope": intended_scope + #"Intended scope": intended_scope }) msg_data = { "title": issue_title, @@ -508,23 +542,26 @@ def node(comment): '' if auth.user_id else T(' '), '' if auth.user_id else INPUT(_type='text',_id='visitor_email',_name='visitor_email',_value=session.get('visitor_email',''),_placeholder="Your email (will be public)"), '' if auth.user_id else BR(), - SELECT( + SELECT( #this option lets us know what labels to use in the GitHub issue tracker + # labels get created if they do not already exist OPTION('What kind of feedback is this?', _value=''), - OPTION('General comment', _value='General comment'), - OPTION('Feedback on tree of life', _value='Error in phylogeny'), - OPTION('Bug report (website behavior)', _value='Bug report'), + OPTION('Feedback on tree of life', _value='Synthesis feedback'), + OPTION('Suggest tree for incorporation', _value='Tree suggestion'), OPTION('New feature request', _value='Feature request'), + OPTION('Bug report (website behavior)', _value='Bug report'), + OPTION('General comment', _value='General comment'), _name='feedback_type',value='',_style='width: auto;'), T(' '), - SELECT( - OPTION('suggest tree for incorporation', _value='Re: suggest tree'), - OPTION('about node placement in the synthetic tree', _value='Re: synthetic tree'), - OPTION('about node placement in the source tree', _value='Re: source tree'), - OPTION('about taxon data in OTT', _value='Re: OTT taxon'), - OPTION('general feedback (none of the above)', _value=''), - _name='intended_scope',value='',_style='width: auto;'), + #SELECT( + #OPTION('about node placement in the synthetic tree', _value='Re: synthetic tree'), + #OPTION('about node placement in the source tree', _value='Re: source tree'), + #OPTION('about taxon data in OTT', _value='Re: OTT taxon'), + #OPTION('general feedback (none of the above)', _value=''), + #_name='intended_scope',value='',_style='width: auto;'), LABEL(INPUT(_type='checkbox',_name=T('claimed_expertise')), T(' I claim expertise in this area'),_style='float: right;',_class='expertise-option'), INPUT(_type='text',_id='issue_title',_name='issue_title',_value='',_placeholder="Short title"), # should appear for proper issues only + INPUT(_type='text',_id='doi',_name='doi',_value='',_placeholder="DOI of the paper containing the tree"), # should appear for tree suggestions only + INPUT(_type='text',_id='tree_id',_name='tree_id',_value='',_placeholder="Which tree from this paper (e.g. Figure 3)"), # should appear for proper issues only TEXTAREA(_name='body',_placeholder="Add more detail; optionally using Markdown for formatting (click 'Markdown help' below to learn more)."), INPUT(_type='hidden',_name='synthtree_id',_value=synthtree_id), INPUT(_type='hidden',_name='synthtree_node_id',_value=synthtree_node_id), diff --git a/webapp/models/plugin_localcomments.py b/webapp/models/plugin_localcomments.py index 5cd053861..95d0f90aa 100644 --- a/webapp/models/plugin_localcomments.py +++ b/webapp/models/plugin_localcomments.py @@ -19,7 +19,8 @@ Field('sourcetree_node_id',readable=True,writable=False), Field('ottol_id',readable=True,writable=False), # first comment in thread can suggest scope (re: synthetic tree placement? OTT taxon?) - Field('intended_scope','text'), + # NOTE that we're disabling this, but will leave this field for now + #Field('intended_scope','text'), # fallback 'url' for other web pages (vs tree-views) Field('url',readable=True,writable=False), # allow for threaded/nested comments? @@ -45,10 +46,10 @@ def plugin_localcomments(filter='synthtree_id,synthtree_node',url='',synthtree_id='',synthtree_node_id='',sourcetree_id='',ottol_id='',target_node_label='',parent_id=None): return LOAD('plugin_localcomments',vars=dict( filter=filter, # show messages matching on these fields - url=url, - synthtree_id=synthtree_id, - synthtree_node_id=synthtree_node_id, - sourcetree_id=sourcetree_id, - ottol_id=ottol_id, - target_node_label=target_node_label, + url=url, + synthtree_id=synthtree_id, + synthtree_node_id=synthtree_node_id, + sourcetree_id=sourcetree_id, + ottol_id=ottol_id, + target_node_label=target_node_label, thread_parent_id=parent_id)) diff --git a/webapp/views/plugin_localcomments/moderation.html b/webapp/views/plugin_localcomments/moderation.html index b6d8d94ea..13c5b1e63 100644 --- a/webapp/views/plugin_localcomments/moderation.html +++ b/webapp/views/plugin_localcomments/moderation.html @@ -1,8 +1,11 @@ {{extend 'layout.html'}} +{{ +# Unused code, originally intended to provide a web-based UI for moderating comments +}} -{{ """ - A friendly "time since" formatter, public domain from - http://flask.pocoo.org/snippets/33/ +{{ """ + A friendly "time since" formatter, public domain from + http://flask.pocoo.org/snippets/33/ """ from datetime import datetime @@ -14,7 +17,7 @@ now = datetime.utcnow() diff = now - dt - + periods = ( (diff.days / 365, "year", "years"), (diff.days / 30, "month", "months"), @@ -33,7 +36,7 @@ return default }} -{{ +{{ # Import machinery to render markdown in comment body from gluon.contrib.markdown.markdown2 import markdown }} @@ -42,7 +45,7 @@ show full info -->

Feedback Moderation

- +
{{= form.custom.begin }} @@ -118,7 +121,7 @@

Feedback Moderation

- 0 comments selected + 0 comments selected   @@ -137,7 +140,7 @@

Feedback Moderation

@@ -171,7 +174,7 @@

Feedback Moderation

{{ import itertools - row_class = itertools.cycle(['even', 'odd']) + row_class = itertools.cycle(['even', 'odd']) for row_class, comment in itertools.izip(row_class, comments): }} {{""" other interesting icons: @@ -179,16 +182,16 @@

Feedback Moderation

icon-fire icon-flag icon-ban-circle - icon-thumbs-down - icon-thumbs-up + icon-thumbs-down + icon-thumbs-up icon-heart - icon-gift + icon-gift """}} Feedback Moderation 'Re: synthetic tree' : 'SYN', 'Re: source tree' : 'SRC', 'Re: OTT taxon' : 'OTT', - 'DEFAULT' : 'GEN', + 'DEFAULT' : 'GEN', } }} {{= scopeValues[comment.intended_scope or "DEFAULT"] }} @@ -233,7 +236,7 @@

Feedback Moderation

- + {{pass}} - {{response.files.append(URL('static','js/argus/raphael-min.js'))}} {{response.files.append(URL('static','js/argus/drawtree.js'))}} @@ -119,6 +109,225 @@ {{include 'web2py_ajax.html'}} + + {{ # using sidebars need to know what sidebar you want to use From 5f77a2ebb9803433037e592090c0df2425493524 Mon Sep 17 00:00:00 2001 From: Karen Cranston Date: Fri, 14 Aug 2015 14:24:15 -0400 Subject: [PATCH 007/310] download newick link now points to download synthetic tree page --- webapp/static/js/treeview.js | 120 ++++++++++++++++++----------------- 1 file changed, 63 insertions(+), 57 deletions(-) diff --git a/webapp/static/js/treeview.js b/webapp/static/js/treeview.js index b7d9ccdb7..d73bbc2b8 100644 --- a/webapp/static/js/treeview.js +++ b/webapp/static/js/treeview.js @@ -1,5 +1,5 @@ -/* -@licstart The following is the entire license notice for the JavaScript code in this page. +/* +@licstart The following is the entire license notice for the JavaScript code in this page. Copyright (c) 2013, Cody Hinchliff Copyright (c) 2013, Joseph W. Brown @@ -33,7 +33,7 @@ */ /* - * Subscribe to history changes (adapted from History.js boilerplate) + * Subscribe to history changes (adapted from History.js boilerplate) */ var History = window.History; // Note: capital H refers to History.js! @@ -99,7 +99,7 @@ function updateTreeView( State ) { +' is flagged as incertae sedis.' +'

If you think this is an error, please' +' create an issue in our bug tracker.'; - // TODO: Explain in more detail: Why wasn't this used? + // TODO: Explain in more detail: Why wasn't this used? showErrorInArgusViewer( errMsg ); } else { errMsg = "Something went wrong on the server. Please wait a moment and reload this page."; @@ -114,7 +114,7 @@ function updateTreeView( State ) { argus.displayNode({"nodeID": ottolID, "domSource": State.data.domSource}); } - + // we'll finish updating the page in a callback from argusObj.loadData() // update all login links to use the new URL @@ -128,7 +128,7 @@ if ( History && History.enabled && pageUsesHistory ) { var State = History.getState(); // Note: We are using History.getState() instead of event.state History.log(State.data, State.title, State.url); updateTreeView( State ); - }); + }); } function getCommentIndexURL( rawURL ) { @@ -145,7 +145,7 @@ function currentScreenSize() { // layout and behavior, and this function makes it easy to stay in sync // with the CSS. // - // ASSUMES that we have these three indicators in the current DOM! + // ASSUMES that we have these three indicators in the current DOM! if ($('#screen-size-indicator .visible-phone').is(':visible')) { return 'PHONE'; } else if ($('#screen-size-indicator .visible-tablet').is(':visible')) { @@ -238,10 +238,10 @@ function loadLocalComments( chosenFilter ) { $(document).ready(function() { // set default starting node and view, if the URL doesn't specify - // NOTE that we override this (using $.extend) with values set in the + // NOTE that we override this (using $.extend) with values set in the // main page template, so we can build it from incoming URL in web2py. var initialState = $.extend({ - viewer: 'argus', + viewer: 'argus', domSource: syntheticTreeID, // from main HTML view nodeID: syntheticTreeDefaultStartingNodeID, // from main HTML view nodeName: '', // names will be updated/corrected by argus callback @@ -266,7 +266,7 @@ $(document).ready(function() { if ( History && History.enabled && pageUsesHistory ) { // if there's no prior state, go to the initial target node in the synthetic tree var priorState = History.getState(); - + // Check first for incoming URL that might override prior history if (initialState.forcedByURL || !(priorState.data.nodeID)) { // apply the state as specified in the URL (or defaults, if prior history is incomplete) @@ -289,7 +289,7 @@ $(document).ready(function() { // add splitter between argus + provenance panel (using jquery.splitter plugin) var viewSplitter = $('#viewer-collection').split({ orientation:'vertical', - limit: 280, // don't come closer than this to edge + limit: 280, // don't come closer than this to edge position:'70%' // initial position }); @@ -328,7 +328,7 @@ $(document).ready(function() { var activeToggleFade = 0.5; var readyToggleFade = 1.0; var toggleFadeSpeed = 'fast'; -function toggleCommentsPanel( hideOrShow ) { +function toggleCommentsPanel( hideOrShow ) { // can be forced by passing hideOrShow ('HIDE'|'SHOW') if ($('#viewer-collection').hasClass('active-comments') && (hideOrShow !== 'SHOW')) { ///console.log('HIDING comments'); @@ -362,7 +362,7 @@ function toggleCommentsPanel( hideOrShow ) { toggleCommentsPanel('HIDE'); }); } - }, + }, 10 ); } @@ -402,7 +402,7 @@ function togglePropertiesPanel( hideOrShow ) { togglePropertiesPanel('HIDE'); }); } - }, + }, 10 ); } @@ -476,7 +476,7 @@ function searchForMatchingTaxa() { // is this unchanged from last time? no need to search again.. if ((searchText == showingResultsForSearchText) && (searchContextName == showingResultsForSearchContextName)) { ///console.log("Search text and context UNCHANGED!"); - return false; + return false; } // stash these to use for later comparison (to avoid redundant searches) @@ -488,12 +488,12 @@ function searchForMatchingTaxa() { $('#search-results').html('
  • Search in progress...
  • '); $('#search-results').dropdown('toggle'); snapViewerFrameToMainTitle(); - + $.ajax({ url: doTNRSForAutocomplete_url, // NOTE that actual server-side method name might be quite different! type: 'POST', dataType: 'json', - data: JSON.stringify({ + data: JSON.stringify({ "queryString": searchText, "contextName": searchContextName }), // data (asterisk required for completion suggestions) @@ -549,7 +549,7 @@ function searchForMatchingTaxa() { visibleResults++; } } - + $('#search-results a') .click(function(e) { // suppress normal dropdown logic and jump to link normally (TODO: Why is this needed?) @@ -559,7 +559,7 @@ function searchForMatchingTaxa() { var $link = $(this); //// WAS constructed literal ('/opentree/'+ "ottol" +'@'+ itsNodeID +'/'+ itsName) var safeURL = historyStateToURL({ - nodeID: $link.attr('href'), + nodeID: $link.attr('href'), domSource: 'ottol', nodeName: $link.html(), viewer: 'argus' @@ -594,7 +594,7 @@ function jumpToExactMatch() { } function fixLoginLinks() { - // update all login links to return directly to the current URL (NOTE that this + // update all login links to return directly to the current URL (NOTE that this // doesn't seem to work for Logout) var currentURL; try { @@ -694,7 +694,7 @@ function buildNodeNameFromTreeData( node ) { + (moreThanTwoDescendants ? ' + ...' : '') // hint at additional descendants + compoundNodeNameSuffix); }; - + // recursively populate any missing (implied) node names (called immediately after argus loads treeData) function buildAllMissingNodeNames( node ) { if (!node.name) { @@ -747,7 +747,7 @@ function URLToHistoryState( url ) { urlState[key] = value; } } - + // fail if critical information is missing if (typeof(urlState.nodeID) === 'undefined') { return null; @@ -780,7 +780,7 @@ function showObjectProperties( objInfo, options ) { // OR pass a reliable identifier? var objType = ''; // 'node' | 'edge' | ? var objName = ''; // eg, 'Chordata' - var objID = null; + var objID = null; var objSource = null; // eg, 'ottol' (a domSource) var displayID = null; // eg, 'ottol@2345' or 'otol.draft.22@4' @@ -823,19 +823,19 @@ function showObjectProperties( objInfo, options ) { case 'node': case 'edge': /* Try to spell out any available properties / provenance, based on - * type. Note that we're going to conflate node and edge properties, + * type. Note that we're going to conflate node and edge properties, * since this is generally a 1:1 relationship, but: * - clicking an edge trigger will highlight edge properties * - if there are multiple edges, others will be dimmed */ var fullNode, parentNode, nodeSection, edgeSection; - + // try to fetch the node from treeData, using ID (preferred) or name - fullNode = argus.getArgusNodeByID( objID ); + fullNode = argus.getArgusNodeByID( objID ); if (!fullNode) { console.log("WARNING: can't find node by ID, trying to match its name..."); fullNode = getTreeDataNode( function(node) { - return (node.name === objName); + return (node.name === objName); }); } if (fullNode && fullNode.parentNodeID) { @@ -884,8 +884,8 @@ function showObjectProperties( objInfo, options ) { /* show ALL taxonomic sources (taxonomies + IDs) for this node * TODO: Handle whatever schemes we use for multiple sources; for now, * they look like one of the following (in order of preference): - - EXAMPLE w/ multiple sources (new format): + + EXAMPLE w/ multiple sources (new format): fullNode.taxSourceArray: [ { "foreignID": "2", "taxSource": "ncbi" }, { "foreignID": "3", "taxSource": "gbif" } @@ -931,7 +931,7 @@ function showObjectProperties( objInfo, options ) { } ); } - + // show taxonomic rank separate from source taxonomies (we don't know from whence it came) if (typeof fullNode.taxRank !== 'undefined') { nodeSection.displayedProperties['Taxonomic rank'] = fullNode.taxRank; @@ -1078,7 +1078,7 @@ function showObjectProperties( objInfo, options ) { function(data) { // JSONP callback if (data.result && (data.result.length > 0) && data.result[0].icon && data.result[0].icon.uid) { $('#provenance-panel .provenance-title').after( - '' // 'thumb.png' = 64px, 'icon.png' = 32px and blue ); $('#provenance-panel .taxon-image').unbind('click').click(function() { @@ -1090,7 +1090,7 @@ function showObjectProperties( objInfo, options ) { } } - var sectionPos, sectionCount = orderedSections.length, + var sectionPos, sectionCount = orderedSections.length, aSection, dLabel, dValues, i, rawVal, displayVal = '', moreInfo; for (sectionPos = 0; sectionPos < sectionCount; sectionPos++) { var aSection = orderedSections[sectionPos]; @@ -1153,7 +1153,7 @@ function showObjectProperties( objInfo, options ) { + 'title="Schäferhoff et al. 2010" target="_blank">Schäferhoff et al. 2010: '+ sourceInfo.taxSourceId +''; break; - case 'OTT': + case 'OTT': /* browse the OTT taxonomy in *local* window? or in a new one? displayVal = 'OTT: '+ sourceInfo.taxSourceId +''; @@ -1202,12 +1202,12 @@ function showObjectProperties( objInfo, options ) { metaMapValues = parseMetaMapKey( rawVal ); if (typeof moreInfo === 'object' && 'sourceDetails' in moreInfo) { // Study details, fetched via AJAX as needed - + if (!(metaMapValues.studyID in supportingStudyInfo)) { // add this study now, plus an empty trees collection supportingStudyInfo[ metaMapValues.studyID ] = $.extend({ supportingTrees: {} }, moreInfo.sourceDetails); } - // add the current tree + // add the current tree supportingStudyInfo[ metaMapValues.studyID ].supportingTrees[ metaMapValues.treeID ] = {}; // TODO: add more info in data objects, e.g., a descriptive tree label @@ -1238,9 +1238,9 @@ function showObjectProperties( objInfo, options ) { } } else { // we have all the details, try to show supporting studies - for (studyID in supportingStudyInfo) { + for (studyID in supportingStudyInfo) { console.warn(">>> adding study info for "+ studyID +"..."); - var studyInfo = supportingStudyInfo[ studyID ]; + var studyInfo = supportingStudyInfo[ studyID ]; var pRef, pCompactYear, pCompactPrimaryAuthor, pCompactRef, pDOITestParts, pURL, pID, pCurator; // assemble and display study info pRef = studyInfo['ot:studyPublicationReference']; @@ -1250,7 +1250,7 @@ function showObjectProperties( objInfo, options ) { displayVal = '
    '+ pRef +'
    '; /* compact ref logic, if needed later - pCompactYear = pRef.match(/(\d{4})/)[0]; + pCompactYear = pRef.match(/(\d{4})/)[0]; // capture the first valid year pCompactPrimaryAuthor = pRef.split(pCompactYear)[0].split(',')[0]; // split on the year to get authors (before), and capture the first surname @@ -1264,7 +1264,7 @@ function showObjectProperties( objInfo, options ) { if (pURL) { displayVal += 'Full publication: '+ pURL +'
    '; } - + pID = studyInfo['ot:studyId']; if (pID) { /* Phylografter link @@ -1305,7 +1305,7 @@ function showObjectProperties( objInfo, options ) { return false; }); break; - + default: // general approach, just show the raw value displayVal = aSection.displayedProperties[dLabel]; @@ -1326,9 +1326,15 @@ function showObjectProperties( objInfo, options ) { var subtreeDepthLimit = 4; if (nodeSection) { $details = $sections.find('.properties-section:first dl'); + // temporarily changing link to download Newick string to go to + // download page because current code uses v1 api call + // without checks on size and therefore hanging for large trees + $details.append('
    Download entire synthetic tree
    '); + + /* $details.append('
    Download subtree as Newick string
    '); $details.append('
     
    '); - + // we can fetch a subtree using an ottol id (if available) or Neo4j node ID var idType = (objSource == 'ottol') ? 'ottol-id' : 'node-id'; // Choose from among the collection of objSources @@ -1339,7 +1345,7 @@ function showObjectProperties( objInfo, options ) { var superSafeDisplayName = makeSafeForWeb2pyURL(displayName); window.location = '/opentree/default/download_subtree/'+ idType +'/'+ objID +'/'+ subtreeDepthLimit +'/'+ superSafeDisplayName; - /* OR this will load the Newick-tree text to show it in-browser + OR this will load the Newick-tree text to show it in-browser $.ajax({ type: 'POST', url: getDraftTreeForOttolID_url, @@ -1352,20 +1358,20 @@ function showObjectProperties( objInfo, options ) { }, dataType: 'json' // should return a complete Newick tree }); - */ + return false; }); $('#extract-subtree-caveats').html('(depth limited to '+ subtreeDepthLimit +' levels)'); - + */ // for proper taxon names (not nodes like '[Canis + Felis]'), link to EOL if ((displayName.indexOf('Unnamed ') !== 0) && (displayName.indexOf('[') !== 0)) { // Attempt to find a page for this taxon in the Encyclopedia of Life website // N.B. This 'external-links' list can hold similar entries. - + // Make this name safe for use in our EOL search URL // (prefer '+' to '%20', but carefully encode other characters) - var urlSafeDisplayName = encodeURIComponent(displayName).replace(/%20/g,'+'); + var urlSafeDisplayName = encodeURIComponent(displayName).replace(/%20/g,'+'); $details.after(''); } } @@ -1374,9 +1380,9 @@ function showObjectProperties( objInfo, options ) { function getTreeDataNode( filterFunc, testNode ) { // helper method to retrieve a matching node from n-level treeData (tree-view JSON) - if (!testNode) { + if (!testNode) { // start at top-most node in tree, if not specified - testNode = argus.treeData; + testNode = argus.treeData; } // test the target node against our requirements (eg, a particular node ID) if (filterFunc(testNode)) { @@ -1447,10 +1453,10 @@ function nodeDataLoaded( nodeTree ) { // nudge static viewer to show second line, if any snapViewerFrameToMainTitle(); - + // now that we have all view data, update the comments and comment editor loadLocalComments(); - + // update properties (provenance) panel to show the target node // NOTE that we won't show it automatically if we're on a narrow screen showObjectProperties( targetNode, (currentScreenSize() === 'PHONE' ? 'HIDDEN' : null) ); @@ -1478,11 +1484,11 @@ if (false) { // replaceState modifies the current history entry (no extra junk in Back/Fwd navigation) History.replaceState({state:3}, "State 3", "?state=3"); // logs {state:3}, "State 3", "?state=3" - // NOTE that the state object can include anything, eg, + // NOTE that the state object can include anything, eg, newState = { - viewer:'argus', - domSource:'ottol', - nodeID:'12321', + viewer:'argus', + domSource:'ottol', + nodeID:'12321', viewport:'24,201,0,800' } History.pushState(newState, "Something changed!", "?state=4"); // logs {}, '', "?state=4" @@ -1492,12 +1498,12 @@ if (false) { History.back(); // logs {state:3}, "State 3", "?state=3" History.back(); // logs {state:1}, "State 1", "?state=1" - History.forward(); + History.forward(); History.go(2); // this is *relative* to the current index (position) in history! ie, .go(-1) is the same at back() } function showErrorInArgusViewer( msg, details ) { - var errorHTML; + var errorHTML; if (!details) { errorHTML = '

    '+ msg +'

    '; } else { @@ -1521,7 +1527,7 @@ function parseMetaMapKey( key ) { treeID: null, commitSHA: null }; - + var keyParts = key.split('_'); // N.B. Study ID might have a prefix! if (isNaN( parseInt( keyParts[0] ) )) { From d18449d159188cbf5efaeffddfd2c8716803f778 Mon Sep 17 00:00:00 2001 From: Karen Cranston Date: Fri, 14 Aug 2015 14:42:12 -0400 Subject: [PATCH 008/310] make download page open in new window --- webapp/static/js/treeview.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/webapp/static/js/treeview.js b/webapp/static/js/treeview.js index d73bbc2b8..868db4778 100644 --- a/webapp/static/js/treeview.js +++ b/webapp/static/js/treeview.js @@ -1329,7 +1329,7 @@ function showObjectProperties( objInfo, options ) { // temporarily changing link to download Newick string to go to // download page because current code uses v1 api call // without checks on size and therefore hanging for large trees - $details.append('
    Download entire synthetic tree
    '); + $details.append('
    Download entire synthetic tree
    '); /* $details.append('
    Download subtree as Newick string
    '); From 1c316332f77a2903527a991386c4ee825f3199b0 Mon Sep 17 00:00:00 2001 From: Jim Allman Date: Thu, 3 Sep 2015 12:35:07 -0400 Subject: [PATCH 009/310] Remove --- curator/views/study/edit.html | 3 --- 1 file changed, 3 deletions(-) diff --git a/curator/views/study/edit.html b/curator/views/study/edit.html index 22a0ad446..4031cc290 100644 --- a/curator/views/study/edit.html +++ b/curator/views/study/edit.html @@ -2611,9 +2611,6 @@

    - - - OpenTree draft tree synthesis From 50391ddb228407d4f0bde1ba2886289468f65042 Mon Sep 17 00:00:00 2001 From: "Mark T. Holder" Date: Sat, 19 Sep 2015 11:48:00 -0500 Subject: [PATCH 010/310] redirect top to a static page --- webapp/controllers/default.py | 2 + webapp/static/statictoppage.html | 1358 ++++++++++++++++++++++++++++++ 2 files changed, 1360 insertions(+) create mode 100644 webapp/static/statictoppage.html diff --git a/webapp/controllers/default.py b/webapp/controllers/default.py index 6b749d8c2..accd2c23d 100644 --- a/webapp/controllers/default.py +++ b/webapp/controllers/default.py @@ -13,6 +13,8 @@ def call(): return service() ### end requires def index(): + if len(request.args) == 0: + redirect('./static/statictoppage.html') # interpret incoming URL as a tree view, in this format # http://[hostname]/opentree/[{viewer}/]{domSource}@{nodeID}/{taxon name} # some valid examples: diff --git a/webapp/static/statictoppage.html b/webapp/static/statictoppage.html new file mode 100644 index 000000000..2ed7f4fcc --- /dev/null +++ b/webapp/static/statictoppage.html @@ -0,0 +1,1358 @@ + + + + + + + opentree + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    +
    + + +
    + + + +
    + + + +
    + + +
    +
    +
    +

    +   +

    +

    Exploring the current synthetic tree

    + + + +
    +
    + + +
    + + + +
    +
    +
    +
    + × +
    +
    Loading…
    +
     
    +
    + +
    or
    +
    +
    +
    +
    + +
     
    + + +
    +
    +
    +
    + × +
    +
     
    +
     
    +
    +
    +
    +
    + +
    +
    + + + + + + + + + +
    + + +
    + +
    + + +
    +
    + +
    +
    + + + + + + From ca361d115c6d8f0dc4129fd9e5031199d78c5944 Mon Sep 17 00:00:00 2001 From: Jim Allman Date: Mon, 21 Sep 2015 13:20:50 -0400 Subject: [PATCH 011/310] Cleanup of statictoppage.html - Removed stale comments in HTML - Removed extra closing `` tag --- webapp/static/statictoppage.html | 423 +------------------------------ 1 file changed, 8 insertions(+), 415 deletions(-) diff --git a/webapp/static/statictoppage.html b/webapp/static/statictoppage.html index 2ed7f4fcc..320810617 100644 --- a/webapp/static/statictoppage.html +++ b/webapp/static/statictoppage.html @@ -721,424 +721,18 @@

     
    -
    or

    @@ -1175,7 +769,6 @@

    // treeview.js will examine this to determine our starting view -

    - Collection ID: + Collection ID:

    -   (on GitHub) @@ -552,7 +555,7 @@