Merge pull request #3137 from omnivore-app/feat/mozilla-opt-in
Add privacy opt-in for firefox extension
This commit is contained in:
@ -11,7 +11,7 @@
|
||||
"url": "https://omnivore.app/"
|
||||
},
|
||||
"homepage_url": "https://omnivore.app/",
|
||||
"content_security_policy": "default-src 'none'; child-src 'none'; manifest-src 'none'; media-src 'none'; object-src 'none'; worker-src 'none'; connect-src https://storage.googleapis.com/ https://api-prod.omnivore.app/api/ blob:; frame-src 'none'; font-src 'none'; img-src data:; script-src 'self'; script-src-elem 'self'; script-src-attr 'none'; style-src 'self'; style-src-elem 'self'; style-src-attr 'none'; base-uri 'none'; form-action 'none'; block-all-mixed-content; upgrade-insecure-requests; report-uri https://api.jeurissen.co/reports/csp/webext/omnivore/",
|
||||
"content_security_policy": "default-src 'none'; child-src 'none'; manifest-src 'none'; media-src 'none'; object-src 'none'; worker-src 'none'; connect-src https://storage.googleapis.com/ https://api-prod.omnivore.app/api/ blob:; frame-src 'none'; font-src 'none'; img-src data:; script-src 'self'; script-src-elem 'self' 'unsafe-inline'; script-src-attr 'unsafe-inline'; style-src 'self' 'unsafe-inline'; style-src-elem 'self' 'unsafe-inline'; style-src-attr 'none'; base-uri 'none'; form-action 'none'; block-all-mixed-content; upgrade-insecure-requests; report-uri https://api.jeurissen.co/reports/csp/webext/omnivore/",
|
||||
"icons": {
|
||||
"16": "/images/extension/icon-16.png",
|
||||
"24": "/images/extension/icon-24.png",
|
||||
|
||||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@ -7,6 +7,7 @@
|
||||
|
||||
<meta name="viewport" content="width=device-width,initial-scale=1">
|
||||
|
||||
<style>
|
||||
|
||||
html,
|
||||
body {
|
||||
@ -28,7 +29,7 @@ body {
|
||||
background: linear-gradient(150deg, #fff 55%, #ffde8c 55%);
|
||||
background-color: #fff;
|
||||
color: #3d3d3d;
|
||||
font-family: sans-serif;
|
||||
font-family: Inter, sans-serif;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
|
||||
@ -1,3 +1,3 @@
|
||||
OMNIVORE_URL="https://demo.omnivore.app"
|
||||
OMNIVORE_GRAPHQL_URL="https://api-demo.omnivore.app/api/"
|
||||
EXTENSION_NAME="Omnivore *DEMO*"
|
||||
EXTENSION_NAME="Omnivore *DEMO*"
|
||||
4
pkg/extension/.env.firefox
Normal file
4
pkg/extension/.env.firefox
Normal file
@ -0,0 +1,4 @@
|
||||
OMNIVORE_URL="https://omnivore.app"
|
||||
OMNIVORE_GRAPHQL_URL="https://api-prod.omnivore.app/api/"
|
||||
EXTENSION_NAME="Omnivore"
|
||||
CONSENT_REQUIRED="true"
|
||||
@ -7,12 +7,14 @@ build: *
|
||||
rm -rf dist
|
||||
yarn build-prod
|
||||
|
||||
firefox: build
|
||||
firefox:
|
||||
echo "building firefox package"
|
||||
rm -rf dist
|
||||
yarn build-firefox
|
||||
FIREFOX_PKG_NAME="firefox-$(shell cat dist/manifest.json| jq -j .version).zip" ; \
|
||||
FIREFOX_SRC_NAME="firefox-$(shell cat dist/manifest.json| jq -j .version)-src.zip" ; \
|
||||
pushd dist; zip -r ../$$FIREFOX_PKG_NAME *; popd; \
|
||||
zip -r $$FIREFOX_SRC_NAME src/* yarn.lock package.json .env.production webpack.js replace-with-process-env.js; \
|
||||
zip -r $$FIREFOX_SRC_NAME src/* Makefile yarn.lock package.json .env.firefox webpack.js replace-with-process-env.js; \
|
||||
echo "done"
|
||||
|
||||
chrome: build
|
||||
|
||||
@ -8,6 +8,7 @@
|
||||
"build": "env EXT_ENV=local webpack --config webpack.js",
|
||||
"build-qa": "env EXT_ENV=qa webpack --config webpack.js",
|
||||
"build-demo": "env EXT_ENV=demo webpack --config webpack.js",
|
||||
"build-firefox": "env EXT_ENV=firefox webpack --config webpack.js",
|
||||
"build-prod": "env EXT_ENV=production webpack --config webpack.js",
|
||||
"pack": "web-ext build --overwrite-dest --source-dir dist --artifacts-dir dist_packed --filename omnivore-v{version}.zip",
|
||||
"pack-stores": "webext-store-incompat-fixer -i dist_packed/omnivore-v{version}.zip"
|
||||
@ -27,4 +28,4 @@
|
||||
"nanoid": "^4.0.2",
|
||||
"uuid": "^8.3.2"
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -2,7 +2,7 @@
|
||||
"manifest_version": 2,
|
||||
"name": "process.env.EXTENSION_NAME",
|
||||
"short_name": "process.env.EXTENSION_NAME",
|
||||
"version": "2.8.2",
|
||||
"version": "2.8.6",
|
||||
"description": "Save PDFs and Articles to your Omnivore library",
|
||||
"author": "Omnivore Media, Inc",
|
||||
"default_locale": "en",
|
||||
@ -11,7 +11,7 @@
|
||||
"url": "https://omnivore.app/"
|
||||
},
|
||||
"homepage_url": "https://omnivore.app/",
|
||||
"content_security_policy": "default-src 'none'; child-src 'none'; manifest-src 'none'; media-src 'none'; object-src 'none'; worker-src 'none'; connect-src https://storage.googleapis.com/ process.env.OMNIVORE_GRAPHQL_URL blob:; frame-src 'none'; font-src 'none'; img-src data:; script-src 'self'; script-src-elem 'self'; script-src-attr 'none'; style-src 'self'; style-src-elem 'self'; style-src-attr 'none'; base-uri 'none'; form-action 'none'; block-all-mixed-content; upgrade-insecure-requests; report-uri https://api.jeurissen.co/reports/csp/webext/omnivore/",
|
||||
"content_security_policy": "default-src 'none'; child-src 'none'; manifest-src 'none'; media-src 'none'; object-src 'none'; worker-src 'none'; connect-src https://storage.googleapis.com/ process.env.OMNIVORE_GRAPHQL_URL blob:; frame-src 'none'; font-src 'none'; img-src data:; script-src 'self'; script-src-elem 'self' 'unsafe-inline'; script-src-attr 'unsafe-inline'; style-src 'self' 'unsafe-inline'; style-src-elem 'self' 'unsafe-inline'; style-src-attr 'none'; base-uri 'none'; form-action 'none'; block-all-mixed-content; upgrade-insecure-requests; report-uri https://api.jeurissen.co/reports/csp/webext/omnivore/",
|
||||
"icons": {
|
||||
"16": "/images/extension/icon-16.png",
|
||||
"24": "/images/extension/icon-24.png",
|
||||
@ -95,4 +95,4 @@
|
||||
"web_accessible_resources": [
|
||||
"views/toast.html"
|
||||
]
|
||||
}
|
||||
}
|
||||
@ -642,10 +642,47 @@ function checkAuthOnFirstClickPostInstall(tabId) {
|
||||
return Promise.resolve(true)
|
||||
}
|
||||
|
||||
function getConsentGranted() {
|
||||
if (!process.env.CONSENT_REQUIRED) {
|
||||
return new Promise((resolve) => {
|
||||
resolve(true)
|
||||
})
|
||||
} else {
|
||||
return getStorageItem('consentGranted').then((consentGrantedStr) => {
|
||||
return consentGrantedStr == 'true'
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
function handleActionClick() {
|
||||
executeAction(function (currentTab) {
|
||||
extensionSaveCurrentPage(currentTab.id)
|
||||
})
|
||||
console.log('process.env.CONSENT_REQUIRED', process.env.CONSENT_REQUIRED)
|
||||
getConsentGranted()
|
||||
.then((consentGranted) => {
|
||||
if (consentGranted) {
|
||||
executeAction(function (currentTab) {
|
||||
extensionSaveCurrentPage(currentTab.id)
|
||||
})
|
||||
} else {
|
||||
getCurrentTab().then((currentTab) => {
|
||||
browserApi.tabs.sendMessage(currentTab.id, {
|
||||
action: ACTIONS.ShowConsentError,
|
||||
})
|
||||
})
|
||||
}
|
||||
})
|
||||
.catch(() => {
|
||||
console.log('extension consent not granted')
|
||||
getCurrentTab().then((currentTab) => {
|
||||
browserApi.tabs.sendMessage(currentTab.id, {
|
||||
action: ACTIONS.ShowConsentError,
|
||||
})
|
||||
})
|
||||
})
|
||||
// } else {
|
||||
// executeAction(function (currentTab) {
|
||||
// extensionSaveCurrentPage(currentTab.id)
|
||||
// })
|
||||
// }
|
||||
}
|
||||
|
||||
function executeAction(action) {
|
||||
@ -684,24 +721,6 @@ function executeAction(action) {
|
||||
})
|
||||
}
|
||||
|
||||
function getActionableState(tab) {
|
||||
if (tab.status !== 'complete') return false
|
||||
|
||||
const tabUrl = tab.pendingUrl || tab.url
|
||||
if (!tabUrl) return false
|
||||
|
||||
if (!tabUrl.startsWith('https://') && !tabUrl.startsWith('http://'))
|
||||
return false
|
||||
|
||||
if (
|
||||
tabUrl.startsWith('https://omnivore.app/') &&
|
||||
tabUrl.startsWith('https://dev.omnivore.app/')
|
||||
)
|
||||
return false
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
function init() {
|
||||
browserApi.tabs.onActivated.addListener(({ tabId }) => {
|
||||
// Due to a chrome bug, chrome.tabs.* may run into an error because onActivated is triggered too fast.
|
||||
@ -820,6 +839,30 @@ function init() {
|
||||
})
|
||||
},
|
||||
})
|
||||
|
||||
browser.runtime.onInstalled.addListener(async ({ reason, temporary }) => {
|
||||
// if (temporary) return // skip during development
|
||||
console.log('onInstalled: ', reason, temporary)
|
||||
switch (reason) {
|
||||
case 'update':
|
||||
getConsentGranted().then((consentGranted) => {
|
||||
if (!consentGranted) {
|
||||
const url = browser.runtime.getURL('views/installed.html')
|
||||
return browser.tabs.create({ url })
|
||||
} else {
|
||||
console.log('consent already granted, not showing installer.')
|
||||
}
|
||||
})
|
||||
break
|
||||
case 'install':
|
||||
{
|
||||
const url = browser.runtime.getURL('views/installed.html')
|
||||
await browser.tabs.create({ url })
|
||||
}
|
||||
break
|
||||
// see below
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
init()
|
||||
|
||||
@ -1,6 +1,7 @@
|
||||
/* global
|
||||
browserApi
|
||||
showToolbar
|
||||
showConsentError
|
||||
prepareContent
|
||||
getPageInfo
|
||||
ACTIONS
|
||||
@ -41,6 +42,8 @@
|
||||
sendResponse({ pong: true })
|
||||
} else if (action === ACTIONS.ShowToolbar) {
|
||||
showToolbar(payload)
|
||||
} else if (action === ACTIONS.ShowConsentError) {
|
||||
showConsentError()
|
||||
} else if (action === ACTIONS.UpdateStatus) {
|
||||
updateStatus(payload)
|
||||
} else if (action === ACTIONS.LabelCacheUpdated) {
|
||||
|
||||
@ -189,6 +189,12 @@
|
||||
)
|
||||
}
|
||||
|
||||
function showConsentError() {
|
||||
alert(
|
||||
'You have not granted the Omnivore extension consent to save pages. Check the extension options page to grant consent.'
|
||||
)
|
||||
}
|
||||
|
||||
function updateLabelsFromCache(payload) {
|
||||
;(async () => {
|
||||
await getStorageItem('labels').then((cachedLabels) => {
|
||||
@ -995,5 +1001,6 @@
|
||||
|
||||
window.showToolbar = showToolbar
|
||||
window.updateStatus = updateStatus
|
||||
window.showConsentError = showConsentError
|
||||
window.updateLabelsFromCache = updateLabelsFromCache
|
||||
})()
|
||||
|
||||
@ -64,6 +64,23 @@ function saveAutoDismissTime() {
|
||||
})
|
||||
}
|
||||
|
||||
function handleConsent() {
|
||||
var consentCheckbox = document.getElementById('consent-checkbox')
|
||||
setStorage({
|
||||
consentGranted: JSON.stringify(consentCheckbox.checked),
|
||||
})
|
||||
.then(() => {
|
||||
console.log('consent granted')
|
||||
})
|
||||
.catch((err) => {
|
||||
alert('Error setting consent: ' + err)
|
||||
})
|
||||
|
||||
if (!consentCheckbox.checked) {
|
||||
alert('This extension can not function without data collection consent.')
|
||||
}
|
||||
}
|
||||
|
||||
;(() => {
|
||||
document
|
||||
.getElementById('save-api-key-btn')
|
||||
@ -81,6 +98,11 @@ function saveAutoDismissTime() {
|
||||
: false
|
||||
})
|
||||
|
||||
getStorageItem('consentGranted').then((value) => {
|
||||
document.getElementById('consent-checkbox').checked =
|
||||
value == 'true' ? true : false
|
||||
})
|
||||
|
||||
document
|
||||
.getElementById('disable-auto-dismiss')
|
||||
.addEventListener('change', autoDismissChanged)
|
||||
|
||||
@ -7,6 +7,7 @@
|
||||
|
||||
<meta name="viewport" content="width=device-width,initial-scale=1">
|
||||
|
||||
<style>
|
||||
|
||||
html,
|
||||
body {
|
||||
@ -28,7 +29,7 @@ body {
|
||||
background: linear-gradient(150deg, #fff 55%, #ffde8c 55%);
|
||||
background-color: #fff;
|
||||
color: #3d3d3d;
|
||||
font-family: sans-serif;
|
||||
font-family: Inter, sans-serif;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
|
||||
117
pkg/extension/src/views/installed.html
Normal file
117
pkg/extension/src/views/installed.html
Normal file
@ -0,0 +1,117 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Data Collection Consent</title>
|
||||
<script src="/scripts/common.js"></script>
|
||||
<style>
|
||||
body {
|
||||
font-family: Arial, sans-serif;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
min-height: 100vh;
|
||||
background-color: #FFEA9F;
|
||||
}
|
||||
|
||||
#consent-container {
|
||||
max-width: 600px;
|
||||
margin: 20px;
|
||||
padding: 20px;
|
||||
border: 1px solid rgb(61, 61, 61);
|
||||
border-radius: 8px;
|
||||
background-color: #fff;
|
||||
box-shadow: rgb(177, 177, 177) 9px 9px 9px -9px;
|
||||
}
|
||||
|
||||
h1 {
|
||||
color: #333;
|
||||
}
|
||||
|
||||
.consent-text {
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.cta-container {
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
#consent-checkbox {
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
#consent-button {
|
||||
background-color: rgb(255, 234, 159);
|
||||
color: black;
|
||||
padding: 10px 20px;
|
||||
border: none;
|
||||
border-radius: 5px;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
#uninstall-link {
|
||||
color: #f44336;
|
||||
text-decoration: underline;
|
||||
margin-top: 10px;
|
||||
display: block;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<div id="consent-container">
|
||||
<h1>Omnivore</h1>
|
||||
<p class="consent-text">Thank you for installing the <a href="https://omnivore.app">Omnivore</a> Firefox extension. Use this extension to save links to your Omnivore library.</p>
|
||||
<p class="consent-text">If you are not familiar with Omnivore, you can learn more about us by reading <a href="https://blog.omnivore.app/p/getting-started-with-omnivore">Getting Started with Omnivore</a></p>
|
||||
|
||||
<p class="consent-text"> </p>
|
||||
|
||||
<h2>Privacy</h2>
|
||||
<p class="consent-text">To function Omnivore needs to collect some data. This data is used to authenticate you with our servers, and save pages to your library.</p>
|
||||
|
||||
|
||||
<p class="consent-text">Omnivore uses the following data:</p>
|
||||
<ul>
|
||||
<li><b>Tab Data</b>: When you invoke the extension, we access the content of the page you are currently viewing. This allows us to save the full content of the page, exactly how you are viewing it. That data is then transmitted to our servers and added to your library</li>
|
||||
<li><b>Authentication cookie</b>: When you invoke the extension we access the auth cookie you used to authenticate at <a href="https://omnivore.app">https://omnivore.app</a>. This cookie identifies you as an Omnivore user and ties the saving operation to your Omnivore account.</li>
|
||||
</ul>
|
||||
<p class="consent-text">For full details about the data we collect you can read our <a href="https://docs.omnivore.app/about/privacy-statement.html">privacy statement</a></p>
|
||||
|
||||
<label for="consent-checkbox">
|
||||
<input type="checkbox" id="consent-checkbox"> I consent to data collection
|
||||
</label>
|
||||
|
||||
<br>
|
||||
<button id="consent-button" onclick="handleConsent()">Give Consent</button>
|
||||
<a id="uninstall-link" href="#">I do not consent, and would like to uninstall the extension</a>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
function handleConsent() {
|
||||
var consentCheckbox = document.getElementById('consent-checkbox');
|
||||
setStorage({
|
||||
consentGranted: JSON.stringify(consentCheckbox.checked)
|
||||
}).then(() => {
|
||||
console.log('consent granted')
|
||||
})
|
||||
.catch((err) => {
|
||||
alert('Error setting consent: ' + err)
|
||||
})
|
||||
|
||||
if (!consentCheckbox.checked) {
|
||||
alert('You must grant consent to use this extension.')
|
||||
}
|
||||
}
|
||||
|
||||
document.getElementById('uninstall-link').addEventListener('click', function (e) {
|
||||
e.preventDefault();
|
||||
let uninstallingSelf = browser.management.uninstallSelf()
|
||||
console.log('uninstall self: ', uninstallingSelf)
|
||||
});
|
||||
</script>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
@ -46,6 +46,26 @@
|
||||
<button id="auto-dismiss-time-btn">Save</button>
|
||||
</p>
|
||||
|
||||
<p> </p>
|
||||
|
||||
<h1>Privacy</h1>
|
||||
<p class="consent-text">To function Omnivore needs to collect some data. This data is used to authenticate you with our servers, and save pages to your library.</p>
|
||||
|
||||
|
||||
<p class="consent-text">Omnivore uses the following data:</p>
|
||||
<ul>
|
||||
<li><b>Tab Data</b>: When you invoke the extension, we access the content of the page you are currently viewing. This allows us to save the full content of the page, exactly how you are viewing it. That data is then transmitted to our servers and added to your library</li>
|
||||
<li><b>Authentication cookie</b>: When you invoke the extension we access the auth cookie you used to authenticate at <a href="https://omnivore.app">https://omnivore.app</a>. This cookie identifies you as an Omnivore user and ties the saving operation to your Omnivore account.</li>
|
||||
</ul>
|
||||
<p class="consent-text">For full details about the data we collect you can read our <a href="https://docs.omnivore.app/about/privacy-statement.html">privacy statement</a></p>
|
||||
|
||||
<label for="consent-checkbox">
|
||||
<input type="checkbox" id="consent-checkbox"> I consent to data collection
|
||||
</label>
|
||||
|
||||
<button id="consent-button" onclick="handleConsent()">Save</button>
|
||||
|
||||
<p> </p>
|
||||
</div>
|
||||
<script src="/scripts/options.js"></script>
|
||||
</body>
|
||||
|
||||
Reference in New Issue
Block a user