How to inject CSS with user origin in a browser extension? - css

I have a very simple extension:
{
"manifest_version": 2,
"name": "User Style Sheet Workaround",
"version": "1",
"content_scripts": [
{
"matches": ["<all_urls>"],
"css": ["default.css"]
}
]
}
I want default.css to be injected as user origin, not as author origin. How can I do it?

Use chrome.tabs.insertCSS with cssOrigin: 'user' in the background script's URL change event.
For example, chrome.webNavigation.onCommitted (requires webNavigation permission) or chrome.tabs.onUpdated (doesn't require any special permissions).
The target sites to allow CSS injection should be added to permissions.
manifest.json:
{
"manifest_version": 2,
"name": "User Style Sheet Workaround",
"version": "1",
"background": {
"scripts": ["bg.js"]
},
"permissions": ["<all_urls>"]
}
bg.js:
chrome.tabs.onUpdated.addListener((tabId, info) => {
if (info.status === 'loading') {
chrome.tabs.insertCSS(tabId, {
file: 'style.css',
cssOrigin: 'user',
runAt: 'document_start',
// allFrames: true,
// matchAboutBlank: true,
}, () => chrome.runtime.lastError); // ignoring errors
}
});

Related

Strapi Hide Content Type

I've search for several hours how to hide a specific content type.
I found some post but they are too older and their solutions doesn't work in the actual's strapi.
For precisions, my collection type is declared inside a local plugin. I juste want to manage my collection inside the plugin page and I doesn't want it appear in the content type in the left menu.
If someone has a solution, it's can be really helpfull.
In new version of Strapi v3.6.6 — Community Edition there is an option in model
{
"kind": "collectionType",
"collectionName": "test",
"info": {
"name": "test"
},
"options": {
"increments": true,
"timestamps": true,
"draftAndPublish": true
},
**"pluginOptions": {
"content-manager": {
"visible": false
}
},**
"attributes": {
"name": {
"type": "string",
"required": true
},
}
}
They are working on this: https://github.com/strapi/rfcs/pull/22
But for now, based on official docs (plugin customization), you can overwrite file in content-manager plugin.
Be sure to check this file on strapi updates to avoid overwriting important code.
Copy file strapi-plugin-content-manager/services/data-mapper.js from your app node_modules into extensions/content-manager/services/
Now edit this file in your project and add your content type to array HIDDEN_CONTENT_TYPES following this pattern: plugins::[plugin-name].[content-type]
For example: plugins::ecommerce.product
...
const HIDDEN_CONTENT_TYPES = [
'plugins::upload.file',
'plugins::users-permissions.permission',
'plugins::users-permissions.role',
'plugins::ecommerce.product',
];
...
You can extend the plugin to make updates to the content-type's schema.
Copy the content-type schema from the plugin to your src folder.
In my case, I copied /strapi-plugin-navigation/server/content-types/audience/schema.json to /src/extensions/navigation/content-types/audience/schema.json (notice that the strapi-plugin- part of the folder name is removed) and added the following to it to hide the "Audience" content type from the content manager and content-type builder:
"pluginOptions": {
"content-manager": {
"visible": false
},
"content-type-builder": {
"visible": false
}
},
Official documentation here.
In Strapi v4 it is "visible": false
{
"kind": "collectionType",
"collectionName": "bookmark",
"info": {
"singularName": "bookmark",
"pluralName": "bookmarks",
"displayName": "Bookmark",
"description": ""
},
"options": {
"increments": true,
"timestamps": true,
"draftAndPublish": true
},
"pluginOptions": {},
"attributes": {
"index": {
"type": "integer",
"unique": false,
"visible": false
},
}
}

copyIndex() inside a listKeys()

We're trying to deploy an ARM template which deploys a Stream Analytics job with n Event Hubs outputs depending on an input parameter.
Right now we're having success with all but the listKeys() function inside the outputs property copy loop function which gets each Event Hub's primary keys:
"sharedAccessPolicyKey": "[listKeys(resourceId('Microsoft.EventHub/namespaces/eventhubs/authorizationRules', variables('clientEventHubNamespace'), parameters('clients')[copyIndex('outputs')].id, variables('clientEventHubClientSharedAccessName')), '2015-08-01').primaryKey]"
We get the error:
17:44:31 - Error: Code=InvalidTemplate; Message=Deployment template
validation failed: 'The template resource
'tailor-router-axgf7t3gtspue' at line '129' and column '10' is not
valid: The template function 'copyIndex' is not expected at this
location. The function can only be used in a resource with copy
specified. Please see https://aka.ms/arm-copy for usage details..
Please see https://aka.ms/arm-template-expressions for usage
details.'.
However, if we change this to be a specific index:
"sharedAccessPolicyKey": "[listKeys(resourceId('Microsoft.EventHub/namespaces/eventhubs/authorizationRules', variables('clientEventHubNamespace'), parameters('clients')[0].id, variables('clientEventHubClientSharedAccessName')), '2015-08-01').primaryKey]"
it works.
Is copyIndex('propertyName') inside a listKeys() a supported function?
If not, is there a workaround that would achieve the same effect?
Kind regards,
Nick
Stream Analytics job resource definition:
{
"apiVersion": "2016-03-01",
"type": "Microsoft.StreamAnalytics/StreamingJobs",
"name": "[variables('routerStreamAnalyticsName')]",
"location": "[variables('location')]",
"dependsOn": [ "clientsEventHubCopy" ],
"tags": {
"boundedContext": "[variables('boundedContextName')]"
},
"properties": {
"sku": {
"name": "[parameters('routerStreamAnalyticsSkuTier')]"
},
"outputErrorPolicy": "drop",
"eventsOutOfOrderPolicy": "adjust",
"eventsOutOfOrderMaxDelayInSeconds": 0,
"eventsLateArrivalMaxDelayInSeconds": 5,
"dataLocale": "en-US",
"compatibilityLevel": "1.0",
"inputs": [
{
"name": "input0",
"properties": {
"type": "stream",
"serialization": {
"type": "Avro"
},
"datasource": {
"type": "Microsoft.ServiceBus/EventHub",
"properties": {
"serviceBusNamespace": "[parameters('input0EventHubNamespace')]",
"sharedAccessPolicyName": "[parameters('input0EventHubSharedAccessPolicyName')]",
"sharedAccessPolicyKey": "[parameters('input0EventHubSharedAccessPolicyKey')]",
"eventHubName": "[parameters('input0EventHubName')]"
}
}
}
}
],
"transformation": {
"name": "routing",
"properties": {
"streamingUnits": "[parameters('routerStreamAnalyticsSkuTier')]",
"query": "omitted"
}
},
"copy": [
{
"name": "outputs",
"count": "[length(parameters('clients'))]",
"input": {
"name": "[parameters('clients')[copyIndex('outputs')].id]",
"properties": {
"datasource": {
"type": "Microsoft.ServiceBus/EventHub",
"properties": {
"serviceBusNamespace": "[variables('clientEventHubNamespace')]",
"sharedAccessPolicyName": "[variables('clientEventHubClientSharedAccessName')]",
"sharedAccessPolicyKey": "[listKeys(resourceId('Microsoft.EventHub/namespaces/eventhubs/authorizationRules', variables('clientEventHubNamespace'), parameters('clients')[copyIndex('outputs')].id, variables('clientEventHubClientSharedAccessName')), '2015-08-01').primaryKey]",
"eventHubName": "[parameters('clients')[copyIndex('outputs')].id]"
}
},
"serialization": {
"type": "Avro"
}
}
}
}
]
}
},
Thanks for reporting this and sorry for the inconvenience.
I just talked to the ARM team, we had an issue when copyindex was inside the index tags eg 'array[copyindex()]'. It should be fixed now.
Let us know how it goes.
Thanks,
JS - Azure Stream Analytics

View not displaying Model Data from Manifest.json

I'm trying to setup my app descriptor file (manifest.json) to include a named model, 'inputs' in its "models" object. From my understanding, after doing so, the model should be available throughout the app when provided the correct path (see XML View).
The reason I'd like to setup this manifest.json is because it's a best practice to configure all models in here.
In the controller, I'd like to get and then set the 'inputs' Model defined in the manifest.json --but how can this be done?
manifest.json (Where I have configured the 'inputs' model)
{
"_version": "1.1.0",
"sap.app": {
"_version": "1.1.0",
"id": "pricingTool",
"type": "application",
"applicationVersion": {
"version": "1.0.0"
},
"title": "{{appTitle}}",
"description": "{{appDescription}}",
"ach": "ach",
"resources": "resources.json",
"sourceTemplate": {
"id": "ui5template.basicSAPUI5ApplicationProject",
"version": "1.30.3"
},
},
"sap.ui": {
"_version": "1.1.0",
"technology": "UI5",
"icons": {
"icon": "",
"favIcon": "",
"phone": "",
"phone#2": "",
"tablet": "",
"tablet#2": ""
},
"deviceTypes": {
"desktop": true,
"tablet": true,
"phone": true
},
"supportedThemes": [
"sap_hcb",
"sap_bluecrystal"
]
},
"sap.ui5": {
"_version": "1.1.0",
"rootView": {
"viewName": "pricingTool.view.Main",
"type": "XML"
},
"dependencies": {
"minUI5Version": "1.30.0",
"libs": {
"sap.ui.core": {},
"sap.m": {},
"sap.ui.layout": {}
}
},
},
"contentDensities": {
"compact": true,
"cozy": true
},
"models": {
"inputs": {
"type": "sap.ui.model.json.JSONModel",
"uri": "model/inputs.json"
},
},
}
Main.controller.js (where the 'inputs' model should be set from the manifest file)
sap.ui.define([
'jquery.sap.global',
'sap/ui/core/mvc/Controller',
'sap/ui/model/json/JSONModel',
'sap/ui/model/Filter',
'sap/ui/model/FilterOperator',
'sap/m/MessageToast',
'pricingTool/model/viewControls',
'pricingTool/model/formatter',
'pricingTool/model/Utility',
'sap/ui/core/util/Export',
'sap/ui/core/util/ExportTypeCSV',
],
function (jQuery, Controller, JSONModel, Filter, FilterOperator, MessageToast, viewControls, formatter, Utility, Export, ExportTypeCSV) {
"use strict";
var mainController = Controller.extend("pricingTool.controller.Main", {
onInit: function(oEvent) {
//define named/default model(s)
var inputs = new JSONModel("./model/inputs.json");
//set model(s) to current xml view
this.getView().setModel(inputs, "inputs");
//this is one solution I have tried, but doesn't do anything:
// this.getView().setModel(this.getOwnerComponent().getModel("inputs"), "inputs");
//another solution I have tried:
//var inputs = this.getModel('input') <--I was hoping this would find the inputs defined in the manifest.json, but this doesn't work either
// this.getView().setModel(inputs, "inputs");
},
...
inputs.json
{
"propA" : "testVal"
}
XML View
<Button text="{inputs>propA}"></Button>
Components.js (Not sure what to do in the Component.js)
sap.ui.define([
'sap/ui/core/UIComponent'
],
function(UIComponent) {
"use strict";
var Component = UIComponent.extend("pricingTool.Component", {
metadata : {
metadata : {
manifest: "json"
},
rootView : "pricingTool.view.Main",
dependencies : {
libs : [
"sap.m",
"sap.ui.layout"
]
},
config : {
sample : {
files : [
"Main.view.xml",
"Main.controller.js"
]
}
}
},
init : function () {
// call the init function of the parent
UIComponent.prototype.init.apply(this, arguments);
}
});
return Component;
});
The problem is that the model property ("propA") is not displaying when I test it on a button control. Can anyone tell me why the model is not displaying in the app?
Summarizing Question
How can I define a model in manifest.json, and then set that model in the controller so I can use it in my xml view?
try putting a forward slash before your property name...
<Button text="{inputs>/propA}"></Button>
...and update your manifest file so that your model definition points to your dataSource defined under sap.app as follows...
{
"_version": "1.1.0",
"sap.app": {
"_version": "1.1.0",
"id": "pricingTool",
"type": "application",
"applicationVersion": {
"version": "1.0.0"
},
"title": "{{appTitle}}",
"description": "{{appDescription}}",
"ach": "ach",
"resources": "resources.json",
"sourceTemplate": {
"id": "ui5template.basicSAPUI5ApplicationProject",
"version": "1.30.3"
},
"dataSources": {
"inputsData": {
"type" : "JSON",
"uri": "model/inputs.json"
}
}
},
"sap.ui": {
"_version": "1.1.0",
"technology": "UI5",
"icons": {
"icon": "",
"favIcon": "",
"phone": "",
"phone#2": "",
"tablet": "",
"tablet#2": ""
},
"deviceTypes": {
"desktop": true,
"tablet": true,
"phone": true
},
"supportedThemes": [
"sap_hcb",
"sap_bluecrystal"
]
},
"sap.ui5": {
"_version": "1.1.0",
"rootView": {
"viewName": "pricingTool.view.Main",
"type": "XML"
},
"dependencies": {
"minUI5Version": "1.30.0",
"libs": {
"sap.ui.core": {},
"sap.m": {},
"sap.ui.layout": {}
}
},
"contentDensities": {
"compact": true,
"cozy": true
},
"models": {
"products": {
"type": "sap.ui.model.json.JSONModel",
"uri": "model/products.json"
},
"inputs": {
"type": "sap.ui.model.json.JSONModel",
"dataSource" : "inputsData"
}
}
}
}
...change your Component.js file to point to your manifest file...
sap.ui.define([
'sap/ui/core/UIComponent'
],
function(UIComponent) {
"use strict";
var Component = UIComponent.extend("pricingTool.Component", {
metadata : {
manifest: "json",
},
init : function () {
// call the init function of the parent
UIComponent.prototype.init.apply(this, arguments);
}
});
});
... and remove the onInit logic within your controller to set the model (this is handled by the component)
Those models you define in the manifest.json file are created in the Component context (if your app based on Component). To make it available in the XML view you have to obtain it from Component and then attach to the view. The code snippet you can use in onInit controller event looks like this:
this.getView().setModel(this.getOwnerComponent().getModel("<your_model_name>"), "<your_model_name>");
if you are using standard template then most likely you have a BaseController as an ancestor, in that case the code can look shorter:
this.setModel(this.getComponentModel("<your_model_name>"), "<your_model_name>");
Here is a minimal example of what you'd like to achieve: https://embed.plnkr.co/l1XF5O/
Models defined in manifest.json (aka. "app descriptor") will be set to the component (since v1.30).
If a descriptor is used, almost all properties of your component's metadata other than manifest: "json" are deprecated and should be avoided. Deprecated properties are listed here.
Views (and their controls inside) inside the root view instantiated by your component inherit models automatically from the component. Thus, setting models to your view explicitly is not needed anymore. Your view already knows the model that is set to the component.*
The binding syntax should be used correctly according to your situation:
Use relative binding syntax (modelName>property) only if a parent control has already a context bound (e.g. the parent control uses aggregation binding, or element binding).
In other cases, use absolute binding syntax. It starts with a slash (modelName>/property), so that the control doesn't look for the binding context of its parent.
*Although the model, which is set on the component, can be used seamlessly in the XMLView, retrieving the component model by calling view.getModel inside the onInit handler will return undefined. More about this: https://stackoverflow.com/a/43941380/5846045

Content script for iframe inside extension popup or background script

I am trying to interact with an iframe located in a chrome-extension popup. I know content.js can be injected in all frame using the manifest.json but it's working with frame inside a webpage and not inside the extension's popup.
Is it doable ? I tried many things but I didn't find a solution yet.
my manifest :
{
"name" :"test",
"version": "1.0",
"manifest_version": 2,
"description" :"Scraping Facebook",
"permissions": [
"cookies",
"background",
"tabs",
"http://*/*",
"https://*/*",
"storage",
"unlimitedStorage"
],
"icons": { "128": "images/pint.png" },
"content_scripts": [
{
"matches": [
"http://*/*",
"https://*/*"
],
"js": ["jquery-3.1.0.min.js","content.js"],
"run_at":"document_end"
}
],
"web_accessible_resources": [
"http://*/*",
"https://*/*",
"styles/*",
"fonts/*"
],
"background": {
"scripts": ["background.js"]
},
"browser_action" :
{
"default_popup": "popup.html",
"default_title": "test"
}
}
Use "all_frames": true in your content script declaration to inject it into an iframe:
"content_scripts": [{
"matches": [ "http://example.com/*" ],
"js": [ "content.js" ],
"all_frames": true
}],
To differentiate this iframe from normal tabs you can add a dummy parameter to the URL when you create the iframe e.g. http://example.com/?foo so you can match it in manifest.json like "http://example.com/*foo*" for example.
Then you can use messaging: the content script initiates it, and the extension script registers a listener.
Trivial one-time sendMessage:
content.js:
chrome.runtime.sendMessage('test', response => {
console.log(response);
});
popup.js (or background.js and so on):
chrome.runtime.onMessage.addListener((msg, sender, sendResponse) => {
console.log('popup got', msg, 'from', sender);
sendResponse('response');
});
Long-lived port:
content.js:
let port = chrome.runtime.connect({name: 'test'});
port.onMessage.addListener((msg, port) => {
console.log(msg);
});
port.postMessage('from-iframe');
popup.js (or background.js and so on):
let iframePort; // in case you want to alter its behavior later in another function
chrome.runtime.onConnect.addListener(port => {
iframePort = port;
port.onMessage.addListener((msg, port) => {
console.log(msg);
});
port.postMessage('from-popup');
});
An example of popup.html is really straightforward:
<html>
<body>
<iframe width="500" height="500" src="http://example.com"></iframe>
<script src="popup.js"></script>
</body>
</html>
Of course you can also add the iframe(s) programmatically using DOM manipulation.

How do you query data by with multiple conditions or indexes?

I'm trying to design a performant NoSQL schema for my Firebase application, but how would I query multiple checkpoints which have a unique tag?
Consider the following rules:
Tiers can have multiple tags
Tags can have multiple Checkpoints
Tag keys (names) are unique for each tier
Checkpoints can have multiple tags, but only 1 tag per tier
Here's what I have so far:
{
"tiers": {
"1": {
"web": true,
"android": true
},
"2": {
"basics": true,
"angular2": true,
"aurelia": true
},
"3": {
"basics": true,
"components": true
}
},
"tags" : {
"1": {
"web": {
"name": "Web Development",
"children": {
"front-end": true,
"angular2": true,
"aurelia": true
}
},
"android": {
"name": "Android Development",
"children": {
"front-end": true
}
}
},
"2": {
"front-end": {
"name": "Basics",
"parents": {
"web": true,
"android": true
},
"children": {
"angular2": true,
"aurelia": true,
"android-studio": true
}
}
},
"3": {
"angular2": {
"name": "Angular 2.x",
"parents": {...},
"children": {...}
},
"aurelia": {
"name": "Aurelia 1.x"
}
}
},
"checkpoints": {
"<randomKey>" : {
"name": "Angular 2 Quick Start",
"tags": {
"1": "web",
"2": "front-end",
"3": "angular2"
}
}
}
}
Right now I can query for all checkpoints under the Tier 1 web tag with:
ref.orderByChild('tags/1').equalTo("web").once('value', snap => console.log(snap.val()));
but since you can only define one indexOn rule, it's not optimized. At least if I can set the indexOn rule I can at least filter out most of the checkpoints then filter the rest in my code.
What can I do to efficiently query my checkpoints based on multiple tags AND the tiers?
Eventually I'd need to do a query for checkpoints with "tags": {"1": "web" AND "2": "front-end"} which I can't for the life of me figure out how to execute efficiently. I was thinking of doing another table with composite keys (e.g. each tier/tag contains references to ALL child checkpoints), but that would lead to requiring me to add and delete references within each tier.
There must be a better way.
I was vastly over complicating the solution - I ended up:
Removing Tier from the equation all together
Adding each checkpoint within each tag it was related to
Querying all checkpoints related to each selected tag and removing the ones which did not appear in each tag selected
Here's my new schema:
allTags = [
{"abc": true, "def": true, "hij": true},
{"abc": true, "def": true}
];
tags: [
{ "0": [
{"software-development": {name: "Software Development", checkpoints: [ {"abc": true}, {"def": true}, {"hij": true}]}}
]},
{"1": [
{"web": {name: "Web", checkpoints: [ {"abc": true}, {"def": true}]}},
{"android": {name: "Android", checkpoints: [{"hij": true}]}}
]}
];
checkpoints: [
{"abc": { name: "Angular2 Quick Start"}},
{"def": { name: "Building global directives in Angular2"}},
{"hij": { name: "Android Quick Start"}},
];
Creating a new Checkpoint:
public createCheckpoint(tags: any[], checkpoint: any) {
// push checkpoint
let checkpointRef = this.firebase.child('test/checkpoints');
let checkpointKey = checkpointRef.push(checkpoint).key(); // might have to do separate call
// Add checkpoint under each related tag
tags.forEach(tag => {
let tagRef = this.firebase.child('test/tags/' + tag.tier + '/' + tag.key + '/checkpoints/' + checkpointKey);
tagRef.set(true)
});
}
Retrieving all checkpoints based on selected tags:
public getCheckpointss(tags: any[]) {
// tag.tier, tag.key
let checkpointKeysToGet: string[] = [];
tags.forEach(tag => {
let ref = this.firebase.child('test/tags/' + tag.tier + '/' + tag.key + '/checkpoints');
ref.once('value', snap => {
let tagKeys:string[] = this.convertToArray(snap.val());
if (checkpointKeysToGet.length == 0){
checkpointKeysToGet = checkpointKeysToGet.concat(tagKeys);
}
else {
checkpointKeysToGet.forEach(existingTagKey => {
let tagKeysInBothTiers = tagKeys.filter(tagKey => {
return checkpointKeysToGet.includes(tagKey, 0)
});
checkpointKeysToGet = tagKeysInBothTiers;
console.log(checkpointKeysToGet);
});
}
});
});
}
All valid criticisms are welcome as long as you propose a solution to go along with it :)

Resources