mirror of
https://github.com/fleetdm/fleet
synced 2026-05-23 17:08:53 +00:00
Website: Update links in query library and syntax highlighting on policy pages. (#23591)
Closes: #23516 Closes: #23517 Changes: - Updated the headings on the /queries page to be clickable links - Updated the query detail page to highlight osquery tables/columns in queries - Updated code blocks on the query details page to prevent the copy button from covering the content in the code block.
This commit is contained in:
parent
1551157c23
commit
c1ed73a8b4
5 changed files with 121 additions and 18 deletions
25
website/api/controllers/view-query-detail.js
vendored
25
website/api/controllers/view-query-detail.js
vendored
|
|
@ -34,6 +34,29 @@ module.exports = {
|
|||
throw 'notFound';
|
||||
}
|
||||
|
||||
// Find the related osquery table documentation for tables used in this query, and grab the keywordsForSyntaxHighlighting from each table used.
|
||||
let allTablesInformation = _.filter(sails.config.builtStaticContent.markdownPages, (pageInfo)=>{
|
||||
return _.startsWith(pageInfo.url, '/tables/');
|
||||
});
|
||||
// Get all the osquery table names, we'll use this list to determine which tables are used.
|
||||
let allTableNames = _.pluck(allTablesInformation, 'title');
|
||||
// Create an array of words in the query.
|
||||
let queryWords = _.words(query.query, /[^ ]+/g);
|
||||
let columnNamesForSyntaxHighlighting = [];
|
||||
let tableNamesForSyntaxHighlighting = [];
|
||||
// Get all of the words that appear in both arrays
|
||||
let intersectionBetweenQueryWordsAndTableNames = _.intersection(queryWords, allTableNames);
|
||||
// For each matched osquery table, add the keywordsForSyntaxHighlighting and the names of the tables used into two arrays.
|
||||
for(let tableName of intersectionBetweenQueryWordsAndTableNames) {
|
||||
let tableMentionedInThisQuery = _.find(sails.config.builtStaticContent.markdownPages, {title: tableName});
|
||||
tableNamesForSyntaxHighlighting.push(tableMentionedInThisQuery.title);
|
||||
let keyWordsForThisTable = tableMentionedInThisQuery.keywordsForSyntaxHighlighting;
|
||||
columnNamesForSyntaxHighlighting = columnNamesForSyntaxHighlighting.concat(keyWordsForThisTable);
|
||||
}
|
||||
// Remove the table names from the array of column names to highlight.
|
||||
columnNamesForSyntaxHighlighting = _.difference(columnNamesForSyntaxHighlighting, tableNamesForSyntaxHighlighting);
|
||||
|
||||
|
||||
// Setting the meta title and description of this page using the query object, and falling back to a generic title or description if query.name or query.description are missing.
|
||||
let pageTitleForMeta = query.name ? query.name + ' | Query details' : 'Query details';
|
||||
let pageDescriptionForMeta = query.description ? query.description : 'View more information about a query in Fleet\'s standard query library';
|
||||
|
|
@ -43,6 +66,8 @@ module.exports = {
|
|||
queryLibraryYmlRepoPath: sails.config.builtStaticContent.queryLibraryYmlRepoPath,
|
||||
pageTitleForMeta,
|
||||
pageDescriptionForMeta,
|
||||
columnNamesForSyntaxHighlighting,
|
||||
tableNamesForSyntaxHighlighting,
|
||||
algoliaPublicKey: sails.config.custom.algoliaPublicKey,
|
||||
};
|
||||
|
||||
|
|
|
|||
54
website/assets/js/pages/query-detail.page.js
vendored
54
website/assets/js/pages/query-detail.page.js
vendored
|
|
@ -27,10 +27,58 @@ parasails.registerPage('query-detail', {
|
|||
},
|
||||
});
|
||||
}
|
||||
|
||||
$('pre code').each((i, block) => {
|
||||
window.hljs.highlightElement(block);
|
||||
let columnNamesForThisQuery = [];
|
||||
let tableNamesForThisQuery = [];
|
||||
if(this.columnNamesForSyntaxHighlighting){
|
||||
columnNamesForThisQuery = this.columnNamesForSyntaxHighlighting;
|
||||
}
|
||||
if(this.tableNamesForSyntaxHighlighting){
|
||||
tableNamesForThisQuery = this.tableNamesForSyntaxHighlighting;
|
||||
}
|
||||
// Sorting the arrays of keywords by length to match larger keywords first.
|
||||
columnNamesForThisQuery = columnNamesForThisQuery.sort((a,b)=>{
|
||||
return a.length < b.length ? 1 : -1;
|
||||
});
|
||||
tableNamesForThisQuery = tableNamesForThisQuery.sort((a,b)=>{
|
||||
return a.length < b.length ? 1 : -1;
|
||||
});
|
||||
(()=>{
|
||||
$('pre code').each((i, block) => {
|
||||
let tableNamesToHighlight = [];// Empty array to track the keywords that we will need to highlight
|
||||
for(let tableName of tableNamesForThisQuery){// Going through the array of keywords for this table, if the entire word matches, we'll add it to the
|
||||
for(let match of block.innerHTML.match(tableName+' ')||[]){
|
||||
tableNamesToHighlight.push(match);
|
||||
}
|
||||
}
|
||||
// Now iterate through the tableNamesToHighlight, replacing all matches in the elements innerHTML.
|
||||
let replacementHMTL = block.innerHTML;
|
||||
for(let keywordInExample of tableNamesToHighlight) {
|
||||
let regexForThisExample = new RegExp(keywordInExample, 'g');
|
||||
replacementHMTL = replacementHMTL.replace(regexForThisExample, '<span class="hljs-attr">'+_.trim(keywordInExample)+'</span> ');
|
||||
}
|
||||
$(block).html(replacementHMTL);
|
||||
let columnNamesToHighlight = [];// Empty array to track the keywords that we will need to highlight
|
||||
for(let columnName of columnNamesForThisQuery){// Going through the array of keywords for this table, if the entire word matches, we'll add it to the
|
||||
for(let match of block.innerHTML.match(columnName)||[]){
|
||||
columnNamesToHighlight.push(match);
|
||||
}
|
||||
}
|
||||
|
||||
for(let keywordInExample of columnNamesToHighlight) {
|
||||
let regexForThisExample = new RegExp(keywordInExample, 'g');
|
||||
replacementHMTL = replacementHMTL.replace(regexForThisExample, '<span class="hljs-string">'+_.trim(keywordInExample)+'</span>');
|
||||
}
|
||||
$(block).html(replacementHMTL);
|
||||
window.hljs.highlightElement(block);
|
||||
// After we've highlighted our keywords, we'll highlight the rest of the codeblock
|
||||
// If this example is a single-line, we'll do some basic formatting to make it more human-readable.
|
||||
if($(block)[0].innerText.match(/\n/gmi)){
|
||||
$(block).addClass('has-linebreaks');
|
||||
} else {
|
||||
$(block).addClass('no-linebreaks');
|
||||
}
|
||||
});
|
||||
})();
|
||||
$('[purpose="copy-button"]').on('click', async function() {
|
||||
let code = $(this).siblings('pre').find('code').text();
|
||||
$(this).addClass('copied');
|
||||
|
|
|
|||
37
website/assets/styles/pages/query-detail.less
vendored
37
website/assets/styles/pages/query-detail.less
vendored
|
|
@ -249,11 +249,11 @@
|
|||
position: absolute;
|
||||
top: 11px;
|
||||
right: 10px;
|
||||
// padding: 9px;
|
||||
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;
|
||||
|
|
@ -273,7 +273,7 @@
|
|||
pre {
|
||||
width: 100%;
|
||||
max-width: 100%;
|
||||
padding: 16px 24px;
|
||||
padding: 16px 44px 16px 24px;
|
||||
border: 1px solid #E2E4EA;
|
||||
background: #F9FAFC;
|
||||
border-radius: 4px;
|
||||
|
|
@ -281,6 +281,12 @@
|
|||
margin-bottom: 24px;
|
||||
code {
|
||||
color: #515774;
|
||||
&.has-linebreaks {
|
||||
white-space: pre;
|
||||
}
|
||||
&.no-linebreaks {
|
||||
white-space: normal;
|
||||
}
|
||||
font-family: 'Source Code Pro';
|
||||
font-size: 14px;
|
||||
font-weight: 400;
|
||||
|
|
@ -288,8 +294,33 @@
|
|||
.hljs-keyword { // SQL keywords (SELECT, FROM, WHERE, IN, etc.)
|
||||
color: #AE6DDF;
|
||||
}
|
||||
[purpose='line-break']:not(:first-of-type)::before {
|
||||
content: '\a';
|
||||
}
|
||||
.hljs-attr { // For table and column names
|
||||
.hljs-keyword {
|
||||
color: #FFF;
|
||||
}
|
||||
.hljs-string { // For words wrapped in quotation marks
|
||||
color: #FFF;
|
||||
}
|
||||
color: #FFF;
|
||||
background-color: #AE6DDF;
|
||||
border-radius: 3px;
|
||||
white-space: pre;
|
||||
vertical-align: baseline;
|
||||
span {
|
||||
padding: 0;
|
||||
}
|
||||
}
|
||||
.hljs-number {
|
||||
color: #f5871f;
|
||||
}
|
||||
.hljs-string { // For words wrapped in quotation marks
|
||||
color: #3DB67B;
|
||||
color: #4fd061;
|
||||
.hljs-keyword {
|
||||
color: #4fd061;
|
||||
}
|
||||
}
|
||||
background-color: @ui-off-white;
|
||||
border: none;
|
||||
|
|
|
|||
17
website/assets/styles/pages/query-library.less
vendored
17
website/assets/styles/pages/query-library.less
vendored
|
|
@ -103,10 +103,6 @@
|
|||
height: 16px;
|
||||
margin-right: 8px;
|
||||
}
|
||||
// span {
|
||||
// padding-left: 15px;
|
||||
// padding-right: 0px;
|
||||
// }
|
||||
background: #FFF;
|
||||
&::placeholder {
|
||||
font-size: 16px;
|
||||
|
|
@ -188,11 +184,14 @@
|
|||
max-width: 663px;
|
||||
}
|
||||
[purpose='policy-name'] {
|
||||
color: #192147;
|
||||
font-size: 20px;
|
||||
font-weight: 800;
|
||||
line-height: 24px;
|
||||
margin-bottom: 8px;
|
||||
a {
|
||||
text-decoration: none;
|
||||
color: #192147;
|
||||
font-size: 20px;
|
||||
font-weight: 800;
|
||||
line-height: 24px;
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
}
|
||||
[purpose='policy-description'] {
|
||||
margin-top: 16px;
|
||||
|
|
|
|||
6
website/views/pages/query-library.ejs
vendored
6
website/views/pages/query-library.ejs
vendored
|
|
@ -53,7 +53,7 @@
|
|||
<div purpose="policy" class="d-flex flex-lg-row flex-column justify-content-between">
|
||||
<div purpose="policy-name-and-description" class="d-flex flex-column">
|
||||
<div class="d-flex flex-column">
|
||||
<p purpose="policy-name"><%- policy.name %></p>
|
||||
<p purpose="policy-name"><a href="/queries/<%- policy.slug%>"><%- policy.name %></a></p>
|
||||
<% if(policy.tags.includes('premium')) {%><div purpose="premium-badge">PREMIUM</div><% } %>
|
||||
<% if(policy.requiresMdm) {%><div purpose="requires-mdm-badge">mdm-required</div><% } %>
|
||||
</div>
|
||||
|
|
@ -77,7 +77,7 @@
|
|||
<div purpose="policy" class="d-flex flex-lg-row flex-column justify-content-between">
|
||||
<div purpose="policy-name-and-description" class="d-flex flex-column">
|
||||
<div class="d-flex flex-column">
|
||||
<p purpose="policy-name"><%- policy.name %></p>
|
||||
<p purpose="policy-name"><a href="/queries/<%- policy.slug%>"><%- policy.name %></a></p>
|
||||
<% if(policy.tags.includes('premium')) {%><div purpose="premium-badge">PREMIUM</div><% } %>
|
||||
<% if(policy.requiresMdm) {%><div purpose="requires-mdm-badge">mdm-required</div><% } %>
|
||||
</div>
|
||||
|
|
@ -101,7 +101,7 @@
|
|||
<div purpose="policy" class="d-flex flex-lg-row flex-column justify-content-between">
|
||||
<div purpose="policy-name-and-description" class="d-flex flex-column">
|
||||
<div class="d-flex flex-column">
|
||||
<p purpose="policy-name"><%- policy.name %></p>
|
||||
<p purpose="policy-name"><a href="/queries/<%- policy.slug%>"><%- policy.name %></a></p>
|
||||
<% if(policy.tags.includes('premium')) {%><div purpose="premium-badge">PREMIUM</div><% } %>
|
||||
<% if(policy.requiresMdm) {%><div purpose="requires-mdm-badge">mdm-required</div><% } %>
|
||||
</div>
|
||||
|
|
|
|||
Loading…
Reference in a new issue