Skip to content
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
20 changes: 20 additions & 0 deletions .github/workflows/validate-site.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
name: Validate site data

on:
pull_request:
push:
branches: [master]

permissions:
contents: read

jobs:
validate:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-python@v5
with:
python-version: "3.12"
- name: Validate CSV and asset contracts
run: python scripts/validate_site.py
29 changes: 29 additions & 0 deletions MAINTAINER_CHECKLIST.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
# VICE Lab Website Maintainer Checklist

## Photos requiring human follow-up

Before adding a portrait, confirm the subject approves its use and that VICE Lab has permission to publish it. Use a square or portrait-oriented JPEG, crop consistently with existing headshots, optimize it for the web, and use the exact filename shown.

- [ ] Obtain an approved portrait for Erin Hestir and save it as `images/people/erin-hestir.jpg`.
- [ ] Obtain an approved portrait for Aditya Sood and save it as `images/people/aditya-sood.jpg` (used by the Sierra HP and CERC-WET projects).
- [ ] Obtain an approved portrait for Megan Mayzelle and save it as `images/people/megan-mayzelle.jpg`.
- [ ] Confirm that `images/people/josue-medellin-azuara.jpg` is the intended current portrait for the Water Systems Management collaborator card.
- [ ] Review collaborator names and affiliations against their official lab pages at least annually.

Until these photographs are supplied, the site displays `images/people/no-picture.jpg` instead of a broken image.

## Content decisions

- [ ] Decide whether the 2021 Community Survey page should be updated, archived, or removed; it still promises a June 2021 report.
- [ ] Update the footer's “Updated October 2020” wording or replace it with an automatically maintained copyright year.
- [ ] Review and update remaining plain-HTTP external links.
- [ ] Confirm whether the archived PPIC site snapshot should remain publicly deployed under `archived-sites/`.
- [ ] Review people referenced by projects, news, and publications but absent from `people.csv`; either add profiles or intentionally render their names without profile links.

## Technical follow-up

- [ ] Upgrade the bundled jQuery 3.3.1 to a supported release and regression-test CSV rendering, menus, filters, and modals.
- [ ] Compress `images/news/casa-ele-2022-launched.jpg` (currently about 21.7 MB) and other multi-megabyte images; target less than 500 KB where practical.
- [ ] Confirm whether the unused `images/news/IMG_0383.png` can be removed.
- [ ] Add a periodic external-link checker after redirects and intentionally archived links are documented.
- [ ] Run `python scripts/validate_site.py` before every content pull request and resolve all errors; review warnings explicitly.
5 changes: 4 additions & 1 deletion collaborators/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -121,7 +121,8 @@ <h2>
default:
people_data= '';
if (data[i].AUTHOR != "") {
people_data = '<div class="meta"><a href="people" class="author"><span class="name">' + data[i].AUTHOR.replace("-", " ") + '</span><img src="/images/people/' + data[i].AUTHOR + '.jpg" alt="" /></a></div>'
var authorImage = data[i].AUTHOR === "josue-medellin" ? "josue-medellin-azuara" : data[i].AUTHOR;
people_data = '<div class="meta"><span class="author"><span class="name">' + data[i].AUTHOR.replaceAll("-", " ") + '</span><img loading="lazy" src="/images/people/' + authorImage + '.jpg" onerror="this.onerror=null;this.src=\'/images/people/no-picture.jpg\'" alt="' + data[i].NAME + ' collaborator portrait" /></span></div>'
}
$("#collaborators").append(
$.parseHTML(
Expand All @@ -130,6 +131,8 @@ <h2>
break;
}
}
}).fail(function () {
$("#collaborators").append("<p>Unable to load collaborators right now. Please try again later.</p>");
});
/*Footer: Controlled by HTML snippet in snippets/footer.html*/
$("#footer").load("../snippets/footer.html");
Expand Down
File renamed without changes
File renamed without changes
18 changes: 11 additions & 7 deletions index.html
Original file line number Diff line number Diff line change
Expand Up @@ -164,30 +164,32 @@ <h2>

$.get("/news.csv").then(function (text, status, xhr) {
var data = $.csv.toObjects(text);
for (var i = 0; i < NUMBER_FRONT_PAGE_NEWS; i++) {
for (var i = 0; i < Math.min(NUMBER_FRONT_PAGE_NEWS, data.length); i++) {
switch (data[i].ID) {
default:
$("#news").append(
$.parseHTML(
'<article class="post">' +
'<header>' +
'<div class="title">' +
'<h2><a href="/news">' + data[i].NAME + '</a></h2>' +
'<h2><a href="/news">' + data[i].NAME + '</a></h2>' +
'</div>' +
'<div class="meta">' +
'<time class="published" datetime="2015-11-01">' + data[i].DATE + '</time>' +
'<a href="/people" class="author"><span class="name">' + data[i].AUTHOR.replace("-", " ") + '</span><img src="/images/people/' + data[i].AUTHOR + '.jpg" alt="" /></a>' +
'<time class="published">' + data[i].DATE + '</time>' +
'<a href="/people" class="author"><span class="name">' + data[i].AUTHOR.replaceAll("-", " ") + '</span><img loading="lazy" src="/images/people/' + data[i].AUTHOR + '.jpg" onerror="this.onerror=null;this.src=\'/images/people/no-picture.jpg\'" alt="' + data[i].AUTHOR.replaceAll("-", " ") + '" /></a>' +
'</div>'+
'</header>' +
'<div style="display: block; overflow: auto">' +
'<img style= "margin: 0 1em 1em 0; float:left; width: 35%;" class="image featured" src="/images/news/' + data[i].ID + '.jpg" alt="" />' +
'<img loading="lazy" style= "margin: 0 1em 1em 0; float:left; width: 35%;" class="image featured" src="/images/news/' + data[i].ID + '.jpg" alt="' + data[i].NAME + '" />' +
'<p style="margin:0;">' + data[i].TEXT + '</p>' +
'</div>' +
'</div>' +
'</article>'
));
break;
}
}
}).fail(function () {
$("#news").append("<p>Unable to load news right now. Please try again later.</p>");
});
$.get("/projects.csv").then(function (text, status, xhr) {
var data = $.csv.toObjects(text);
Expand All @@ -200,11 +202,13 @@ <h2>
break;
default:
$("#projects").append(
$.parseHTML('<article class="mini-post"><header><h3><a href="/projects">' + data[i].NAME + '</a></h3><time class="published" datetime="2015-10-18">' + data[i].DATE + '</time><a href="/people" class="author"><img src="/images/people/' + data[i].AUTHOR.split(";")[0] + '.jpg" alt="" /></a></header><a href="/projects" class="image"><img src="images/projects/' + data[i].ID + '.jpg" onerror="this.onerror=null;this.src=\'/images/projects/' + data[i].ID + '.svg\'" alt="" /></a></article>'
$.parseHTML('<article class="mini-post"><header><h3><a href="/projects">' + data[i].NAME + '</a></h3><time class="published">' + data[i].DATE + '</time><a href="/people" class="author"><img loading="lazy" src="/images/people/' + data[i].AUTHOR.split(";")[0] + '.jpg" onerror="this.onerror=null;this.src=\'/images/people/no-picture.jpg\'" alt="' + data[i].AUTHOR.split(";")[0].replaceAll("-", " ") + '" /></a></header><a href="/projects" class="image"><img loading="lazy" src="images/projects/' + data[i].ID + '.jpg" onerror="if(this.dataset.fallback){this.onerror=null;this.src=\'/images/people/no-picture.jpg\';}else{this.dataset.fallback=\'1\';this.src=\'/images/projects/' + data[i].ID + '.svg\';}" alt="' + data[i].NAME + '" /></a></article>'
));
break;
}
}
}).fail(function () {
$("#projects").append("<p>Unable to load projects right now. Please try again later.</p>");
});
});
</script>
Expand Down
12 changes: 7 additions & 5 deletions news/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -121,20 +121,22 @@ <h2>
'<article class="post">' +
'<header>' +
'<div class="title">' +
'<h2 id ="' + data[i].ID + '">' + data[i].NAME + '</h2>' +
'<h2 id ="' + data[i].ID + '">' + data[i].NAME + '</h2>' +
'</div>' +
'<div class="meta">' +
'<time class="published" datetime="2015-11-01">' + data[i].DATE + '</time>' +
'<a href="/people" class="author"><span class="name">' + data[i].AUTHOR.replace("-", " ") + '</span><img src="/images/people/' + data[i].AUTHOR + '.jpg" alt="" /></a>' +
'<time class="published">' + data[i].DATE + '</time>' +
'<a href="/people" class="author"><span class="name">' + data[i].AUTHOR.replaceAll("-", " ") + '</span><img loading="lazy" src="/images/people/' + data[i].AUTHOR + '.jpg" onerror="this.onerror=null;this.src=\'/images/people/no-picture.jpg\'" alt="' + data[i].AUTHOR.replaceAll("-", " ") + '" /></a>' +
'</div>'+
'</header>' +
'<div style="display: block; overflow: auto">' +
'<img style= "margin: 0 1em 1em 0; float:left; width: 25%;" class="image featured" src="/images/news/' + data[i].ID + '.jpg" alt="" />' +
'<img loading="lazy" style= "margin: 0 1em 1em 0; float:left; width: 25%;" class="image featured" src="/images/news/' + data[i].ID + '.jpg" alt="' + data[i].NAME + '" />' +
'<p style="margin:0;">' + data[i].TEXT + '</p>' +
'</div>' +
'</div>' +
'</article>'
));
}
}).fail(function () {
$("#news").append("<p>Unable to load news right now. Please try again later.</p>");
});
/*Footer: Controlled by HTML snippet in snippets/footer.html*/
$("#footer").load("../snippets/footer.html");
Expand Down
43 changes: 27 additions & 16 deletions people/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -120,7 +120,7 @@ <h3 id="modal-title" style="margin:3em; color:white">
<script src="../assets/js/util.js"></script>
<script src="../assets/js/main.js"></script>
<script>
var data, news_data, project_data, publication_data;
var data = [], news_data = [], project_data = [], publication_data = [];

var modal = document.getElementById('people-modal');
modal.style.display = "none";
Expand All @@ -129,16 +129,22 @@ <h3 id="modal-title" style="margin:3em; color:white">

function loadModal(id) {
if (id != '') {
if (!getInfo(id)) { modal.style.display = "none"; window.location.hash = ""; return; } // stale/unknown person id: do not crash
var personInfo = getInfo(id);
if (!personInfo) { modal.style.display = "none"; window.location.hash = ""; return; } // stale/unknown person id: do not crash
modal.style.display = "block";
var logo = document.getElementById('modal-image');
logo.src = '/images/people/' + id + '.jpg';
logo.alt = personInfo.NAME;
logo.onerror = function () {
this.onerror = null;
this.src = '/images/people/no-picture.jpg';
};
var name = document.getElementById('modal-name');
name.innerHTML = getInfo(id).NAME;
name.textContent = personInfo.NAME;
var title = document.getElementById('modal-title');
title.innerHTML = getInfo(id).TITLE;
title.textContent = personInfo.TITLE;
var biography = document.getElementById('modal-biography');
biography.innerHTML = getInfo(id).BIOGRAPHY + '<hr>';
biography.innerHTML = personInfo.BIOGRAPHY + '<hr>';

if (id == "josh-viers") {
biography.innerHTML += '<ul class="actions fit"><li><a href="/news" class="button fit">News</a></li><li><a href="/projects" class="button fit">Projects</a></li><li><a href="/publications" class="button fit">Publications</a></li><li><a href="/people" class="button fit">People</a></li><li><a href="/collaborators" class="button fit">Collaborators</a></li></ul>';
Expand Down Expand Up @@ -224,33 +230,38 @@ <h3 id="modal-title" style="margin:3em; color:white">
$("#people").append(
$.parseHTML(
'<a href= "#' + data[i].ID + '">' +
'<div style="display:inline-block;width:15em;height:100%;"><img class="image featured" style="margin: 0 0 1em 0; height: 240px; width: 240px;" src="/images/people/' + data[i].ID + '.jpg" alt=""/><center><h2 style="height:4em">' + data[i].NAME + '</h2\><br><h3>' + data[i].TITLE + '</h3><ul class="icons">' + email_html + twitter_html + github_html + '</ul></center></div>' +
'<div style="display:inline-block;width:15em;height:100%;"><img loading="lazy" class="image featured" style="margin: 0 0 1em 0; height: 240px; width: 240px;" src="/images/people/' + data[i].ID + '.jpg" onerror="this.onerror=null;this.src=\'/images/people/no-picture.jpg\'" alt="' + data[i].NAME + '"/><center><h2 style="height:4em">' + data[i].NAME + '</h2><br><h3>' + data[i].TITLE + '</h3><ul class="icons">' + email_html + twitter_html + github_html + '</ul></center></div>' +
'</a> '
));
}else{
$("#people").append(
$.parseHTML(
'<a href= "#' + data[i].ID + '">' +
'<div style="display:inline-flex; flex-wrap: wrap;width:15em;height:100%;"><img class="image featured" style="margin: 0 0 1em 0; height: 240px; width: 240px;" src="/images/people/' + data[i].ID + '.jpg" alt=""/><center><h2 style="height:4em">' + data[i].NAME + '</h2\><br><h3>' + data[i].TITLE + '</h3><ul class="icons">' + email_html + twitter_html + github_html + '</ul></center></div>' +
'<div style="display:inline-flex; flex-wrap: wrap;width:15em;height:100%;"><img loading="lazy" class="image featured" style="margin: 0 0 1em 0; height: 240px; width: 240px;" src="/images/people/' + data[i].ID + '.jpg" onerror="this.onerror=null;this.src=\'/images/people/no-picture.jpg\'" alt="' + data[i].NAME + '"/><center><h2 style="height:4em">' + data[i].NAME + '</h2><br><h3>' + data[i].TITLE + '</h3><ul class="icons">' + email_html + twitter_html + github_html + '</ul></center></div>' +
'</a> '
));
}

}
break;
}
}

loadModal(window.location.hash.substring(1));
}).fail(function () {
$("#people").append("<p>Unable to load people right now. Please try again later.</p>");
});
$.get("/news.csv").then(function (text, status, xhr) {
news_data = $.csv.toObjects(text);
});
$.get("/projects.csv").then(function (text, status, xhr) {
project_data = $.csv.toObjects(text);
});
$.get("/publications.csv").then(function (text, status, xhr) {
publication_data = $.csv.toObjects(text);
$.when(
$.get("/news.csv"),
$.get("/projects.csv"),
$.get("/publications.csv")
).done(function (newsResponse, projectResponse, publicationResponse) {
news_data = $.csv.toObjects(newsResponse[0]);
project_data = $.csv.toObjects(projectResponse[0]);
publication_data = $.csv.toObjects(publicationResponse[0]);
loadModal(window.location.hash.substring(1));
}).fail(function () {
console.error("Unable to load related news, projects, or publications.");
});

});
Expand Down
14 changes: 8 additions & 6 deletions projects/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -141,7 +141,7 @@ <h2>Locations</h2>
var authors = data[i].AUTHOR.split(";");
var authors_html = '';
for (var j = 0; j < authors.length; j++) {
authors_html += '<a href="/people/#' + authors[j] + '" class="author"><span class="name">' + authors[j].replace("-", " ") + '</span><img src="/images/people/' + authors[j] + '.jpg" alt="" /></a>'
authors_html += '<a href="/people/#' + authors[j] + '" class="author"><span class="name">' + authors[j].replaceAll("-", " ") + '</span><img loading="lazy" src="/images/people/' + authors[j] + '.jpg" onerror="this.onerror=null;this.src=\'/images/people/no-picture.jpg\'" alt="' + authors[j].replaceAll("-", " ") + '" /></a>'
}
var tags = data[i].TAGS.split(";");
var tags_buttons_html = '';
Expand All @@ -167,34 +167,36 @@ <h2>Locations</h2>
if (!visited_locations.includes(locationArr[j])) {
visited_locations.push(locationArr[j]);
$("#locations").append(
'<a href="#" class="button" onclick="filterSelection(\'' + locationArr[j] + '\')">' + data[i].LOCATION.replace("-", " ") + '</a> '
'<a href="#" class="button" onclick="filterSelection(\'' + locationArr[j] + '\')">' + locationArr[j].replaceAll("-", " ") + '</a> '
);
}
}
location_html += '"';
$("#projects").append(
$.parseHTML(
'<article ' + tags_html + location_html + '><header><div class="title"><h2 id="' + data[i].ID + '">' + data[i].NAME + '</h3>' + tags_buttons_html + /*location_buttons_html + */ '</div><div class="meta"><time class="published" datetime="2015-11-01">' + data[i].DATE + '</time>' + authors_html + '</a></div></header>' +
'<article ' + tags_html + location_html + '><header><div class="title"><h2 id="' + data[i].ID + '">' + data[i].NAME + '</h2>' + tags_buttons_html + /*location_buttons_html + */ '</div><div class="meta"><time class="published">' + data[i].DATE + '</time>' + authors_html + '</div></header>' +
'<div style="display: block; overflow: auto">' +
'<img style= "margin: 0 1em 1em 0; float:left; width: 35%;" class="image featured" src="/images/projects/' + data[i].ID + '.jpg" onerror="this.onerror=null;this.src=\'/images/projects/' + data[i].ID + '.svg\'" alt="" />' +
'<img loading="lazy" style= "margin: 0 1em 1em 0; float:left; width: 35%;" class="image featured" src="/images/projects/' + data[i].ID + '.jpg" onerror="if(this.dataset.fallback){this.onerror=null;this.src=\'/images/people/no-picture.jpg\';}else{this.dataset.fallback=\'1\';this.src=\'/images/projects/' + data[i].ID + '.svg\';}" alt="' + data[i].NAME + '" />' +
'<p style="margin:0;">' + data[i].TEXT + '</p>' +
'</div>' +
'</article>'
));
break;
}
}
}).fail(function () {
$("#projects").append("<p>Unable to load projects right now. Please try again later.</p>");
});
});
filterSelection("all")
function filterSelection(c) {
var x, i;
//Needs each publication to have the tags be callable
x = document.getElementsByClassName("post");
x = document.querySelectorAll("#projects .post");
if (c == "all") c = "";
for (i = 0; i < x.length; i++) {
addElement(x[i], "hide");
if (x[i].className.indexOf(c) > -1) removeElement(x[i], "hide");
if (c === "" || x[i].classList.contains(c)) removeElement(x[i], "hide");
}
}
function addElement(element, name) {
Expand Down
4 changes: 2 additions & 2 deletions publications/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -228,10 +228,10 @@ <h3 id="${item.ID}">
});

function filterSelection(c) {
const posts = document.getElementsByClassName("post");
const posts = document.querySelectorAll("#publications .post");
Array.from(posts).forEach(post => {
post.style.display = "none";
if (c === "all" || post.className.includes(c)) {
if (c === "all" || post.classList.contains(c)) {
post.style.display = "block";
}
});
Expand Down
Loading
Loading