Skip to content

Commit

Permalink
Add accordion menus for vertical timeline events (#738)
Browse files Browse the repository at this point in the history
* Add file and developer linking

* Add accordion menu for vert timeline events (#683)

* Add 'notes' JSON field and developer route (#683)

* Add info icon for dev's nickname (#683)

* Fix "See Less" and "See More" buttons (#683)

* Linkify dev names; reposition info popup (#683)

* Change how commit hashes are handled (#683)

Also fixed the lines of code inserted and deleted not showing
up in the weekly_overall description.

Co-authored-by: Andy Meneely <andy@se.rit.edu>
  • Loading branch information
Xetnus and andymeneely committed Jun 5, 2020
1 parent 68aeef3 commit d119f4e
Show file tree
Hide file tree
Showing 13 changed files with 218 additions and 38 deletions.
85 changes: 72 additions & 13 deletions app/assets/javascripts/vulnerabilities/show.js
Original file line number Diff line number Diff line change
Expand Up @@ -114,32 +114,85 @@ function populateHTimeline(hTimeline, vulnEvents) {
}

function populateVTimeline(vulnEvents) {
function pluralize(count, text) {
return count > 1 ? text + "s" : text;
}

for (let e of vulnEvents) {
var notes = JSON.parse(JSON.parse(JSON.stringify(e.notes)));
var accordion = '';
if (notes) {
var template = '<ul class="accordion vertical menu" data-accordion-menu>' +
'<li><a href="#">:menu_name:</a><ul class="menu vertical nested">' +
':menu_items:</ul></li></ul>';

if (notes.hasOwnProperty('commits')) {
var commits = notes.commits;
var temp_acc = template.replace(':menu_name:', pluralize(Object.keys(commits).length, "Commit"));
commit_rows = []
Object.keys(commits).map(function(h) {
var short_hash = h.substring(0, 12);
if (commits[h] == "") {
commit_rows.push(`<li><a class="disabled">${short_hash} (Untracked)</a></li>`);
} else {
commit_rows.push(`<li><a href='/commits/${h}'>${short_hash} - ${commits[h]}</a></li>`);
}
});
temp_acc = temp_acc.replace(':menu_items:', commit_rows.join(''));
accordion += temp_acc;
}

if (notes.hasOwnProperty('files')) {
var files = notes.files;
var temp_acc = template.replace(':menu_name:',
pluralize(Object.keys(files).length, "Impacted File"));
// Some files aren't found in the DB. In that case, disable the link.
files = files.map(f => `<li><a ${f.slug == "" ? 'class="disabled"' :
`href="/filepaths/${f.slug}"`}>${f.file}</a></li>`);
temp_acc = temp_acc.replace(':menu_items:', files.join(''));
accordion += temp_acc;
}

if (notes.hasOwnProperty('developers')) {
var info = '<i class="vhp-icon-info"></i><span>To protect their ' +
'privacy, developers are given animal-related nicknames.</span>';
var devs = notes.developers;
var temp_acc = template.replace(':menu_name:', pluralize(devs.length, "Developer") + info);
devs = devs.map(d => d == null ? `<li><a class="disabled">Unidentified Developer</a></li>` :
`<li><a href='/developers/${d}'>${d}</a></li>`);
temp_acc = temp_acc.replace(':menu_items:', devs.join(''));
accordion += temp_acc;
}
}

block = $('#vtimeline-template').clone();
block.attr('data-id', e.id);
block.attr('data-type', e.event_type);
block.find('.vtimeline-anchor').attr('id',"event_" + e.id);
if (e.event_type == 'release') {
block.attr('id', 'release_block_' + e.id);
block.find('.vtimeline-anchor').attr('id', "release_" + e.id);
} else {
block.attr('id', 'event_block_' + e.id);
block.find('.vtimeline-anchor').attr('id', "event_" + e.id);
}
block.find('#title').html(e.title);
block.find('#description').html(e.description);
block.find('#description').addClass('shortened'); // applies fading
block.find('#isodate').html(e.date);
block.find('.title').html(e.title);
block.find('.description').html(e.description + accordion);
block.find('.description').addClass('shortened'); // applies fading
block.find('.isodate').html(e.date);
const event_date = moment(e.date,'YYYY-MM-DD hh:mm:ss Z')
.format('MMMM DD, YYYY');
block.find('#pretty_date').html(event_date);
block.find('#timeline-icon').html(e.style_icon);
block.find('.pretty_date').html(event_date);
block.find('.timeline-icon').html(e.style_icon);
block.find('.vtimeline-img').css('background-color', e.style_color);
$('#vtimeline').append(block);
block.show();

// Hide "See More" and "See Less" elements if the description
// is small enough or if there is a trivial amount of overflow
desc = block.find('#description');
// Hide "See More" and "See Less" elements if the description is small
// enough or if there is a trivial amount of overflow. Note that at this
// point in the code each description has already been shortened, so the
// scrollHeight and clientHeight properties below will reflect that.
desc = block.find('.description');
if (desc.prop('scrollHeight') - desc.prop('clientHeight') < 75) {
desc.removeClass('shortened');
desc.css('height', 'initial');
Expand All @@ -152,18 +205,24 @@ function populateVTimeline(vulnEvents) {
// Expand event description when clicked
$('.see_more').click(function() {
$(this).attr('hidden', 'true');
$(this).parent().find('#description').css('height', 'initial');
$(this).parent().find('#description').toggleClass('shortened');
$(this).parent().find('.description').css('height', 'initial');
$(this).parent().find('.description').toggleClass('shortened');
$(this).parent().find('.see_less').removeAttr('hidden');
});

// Shorten event description when clicked
$('.see_less').click(function() {
$(this).attr('hidden', 'true');
$(this).parent().find('#description').css('height', '');
$(this).parent().find('#description').toggleClass('shortened');
$(this).parent().find('.description').css('height', '');
$(this).parent().find('.description').toggleClass('shortened');
$(this).parent().find('.see_more').removeAttr('hidden');
});

// Create all of the accordion menus for the numerous links on the page
// e.g. commits and files
$('.accordion').each(function(i) {
var e = new Foundation.AccordionMenu($(this));
});
}

$(document).ready( function() {
Expand Down
63 changes: 62 additions & 1 deletion app/assets/stylesheets/common/vhp.scss
Original file line number Diff line number Diff line change
Expand Up @@ -1294,7 +1294,7 @@ Vertical Timeline components
display: table;
clear: both;
}
#description {
.description {
height: 200px; // overflow cutoff
overflow: hidden;
margin-bottom: 0;
Expand Down Expand Up @@ -1336,6 +1336,67 @@ Vertical Timeline components
border-right: 7px solid white;
}

.accordion {
min-height: 3em;

// Colors the background of the accordion headers
> li > a {
background: #e6e6e6;
}

li .vhp-icon-info {
cursor: help;
margin-left: 1em;
float: right;
visibility: hidden;

& + span {
background: #e6e6e6;
text-align: center;
font-weight: normal;
max-width: 50%;
font-size: 80%;
line-height: 15px;
margin: 0 auto;
padding: 3px;
position: absolute;
z-index: 1;
right: 3em;
top: 0;
display: none;
border: 1px solid $medium-gray;
}
}

// Fade in the information icon when the accordion as expanded
li[aria-expanded="true"] .vhp-icon-info {
visibility: visible;
animation: fadeIn 1s forwards;

@keyframes fadeIn {
from { opacity: 0; }
to { opacity: 1; }
}

&:hover + span {
display: inline;
}
}

a {
outline: none;
color: black;

&:hover {
font-weight: bold;
}
}

// Disable normal anchor behavior when it's disabled
.submenu a.disabled {
pointer-events: none;
}
}
}

.vtimeline-content p, .vtimeline-content .vtimeline, .vtimeline-content {
Expand Down
6 changes: 5 additions & 1 deletion app/controllers/developers_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,11 @@ def commits
private
# Use callbacks to share common setup or constraints between actions.
def set_developer
@developer = Developer.find(params[:id])
if params[:id].match(/^(\d)+$/).present?
@developer = Developer.find(params[:id])
else
@developer = Developer.find_by(nickname: params[:id].gsub('_', ' '))
end
end

# Never trust parameters from the scary internet, only allow the white list through.
Expand Down
1 change: 1 addition & 0 deletions app/controllers/vulnerabilities_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ def events
event_type: e.event_type,
style_color: e.style.color,
style_icon: e.style.icon,
notes: e.notes,
start_hidden: e.start_hidden,
}
end
Expand Down
10 changes: 5 additions & 5 deletions app/views/vulnerabilities/show.html.erb
Original file line number Diff line number Diff line change
Expand Up @@ -252,18 +252,18 @@ This DOM element is duplicated, populated, and then shown.
class="vtimeline-block">
<a id="" class="vtimeline-anchor"></a> <!-- Anchor to scroll to from -->
<div class="vtimeline-img" style=""> <!-- style_color -->
<p id="timeline-icon" class="material-icons md-32 md-light">
<p class="timeline-icon material-icons md-32 md-light">
<!-- style_icon -->
</p>
</div>
<div class="vtimeline-content">
<h2 id="title"><!-- title --></h2>
<p id="description"><!-- description --></p>
<h2 class="title"><!-- title --></h2>
<p class="description"><!-- description --></p>
<a class="see_more">See More</a>
<a class="see_less" hidden>See Less</a>
<span class="vtimeline-date">
<div id="isodate" style="display:none"><!--date--></div>
<p id="pretty_date"><!--date--></p>
<div class="isodate" style="display:none"><!--date--></div>
<p class="pretty_date"><!--date--></p>
</span>
</div>
</div>
Expand Down
1 change: 1 addition & 0 deletions db/schema.rb
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,7 @@
t.string "event_type", null: false, default: ''
t.string "date", null: false, default: ':date:'
t.integer "style_id", null: false
t.jsonb "notes", null: false, default: {}
t.boolean "start_hidden", null: false, default: false
end

Expand Down
1 change: 1 addition & 0 deletions lib/event_generators/bounty_events.rb
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ def generate
event_type: 'bounty',
date: vuln.notes['bounty']['date'],
description: markdown(Writing.embed_details(@desc, vuln)),
notes: "null",
start_hidden: false
)
vuln.events << e
Expand Down
1 change: 1 addition & 0 deletions lib/event_generators/fix_events.rb
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ def generate
date: fix.commit.date,
event_type: 'fix',
description: markdown(Writing.embed_details(@desc, fix)),
notes: "null",
start_hidden: false
)
events << e
Expand Down
15 changes: 9 additions & 6 deletions lib/event_generators/release_events.rb
Original file line number Diff line number Diff line change
Expand Up @@ -15,12 +15,15 @@ def initialize(project)

def generate
Release.all.each do |release|
release.notes["title"] = "Major Release: #{release.name}"
release.notes["description"] = markdown(Writing.embed_details(@desc, release))
release.notes["event_type"] = @style.name
release.notes["color"] = @style.color
release.notes["icon"] = @style.icon
release.save
events << Event.create!(
style: style,
title: ':title:',
event_type: 'release',
date: release.date_released,
description: Writing.event_article('release'),
notes: "null",
start_hidden: false
)
end
end
end
6 changes: 5 additions & 1 deletion lib/event_generators/similar_events.rb
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@ def cwe_events()
See other cases of [:tags: #{vuln['cwe']}](/tags/#{vuln['tag_shortname']}).
EOSTR
),
notes: "null",
start_hidden: false
)
events << e
Expand Down Expand Up @@ -93,10 +94,13 @@ def similar_directory_events()
event_type: 'same directory',
title: "#{cve_link} fixed in same directory #{dir}",
date: vuln['announced'],
description: <<~EOSTR
description: (<<~EOSTR
A vulnerability, #{cve_link} that was also fixed in this
vulnerability's directory (#{dir}) was announced.
EOSTR
),
notes: "null",
start_hidden: false
)
events << e
vuln_events << VulnerabilityEvent.new(vulnerability_id: vuln['vuln1_id'], event: e)
Expand Down
1 change: 1 addition & 0 deletions lib/event_generators/vcc_events.rb
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ def generate
date: vcc.commit.date,
event_type: 'vcc',
description: markdown(Writing.embed_details(@desc, vcc)),
notes: "null",
start_hidden: false
)
events << e
Expand Down
1 change: 1 addition & 0 deletions lib/event_generators/vulnerability_events.rb
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ def generate
event_type: 'announced',
title: "Vulnerability Announced for #{v.cve}",
description: markdown("The vulnerability was officially announced by the development team."),
notes: "null",
start_hidden: false
)
v.events << e
Expand Down
Loading

0 comments on commit d119f4e

Please sign in to comment.