Conditional Search Results - google-app-maker

I'm creating a 'Kiosk' where users can sign-in and out.
I'm stuck on the sign-out component.
I'd like to have a table that only returns visitors that have not signed out after the end-user searches.
My search box has the following properties:
Value:
#datasource.query.filters.fullname._startsWith
On value change:
if (widget.value === null || (widget.value).length === 0){
widget.datasource.unload();
} else {
widget.datasource.load();
}
I'm new to this & JS as a whole. How can I filter the search to only contain users that have not signed out?

As suggested in the comments you need to add widget.datasource.query.filters.signedout._equals = false; line before you load your data source. However you should make one more change while unloading.
Here's the full code.
if (widget.value === null || (widget.value).length === 0){
widget.datasource.unload();
widget.datasource.load();
} else {
widget.datasource.unload();
widget.datasource.query.filters.signedout._equals = false;
widget.datasource.load();
}
Here unload() will unload previous data and load() will reload it. Now while reloading you can pass multiple filters so it reloads only filtered records.

Related

How can I make the command work on page specific

Thank you in advance for your help. How can I make the following command work only on the page named "customer" and not on other pages?
function onEdit(e) {
if (e.range.columnStart > 9)
return;
e.source.getActiveSheet().getRange(e.range.rowStart, 12).setValue(new Date());
}
If you want that code to run only inside a given page, you should include that code only in that page.
But in case you have a general js that you are going to include anywhere and you want to add logic to it so that it can make guesses on which page is loaded when it's running, you can read the value of window.location.href that will return the entire url of the page currently loaded in viewport:
var isCurrentPageCustomer = false;
//this regex will extract the final part of the url after the last slash (/)
//so if the url was http://mydomain/path/to/page the match will look for /page
var match = /\/([^/]+)$/.exec(window.location.href);
if (match != null) {
//result is holding "page" without the trailing slash (/)
result = match[1];
//if that part (eg.: 'page') matches 'customer', switch the flag=true
if (result === 'customer') isCurrentPageCustomer = true;
}
if(isCurrentPageCustomer)
console.log('current page is customer.');
else
console.log('current page is NOT customer.');
But it turned out later that you meant to run some code only when the current active sheet (in Google Sheets) is named customer. In that case I just changed your onEdit handler function and used the property name of the active sheet to decide to return from the function instantly if it doesn't match with 'customer'
function onEdit(e){
//if the name of the active sheet is not 'customer'
if (e.source.getActiveSheet().getName() != 'customer')
//exits the function without executing the rest of its code
return;
//the rest of your code here
}

Enhanced ecommerce don't recognize internal promotion view object

I've been implementing all the enhanced eCommerce tracks for the past few weeks and I could do most of the job successfully thanks to Simo Ahava's blog. But now I'm struggling with the internal promotion view tracking.
I choose to implement the view tracking with the concept of True View Impressions also with a base on Simo's work and for products it was ok. So I modified the customTasks from the link to track internal promotion but, for some reason, the enhanced eCommerce isn't recognizing the promoView object. But it's recognizing the promoClick (?).
I've made a test: I substitute the promoClick for a impression object and it works! So, my strong guest, it's that the problem it's really on my object. My object's format can be seen here.
And to illustrate the way the object it's being constructed:
var targetElement = {{Click Element}},
event = {{Event}},
batch = window[promoBatchVariableName],
impressions = google_tag_manager[{{Container ID}}].dataLayer.get('ecommerce.promoView.promotions'),
ecomObj = { };
if (event === 'gtm.click') {
while (!targetElement.getAttribute(promoIdAttribute) && targetElement.tagName !== 'BODY') {
targetElement = targetElement.parentElement;
}
}
var latestPromoImpression = impressions.filter(function(impression) {
return impression.id === targetElement.getAttribute(promoIdAttribute);
}).shift();
var promoImpressionsArr = batch.map(function(id) {
return impressions.filter(function(impression) {
return impression.id === id;
}).shift();
});
if (event === 'gtm.elementVisibility'){
promoImpressionsArr[maxPromoBatch - 1] = latestPromoImpression;
}
console.log(ecomObj)
ecomObj.promoView = { promotions: promoImpressionsArr};
if (event === 'gtm.click') {
ecomObj.promoClick = {
promotions: [latestPromoImpression]
};
console.log("click")
}
return {
ecommerce: ecomObj
};
}
Could someone help me with some ideas?
This answer is just to close the question! As I pointed in the comments:
" I found the problem. And it's not on my object itself only. xD The problem is the undefined elements as you pointed at the beginning of our talk. I'm waiting for the dev team to change the data-attributes of the elements on our site's pages because sometimes we don't get any individual identifier variable. So, in the meantime, I've implemented a way to get always a product id even in these cases but as the identifier doesn't exist in the CSS selector if the element has an id in the 'entrance object', the element is set as undefined. "

Getting a person's ORCID using the ORCID API

Best way to search a user's ORCID via name.
Is there any equivalent to what is essentially this page: https://orcid.org/orcid-search/search in the ORCID API ?
As in, and endpoint where I can just search as /api?name=john%20doe and get back their id as the response ?
Note: The API seems to have documentation, but that documentation doesn't include, as far as I've seen, any guidance to actually using the API, only pages which explain how the api should be used, without giving you the actual urls, query parameters or example calls... :/
Here you see how you have to get an Client_API ID and the secret for searching for ones name.
https://info.orcid.org/documentation/api-tutorials/api-tutorial-searching-the-orcid-registry/#easy-faq-2532
Validating ORCID using javascript
function validOrcID(str) {
/* ORCID format is XXXX-XXXX-XXXX-XXXX. */
if (!str) return false;
testStr = new String(str);
len = testStr.length;
if (len!=19) return false
dashTest = testStr.split('-');
if (dashTest.length !=4) return false;
if (dashTest[0].length !=4 || dashTest[1].length!=4 || dashTest[2].length!=4 || dashTest[3].length!=4) return false
if (isNaN(dashTest[0]) || isNaN(dashTest[1]) || isNaN(dashTest[2]) || isNaN(dashTest[3])) return false
return true;
}

Sage CRM - Loading a screen with a entity data?

Given this code:
var Container = CRM.GetBlock("Container");
var CustomCommunicationDetailBox = CRM.GetBlock("CustomCommunicationDetailBox");
Container.AddBlock(CustomCommunicationDetailBox);
if(!Defined(Request.Form)){
CRM.Mode=Edit;
}else{
CRM.Mode=Save;
}
CRM.AddContent(Container.Execute());
var sHTML=CRM.GetPageNoFrameset();
Response.Write(sHTML);
Im calling this .asp page with this parameters but does not seems to work
popupscreeens.asp?SID=33185868154102&Key0=1&Key1=68&Key2=82&J=syncromurano%2Ftabs%2FCompany%2FCalendarioCitas%2Fcalendariocitas.asp&T=Company&Capt=Calendario%2Bcitas&CLk=T&PopupWin=Y&Key6=1443Act=512
Note the Key6=Comm_Id and Act=512??? which i believe it is when editing?
How can i achieve to fill the screen's field with entity dada?
In this case it is a communication entity
In order to populate a custom screen with data, you need to pass the data to the screen.
First, you need to get the Id value. In this case, we're getting it from the URL:
var CommId = Request.QueryString("Key6") + '';
We're going to put a few other checks in though. These are mainly to handle scenarios that have come up in different versions or from different user actions.
// check we have a value and get the Id from context if we don't
if(CommId == 'undefined'){
CommId = CRM.GetContextInfo("Communication","comm_communicationid");
}
// if CommId is still undefined, set it to zero to check later
// otherwise, make sure the URL only contains one CommId
if(CommId == 'undefined'){
CommId = 0;
} else if(CommId.indexOf(",") > -1){
CommId = CommId.substr(0,CommId.indexOf(","));
}
Certain user actions can make the URL hold multiple Ids in the same attribute. In these cases, those Ids are separated by commas. So, if the Id is not defined, we check if there is a comma in it. If there is, we take the 1st Id.
After we have the Id, we need to load the record. At this point, you should have already checked you have a valid id (E.g. not zero) and put some error handling in. In some pages you may want to display an error, in others you may want to create a new, blank record. This gets the record:
var CommRecord = CRM.FindRecord("communication","comm_communicationid = " + CommId);
After that, you need to apply the record to the screen. Using your example above:
CustomCommunicationDetailBox.ArgObj = CommRecord;
Adding all that to your script, you get:
var CommId = Request.QueryString("Key6") + '';
// check we have a value and get the Id from context if we don't
if(CommId == 'undefined'){
CommId = CRM.GetContextInfo("Communication","comm_communicationid");
}
// if CommId is still undefined, set it to zero to check later
// otherwise, make sure the URL only contains one CommId
if(CommId == 'undefined'){
CommId = 0;
} else if(CommId.indexOf(",") > -1){
CommId = CommId.substr(0,CommId.indexOf(","));
}
// add some error checking here
// get the communication record
var CommRecord = CRM.FindRecord("communication","comm_communicationid = " + CommId);
// get the container and the detail box
var Container = CRM.GetBlock("Container");
var CustomCommunicationDetailBox = CRM.GetBlock("CustomCommunicationDetailBox");
// apply the communication record to the detail box
CustomCommunicationDetailBox.ArgObj = CommRecord;
// add the box to the container
Container.AddBlock(CustomCommunicationDetailBox);
// set the moder
if(!Defined(Request.Form)){
CRM.Mode=Edit;
} else {
CRM.Mode=Save;
}
// output
CRM.AddContent(Container.Execute());
var sHTML=CRM.GetPageNoFrameset();
Response.Write(sHTML);
However, we would advise putting in more error/exception handling. If the user is saving the record, you will also need to add a redirect in after the page is written.
Six Ticks Support

Change publicated collection fields value

hope my first question here is not a stupid one.
Lets say we want to build a chat application with meteor, with logedin and anonymous users. The chat should be filled like that:
var post = {
userId: user._id, // is empty if anonymous user
submitted: new Date().getTime(),
text: chat_message
});
var postId = Posts.insert(post);
The publication could looks like this to make sure that the userId is not transfered
Meteor.publish('getTheChat', function() {
return Post.find({}, {fields: {userId: false});
});
But is there a way to add a field in the returned collection dynamically?
The userId should not be published but a status like "Your_Post","another_logedin_user" or "an_anonymous_user". By having that, I could include some css, so the chat looks a little bit more like WhatsApp or iMessage.
The logic inside the publish method could be something like
if (userId == this.userId) {
status = "Your_Post";
} else if (userId != null) {
status = "another_logedin_user";
} else {
status = "an_anonymous_user";
}
You see, the publication should include different values when called from different users. Is there a way in Meteor.publish?
Thanks for any insight or suggestions
Thank you both for your ideas! But as I had to find out (just for my inward peace) how it is possible inside the publish method server sided, I came, with the help of David's link, to this solution -maybe it will help someone later:
Meteor.publish('getTheChat', function(postId) {
var currentUserId = this.userId;
var ownerUserId = Posts.findOne({'_id':postId}).userId;
var findOptions = {}; // in my final coding these differ for 'chat room owners' and 'normal users'
var transform = function(post) {
if (currentUserId && post.userId == currentUserId) {
post.userId = "posted by you";
} else if (post.userId == null) {
post.userId = "anonym posted";
} else if (post.userId == ownerUserId) {
post.userId = "posted by owner";
} else {
post.userID = "posted by another loged in";
return post;
};
var self = this;
var handle = Posts.find(findOptions).observe({
added: function (document) {
self.added('posts', document._id, transform(document));
},
changed: function (newDocument, oldDocument) {
self.changed('posts', document._id, transform(newDocument));
},
removed: function (oldDocument) {
self.removed('posts', oldDocument._id);
}
});
self.ready();
self.onStop(function(){
handle.stop();
});
By having this I am finally able to overwrite values dynamically.
It looks like you need to add a transform on your Posts collection. You can do this in your publish function (as seen here), but server-side transforms are computationally inefficient and tricky to write. Though they are necessary in cases where only the server could perform the action - e.g. signed URLs. In your case, I'd recommend a standard collection transform which is a filter applied after the documents are fetched.
Unfortunately, this kind of transform would require the userId on the client. I've never seen a case where simply publishing a id could cause a security issue. If you believe this is the case with your app, I'm very interested to know why. If you can overcome this restriction, keep reading...
You can read about transforms in the documentation on collections, and you can see an example on evented mind. Personally I like to use the collection-helpers package for this.
If you try collection-helpers, your transform could look like:
Posts.helpers({
status: function() {
if (this.userId === Meteor.userId()) {
return 'Your_Post';
} else if (this.userId != null) {
return 'another_logedin_user';
} else {
return 'an_anonymous_user';
}
}
});
And then you could use it in your template like:
{{#each posts}}
<p>{{text}} - <span class='status'>{{status}}</span></p>
{{/each}}
Of course, you can also use template helpers to achieve the same result but transforms are more easily reusable across your application.
Sadly, this has been a huge issue for me too, and I am sorry to say, it is not technically possible to just add a field on the publisher's query and use it conveniently in your view. BUT, I have a solution that may work for you. It will also give you an idea of how complex it can become as soon as you want to keep some reactive data private in Meteor.
Server side:
First, create two different publishers: one for the current user's posts, one for all the others:
Meteor.publish('getTheOthersChat', function() {
return Post.find({$ne:{userId: this.userId}}, {fields: {userId: false});
});
Meteor.publish('getTheOwnChat', function() {
return Post.find({userId: this.userId});
});
Client/router side:
Then, subscribe to both of these: what this will do is include the post's userId only when it is the own user's id. If not, it'll be undefined.
Then, we still need to identify the case "anonymously posted" vs "posted by user". For this, you can add another field during the post creation, for example is_anonymous, which you then set to true or false depending on the case if the user is logged in or not. The check would then be:
if (userId) {
status = "Your_Post";
} else if (is_anonymous === false) {
status = "another_logedin_user";
} else {
status = "an_anonymous_user";
}
This solution should work. I know, it is sad to have to come to this kind of means. It makes Meteor look clunky and impractical for tasks that should be dead easy. Such a shame for such a cool framework!

Resources