Skip to content

Commit b06661c

Browse files
committed
Added google search results chrome extension example
1 parent 2d87842 commit b06661c

File tree

6 files changed

+235
-0
lines changed

6 files changed

+235
-0
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
// Listen to messages from our content-script
2+
chrome.runtime.onMessage.addListener(function(request, sender, sendResponse) {
3+
4+
5+
// If we're receiving a message with a query, search BookStack
6+
// and return the BookStack results in the response.
7+
if (request.query) {
8+
searchBookStack(request.query).then(results => {
9+
if (results) {
10+
sendResponse({results});
11+
}
12+
});
13+
}
14+
15+
// Return true enables 'sendResponse' to work async
16+
return true;
17+
});
18+
19+
20+
// Search our BookStack instance using the given query
21+
async function searchBookStack(query) {
22+
23+
// Load BookStack API details from our options
24+
const options = await loadOptions();
25+
for (const option of Object.values(options)) {
26+
if (!option) {
27+
console.log('Missing a required option');
28+
return;
29+
}
30+
}
31+
32+
// Query BookStack, making an authorized API search request
33+
const url = `${options.baseUrl}/api/search?query=${encodeURIComponent(query)}`;
34+
const resp = await fetch(url, {
35+
method: 'GET',
36+
headers: {
37+
Authorization: `Token ${options.tokenId}:${options.tokenSecret}`,
38+
}
39+
});
40+
41+
// Parse the JSON response and return the results
42+
const data = await resp.json();
43+
return data.data || null;
44+
}
45+
46+
47+
/**
48+
* Load our options from chrome's storage.
49+
* @returns Promise<Object>
50+
*/
51+
function loadOptions() {
52+
return new Promise((res, rej) => {
53+
chrome.storage.sync.get({
54+
tokenId: '',
55+
tokenSecret: '',
56+
baseUrl: '',
57+
}, options => {
58+
res(options);
59+
})
60+
});
61+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
const url = new URL(window.location.href);
2+
const query = url.searchParams.get("q");
3+
const resultContainer = document.getElementById('search');
4+
5+
// If we have a query in the URL, and a '#search' section, we are
6+
// likely on a search results page so we kick-off the display of
7+
// results by messaging the back-end to make the request to BookStack.
8+
if (query && resultContainer) {
9+
10+
chrome.runtime.sendMessage({query}, function(response) {
11+
// If re receive results back from our background script API call,
12+
// show them on the current page.
13+
if (response.results) {
14+
showResults(response.results);
15+
}
16+
});
17+
18+
}
19+
20+
/**
21+
* Display the given API search result objects as a list of links on
22+
* the current page, within the '#search' section.
23+
* @param {Object[]} results
24+
*/
25+
function showResults(results) {
26+
const resultHTML = results.map(result => {
27+
return `
28+
<a href="${result.url}">
29+
<h3>${result.type.toUpperCase()}: ${result.preview_html.name}</h3>
30+
<p style="color: #444; text-decoration: none;font-size:0.8em;">${result.preview_html.content}</p>
31+
</a>
32+
`;
33+
}).join('\n');
34+
35+
const header = `<h4>BookStack Results</h4>`;
36+
const container = document.createElement('div');
37+
container.innerHTML = header + resultHTML + '<hr>';
38+
resultContainer.prepend(container);
39+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
{
2+
"name": "BookStack Google Search",
3+
"description": "A simple demo extension to show BookStack results in google",
4+
"version": "1.0",
5+
"manifest_version": 3,
6+
"permissions": ["storage"],
7+
"host_permissions": ["http://*/", "https://*/"],
8+
"options_page": "options.html",
9+
"background": {
10+
"service_worker": "background.js"
11+
},
12+
"externally_connectable": {
13+
"matches": ["https://*.google.com/*"]
14+
},
15+
"content_scripts": [
16+
{
17+
"matches": ["https://*.google.com/*"],
18+
"js": ["content-script.js"]
19+
}
20+
]
21+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
<!DOCTYPE html>
2+
<html lang="en">
3+
<head>
4+
<meta charset="UTF-8">
5+
<meta http-equiv="X-UA-Compatible" content="IE=edge">
6+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
7+
<title>Options</title>
8+
9+
<style>
10+
form > div {
11+
margin-bottom: 1rem;
12+
}
13+
form p {
14+
margin: 0;
15+
}
16+
label {
17+
display: block;
18+
font-weight: bold;
19+
margin-bottom: 0.2rem;
20+
}
21+
</style>
22+
</head>
23+
<body>
24+
25+
<!-- This is a very simplistic options page to capture BookStack instance API details -->
26+
27+
<form>
28+
29+
<div>
30+
<label for="base-url">BookStack Base URL</label>
31+
<input id="base-url" name="baseUrl" type="text">
32+
<p>(No trailing slashes, Do not include '/api/' in URL, Must start with https:// or http://)</p>
33+
</div>
34+
35+
<div>
36+
<label for="token-id">API Token ID</label>
37+
<input id="token-id" name="tokenId" type="text">
38+
</div>
39+
40+
<div>
41+
<label for="token-secret">API Token Secret</label>
42+
<input id="token-secret" name="tokenSecret" type="text">
43+
</div>
44+
45+
<button>Save</button>
46+
47+
<p id="message"></p>
48+
49+
</form>
50+
51+
52+
<script src="options.js"></script>
53+
54+
</body>
55+
</html>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
const inputs = [...document.querySelectorAll('input[type="text"]')];
2+
const form = document.querySelector('form');
3+
const message = document.getElementById('message');
4+
5+
// Store settings on submit
6+
form.addEventListener('submit', event => {
7+
8+
event.preventDefault();
9+
10+
const settings = {};
11+
for (const input of inputs) {
12+
settings[input.name] = input.value;
13+
}
14+
15+
chrome.storage.sync.set(settings, () => {
16+
message.textContent = 'Settings updated!';
17+
});
18+
19+
});
20+
21+
// Restore settings on load
22+
chrome.storage.sync.get({
23+
tokenId: '',
24+
tokenSecret: '',
25+
baseUrl: '',
26+
}, settings => {
27+
for (const input of inputs) {
28+
input.value = settings[input.name];
29+
}
30+
});
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
# BookStack in Google Search Results Chrome Extension
2+
3+
This is a very rough and simplistic example of a Google chrome extension that will inject BookStack
4+
search results into the page when making google.com searches.
5+
6+
**This is only meant as an example or foundation**, it is not a fully featured/finished/tested extension.
7+
The styles are quite bad and it may be prone to breaking. I am not looking to improve or expand this extension
8+
so PRs, unless minor issue fixes, will not be accepted.
9+
10+
If you look to build this out into a proper chrome-store extension, please don't use the "BookStack" name
11+
or logo alone and make it clear your extension is unofficial.
12+
13+
## Requirements
14+
15+
You will need a Chrome (or Chromium based browser) instance where you can enable developer mode in extensions.
16+
You will also need BookStack API credentials (TOKEN_ID & TOKEN_SECRET) at the ready.
17+
18+
## Usage
19+
20+
This extension is not on the app store but you can side-load it with relative ease.
21+
Within chrome:
22+
23+
- Go to "Manage Extensions"
24+
- Toggle "Developer mode" on.
25+
- Click the "Load unpacked" option.
26+
- Select this folder.
27+
28+
You will need to configure the extension options and fill in your BookStack instance API details.
29+
You can get to this by right-clicking the extension in the top of the browser and clicking "Options", or via "Manage Extension" > Click "Details" on the extension > "Extension Options".

0 commit comments

Comments
 (0)