Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add accordion menus for vertical timeline events #738

Merged
merged 8 commits into from
Jun 5, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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