]> BookStack Code Mirror - api-scripts/commitdiff
Added google search results chrome extension example
authorDan Brown <redacted>
Mon, 9 May 2022 17:08:36 +0000 (18:08 +0100)
committerDan Brown <redacted>
Mon, 9 May 2022 17:08:36 +0000 (18:08 +0100)
chrome-extension-google-search-results/background.js [new file with mode: 0644]
chrome-extension-google-search-results/content-script.js [new file with mode: 0644]
chrome-extension-google-search-results/manifest.json [new file with mode: 0644]
chrome-extension-google-search-results/options.html [new file with mode: 0644]
chrome-extension-google-search-results/options.js [new file with mode: 0644]
chrome-extension-google-search-results/readme.md [new file with mode: 0644]

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