Storage Access API doesn't work with Safari? - sharepoint

I'm working on a Sharepoint SPFx project, which has an Iframe embedding content from our server. The issue is Safari blocking third-party cookies with Prevent Cross-site Tracking settings, which disallows iframe content to be shown on my Sharepoint page. The target is to work around without forcing users to manually disable the setting. I tried to follow instruction from MDN and Webkit, also from posts around here, but none works.
Applying the code to the project, document.hasStorageAccess() always return true on Safari code from instruction, but still the content is not shown. The target is to show click button when there's no access, and hide button when the access is already granted.
This is how i tried to work with it following instruction:
protected doThingsWithFirstPartyStorageAccess(): void {
document.cookie = "foo=bar";
localStorage.setItem("accessStatus", "accessed")
}
protected getAccessToFirstPartyStorage(): void {
const clickBtn = document.getElementById("btn");
if (typeof document.hasStorageAccess === 'function'
&& typeof document.requestStorageAccess === 'function') {
//trying to work around with Chrome not supporting these
if (document.hasStorageAccess === null) {
document.getElementById("btn").style.display = "none"
console.log("This browser doesn't support")
} else {
document.hasStorageAccess().then((hasAccess) => {
if (hasAccess) {
clickBtn.style.display = "none"
this.doThingsWithFirstPartyStorageAccess()
console.log("Already have access")
} else {
clickBtn.style.display = "block"
console.log("no access, need requesting")
clickBtn.addEventListener ('click', () => {
document.requestStorageAccess().then(() => {
this.doThingsWithFirstPartyStorageAccess()
console.log("Now it should have access")
}).catch((err) => {
console.log("Error obtaining storage access", err)
})
})
}
})
}
}
}
This is the render. I planned to call getAccessToFirstPartyStorage() function right when rendering. The src of iframe is basically submitted through a form:
public render(): void {
this.getAccessToFirstPartyStorage()
this.domElement.innerHTML = `
<section id="iframeID" class="
${!!this.context.sdks.microsoftTeams ? styles.teams : ''}"
>
<iframe id="iframeElemID"></iframe>
<button id="btn">
Allow Access Cookies
</button>
</section>`;
I'm new to this, any opinions to fix this broken code are all welcomed.

Related

how to change website theme and save this theme so it doesn't change on reload?

I found a lot of website, that it allows me to change the website theme , without being logged in.
and when i refresh the page the theme doesn't reset. I was wondering how can i do that , or can someone put me on the right pass.
do i focus on the back-end , or it's only on front-end (client-side).
does it website to browser related. or it's something else ? i really looked a lot tried to save the the client choose into his db, but now when i see website that i can change it, without even being logged in so it's not related to db.
or is it session related ?
You can keep the theme information in localStorage in your users' browsers. When the app starts you can check if there is any theme information in the localStorage and use that, and when you update the theme you can set it in localStorage.
Example
class App extends React.Component {
state = {
theme: localStorage.getItem("theme") || "red"
};
toggleTheme = () => {
this.setState(
({ theme }) => ({
theme: theme === "red" ? "green" : "red"
}),
() => {
localStorage.setItem("theme", this.state.theme);
}
);
};
render() {
return (
<div style={{ backgroundColor: this.state.theme }}>
<div>Welcome to my website</div>
<button onClick={this.toggleTheme}> Toggle theme </button>
</div>
);
}
}
You can use something like this in Reactjs (obviously you would need to implement the MyTheme and SelectInput components yourself):
class MyReactComp extends React.Component {
setTheme(theme) {
localStorage.setItem('theme', selectedTheme);
}
render() {
<MyThemeLayout theme={localStorage.getItem('theme')} />
<MySelectInput onChange={theme => this.setTheme(theme)} />
}
}
Otherwise, you could send the theme choice to the backend, store it in MongoDB or something and then set an ID cookie so that the theme can be retrieved when the user next visits the site.

search engine laravel and vue.js without scout

Hi am trying to make search engine with laravel and vue.js but i have no result:
this is my SearchController.php
namespace Amp\Http\Controllers;
use Amp\User;
use Illuminate\Http\Request;
class SearchController extends Controller
{
/**
* #param Request $request
* #return array
*/
public function search(Request $request)
{
$error = ['error' => 'No results found, please try with different keywords.'];
if ($request->has('q')) {
$users = User::search($request->get('q'))->get();
return $users->count() ? $users : $error;
}
return $error;
}
}
this my TopNavbar.vue:
<template>
<div>
<input type="text" v-model="keywords">
<ul v-if="results.length > 0">
<li v-for="result in results" :key="result.id" v-text="result.name"></li>
</ul>
</div>
</template>
<script>
import axios from 'axios'
export default {
data() {
return {
keywords: null,
results: []
};
},
watch: {
keywords(after, before) {
this.fetch();
}
},
methods: {
fetch() {
axios.get('api/search', { params: { keywords: this.keywords } })
.then(response => this.results = response.data)
.catch(error => {});
}
}
}
</script>
If i use only the api url then i have result and work proprely i mean if i make search with url on the browser something like this: api/search?q=XXXX then work pefect but only on browser wen i try to make search on then nothing
thank you for your help
To get the keywords sent from axios inside the controller, you would need to use
$keywords = $request->get('keywords');
In the code shared, you are looking for a request parameter named q. When you are entering the URL through the browser, you are entering the parameter with the name q. So the search works. I hope you are clear about the issue now.
So, assuming that you are handling the search method with eloquent, the controller action becomes:
public function search(Request $request)
{
$error = ['error' => 'No results found, please try with different keywords.'];
$keywords = $request->get('keywords')?? null;
if ($keywords) {
$users = User::search($keywords)->get();
return $users->count() ? $users : $error;
}
return $error;
}
For send Request as ajax you must use X-CSRF-Token or disable (exception) validate this token for this url.
For API url validate token disabled.
Read more:
https://laravel.com/docs/5.6/csrf

Using Fragment to insert HTML rendered on the back end via dangerouslySetInnerHTML

I used to compile and insert JSX components via
<div key={ ID } dangerouslySetInnerHTML={ { __html: HTML } } />
which wrapped my HTML into a <div>:
<div>my html from the HTML object</div>
Now react > 16.2.0 has support for Fragments and I wonder if I can use that somehow to avoid wrapping my HTML in a <div> each time I get data from the back end.
Running
<Fragment key={ ID } dangerouslySetInnerHTML={ { __html: HTML } } />
will throw a warning
Warning: Invalid prop `dangerouslySetInnerHTML` supplied to `React.Fragment`. React.Fragment can only have `key` and `children` props.
in React.Fragment
Is this supported yet at all? Is there another way to solve this?
Update
Created an issue in the react repo for it if you want to upvote it.
Short Answer
Not possible:
key is the only attribute that can be passed to Fragment. In the
future, we may add support for additional attributes, such as event
handlers.
https://reactjs.org/docs/fragments.html
You may want to chime in and suggest this as a future addition.
https://github.com/facebook/react/issues
In the Meantime
You may want to consider using an HTML parsing library like:
https://github.com/remarkablemark/html-react-parser
Check out this example to see how it will accomplish your goal:
http://remarkablemark.org/blog/2016/10/07/dangerously-set-innerhtml-alternative/
In Short
You'll be able to do this:
<>
{require('html-react-parser')(
'<em>foo</em>'
)}
</>
Update December 2020
This issue (also mentioned by OP) was closed on Oct 2, 2019. - However, stemming from the original issue, it seems a RawHTML component has entered the RFC process but has not reached production, and has no set timeline for when a working solution may be available.
That being said, I would now like to allude to a solution I currently use to get around this issue.
In my case, dangerouslySetInnerHTML was utilized to render plain HTML for a user to download; it was not ideal to have additional wrapper tags included in the output.
After reading around the web and StackOverflow, it seemed most solutions mentioned using an external library like html-react-parser.
For this use-case, html-react-parser would not suffice because it converts HTML strings to React element(s). Meaning, it would strip all HTML that wasn't standard JSX.
Solution:
The code below is the no library solution I opted to use:
//HTML that will be set using dangerouslySetInnerHTML
const html = `<div>This is a div</div>`
The wrapper div within the RawHtml component is purposely named "unwanteddiv".
//Component that will return our dangerouslySetInnerHTML
//Note that we are using "unwanteddiv" as a wrapper
const RawHtml = () => {
return (
<unwanteddiv key={[]}
dangerouslySetInnerHTML={{
__html: html,
}}
/>
);
};
For the purpose of this example, we will use renderToStaticMarkup.
const staticHtml = ReactDomServer.renderToStaticMarkup(
<RawHtml/>
);
The ParseStaticHtml function is where the magic happens, here you will see why we named the wrapper div "unwanteddiv".
//The ParseStaticHtml function will check the staticHtml
//If the staticHtml type is 'string'
//We will remove "<unwanteddiv/>" leaving us with only the desired output
const ParseStaticHtml = (html) => {
if (typeof html === 'string') {
return html.replace(/<unwanteddiv>/g, '').replace(/<\/unwanteddiv>/g, '');
} else {
return html;
}
};
Now, if we pass the staticHtml through the ParseStaticHtml function you will see the desired output without the additional wrapper div:
console.log(ParseStaticHtml(staticHtml));
Additionally, I have created a codesandbox example that shows this in action.
Notice, the console log will throw a warning: "The tag <unwanteddiv> is unrecognized in this browser..." - However, this is fine because we intentionally gave it a unique name so we can easily differentiate and target the wrapper with our replace method and essentially remove it before output.
Besides, receiving a mild scolding from a code linter is not as bad as adding more dependencies for something that should be more simply implemented.
i found a workaround
by using react's ref
import React, { FC, useEffect, useRef } from 'react'
interface RawHtmlProps {
html: string
}
const RawHtml: FC<RawHtmlProps> = ({ html }) => {
const ref = useRef<HTMLDivElement>(null)
useEffect(() => {
if (!ref.current) return
// make a js fragment element
const fragment = document.createDocumentFragment()
// move every child from our div to new fragment
while (ref.current.childNodes[0]) {
fragment.appendChild(ref.current.childNodes[0])
}
// and after all replace the div with fragment
ref.current.replaceWith(fragment)
}, [ref])
return <div ref={ref} dangerouslySetInnerHTML={{ __html: html }}></div>
}
export { RawHtml }
Here's a solution that works for <td> elements only:
type DangerousHtml = {__html:string}
function isHtml(x: any): x is DangerousHtml {
if(!x) return false;
if(typeof x !== 'object') return false;
const keys = Object.keys(x)
if(keys.length !== 1) return false;
return keys[0] === '__html'
}
const DangerousTD = forwardRef<HTMLTableCellElement,Override<React.ComponentPropsWithoutRef<'td'>,{children: ReactNode|DangerousHtml}>>(({children,...props}, ref) => {
if(isHtml(children)) {
return <td dangerouslySetInnerHTML={children} {...props} ref={ref}/>
}
return <td {...props} ref={ref}>{children}</td>
})
With a bit of work you can make this more generic, but that should give the general idea.
Usage:
<DangerousTD>{{__html: "<span>foo</span>"}}</DangerousTD>

google extension inline install and Verified not working

google.com/webstore i have add my extension
i Have check "This item uses inline install."
Websites: chose Verify site
google.com/webmasters i have add site and Verifyed.
when i put this code on me site:
<link rel="chrome-webstore-item"href="https://chrome.google.com/webstore/detail/itemID">
<button onclick="chrome.webstore.install()" id="install-button">Add to Chrome</button>
<script>
if (document.getElementById('extension-is-installed')) {
document.getElementById('install-button').style.display = 'none';
}
</script>
i click on button "Add to Chrome" install app extension, but when i refresh site button "Add to Chrome" is display. why? i cant Understanding
You're obviously following the guide at https://developer.chrome.com/webstore/inline_installation
In that case, you missed a step.. Let's look at the code.
if (document.getElementById('extension-is-installed')) {
document.getElementById('install-button').style.display = 'none';
}
The condition here is whether an element with ID extension-is-installed is present on the page. But what adds it?
A step back:
For example, you could have a content script that targets the installation page:
var isInstalledNode = document.createElement('div');
isInstalledNode.id = 'extension-is-installed';
document.body.appendChild(isInstalledNode);
So, you need to add a Content Script that adds that element to the page.
However, I doubt that guide will work. By default, content scripts execute after DOM is loaded (and therefore, that hiding script has executed). You can make them run at document_start, but then body does not exist yet.
Let me make an alternative hiding script, based on communicating with the extension using "externally_connectable". Suppose your website is example.com, and your extension's ID is itemID
Add example.com to sites you want to be messaged from:
"externally_connectable" : {
"matches" : [
"*://*.example.com/*"
]
},
In your background page, prepare for the message from the webpage:
chrome.runtime.onMessageExternal.addListener(
function(message, sender, sendResponse) {
if(message.areYouThere) sendResponse(true);
}
);
In your page at example.com, add a button (hidden by default) and code to show it when appropriate:
<button onclick="chrome.webstore.install()"
id="install-button" style="display:none;">
Add to Chrome
</button>
<script>
if (chrome) {
// The browser is Chrome, so we may need to show the button
if(chrome.runtime && chrome.runtime.sendMessage) {
// Some extension is ready to receive messages from us
// Test it:
chrome.runtime.sendMessage(
"itemID",
{areYouThere: true},
function(response) {
if(response) {
// Extension is already installed, keep hidden
} else {
// No positive answer - it wasn't our extension
document.getElementById('install-button').style.display = 'block';
}
}
);
} else {
// Extension is not installed, show button
document.getElementById('install-button').style.display = 'block';
}
}
</script>
Was requested to add page reload after install. chrome.webstore.install has a callback parameter specifically for this.
Instead of using onclick attribute, assign a function:
document.getElementById('install-button').addEventListener("click", function(e) {
chrome.webstore.install(function() {
// Installation successful
location.reload();
});
});

Disable Specific Keys in IE 6

I need to disable specific keys (Ctrl and Backspace) in Internet Explorer 6. Is there a registry hack to do this. It has to be IE6. Thanks.
Long Edit:
#apandit: Whoops. I need to more specific about the backspace thing. When I say disable backspace, I mean disable the ability for Backspace to mimic the Back browser button. In IE, pressing Backspace when the focus is not in a text entry field is equivalent to pressing Back (browsing to the previous page).
As for the Ctrl key. There are some pages which have links which create new IE windows. I have the popup blocker turned on, which block this. But, Ctrl clicking result in the new window being launched.
This is for a kiosk application, which is currently a web based application. Clients do not have the funds at this time to make their site kiosk friendly. Things like URL filtering and disabling the URL entry field is already done.
Thanks.
For what purpose do you need this? Because disabling the backspace would be hell for typing urls or emails, etc.
We could recommend other workarounds if we knew the problem better.
EDIT 1:
This website seems to have some information as to how it's done. I can't verify it currently, but I'll look into it:
http://www.ozzu.com/programming-forum/disable-key-and-back-t44867.html
Edit 2:
This website has some key codes:
http://www.advscheduler.com/docs/manual/type_sendkeys.html
It seems BACKSPACE is 08.
EDIT 3:
Found some more code for blocking, check this out:
<script type="text/javascript">var sType = "keypress";</script>
<!--[if IE]>
<script type="text/javascript">sType = "keydown";</script>
<![endif]-->
<script type="text/javascript">
fIntercept = function(e) {
// alert(e.keyCode);
e = e || event.e;
if (e.keyCode == 116) {
// When F5 is pressed
fCancel(e);
} else if (e.ctrlKey && (e.keyCode == 0 || e.keyCode == 82)) {
// When ctrl is pressed with R
fCancel(e);
}
};
fCancel = function(e) {
if (e.preventDefault) {
e.stopPropagation();
e.preventDefault();
} else {
e.keyCode = 0;
e.returnValue = false;
e.cancelBubble = true;
}
return false;
};
fAddEvent = function(obj, type, fn) {
if (obj.addEventListener) {
obj.addEventListener(type, fn, false);
} else {
obj['e'+type+fn] = fn;
obj[type+fn] = function() {
obj['e'+type+fn](window.event);
}
obj.attachEvent('on'+type, obj[type+fn]);
}
};
fAddEvent(document, sType, fIntercept);
</script>
Ok, now you should have all you need to do it. To disable backspace, the keycode is 08. You can probably just use the code I posted with slight modifications only... :\
Try it out and see if it's what you needed. (I hope you know how to use Javascript.)
You can't do it from a web page. One of the main purposes of a web browser is to protect users from the internet. They define a very specific set of things that web sites can do, and disabling buttons isn't in the list.
On the other hand, if you're a network admin and just want to mess with your users, you might be able to do it via some desktop software. But I wouldn't hold my breath.
I'm using this jQuery solution (tested on ie6 and firefox 3.6):
$(document).keydown(function(e) {
var tag = e.target.tagName;
var ro = e.target.readOnly;
var type = e.target.type;
var tags = {
INPUT : '',
TEXTAREA : ''
};
if (e.keyCode == 8) {// backspace
if (!(tag in tags && !ro && /text/.test(type))) {
e.stopPropagation();
e.preventDefault();
}
}
});
hope it helps someone

Resources