diff --git a/announcements/src/org/labkey/announcements/announcementThread.jsp b/announcements/src/org/labkey/announcements/announcementThread.jsp index b636af6df12..1b8711bc415 100644 --- a/announcements/src/org/labkey/announcements/announcementThread.jsp +++ b/announcements/src/org/labkey/announcements/announcementThread.jsp @@ -127,7 +127,7 @@ if (!announcementModel.getAttachments().isEmpty()) { ActionURL downloadURL = AnnouncementsController.getDownloadURL(announcementModel, d.getName()); %> -  <%=h(d.getName())%> <% + <%=d.renderDownloadLink(downloadURL)%> <% } %> <% @@ -210,7 +210,7 @@ if (!announcementModel.getResponses().isEmpty()) { ActionURL downloadURL = AnnouncementsController.getDownloadURL(r, rd.getName()); %> -  <%=h(rd.getName())%> <% + <%=rd.renderDownloadLink(downloadURL)%> <% } %> diff --git a/announcements/src/org/labkey/announcements/announcementWebPartSimple.jsp b/announcements/src/org/labkey/announcements/announcementWebPartSimple.jsp index 713869f8ee8..dac75ebc99c 100644 --- a/announcements/src/org/labkey/announcements/announcementWebPartSimple.jsp +++ b/announcements/src/org/labkey/announcements/announcementWebPartSimple.jsp @@ -195,7 +195,7 @@ for (AnnouncementModel a : bean.announcementModels) for (Attachment d : a.getAttachments()) { ActionURL downloadURL = AnnouncementsController.getDownloadURL(a, d.getName()); - %> <%=h(d.getName())%> <% + %><%=d.renderDownloadLink(downloadURL)%> <% } %><% } diff --git a/announcements/src/org/labkey/announcements/announcementWebPartWithExpandos.jsp b/announcements/src/org/labkey/announcements/announcementWebPartWithExpandos.jsp index 4322dc4b7ed..8f061e75af7 100644 --- a/announcements/src/org/labkey/announcements/announcementWebPartWithExpandos.jsp +++ b/announcements/src/org/labkey/announcements/announcementWebPartWithExpandos.jsp @@ -217,7 +217,7 @@ for (AnnouncementModel a : bean.announcementModels) for (Attachment d : a.getAttachments()) { ActionURL downloadURL = AnnouncementsController.getDownloadURL(a, d.getName()); - %> <%=h(d.getName())%> <% + %><%=d.renderDownloadLink(downloadURL)%> <% } %><% } diff --git a/announcements/src/org/labkey/announcements/update.jsp b/announcements/src/org/labkey/announcements/update.jsp index 1797588ed54..c5e54ef947b 100644 --- a/announcements/src/org/labkey/announcements/update.jsp +++ b/announcements/src/org/labkey/announcements/update.jsp @@ -140,7 +140,6 @@ if (settings.hasExpires()) <% int x = -1; - String id; for (Attachment att : ann.getAttachments()) { x++; diff --git a/api/src/org/labkey/api/attachments/Attachment.java b/api/src/org/labkey/api/attachments/Attachment.java index 904972dae6e..b946c8012d4 100644 --- a/api/src/org/labkey/api/attachments/Attachment.java +++ b/api/src/org/labkey/api/attachments/Attachment.java @@ -21,9 +21,13 @@ import org.labkey.api.security.User; import org.labkey.api.security.UserManager; import org.labkey.api.services.ServiceRegistry; +import org.labkey.api.util.DOM; +import org.labkey.api.util.HtmlString; import org.labkey.api.util.MemTracker; import org.labkey.api.util.MimeMap; +import org.labkey.api.util.PageFlowUtil; import org.labkey.api.util.Path; +import org.labkey.api.view.ActionURL; import org.labkey.api.view.ViewServlet; import org.labkey.api.webdav.WebdavResolver; @@ -350,4 +354,29 @@ public void setDocumentSize(int documentSize) { _documentSize = documentSize; } + + /** + * Returns an HtmlString rendering a download link: an anchor containing a file type icon and the filename. + * The icon is marked aria-hidden since it is decorative; the link text serves as the accessible name. + */ + public HtmlString renderDownloadLink(ActionURL downloadURL) + { + return renderDownloadLink(downloadURL, getName()); + } + + /** + * Returns an HtmlString rendering a download link: an anchor containing a file type icon and custom link text. + * Use this overload when the visible link label differs from the filename (e.g. "Study Protocol Document"). + * The icon is marked aria-hidden since it is decorative; linkText serves as the accessible name. + */ + public HtmlString renderDownloadLink(ActionURL downloadURL, String linkText) + { + return DOM.createHtmlFragment( + DOM.A(DOM.at(DOM.Attribute.href, downloadURL.toString()), + DOM.IMG(DOM.at(DOM.Attribute.alt, "").at(DOM.Attribute.src, PageFlowUtil.staticResourceUrl(getFileIcon()))), + HtmlString.NBSP, + linkText + ) + ); + } } diff --git a/api/src/org/labkey/api/util/DOM.java b/api/src/org/labkey/api/util/DOM.java index 1feb02da7b1..140e91deba9 100644 --- a/api/src/org/labkey/api/util/DOM.java +++ b/api/src/org/labkey/api/util/DOM.java @@ -362,6 +362,54 @@ public enum Attribute action, align, alt, + aria_activedescendant, + aria_atomic, + aria_autocomplete, + aria_busy, + aria_checked, + aria_colcount, + aria_colindex, + aria_colspan, + aria_controls, + aria_current, + aria_describedby, + aria_details, + aria_disabled, + aria_dropeffect, + aria_errormessage, + aria_expanded, + aria_flowto, + aria_grabbed, + aria_haspopup, + aria_hidden, + aria_invalid, + aria_keyshortcuts, + aria_label, + aria_labelledby, + aria_level, + aria_live, + aria_modal, + aria_multiline, + aria_multiselectable, + aria_orientation, + aria_owns, + aria_placeholder, + aria_posinset, + aria_pressed, + aria_readonly, + aria_relevant, + aria_required, + aria_roledescription, + aria_rowcount, + aria_rowindex, + aria_rowspan, + aria_selected, + aria_setsize, + aria_sort, + aria_valuemax, + aria_valuemin, + aria_valuenow, + aria_valuetext, async, autocomplete, autofocus, @@ -570,7 +618,6 @@ public _Attributes data(boolean condition, String datakey, Object value) } return this; } - public _Attributes cl(String...names) { if (null != names) @@ -891,7 +938,7 @@ private static Appendable appendAttribute(Appendable html, Attribute key, Object if (null==value) return html; html.append(" "); - html.append(key.name()); + html.append(key.name().replace('_', '-')); html.append("=\""); // NOTE it is somewhat unusual to pass in a Renderable, but it is possible that we // want to render HTML into an attribute. We still need to re-encode the value before trying to wrap with "". diff --git a/api/webapp/clientapi/dom/DataRegion.js b/api/webapp/clientapi/dom/DataRegion.js index 07ddf75f5cc..a13e1f2c335 100644 --- a/api/webapp/clientapi/dom/DataRegion.js +++ b/api/webapp/clientapi/dom/DataRegion.js @@ -1713,8 +1713,8 @@ if (!LABKEY.DataRegions) { ct.append([ '
', - '', - '', + '', + '', '
' ].join('')); diff --git a/core/src/org/labkey/core/login/resetPassword.jsp b/core/src/org/labkey/core/login/resetPassword.jsp index ea9e89d1c86..9a62a04ad5a 100644 --- a/core/src/org/labkey/core/login/resetPassword.jsp +++ b/core/src/org/labkey/core/login/resetPassword.jsp @@ -46,7 +46,7 @@ <% } %>

To reset your password, type in your email address and click the Reset button.

- +
<%= button("Reset").submit(true).name("reset")%> <%= button("Cancel").href(urlProvider(LoginUrls.class).getLoginURL(doneURL)) %> diff --git a/core/src/org/labkey/core/view/template/bootstrap/header.jsp b/core/src/org/labkey/core/view/template/bootstrap/header.jsp index 061a708bc82..3a0ea0ccfc6 100644 --- a/core/src/org/labkey/core/view/template/bootstrap/header.jsp +++ b/core/src/org/labkey/core/view/template/bootstrap/header.jsp @@ -154,7 +154,7 @@