Website: Add app library & app details page (#24205)

Related to: #23792

Changes:
- Added /app-library, a page that displays information about
Fleet-maintained apps
- Added the app details page (/app-library/{app identifier}), a page
that gives users detailed information about a single Fleet-maintained
app
- Updated the build-static-content script to add information about
Fleet-maintained apps to the website's configuration.
This commit is contained in:
Eric
2024-11-27 13:43:23 -06:00
committed by GitHub
parent 459faf45d7
commit 0654172fa2
33 changed files with 1000 additions and 1 deletions
+60
View File
@@ -0,0 +1,60 @@
module.exports = {
friendlyName: 'View app details',
description: 'Display "App details" page.',
inputs: {
appIdentifier: {
type: 'string',
required: true,
description: 'the identifier of an app in Fleet\'s maintained app library.',
example: '1password'
},
},
exits: {
success: {
viewTemplatePath: 'pages/app-details'
},
badConfig: {
responseType: 'badConfig'
},
notFound: {
responseType: 'notFound'
},
},
fn: async function ({appIdentifier}) {
if (!_.isObject(sails.config.builtStaticContent) || !_.isArray(sails.config.builtStaticContent.appLibrary) || !sails.config.builtStaticContent.appLibrary) {
throw {badConfig: 'builtStaticContent.appLibrary'};
}
let thisApp = _.find(sails.config.builtStaticContent.appLibrary, { identifier: appIdentifier });
if (!thisApp) {
throw 'notFound';
}
// FUTURE: make these better.
let pageTitleForMeta = thisApp.name + ' | Fleet app library';
// let pageDescriptionForMeta = 'TODO'
// Respond with view.
return {
thisApp,
// pageDescriptionForMeta,
pageTitleForMeta,
};
}
};
+33
View File
@@ -0,0 +1,33 @@
module.exports = {
friendlyName: 'View app library',
description: 'Display "App library" page.',
exits: {
success: {
viewTemplatePath: 'pages/app-library'
},
badConfig: { responseType: 'badConfig' },
},
fn: async function () {
if (!_.isObject(sails.config.builtStaticContent) || !_.isArray(sails.config.builtStaticContent.appLibrary) || !sails.config.builtStaticContent.appLibrary) {
throw {badConfig: 'builtStaticContent.appLibrary'};
}
let allApps = sails.config.builtStaticContent.appLibrary;
allApps = _.sortBy(allApps, 'name');
// Respond with view.
return {allApps};
}
};
Binary file not shown.

After

Width:  |  Height:  |  Size: 3.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1016 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 977 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.4 KiB

+47
View File
@@ -0,0 +1,47 @@
parasails.registerPage('app-details', {
// ╦╔╗╔╦╔╦╗╦╔═╗╦ ╔═╗╔╦╗╔═╗╔╦╗╔═╗
// ║║║║║ ║ ║╠═╣║ ╚═╗ ║ ╠═╣ ║ ║╣
// ╩╝╚╝╩ ╩ ╩╩ ╩╩═╝ ╚═╝ ╩ ╩ ╩ ╩ ╚═╝
data: {
//…
},
// ╦ ╦╔═╗╔═╗╔═╗╦ ╦╔═╗╦ ╔═╗
// ║ ║╠╣ ║╣ ║ ╚╦╝║ ║ ║╣
// ╩═╝╩╚ ╚═╝╚═╝ ╩ ╚═╝╩═╝╚═╝
beforeMount: function() {
//…
},
mounted: async function() {
if(this.algoliaPublicKey) { // Note: Docsearch will only be enabled if sails.config.custom.algoliaPublicKey is set. If the value is undefined, the documentation search will be disabled.
docsearch({
appId: 'NZXAYZXDGH',
apiKey: this.algoliaPublicKey,
indexName: 'fleetdm',
container: '#docsearch-query',
placeholder: 'Search',
debug: false,
searchParameters: {
'facetFilters': ['section:queries']
},
});
}
$('[purpose="copy-button"]').on('click', async function() {
let code = $(this).siblings('pre').find('code').text();
$(this).addClass('copied');
await setTimeout(()=>{
$(this).removeClass('copied');
}, 2000);
navigator.clipboard.writeText(code);
});
},
// ╦╔╗╔╔╦╗╔═╗╦═╗╔═╗╔═╗╔╦╗╦╔═╗╔╗╔╔═╗
// ║║║║ ║ ║╣ ╠╦╝╠═╣║ ║ ║║ ║║║║╚═╗
// ╩╝╚╝ ╩ ╚═╝╩╚═╩ ╩╚═╝ ╩ ╩╚═╝╝╚╝╚═╝
methods: {
//…
}
});
+25
View File
@@ -0,0 +1,25 @@
parasails.registerPage('app-library', {
// ╦╔╗╔╦╔╦╗╦╔═╗╦ ╔═╗╔╦╗╔═╗╔╦╗╔═╗
// ║║║║║ ║ ║╠═╣║ ╚═╗ ║ ╠═╣ ║ ║╣
// ╩╝╚╝╩ ╩ ╩╩ ╩╩═╝ ╚═╝ ╩ ╩ ╩ ╩ ╚═╝
data: {
//…
},
// ╦ ╦╔═╗╔═╗╔═╗╦ ╦╔═╗╦ ╔═╗
// ║ ║╠╣ ║╣ ║ ╚╦╝║ ║ ║╣
// ╩═╝╩╚ ╚═╝╚═╝ ╩ ╚═╝╩═╝╚═╝
beforeMount: function() {
//…
},
mounted: async function() {
//…
},
// ╦╔╗╔╔╦╗╔═╗╦═╗╔═╗╔═╗╔╦╗╦╔═╗╔╗╔╔═╗
// ║║║║ ║ ║╣ ╠╦╝╠═╣║ ║ ║║ ║║║║╚═╗
// ╩╝╚╝ ╩ ╚═╝╩╚═╩ ╩╚═╝ ╩ ╩╚═╝╝╚╝╚═╝
methods: {
//…
}
});
+2
View File
@@ -78,4 +78,6 @@
@import 'pages/start.less';
@import 'pages/deals.less';
@import 'pages/testimonials.less';
@import 'pages/app-library.less';
@import 'pages/app-details.less';
+418
View File
@@ -0,0 +1,418 @@
#app-details {
h3 {
padding-top: 32px;
color: #192147;
font-size: 24px;
font-weight: 800;
line-height: 120%;
margin-bottom: 0px;
}
p {
color: #515774;
font-size: 16px;
font-weight: 400;
line-height: 150%;
}
[purpose='page-container'] {
padding: 64px 64px 32px 64px;
}
[purpose='page-content'] {
margin-left: auto;
margin-right: auto;
max-width: 1072px;
}
[purpose='breadcrumbs-and-search'] {
margin-bottom: 64px;
max-width: 1072px;
font-size: 14px;
[purpose='breadcrumbs'] {
margin-right: 24px;
}
[purpose='search'] {
// Note: We're using classes here to override the default Docsearch styles;
button {
width: 100%;
cursor: text;
margin: 0;
}
.DocSearch-Button {
border-top-left-radius: 6px;
border-bottom-left-radius: 6px;
border-top-right-radius: 6px;
border-bottom-right-radius: 6px;
border: 1px solid @core-fleet-black-25;
background-color: #FFF;
padding: 6px;
height: 36px;
margin: 0;
width: 256px;
}
.DocSearch-Button:hover {
box-shadow: none;
border: 1px solid @core-fleet-black-25;
color: @core-fleet-black-50;
}
.DocSearch-Search-Icon {
margin-left: 10px;
height: 16px;
width: 16px;
color: @core-fleet-black-50;
stroke-width: 3px;
}
.DocSearch-Button-Keys {
display: none;
}
.input-group:focus-within {
border: 1px solid @core-vibrant-blue;
}
.DocSearch-Button-Placeholder {
font-size: 16px;
font-weight: 400;
padding-left: 12px;
}
[purpose='disabled-search'] {
input {
padding-top: 6px;
padding-bottom: 6px;
border: none;
} &::placeholder {
font-size: 16px;
line-height: 24px;
color: #8B8FA2;
}
.input-group {
border-top-left-radius: 6px;
border-bottom-left-radius: 6px;
border-top-right-radius: 6px;
border-bottom-right-radius: 6px;
border: 1px solid @core-fleet-black-25;
background: #FFF;
}
.input-group:focus-within {
border: 1px solid @core-vibrant-blue;
}
.form-control {
border-radius: 6px;
padding: 6px;
height: 36px;
margin: 0;
width: 212px;
}
.docsearch-input:focus-visible {
outline: none;
}
.ds-input:focus {
outline: rgba(0, 0, 0, 0);
}
.input-group-text {
color: @core-fleet-black-50;
}
.form-control {
height: 36px;
padding: 0px;
font-size: 16px;
} &:focus {
border: none;
}
}
}
[purpose='breadcrumbs-category'] {
color: #8B8FA2;
margin-right: 8px;
font-size: 14px;
font-weight: 400;
line-height: 150%; /* */
&:hover {
color: #192147;
text-decoration: none;
}
}
[purpose='breadcrumbs-title'] {
margin-left: 8px;
}
}
[purpose='icon-and-name'] {
flex-direction: row;
align-items: center;
}
[purpose='app-icon'] {
img {
height: 80px;
}
margin-right: 24px;
}
[purpose='app-name'] {
color: #192147;
font-size: 32px;
font-style: normal;
font-weight: 800;
line-height: 150%;
margin-bottom: 0px;
}
[purpose='app-description'] {
color: #515774;
font-size: 16px;
font-style: normal;
font-weight: 400;
line-height: 150%;
margin-bottom: 0px;
padding: 16px 0px 32px 0px;
}
[purpose='app-details'] {
padding-right: 64px;
max-width: 800px;
width: 100%;
p {
margin-bottom: 24px;
margin-top: 16px;
color: #515774;
font-size: 16px;
font-weight: 400;
line-height: 150%;
}
a {
color: #515774;
text-decoration: underline;
text-underline-offset: 1px;
&:hover {
color: #515774;
}
}
[purpose='platform-and-version'] {
p {
color: #8B8FA2;
font-size: 16px;
font-weight: 400;
line-height: 150%;
margin-bottom: 0px;
margin-top: 0px;
}
}
}
[purpose='app-uninstall'] {
pre {
height: 141px;
}
}
[purpose='app-install'] {
ol {
counter-reset: custom-counter;
list-style-type: none;
padding-inline-start: 0px;
padding: 0;
margin-top: 16px;
margin-bottom: 32px;
ul > li {
text-indent: 0px;
margin-left: 0px;
}
> li {
counter-increment: custom-counter;
margin-left: 36px;
text-indent: -36px;
padding-left: 0px;
margin-bottom: 16px;
code:not(.nohighlight):not(.mermaid) {
display: inline;
}
p {
display: inline;
margin-bottom: 0px;
}
blockquote {
text-indent: 0px;
}
}
> li::before {
content: counter(custom-counter);
background-color: #E2E4EA;
width: 24px;
font-size: 13px;
display: inline-block;
border-radius: 50%;
margin-right: 10px;
padding: 2px 4px;
text-align: center;
line-height: 20px;
text-indent: 0px;
}
}
}
[purpose='app-check'] {
padding-bottom: 24px;
}
[purpose='right-sidebar'] {
width: 256px;
margin-left: 16px;
font-size: 14px;
transition-property: transform;
transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1);
transition-duration: 500ms;
a:not([purpose='edit-button']) {
margin-bottom: 8px;
display: block;
color: #515774;
&:hover {
text-decoration: none;
color: @core-fleet-black;
}
}
}
[purpose='docs-links'] {
a {
display: block;
}
}
[purpose='social-share-buttons'] {
padding-bottom: 24px;
margin-bottom: 24px;
border-bottom: 1px solid #E2E4EA;
a {
margin-right: 16px;
}
img {
height: 20px;
width: 20px;
}
}
[purpose='edit-button'] {
margin-top: 24px;
img {
width: 16px;
height: 16px;
display: inline;
margin-right: 8px;
}
padding: 6px 8px;
display: block;
color: @core-fleet-black-75;
text-decoration: none;
font-size: 14px;
line-height: 21px;
border-radius: 6px;
width: 102px;
background: rgba(25, 33, 71, 0.05);
&:hover {
background-color: rgba(25, 33, 71, 0.1);
}
&:active {
background-color: rgba(25, 33, 71, 0.1);
}
}
[purpose='codeblock'] {
padding: 0;
position: relative;
[purpose='copy-button'] {
position: absolute;
top: 11px;
right: 10px;
border-radius: 8px;
height: 32px;
width: 32px;
background: url('/images/icon-copy-16x16@2x.png');
background-color: #F9FAFC;
background-size: 14px 14px;
background-position: center;
background-repeat: no-repeat;
cursor: pointer;
&:hover {
background-color: #F2F2F5;
}
&.copied {
background: url('/images/icon-copy-clicked-checkmark-32x32@2x.png');
background-size: 32px 32px;
background-repeat: no-repeat;
background-position: center;
}
}
}
pre {
width: 100%;
max-width: 100%;
padding: 16px 44px 16px 24px;
border: 1px solid #E2E4EA;
background: #F9FAFC;
border-radius: 4px;
margin-top: 32px;
margin-bottom: 24px;
white-space: pre-wrap;
word-wrap: break-word;
overflow: auto;
code {
color: #515774;
font-family: 'Source Code Pro';
font-size: 14px;
font-weight: 400;
line-height: 150%;
background-color: @ui-off-white;
border: none;
padding: 0;
dispaly: block;
}
}
@media (max-width: 1200px) {
}
@media (max-width: 991px) {
[purpose='page-container'] {
padding: 32px;
}
[purpose='app-details'] {
padding-right: 0px;
max-width: 100%;
}
[purpose='right-sidebar'] {
width: 100%;
margin-left: 0px;
}
[purpose='breadcrumbs-and-search'] {
margin-bottom: 32px;
}
}
@media (max-width: 768px) {
[purpose='breadcrumbs-and-search'] {
max-width: 1072px;
font-size: 14px;
[purpose='breadcrumbs'] {
margin-bottom: 24px;
}
[purpose='search'] {
width: 100%;
.DocSearch-Button {
width: 100%;
}
}
}
}
@media (max-width: 575px) {
[purpose='page-container'] {
padding: 32px 24px;
}
}
@media (max-width: 375px) {
[purpose='icon-and-name'] {
flex-direction: column;
align-items: flex-start;
}
}
}
+205
View File
@@ -0,0 +1,205 @@
#app-library {
[purpose='page-container'] {
padding: 64px;
}
[purpose='page-content'] {
max-width: 1072px;
margin-left: auto;
margin-right: auto;
}
[purpose='search-and-headline'] {
margin-bottom: 64px;
}
[purpose='page-title'] {
max-width: 662px;
margin-right: 16px;
}
[purpose='app-search'] {
// Note: We're using classes here to override the default Docsearch styles;
button {
width: 100%;
cursor: text;
margin: 0;
}
.DocSearch-Button {
border-top-left-radius: 6px;
border-bottom-left-radius: 6px;
border-top-right-radius: 6px;
border-bottom-right-radius: 6px;
border: 1px solid @core-fleet-black-25;
background-color: #FFF;
padding: 8px 15px;
height: 36px;
margin: 0;
width: 221px;
}
.DocSearch-Button:hover {
box-shadow: none;
border: 1px solid @core-fleet-black-25;
color: @core-fleet-black-50;
}
.DocSearch-Search-Icon {
margin-left: 0px;
margin-right: 8px;
height: 16px;
width: 16px;
color: @core-fleet-black-50;
stroke-width: 3px;
}
.DocSearch-Button-Keys {
display: none;
}
.input-group:focus-within {
border: 1px solid @core-vibrant-blue;
}
.DocSearch-Button-Placeholder {
font-size: 16px;
line-height: 16px;
font-weight: 400;
padding-left: 0px;
}
[purpose='disabled-search'] {
input {
padding-top: 6px;
padding-bottom: 6px;
border: none;
} &::placeholder {
font-size: 16px;
line-height: 24px;
color: #8B8FA2;
}
.input-group {
border-top-left-radius: 6px;
border-bottom-left-radius: 6px;
border-top-right-radius: 6px;
border-bottom-right-radius: 6px;
border: 1px solid @core-fleet-black-25;
background: #FFF;
}
.input-group:focus-within {
border: 1px solid @core-vibrant-blue;
}
.form-control {
border-radius: 6px;
padding: 6px;
height: 36px;
margin: 0;
width: 212px;
}
.docsearch-input:focus-visible {
outline: none;
}
.ds-input:focus {
outline: rgba(0, 0, 0, 0);
}
.input-group-text {
color: @core-fleet-black-50;
}
.form-control {
height: 36px;
padding: 0px;
font-size: 16px;
} &:focus {
border: none;
}
}
img {
height: 16px;
margin-right: 8px;
}
background: #FFF;
&::placeholder {
font-size: 16px;
color: @core-fleet-black-50;
}
}
[purpose='app-cards'] {
column-count: 3;
margin-right: -8px;
margin-left: -8px;
margin-bottom: -8px;
margin-top: -8px;
}
[purpose='app-card'] {
margin-right: 8px;
margin-left: 8px;
margin-bottom: 8px;
margin-top: 8px;
display: flex;
height: 92px;
min-width: 30%;
padding: 16px;
align-items: flex-start;
gap: 16px;
// flex: 1 0 0;
border-radius: 8px;
border: 1px solid #E2E4EA;
background: #FFF;
box-shadow: none;
&:hover {
text-decoration: none;
color: unset;
box-shadow: 0px 0px 0px 2px rgba(0, 0, 0, 0.03);
}
h4 {
color: var(--text-text-brand, #192147);
/* Title XS */
font-family: Inter;
font-size: 16px;
font-style: normal;
font-weight: 800;
line-height: 19.2px; /* 120% */
margin-bottom: 0px;
}
p {
color: var(--text-text-primary, #515774);
/* Body SM (FKA Card text) */
font-family: Inter;
font-size: 14px;
font-style: normal;
font-weight: 400;
line-height: 21px; /* 150% */
}
}
[purpose='app-icon'] {
margin-right: 16px;
img {
height: 60px;
}
}
@media (max-width: 991px) {
[purpose='page-container'] {
padding: 64px 32px;
}
[purpose='app-card'] {
min-width: 40%;
}
}
@media (max-width: 768px) {
[purpose='page-container'] {
padding: 48px 24px;
}
[purpose='app-search'] {
margin-top: 32px;
width: 100%;
.input-group {
width: 100%;
}
}
}
@media (max-width: 575px) {
[purpose='page-container'] {
padding: 32px 24px;
}
}
}
+2
View File
@@ -58,4 +58,6 @@ module.exports.policies = {
'deliver-deal-registration-submission': true,
'get-est-device-certificate': true,
'view-testimonials': true,
'view-app-library': true,
'view-app-details': true,
};
+13 -1
View File
@@ -297,10 +297,22 @@ module.exports.routes = {
action: 'view-testimonials',
locals: {
pageTitleForMeta: 'Customer stories',
pageDescriptionForMeta: 'See what people are saying about Fleet'
pageDescriptionForMeta: 'See what people are saying about Fleet.'
}
},
'GET /app-library': {
action: 'view-app-library',
locals: {
pageTitleForMeta: 'App library',
pageDescriptionForMeta: 'Discover what is available for install in Fleet\'s maintained app library.',
}
},
'GET /app-library/:appIdentifier': {
action: 'view-app-details',// Meta title and description set in view action
},
// ╦ ╔═╗╔═╗╔═╗╔═╗╦ ╦ ╦═╗╔═╗╔╦╗╦╦═╗╔═╗╔═╗╔╦╗╔═╗
// ║ ║╣ ║ ╦╠═╣║ ╚╦╝ ╠╦╝║╣ ║║║╠╦╝║╣ ║ ║ ╚═╗
// ╩═╝╚═╝╚═╝╩ ╩╚═╝ ╩ ╩╚═╚═╝═╩╝╩╩╚═╚═╝╚═╝ ╩ ╚═╝
+41
View File
@@ -1116,7 +1116,48 @@ module.exports = {
// Add the rituals dictionary to builtStaticContent.rituals
builtStaticContent.rituals = rituals;
},
//
// █████╗ ██████╗ ██████╗ ██╗ ██╗██████╗ ██████╗ █████╗ ██████╗ ██╗ ██╗
// ██╔══██╗██╔══██╗██╔══██╗ ██║ ██║██╔══██╗██╔══██╗██╔══██╗██╔══██╗╚██╗ ██╔╝
// ███████║██████╔╝██████╔╝ ██║ ██║██████╔╝██████╔╝███████║██████╔╝ ╚████╔╝
// ██╔══██║██╔═══╝ ██╔═══╝ ██║ ██║██╔══██╗██╔══██╗██╔══██║██╔══██╗ ╚██╔╝
// ██║ ██║██║ ██║ ███████╗██║██████╔╝██║ ██║██║ ██║██║ ██║ ██║
// ╚═╝ ╚═╝╚═╝ ╚═╝ ╚══════╝╚═╝╚═════╝ ╚═╝ ╚═╝╚═╝ ╚═╝╚═╝ ╚═╝ ╚═╝
//
async()=>{
let appLibrary = [];
// Get app library json
let appsJsonData = await sails.helpers.fs.readJson(path.join(topLvlRepoPath, '/server/mdm/maintainedapps/apps.json'));
// Then for each item in the json, build a configuration object to add to the sails.builtStaticContent.appLibrary array.
await sails.helpers.flow.simultaneouslyForEach(appsJsonData, async(app)=>{
let appInformation = {
identifier: app.identifier,
bundleIdentifier: app.bundle_identifier,
installerFormat: app.installer_format,
};
// Note: This method of getting information about the apps will be out of date until the JSON files in the /server/mdm/maintainedapps/testdata/ folder are updated.
let detailedInformationAboutThisApp = await sails.helpers.fs.readJson(path.join(topLvlRepoPath, '/server/mdm/maintainedapps/testdata/'+app.identifier+'.json'))
.intercept('doesNotExist', ()=>{
return new Error(`Could not build app library configuration from testdata folder. When attempting to read a JSON configuration file for ${app.identifier}, no file was found at ${path.join(topLvlRepoPath, '/server/mdm/maintainedapps/testdata/'+app.identifier+'.json. Was it moved?')}.`);
});
// Grab the latest information about these apps from the Homebrew API.
// let detailedInformationAboutThisApp = await sails.helpers.http.get(`https://formulae.brew.sh/api/cask/${app.identifier}.json`)
// .intercept((error)=>{
// return new Error(`Could not build app library configuration. When attempting to send a request to the homebrew API to get the latest information about ${app.identifier}, an error occured. Full error: ${util.inspect(error, {depth: null})}`);
// });
// let scriptToUninstallThisApp = await sails.helpers.fs.read(path.join(topLvlRepoPath, `/server/mdm/maintainedapps/testdata/scripts/${app.identifier}_uninstall.golden.sh`))
// .intercept('doesNotExist', ()=>{
// return new Error(`Could not build app library configuration from testdata folder. When attempting to read an uninstall script for ${app.identifier}, no file was found at ${path.join(topLvlRepoPath, '/server/mdm/maintainedapps/testdata/scripts/'+app.identifier+'_uninstall.golden.sh. Was it moved?')}.`);
// });
// appInformation.uninstallScript = scriptToUninstallThisApp;
appInformation.version = detailedInformationAboutThisApp.version.split(',')[0];
appInformation.description = detailedInformationAboutThisApp.desc;
appInformation.name = detailedInformationAboutThisApp.name[0];
appLibrary.push(appInformation);
});
builtStaticContent.appLibrary = appLibrary;
},
]);
// ██████╗ ███████╗██████╗ ██╗ █████╗ ██████╗███████╗ ███████╗ █████╗ ██╗██╗ ███████╗██████╗ ██████╗
// ██╔══██╗██╔════╝██╔══██╗██║ ██╔══██╗██╔════╝██╔════╝ ██╔════╝██╔══██╗██║██║ ██╔════╝██╔══██╗██╔════╝██╗
+2
View File
@@ -467,6 +467,8 @@
<script src="/js/pages/admin/email-templates.page.js"></script>
<script src="/js/pages/admin/generate-license.page.js"></script>
<script src="/js/pages/admin/sandbox-waitlist.page.js"></script>
<script src="/js/pages/app-details.page.js"></script>
<script src="/js/pages/app-library.page.js"></script>
<script src="/js/pages/articles/articles.page.js"></script>
<script src="/js/pages/articles/basic-article.page.js"></script>
<script src="/js/pages/connect-vanta.page.js"></script>
+88
View File
@@ -0,0 +1,88 @@
<div id="app-details" v-cloak>
<div purpose="page-container">
<div purpose="page-content" class="container p-0">
<div purpose="breadcrumbs-and-search" class="d-flex flex-md-row flex-column justify-content-between align-items-md-center align-items-start">
<div purpose="breadcrumbs" class="d-flex flex-row align-items-start">
<div>
<a purpose="breadcrumbs-category" class="text-nowrap" href="/app-library">App library</a>/
</div>
<div purpose="breadcrumbs-title">
<span><%- thisApp.name %></span>
</div>
</div>
<!-- <div purpose="search" id="docsearch-query" class="d-flex">
<div purpose="disabled-search" class="d-flex w-100">
<div class="input-group">
<div class="input-group-prepend">
<span class="input-group-text border-0 bg-transparent pl-3" >
<img style="height: 16px; width: 16px;" class="search" alt="search" src="/images/icon-search-16x16@2x.png">
</span>
</div>
<div class="form-control border-0 ">
<input class="docsearch-input pr-1"
placeholder="Search" aria-label="Search the handbook"
/>
</div>
</div>
</div>
</div> -->
</div>
<div purpose="app-details-and-sidebar" class="d-flex flex-nowrap flex-lg-row flex-column">
<div purpose="app-details" class="d-inline-flex flex-column flex-shrink w-0">
<div purpose="icon-and-name" class="d-flex">
<div purpose="app-icon"><img alt="<%- thisApp.name %> icon" src="/images/app-icon-<%- thisApp.identifier %>-60x60@2x.png"></div>
<div class="d-flex flex-column">
<h2 purpose="app-name"><%- thisApp.name %></h2>
<div purpose="platform-and-version" class="d-flex flex-row">
<p>macOS | <%- thisApp.version %></p>
</div>
</div>
</div>
<p purpose="app-description"><%- thisApp.description %></p>
<div purpose="app-install">
<h3>Self-service install</h3>
<p>To install <%- thisApp.name %> on your work computer:</p>
<ol>
<li>Navigate to the <strong>Fleet Desktop</strong> icon in the OS menu bar and select <strong>My device</strong>.</li>
<li>From the <strong>Self-service</strong> tab, navigate to <%- thisApp.name %> and click <strong>Install</strong>.</li>
</ol>
<p>Dont see <%- thisApp.name %> or the Fleet Desktop icon? Send a <a :href="'/app-library/'+thisApp.identifier">link to this page</a> to your IT team.</p>
</div>
<!-- <div purpose="app-uninstall">
<h3>Uninstall <%- thisApp.name %></h3>
<p>Run the following script in your terminal to uninstall <%- thisApp.name %>:</p>
<div purpose="codeblock">
<div purpose="copy-button"></div>
<pre><code class="hljs bash"><%= thisApp.uninstallScript %></code></pre>
</div>
</div> -->
<div purpose="app-check">
<h3>Is <%- thisApp.name %> up to date?</h3>
<p>Run this query in Fleet to find old versions of 1Password across all your computers:</p>
<div purpose="codeblock">
<div purpose="copy-button"></div>
<pre><code class="hljs sql">SELECT 1 FROM apps WHERE bundle_identifier = '<%= thisApp.bundleIdentifier %>';</code></pre>
</div>
</div>
</div>
<div purpose="right-sidebar" class="d-flex flex-column">
<div purpose="social-share-buttons" class="d-flex flex-column order-lg-2 order-1">
<p><strong>Share</strong></p>
<div class="d-flex flex-row">
<a :href="`https://news.ycombinator.com/submitlink?u=https://fleetdm.com/app-library/${encodeURIComponent(thisApp.identifier)}&t=${encodeURIComponent(thisApp.name)}`"><img src="/images/social-share-icon-hacker-news-20x20@2x.png" alt="Share this article on Hacker News"></a>
<a :href="`https://www.linkedin.com/sharing/share-offsite/?url=${encodeURIComponent('https://fleetdm.com/app-library/'+thisApp.identifier)}`"><img src="/images/social-share-icon-linkedin-20x20@2x.png" alt="Share this article on LinkedIn"></a>
<a :href="`https://twitter.com/intent/tweet?url=${encodeURIComponent('https://fleetdm.com/app-library/'+thisApp.identifier)}`"><img src="/images/social-share-icon-twitter-20x20@2x.png" alt="Share this article on Twitter"></a>
</div>
</div>
<div purpose="docs-links" class="order-3">
<a href="/docs">Docs</a>
<a href="/docs/rest-api">REST API</a>
<a href="/guides">Guides</a>
<!-- <a purpose="edit-button" class="d-flex align-items-center text-nowrap" target="_blank" :href="'https://github.com/fleetdm/fleet/edit/main/'+queryLibraryYmlRepoPath"><img alt="A pencil icon" src="/images/pencil-16x16@2x.png">Edit page</a> -->
</div>
</div>
</div>
</div>
</div>
</div>
<%- /* Expose server-rendered data as window.SAILS_LOCALS :: */ exposeLocalsToBrowser() %>
+64
View File
@@ -0,0 +1,64 @@
<div id="app-library" v-cloak>
<div purpose="page-container">
<div purpose="page-content" class="d-flex flex-column">
<div purpose="search-and-headline" class="d-flex flex-md-row flex-column justify-content-between align-items-md-center">
<div purpose="page-title" class="d-flex flex-column">
<h1>App library</h1>
<!-- <p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Etiam porta urna non massa viverra, in consequat urna tincidunt.</p> -->
</div>
<!-- <div purpose="app-search" id="docsearch-query" class="d-flex">
<div purpose="disabled-search" class="d-flex w-100">
<div class="input-group d-flex flex-nowrap">
<div class="input-group-prepend">
<span class="input-group-text border-0 bg-transparent" >
<img style="height: 16px; width: 16px;" class="search" alt="search" src="/images/icon-search-16x16@2x.png">
</span>
</div>
<div class="form-control border-0 ">
<input class="docsearch-input pr-1"
placeholder="Search" aria-label="Search"
/>
</div>
</div>
</div>
</div> -->
</div>
<div purpose="app-cards" class="card-deck flex-wrap">
<% for(let app of allApps) { %>
<a href="/app-library/<%- app.identifier %>" purpose="app-card" class="card d-flex">
<div class="d-flex flex-row align-items-center justify-content-start">
<div purpose="app-icon">
<img alt="<%- app.name %> icon" src="/images/app-icon-<%- app.identifier %>-60x60@2x.png">
</div>
<div class="flex-column">
<h4><%- app.name %></h4>
<p purpose="app-version" class="mb-0">
<%- app.version %>
</p>
</div>
</div>
</a>
<% } %>
<!--
<a :href="'/app-library/'+app.identifier" purpose="app-card" class="card d-flex" v-for="app in allApps">
<div class="d-flex flex-row align-items-center justify-content-start">
<div purpose="app-icon">
<img :src="'/images/app-icon-'+app.identifier+'-60x60@2x.png'">
</div>
<div class="flex-column">
<h4>{{app.name}}</h4>
<p purpose="app-version" class="mb-0">
{{app.version}}
</p>
</div>
</div>
</a> -->
<div purpose="app-card" class="card d-flex invisible"></div>
<div purpose="app-card" class="card d-flex invisible"></div>
<div purpose="app-card" class="card d-flex invisible"></div>
</div>
</div>
</div>
</div>
<%- /* Expose server-rendered data as window.SAILS_LOCALS :: */ exposeLocalsToBrowser() %>