I have a custom form that makes a few requests to a database to verify the user. I noticed that if I have a single google account it works fine but it doesn't with multiple. The other thing I noticed is that the script doesn't throw any error it just doesn't communicate back the result from the custom form.
This is how my custom forms look like:
<!DOCTYPE html>
<html>
<head>
<base target="_top">
</head>
<body>
<div class="container select-client">
<div class="client">Client</div>
<select class="client-select">
<option>Select Client</option>
<!-- ...options -->
</select>
<div class="market">Market</div>
<select class="market-select">
<option>Select Market</option>
<!-- ...options -->
</select>
<div class="error-message"></div>
<button class="button" id="select-button" onclick="handleSelect()">Select</button>
</div>
<script>
// ...code to validate the user
function handleSelect() {
var _client = clients.find(
(client) => client.id === parseInt(selectedClient)
);
var _market = markets.find(
(market) => market.id === parseInt(selectedMarkets)
);
if (!_client && !_market) {
return;
}
if (!_client) {
errorMessageClientMarket.innerHTML = 'Please select client';
return;
}
if (!_market) {
errorMessageClientMarket.innerHTML = 'Please select market';
return;
}
google.script.run
.withSuccessHandler()
.loginData({ token, market: _market, client: _client, user: userInfo, platform });
google.script.host.close();
}
</script>
</body>
</html>
This is how I create the custom form using app script
const loginForm = () => {
const html = HtmlService.createHtmlOutputFromFile('loginFormHtml')
.setSandboxMode(HtmlService.SandboxMode.IFRAME)
.setXFrameOptionsMode(HtmlService.XFrameOptionsMode.ALLOWALL)
.setWidth(600)
.setHeight(600);
const ui = SpreadsheetApp.getUi();
ui.showModalDialog(html, `Login`);
};
This is the callback function:
const loginData = (data) => { // <--- this function is ignored when a the users has multiple google accounts
console.log('LOGIN FORM');
const { token, market, user, client, platform } = data;
UserProperties.setProperty('token', token);
UserProperties.setProperty('userId', user.id);
UserProperties.setProperty('clientId', client.id);
UserProperties.setProperty('clientName', client.name);
UserProperties.setProperty('marketId', market.id);
UserProperties.setProperty('marketName', market.code_name);
UserProperties.setProperty('username', `${user.first_name} ${user.last_name}`);
UserProperties.setProperty('userEmailAddress', user.email);
UserProperties.setProperty('platform', platform);
const info = UserProperties.getProperties();
console.log('info ---> ', info)
const ui = SpreadsheetApp.getUi();
getMenu(true);
ui.alert('Logged in Successfully');
};
Does anyone know if there's a away to fix this?
Related
Im currently trying to find out if its possible to change a text in extension when a submit has happend. I currently have a text saying "ENTER DISCORD USER ID AND SUBMIT" and when an user has entered its USER ID and submitted, the text should be changed to "USER ID SUBMITTED" and the text should always say that afterwards, meaning that if someone closes and opens the extensions - the text should still say "USER ID SUBMITTED".
<!DOCTYPE html>
<head>
<link rel="stylesheet" href="../css/style.css">
<script src="../js/popup.js"></script>
</head>
<body>
<div class="text-center">
<form id="form" class="form-control mt10">
<label> <input type="number" id="discord-id-input" name="discord-id-input"></label>
<button id="discord-id-button" type="submit" class="submit"></button>
<output id="help-text" class="help-text" value="">ENTER DISCORD USER ID AND SUBMIT</output>
</form>
</div>
</body>
function get_discord_id(callback) {
chrome.storage.sync.get(["discord_id"], (result) => {
callback(result.discord_id);
});
}
function set_discord_id(discord_id) {
chrome.storage.sync.set({ discord_id: discord_id }, () => {});
}
window.addEventListener("DOMContentLoaded", (e) => {
// check if discord ID is already stored
get_discord_id((discord_id) => {
if (discord_id == null) {
form.addEventListener('submit', () => {
let value = document.getElementById("discord-id-input").value;
chrome.runtime.sendMessage({ discord_id: value }, function(response) {});
set_discord_id(value);
document.getElementById('help-text').innerHTML = 'USER ID SUBMITTED';
});
e.preventDefault();
};
});
});
I wonder how I am able to permanent change a text after a submitted trigger has happend?
Since you already store the discord_id and retrieve it when the popup window is opened, you can determine whether the id has been submitted based on the presents of discord_id.
Here is the updated JS code to do that
function get_discord_id(callback) {
chrome.storage.sync.get(["discord_id"], (result) => {
callback(result.discord_id);
});
}
function set_discord_id(discord_id) {
chrome.storage.sync.set({ discord_id: discord_id }, () => {});
}
window.addEventListener("DOMContentLoaded", (e) => {
// check if discord ID is already stored
get_discord_id((discord_id) => {
if (discord_id) {
document.getElementById('help-text').innerHTML = 'USER ID SUBMITTED';
} else {
form.addEventListener('submit', () => {
let value = document.getElementById("discord-id-input").value;
chrome.runtime.sendMessage({ discord_id: value }, function(response) {});
set_discord_id(value);
document.getElementById('help-text').innerHTML = 'USER ID SUBMITTED';
});
e.preventDefault();
};
});
});
I use the samples (https://github.com/stripe-samples/checkout-single-subscription/tree/master/server/php) from Stripe to create a subscription. What I don't really understand, how can I pass metadata from my index.html over script.js to the create-checkout-session.php.
I thought I just add data attributes to the index.html:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<title>Stripe</title>
<meta name="description" content="A demo of Stripe Payment Intents" />
<link rel="icon" href="favicon.ico" type="image/x-icon" />
<script src="https://js.stripe.com/v3/"></script>
<script src="./script.js" defer></script>
</head>
<body>
<div class="sr-root">
<div class="sr-main" style="display: flex;">
<div class="sr-container">
<section class="container">
<button id="basic-plan-btn" data-partner="name" data-package="basic">USD 6.90</button>
</section>
<section class="container">
<button id="pro-plan-btn" data-partner="name" data-package="premium">USD 11.90</button>
</section>
</div>
</div>
</div>
</body>
</html>
then I have to read them somehow out in the script.js. But that I don't really figure out how.
// Create a Checkout Session with the selected plan ID
var createCheckoutSession = function(priceId) {
return fetch("/fileadmin/restaurant/stripe/create-checkout-session.php", {
method: "POST",
headers: {
"Content-Type": "application/json"
},
body: JSON.stringify({
priceId: priceId,
partner: 'name',
package: 'premium'
})
}).then(function(result) {
return result.json();
});
};
// Handle any errors returned from Checkout
var handleResult = function(result) {
if (result.error) {
var displayError = document.getElementById("error-message");
displayError.textContent = result.error.message;
}
};
/* Get your Stripe publishable key to initialize Stripe.js */
fetch("/fileadmin/restaurant/stripe/config.php")
.then(function(result) {
return result.json();
})
.then(function(json) {
var publishableKey = json.publishableKey;
var basicPriceId = json.basicPrice;
var proPriceId = json.proPrice;
var stripe = Stripe(publishableKey);
// Setup event handler to create a Checkout Session when button is clicked
document
.getElementById("basic-plan-btn")
.addEventListener("click", function(evt) {
createCheckoutSession(basicPriceId).then(function(data) {
// Call Stripe.js method to redirect to the new Checkout page
stripe
.redirectToCheckout({
sessionId: data.sessionId
})
.then(handleResult);
});
});
// Setup event handler to create a Checkout Session when button is clicked
document
.getElementById("pro-plan-btn")
.addEventListener("click", function(evt) {
createCheckoutSession(proPriceId).then(function(data) {
// Call Stripe.js method to redirect to the new Checkout page
stripe
.redirectToCheckout({
sessionId: data.sessionId
})
.then(handleResult);
});
});
});
by that I receive them in the create-checkout-session.php
<?php
require_once 'shared.php';
$domain_url = $config['domain'];
$checkout_session = \Stripe\Checkout\Session::create([
'success_url' => $domain_url . 'success.php?session_id={CHECKOUT_SESSION_ID}',
'cancel_url' => $domain_url . 'canceled.php',
'payment_method_types' => ['card'],
'mode' => 'subscription',
'allow_promotion_codes' => true,
'line_items' => [[
'price' => $body->priceId,
'quantity' => 1,
]],
'subscription_data' => ['trial_period_days' => 60],
'metadata' => [
'partner' => $body->partner,
'package' => $body->package
],
]);
echo json_encode(['sessionId' => $checkout_session['id']]);
Thank You.
What you've done adding to the JSON body of the fetch call looks right to me. If you're trying to set the 'name' and 'premium' values dynamically from some input, then take a look at this previous answer for some approaches for getting input values.
I create a sort of a company forum. Users can create post to share an information. To do so and at the moment, they complete a form with a basic textarea. My problem is that when they write a word with an apostrophe, the code interpret the apostrophe as single quote and it create en exception. I show you the code and an exemple below.
Html :
<!DOCTYPE html>
<html lang="fr">
<head>
<meta charset="utf-8">
</head>
<body>
<div id="admin">
<div v-if="seenNews" id="news">
<div class="form">
<h4>Create a post</h4>
<form class="newPost" action="/newPost" method="POST">
<label id="titleLabel" for="titleInput">Title : </label>
<input type="text" id="titleInput" name="titleInput" required>
<label id="contentLabel" for="contentInput">Content : </label>
<textarea id="contentInput" name="contentInput" cols="40" rows="5" required></textarea>
<button type="submit">Create</button>
</form>
</div>
</div>
</div>
</body>
</html>
Back-end javascript :
app.js
const Server = require('./server/Server');
const express = require('express');
const DAO = require('./server/DAO');
const server = new Server();
server.start();
const dao = new DAO();
dao.connect();
server.app.post("/newPost", function(req, res) {
try {
dao.newPost(req.body.titleInput, req.body.contentInput).then(value => {
res.redirect('/Admin');
})
} catch (e) {
console.log(e);
}
})
DAO.js
const sql = require('mssql');
class DAO {
constructor() {
this.sqlConfig = {
user: 'username',
password: 'password',
server: 'SERVER',
port:port,
database: 'DB',
options: {
enableArithAbort: false,
encrypt:false
}
}
}
async newPost(title, content) {
try {
let req = 'INSERT INTO DB.dbo.[Table] (title, content) VALUES (\''+title+'\',\''+content+'\')';
console.log(req);
await sql.query(req).then(value => {
return true;
});
} catch (e) {
console.log(e);
return false;
}
}
}
As exemple, if a user create a post with this content : Ms. Smith's keys are at the reception desk, I would have this in console :
RequestError: Unclosed quotation mark after the character string ')'.
Maybe if I create a function to find en encode the character it can fix it, but I don't see how I can do so.
I finally use the JS function replace() to replace simple quote in my string by two simple quote. '' is the equivalent of js \' in sql server.
'INSERT INTO PROFACE.dbo.[Posts] (title, content) VALUES (\''+title.replace(/'/gi,"''")+'\',\''+content.replace(/'/gi,"''")+'\')'
I have a very basic static app that has business logic to redirect a user based on what element they click in the UI. I added Mixpanel, and track an event before the user is redirected. I'm trying to create tests using the Jest testing framework, but am having difficulties mocking the track method invocation on mixpanel.
The crux of the issue is I'm unable to mock mixpanel while running tests. I've read the Jest documentation and searched the community for answers, but every time I run tests, it fails with TypeError: Cannot read property 'track' of undefined. You'll have to forgive me if it is something obvious, JavaScript is not my native programming language, so when it comes time to build tests, I'm rusty at it. :)
index.html
<!doctype html>
<html class="no-js" lang="">
<head>
<!-- start Mixpanel -->
<script type="text/javascript">...Mixpanel script</script>
<script src="js/scripts.js"></script>
<script type="text/javascript">
const { sources } = window.parseSourcesFromURL(window.location.href)
const sourceObj = window.convertToObj(sources)
mixpanel.track("Page View", sourceObj)
</script>
<!-- end Mixpanel -->
</head>
<body>
<div>
<div class="age-links">
<button onClick=redirectFunc(true)>YES</button>
<button onClick=redirectFunc(false)>NO</button>
</div>
</div>
</body>
</html>
js/scripts.js
(function() {
const convertToObj = () => ...
const getTrackingData = () => ...
const parseSourcesFromURL = () => ...
const redirectFunc = (p) => {
const { sources, link1, link2 } = parseSourcesFromURL(window.location.href)
const redirect = `${p ? link1 : link2}?${sources.join('&')}`
const mixpanelTrackingData = getTrackingData(sources, p, redirect)
mixpanel.track('Button Clicked', mixpanelTrackingData, () => {
window.location.href = redirect;
})
}
if (typeof module !== 'undefined' && typeof module.exports !== 'undefined')
module.exports = {
....
};
else
window.utmSourcesToObject = utmSourcesToObject;
window.parseReferrerForLinksAndUTM = parseReferrerForLinksAndUTM;
window.redirectBasedOnAge = redirectBasedOnAge;
})();
js/scripts.test.js
const { redirectFunc, parseSourcesFromURL } = require('./js/scripts')
const testURL = 'https://test.com/'
describe('parseReferrerForLinksAndUTM', () => {
beforeEach(() => {
global.window = Object.create(window)
Object.defineProperty(window, 'location', {
value: { href: testURL },
writable: true
})
### THIS DOESNT WORK ###
Object.defineProperty(window, 'mixpanel', {
track: jest.fn()
})
})
const { sources, link1, link2 } = parseSourcesFromURL(testURL)
test('redirect link is correct', () => {
### THIS DOESNT WORK ###
global.mixpanel = Object.create({})
global.mixpanel.track = jest.fn()
expect(link1).toBe('https://link1.com')
})
})
I'm trying to build a simple website builder that allow users to save their generated html created with Vue component and see it at a certain URL.
Because of it I have to store and retrieve the html generated but I have some problems with retrieving of the code. Here is my step:
When user click "save" this function is fired, that select the portion of HTML that include the "website" built by the user:
saveBuilders: function () {
let pages = [];
let builders = $('[id*="builder-container-"]');
$.each(builders, function (key, builder) {
let singleElem = $(builder).attr('id');
pages.push(clearElement.html());
});
this.storeInDb(pages);
},
storeInDb: function (pagesList) {
axios.post("/landing-page/store", {
name: this.name,
description: this.description,
html: pagesList
})
.then(function (response) {
console.log('Cool');
})
.catch(function (error) {
console.log('ERROR', error.response);
});
},
The Axios request is handled by this function that store the html portion in DB
public function store(Request $request)
{
$data = $request->all();
$html = $data['html'];
$landingPage = new LandingPage();
$landingPage->name = $data['name'];
$landingPage->description = $data['description'];
$landingPage->user_id = Auth::user()->id;
$landingPage->html = json_encode($html);
try {
$landingPage->save();
return 'true';
} catch (exception $e) {
return $e;
}
}
Now when the user visit a certain URL, for keep thing simple suppose is example.it/website/0, this function is fired:
public function show($landing_id)
{
try {
$landingPage = LandingPage::where([
'id' => $landing_id,
'user_id' => Auth::user()->id
])->first();
} catch (\Exception $e) {
$landingPage = null;
}
if ($landingPage != null) {
//GET THE HTML
$page = json_decode($landingPage->html);
return view('landing_page.show')->with('page', $page)
} else {
abort(404, 'Error');
}
}
And this the blade where I'm trying to re-create the Vue.js environment
<body>
<span id="countdown"></span>
<div id="builder-pagina">
<builder>
{!! $page !!}}
</builder>
</div>
<script src="https://cdn.jsdelivr.net/npm/vue"></script>
<script src="{{asset('js/landing_page/app.js')}}"></script>
</body>
</html>
I thought that having the html generated by vue similar to something like that into the DB...
<div data-v-29b64d26="" >
<h1>This piece of code was stored into my DB</h1>
<div data-v-56f62f0a="">
</div>
</div>
...you could create everything working simply by pasting the code and by using the same js file used for compiling vue.js.
I've tried pass the entire code by props but is not working. Also tried with slot. Any suggestions?