feat: Embedded Microsoft Office Viewer and Google Docs Viewer web services and no longer download as default action when viewer is supported when loading directory with file attachment that doesn't automatically render.

This commit is contained in:
David Ball 2024-06-24 01:02:26 -04:00
parent e0b93cf355
commit 1f1071f5a9
6 changed files with 104 additions and 44 deletions

View File

@ -186,6 +186,14 @@ const renderArchive = (html: string, paths: string[]) => {
return html;
}
const isMsOfficeViewerSupported = (file: string) => {
return path.extname(file).search(/^((?:.pptx)|(?:.docx)|(?:.xlsx)|(?:.ppt)|(?:.doc)|(?:.xls))$/ig) != -1;
}
const isGoogleDocsViewerSupported = (file: string) => {
return isMsOfficeViewerSupported(file) || path.extname(file).search(/^((?:.pdf))$/ig) != -1;
}
export default {
leftTrimFirstDirectory,
trimSlashes,
@ -205,4 +213,6 @@ export default {
inspect,
md,
moment,
isMsOfficeViewerSupported,
isGoogleDocsViewerSupported,
};

View File

@ -78,7 +78,7 @@ export default function () {
}
});
const renderData = { breadcrumbs, content, filePath, fullFilePath, paths, req, route, ...fmData };
res.render("page", { h: helpers, ...renderData });
res.render("page", { h: helpers, path, config, ...renderData });
});
});
@ -122,7 +122,7 @@ export default function () {
console.log(`Setting route for ${route}`);
pageRouter.get(route, async (req, res) => {
const html = fs.readFileSync(fullFilePath).toString();
const renderData = { route, filePath, fullFilePath, req, paths, html };
const renderData = { route, filePath, fullFilePath, req, paths, html, path, config };
res.render("archive", { h: helpers, ...renderData });
});
});
@ -183,7 +183,7 @@ export default function () {
}
});
const renderData = { breadcrumbs, route, filePath, fullFilePath, req, paths, directory: path.join('public', directory), videoURL, subtitleURL, subtitleVTT, info };
res.render("video-player", { h: helpers, ...renderData });
res.render("video-player", { h: helpers, path, config, ...renderData });
}
});
});

View File

@ -81,15 +81,15 @@ export interface ServeHandlerOptions {
* current working directory.
*
* For example, if serving a Jekyll app, it would look like this:
* {
* "public": "_site"
* }
* {
* "public": "_site"
* }
*
* Using absolute path:
*
* {
* "public": "/path/to/your/_site"
* }
* {
* "public": "/path/to/your/_site"
* }
*
* NOTE: The path cannot contain globs or regular expressions.
*/
@ -101,17 +101,17 @@ export interface ServeHandlerOptions {
* with status code 301 to the same path, but with the extension dropped.
*
* You can disable the feature like follows:
* {
* "cleanUrls": false
* }
* {
* "cleanUrls": false
* }
*
* However, you can also restrict it to certain paths:
* {
* "cleanUrls": [
* "/app/**",
* "/!components/**"
* ]
* }
* {
* "cleanUrls": [
* "/app/**",
* "/!components/**"
* ]
* }
*
* NOTE: The paths can only contain globs that are matched using minimatch.
*/
@ -121,19 +121,19 @@ export interface ServeHandlerOptions {
* different one behind the curtains, this option is what you need.
*
* It's perfect for single page applications (SPAs), for example:
* {
* "rewrites": [
* { "source": "app/**", "destination": "/index.html" },
* { "source": "projects/edit", "destination": "/edit-project.html" }
* ]
* }
* {
* "rewrites": [
* { "source": "app/**", "destination": "/index.html" },
* { "source": "projects/edit", "destination": "/edit-project.html" }
* ]
* }
*
* You can also use so-called "routing segments" as follows:
* {
* "rewrites": [
* { "source": "/projects/:id/edit", "destination": "/edit-project-:id.html" },
* ]
* }
* {
* "rewrites": [
* { "source": "/projects/:id/edit", "destination": "/edit-project-:id.html" },
* ]
* }
*
* Now, if a visitor accesses /projects/123/edit, it will respond with the file /edit-project-123.html.
*
@ -184,8 +184,9 @@ export const directoryTemplate = (vals: ServeDirectoryTemplateParameters) => {
});
}
return new Promise((resolve, reject) => {
ejs.renderFile(path.join(config.viewsPath, 'directory.ejs'), { breadcrumbs, h: helpers, ...vals }, (err, str) => {
ejs.renderFile(path.join(config.viewsPath, 'directory.ejs'), { h: helpers, path, config, breadcrumbs, ...vals }, (err, str) => {
if (err) {
console.error(err);
reject(err);
} else {
resolve(str);
@ -196,8 +197,9 @@ export const directoryTemplate = (vals: ServeDirectoryTemplateParameters) => {
export const errorTemplate = (vals: ServeErrorTemplateParameters) => {
return new Promise((resolve, reject) => {
ejs.renderFile(path.join(config.viewsPath, 'error.ejs'), { h: helpers, ...vals }, (err, str) => {
ejs.renderFile(path.join(config.viewsPath, 'error.ejs'), { h: helpers, path, config, ...vals }, (err, str) => {
if (err) {
console.error(err);
reject(err);
} else {
resolve(str);
@ -1012,4 +1014,4 @@ export default async (request: Request, response: ServerResponse, serveConfig: S
response.writeHead(response.statusCode || 200, headers);
stream.pipe(response);
};
};

View File

@ -4,18 +4,13 @@
<title><%=h.getDirectoryTitle(directory)%></title>
<%- include('./includes/common-head.ejs') %>
</head>
<body onload="initPage()">
<%- include('./includes/top-navbar.ejs') %>
<%- include('./includes/no-trash-svg.ejs') %>
<main class="container">
<header>
<%- include('./includes/breadcrumbs.ejs') %>
</header>
<% if (h.directoryContainsReadme(directory)) {%>
<div class="row p-4 pb-0 pe-lg-0 pt-lg-5 align-items-center rounded-3 border shadow-lg">
<div class="col-lg-12 p-3 p-lg-5 pt-lg-3">
@ -27,13 +22,69 @@
<b>Document Date:</b> <%= h.moment(h.readmeFm(directory).docDate).format('MMMM D, YYYY') %>
<% } %>
<%if (typeof h.readmeFm(directory).file !== 'undefined') { %>
<b>Attached Document:</b> <a href="<%- encodeURI(h.readmeFm(directory).file) %>"><%- h.readmeFm(directory).file %></a>
<b>Document:</b> <a href="<%- encodeURI(path.basename(h.readmeFm(directory).file)) %>"><%- h.readmeFm(directory).file %></a>
<% } %>
</small>
</p>
<% } %>
<% if (typeof h.readmeFm(directory).file !== 'undefined') { %>
<iframe src="<%- encodeURI(h.readmeFm(directory).file) %>" style="width: 100%; height: 90vh;"></iframe>
<ul class="nav nav-tabs" id="readerTab" role="tablist">
<% if (h.isMsOfficeViewerSupported(h.readmeFm(directory).file)) { %>
<li class="nav-item" role="presentation">
<button class="nav-link active" id="ms-office-viewer-tab" data-bs-toggle="tab" data-bs-target="#ms-office-viewer" type="button" role="tab" aria-controls="ms-office-viewer" aria-selected="true">Microsoft Office Web Viewer</button>
</li>
<% } %>
<% if (h.isGoogleDocsViewerSupported(h.readmeFm(directory).file)) { %>
<li class="nav-item" role="presentation">
<button class="nav-link<%=h.isMsOfficeViewerSupported(h.readmeFm(directory).file) ? '' : ' active'%>" id="google-docs-viewer-tab" data-bs-toggle="tab" data-bs-target="#google-docs-viewer" type="button" role="tab" aria-controls="google-docs-viewer" aria-selected="false">Google Docs Viewer</button>
</li>
<% } %>
<li class="nav-item" role="presentation">
<button class="nav-link" id="view-download-tab" data-bs-toggle="tab" data-bs-target="#view-download" type="button" role="tab" aria-controls="view-download" aria-selected="false">View/Download</button>
</li>
</ul>
<div class="tab-content">
<% if (h.isMsOfficeViewerSupported(h.readmeFm(directory).file)) { %>
<div class="tab-pane active" id="ms-office-viewer" role="tabpanel" aria-labelledby="ms-office-viewer-tab" tabindex="0">
<iframe src="https://view.officeapps.live.com/op/embed.aspx?src=<%- encodeURIComponent(h.trimSlashes(config.siteUrl) + path.posix.join(breadcrumbs[breadcrumbs.length-1].url, (h.readmeFm(directory).file.replaceAll('\\', '/')))) %>" style="width: 100%; height: 85vh; border: solid 1px #dfd7ca; border-top: 0px; border-bottom-left-radius: 0.25rem; border-bottom-right-radius: 0.25rem;" frameborder="0"></iframe>
</div>
<% } %>
<% if (h.isGoogleDocsViewerSupported(h.readmeFm(directory).file)) { %>
<div class="tab-pane<%=h.isMsOfficeViewerSupported(h.readmeFm(directory).file) ? '' : ' active'%>" id="google-docs-viewer" role="tabpanel" aria-labelledby="google-docs-viewer-tab" tabindex="0">
<iframe src="https://docs.google.com/gview?embedded=true&url=<%- encodeURIComponent(h.trimSlashes(config.siteUrl) + path.posix.join(breadcrumbs[breadcrumbs.length-1].url, (h.readmeFm(directory).file.replaceAll('\\', '/')))) %>" style="width: 100%; height: 85vh; border: solid 1px #dfd7ca; border-top: 0px; border-bottom-left-radius: 0.25rem; border-bottom-right-radius: 0.25rem;" frameborder="0"></iframe>
</div>
<% } %>
<div class="tab-pane<%=(h.isMsOfficeViewerSupported(h.readmeFm(directory).file) || h.isGoogleDocsViewerSupported(h.readmeFm(directory).file)) ? '' : ' active'%>" id="view-download" role="tabpanel" aria-labelledby="view-download-tab" tabindex="0">
<iframe src="about:blank" style="width: 100%; height: 85vh; border: solid 1px #dfd7ca; border-top: 0px; border-bottom-left-radius: 0.25rem; border-bottom-right-radius: 0.25rem;" frameborder="0"></iframe>
</div>
<script>
$(document).ready(function () {
var viewDownloadTabButton = $('button#view-download-tab');
var viewDownloadTab = new bootstrap.Tab(viewDownloadTabButton);
var viewDownloadTabPaneIFrame = $('#view-download iframe');
viewDownloadTabButton.on('click', function (event) {
event.preventDefault();
viewDownloadTab.show();
if (viewDownloadTabPaneIFrame.attr('src') == 'about:blank') {
console.log('View/Download tab clicked. Loading iframe.');
viewDownloadTabPaneIFrame.attr('src', '<%- encodeURI(h.readmeFm(directory).file) %>');
} else {
console.log('View/Download tab clicked. iframe previously loaded.');
}
})
})
</script>
<% if (!h.isMsOfficeViewerSupported(h.readmeFm(directory).file) && !h.isGoogleDocsViewerSupported(h.readmeFm(directory).file)) { %>
<script>
$(document).ready(function () {
setTimeout(function () {
var viewDownloadTabButton = $('button#view-download-tab');
viewDownloadTabButton.click();
}, 100);
});
</script>
<% } %>
</div>
<% } else { %>
<%- h.printReadme(directory) %>
<% } %>
@ -54,7 +105,6 @@
</div>
<% } %>
<% } %>
<ul id="files" class="list-group shadow-lg">
<% files.forEach(function(value, index) { %>
<li class="list-group-item list-group-item-action flex-column align-items-start">
@ -65,7 +115,6 @@
<% }); %>
</ul>
</main>
<%- include('./includes/bottom-navbar.ejs') %>
<%- include('./includes/bottom-scripts.ejs') %>
</body>

View File

@ -1,6 +1,5 @@
<!-- Bootstrap JS (optional, if you need Bootstrap JS features) -->
<script src="https://daball.me/vendor/jquery/jquery.min.js"></script>
<script src="https://daball.me/vendor/popper.js/dist/popper.min.js"></script>
<script src="https://daball.me/vendor/bootstrap/js/bootstrap.bundle.min.js"></script>
<script src="https://daball.me/vendor/jquery-easing/jquery.easing.min.js"></script>
<script src="https://daball.me/layouts/blog/js/blog.min.js"></script>
<script src="https://daball.me/layouts/blog/js/blog.min.js"></script>

View File

@ -10,7 +10,6 @@
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<script async src="https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-8937572456576531" crossorigin="anonymous"></script>
<!-- Bootstrap CSS -->
<link href="https://daball.me/vendor/bootstrap/css/bootstrap.min.css" rel="stylesheet" />
<link href="https://fonts.googleapis.com/css?family=Saira+Extra+Condensed:100,200,300,400,500,600,700,800,900" rel="stylesheet" />
@ -26,3 +25,4 @@
<link href="https://daball.me/vendor/simple-line-icons/css/simple-line-icons.css" rel="stylesheet" />
<link href="https://daball.me/layouts/blog/css/blog.min.css" rel="stylesheet" />
<link href="/css/nm3clol.css" rel="stylesheet" />
<script src="https://daball.me/vendor/jquery/jquery.min.js"></script>