Google Sites ListItem Data to Database Event - database

I have a Google Sites site with a List page.
I want to get the data from the ListItems into a database (ScriptDB or JDBC to MySQL/SQL Server.)
I'd like to fire an update as a result of an onUpdate type event, but can't find anything like that. Is the solution to run a script every so often?
The downside of running a script periodically is that I can't capture all changes, only the current state when the script runs. I would like a full audit of changes if possible. I'd also have to interrogate each ListItem to check the lastUpdated date to see if it is newer than the records already in the database, which seems like a lot of redundant processing.
Any advice?

Indeed you cannot run an onUpdate event as they are reserved for Spreadsheets.
However you have the information on the listItems and you can run a loop like this to get the update time and publication time of your items:
function getChanges(){
var listItems=page.getListItems();
var update;
var datePublished;
for(var i=0;i<listItems.length;++i){
update=item[i].getLastUpdate();
datePublished=item[i].getDatePublished();
list of stuff to do like compare with the content of your database;
}
}
A clock trigger is then the best solution to capture the changes (like a 10 minute trigger).
Unfortunately you cannot do more than this.
Cheers
Nicolas

Thanks Nicolas. In the end I already implemented something pretty similar:
function updatePeople(){
for(var j in peopleList){
// Check if the record in the peopleList is newer than the last database (not record) update.
var personUpdated=peopleList[j].getLastUpdated().getTime();
var dbUpdated= db.query({Type:"dbUpdated"}).next().dbUpdated;
// If the record is newer than the last database update...
if(personUpdated>dbUpdated)
{
// ...check if the record exists in the database (check for current record using Is_Current = 1)
// If it does, set the Valid_To date on the old record to be 1 second before the Valid_From date of the new record.
var result = db.query({Type:"Person", Initials: peopleList[j].getValueByName("Initials"), Is_Current:1}).getSize();
if(result>0){
var oldPerson = db.query({Type:"Person", Initials: peopleList[j].getValueByName("Initials"), Is_Current:1}).next();
var validTo = personUpdated-1000;
oldPerson.Valid_To = validTo;
oldPerson.Is_Current = 0;
db.save(oldPerson);
// now add a new Person record.
addPerson(j);
}
else {
addPerson(j);
}
}
}
updateDbUpdatedDate();
}
The addPerson() function is a pretty simple function to add a new record.
function addPerson(j){
var person={};
person["Valid_From"]=peopleList[j].getLastUpdated().getTime();
person["Valid_To"]=9999999999999;
person["Is_Current"]=1;
person["Type"]="Person";
person["Initials"]= peopleList[j].getValueByName("Initials");
person["Name"]=peopleList[j].getValueByName("Name");
person["Email_Address"]= peopleList[j].getValueByName("Email Address");
person["Team"]=peopleList[j].getValueByName("Team");
person["Grade"]=peopleList[j].getValueByName("Grade");
person["Admin"]=peopleList[j].getValueByName("Admin");
db.save(person);
}

Related

Firebase cloud function not updating record

imagine this scenario:
You have a list with lets say 100 items, a user favorites 12 items from the list.
The user now wants these 12 items displayed in a new list with the up to date data (if data changes from the original list/node)
What would be the best way to accomplish this without having to denormalize all of the data for each item?
is there a way to orderByChild().equalTo(multiple items)
(the multiple items being the favorited items)
Currently, I am successfully displaying the favorites in a new list but I am pushing all the data to the users node with the favorites, problem is when i change data, their data from favorites wont change.
note - these changes are made manually in the db and cannot be changed by the user (meta data that can potentially change)
UPDATE
I'm trying to achieve this now with cloud functions. I am trying to update the item but I can't seem to get it working.
Here is my code:
exports.itemUpdate = functions.database
.ref('/items/{itemId}')
.onUpdate((change, context) => {
const before = change.before.val(); // DataSnapshot after the change
const after = change.after.val(); // DataSnapshot after the change
console.log(after);
if (before.effects === after.effects) {
console.log('effects didnt change')
return null;
}
const ref = admin.database().ref('users')
.orderByChild('likedItems')
.equalTo(before.title);
return ref.update(after);
});
I'm not to sure what I am doing wrong here.
Cheers!
There isn't a way to do multiple items in the orderByChild, denormalising in NoSQL is fine its just about keeping it in sync like you mention. I would recommend using a Cloud Function which will help you to keep things in sync.
Another option is to use Firestore instead of the Realtime Database as it has better querying capabilities where you could store a users id in the document and using an array contains filter you could get all the users posts.
The below is the start of a Cloud function for a realtime database trigger for an update of an item.
exports.itemUpdate = functions.database.ref('/items/{itemId}')
.onUpdate((snap, context) => {
// Query your users node for matching items and update them
});

AngularJS UI-calendar not updating events on Calendar

I am using Angular UI-Calendar to show some events on the Calendar. The events are showing fine on the Calendar. But when I update any event's details, the event's detail is actually modified, but not modified on the Calendar display(eg: start).
Initially, after I modified the event's details, I did a page reload to display modified changes and it worked too.In that method, I had empty $scope.events = []; array, which I filled after retrieving entries from DB.
But now, I want to avoid that page reload. So, once the event's details are modified from modal window, I clear the contents of $scope.events array using $scope.events = []; and then using API call, I fill the new events again in $scope.events array. This is working fine as the List view shows the modified events details. But the Calendar itself shows old entries. e.g if I change start from 11 April to 13 April, the calendar shows event on 11 April whereas List views shows the modified data i.e 13 April. Using Page reload, corrects this i.e event is shown on modified date(13 April).
How can I ensure that the event is modified on Calendar too without a Page reload ?
I tried calendar.fullCalendar('render'); after fetching new entries from DB, but it does not solve the Problem.
Here are some codes :
Initially I did this to send data to DB and then did a page reload to retrieve updated data.
$http.put(url,senddata).success(function(data){$window.location.reload();});
Now I want to avoid the page reload, so I did
$http.put(url,senddata).success(function(data){//$window.location.reload();//sends modified data to server
$scope.events = []; //clear events array
$http.get(url2).success(function(data){ //fetch new events from server, and push in array
$scope.schedule = data;
for(var i=0;i<data.length;i++)
{
$scope.events.push({
idx: data[i].idx,
title: data[i].title,
description : data[i].description,
allDay: false,
start: new Date(data[i].start),
end: new Date(data[i].end),
});
calendar.fullCalendar('render'); //Tried even this, didn't work
}
});
Above code pushes modified event in event array as visible in List view, but calendar still shows old event until page is reloaded.
Try maintaining the same array instance.
Instead of doing:
$scope.events = []
Try:
$scope.events.slice(0, $scope.events.length);
Then when your server request comes back, add each item individually to the existing array:
for(var i = 0; i < newEvents.length; ++i) {
$scope.events.push(newEvents[i]);
}
The reason I suggest this is because what you're describing sounds like the calendar might be holding onto the old list of events. The calendar might not know to update its reference, so instead, let it keep the same reference but change the contents of the array.
Just a quick correction to Studio4Development's answer. You should use "splice" not "slice". Slice returns the trimmed array. What we want to do is actually alter the original array. So you can use:
$scope.events.splice(0, $scope.events.length)
and to add new events:
$scope.events.push(newEvent)
Don't know if you found the solution to your problem, but what worked for me was:
calendar.fullCalendar('refetchEvents');
Here is how I fixed a similar problem on my page.
view (simplified, note using jade)
div#calendarNugget(ng-show="activeCalendar == 'Nugget'" ui-calendar="uiConfig.calendarNugget" ng-model="eventSources")
div#calendarWillow(ng-show="activeCalendar == 'Willow'" ui-calendar="uiConfig.calendarWillow" ng-model="eventSources2")
controller:
As per ui-calendar docs, I start with an empty array for my event sources. Ignore that I should probably rename these eventSources to something about the shop names (willow, nugget)
$scope.eventSources = [];
$scope.eventSources2 = [];
I then call a factory function that makes an http request and pulls a series of events from the DB. The events all have "title", "start", "end" properties (make sure your Date format is correct). They also have a "shop" property, which tells me which calendar to add the event to.
So after receiving the data I make two local arrays, loop through the received http data, and assign the events to those local arrays by shop. Finally, I can re-render the calendars with the proper event data by calling addEventSource, which automatically re-renders as per the fullCalendar docs
It looks something along the lines of this iirc:
function splitShiftsByShop(shifts) {
var nuggetShifts = [];
var willowShifts = [];
for (var i=0; i<shifts.length; i++) {
if (shifts[i].shop === "Nugget") {
var nshift = {
title : shifts[i].employee,
start : new Date(shifts[i].start),
end : new Date(shifts[i].end),
allDay: false
}
nuggetShifts.push(nshift);
} else if (shifts[i].shop === "Willow") {
var wshift = {
title : shifts[i].employee,
start : new Date(shifts[i].start),
end : new Date(shifts[i].end),
allDay: false
}
willowShifts.push(wshift);
}
}
/*render the calendars again*/
$('#calendarNugget').fullCalendar('addEventSource', nuggetShifts);
$('#calendarWillow').fullCalendar('addEventSource', willowShifts);
}
I was having some similar issues where events weren't being refetched after emptying my "eventSource" array ($scope.eventSources = [$scope.completedEvents]) and repopulating it with new events. I was able to overcome this at first by calling 'removeEvents',
uiCalendarConfig.calendars.calendar.fullcalendar('removeEvents')
This is hackish, so after further tinkering I found that events were refetched when my child array is modified,
$scope.completedEvents.splice(0, $scope.completedEvents.length)
After reviewing the source, I can see that the eventSource array is being watched, but the 'refetch' is never occurring. furthermore, I was never able to get the following to work,
uiCalendarConfig.calendars.calendar.fullcalendar('refetchEvents')
According to ui-calendar code, it does actually watch eventSources but never the actual individual sources. So doing a push to, let's say $scope.events ($scope.eventSources = [$scope.events]), will never trigger anything.
So push events to $scope.eventSources[0]
$scope.eventSources[0].splice(0, $scope.eventSources[0].length);
$scope.eventSources[0].push({
title : title,
start : start,
end : end
});
As you already know Full Calendar is dependant on JQuery. Therefore, all you need to do is put an ID on your calendar like this:
<div id="calendar" ui-calendar="uiConfig.calendar" class="span8 calendar" ng-model="eventSources"></div>
and then when ever you want to update the calendar just call:
$('#calendar').fullCalendar('refetchEvents')
I struggled with this for some time as well and this fixed all my problems. I hope this will help you too!
Goodluck!
Although it's late response but hope it may help some. I am putting it forth step by step.
You will need to maintain the same event source as Studio4Development mentioned earlier.
$scope.events //no need to reinitialize it
You will remove the updated event from events array like this (for last event in event array):
$scope.events.splice($scope.events.length - 1, 1);
For event that may exist anywhere in the events array you'll need to find its index using:
var eventIndex = $scope.events.map(function (x) { return x.Id; }).indexOf(eventId);
and then you'll remove it from events array like this:
$scope.events.splice(eventIndex, 1);
Next you'll fetch this event from the API again like this:
getEvent(data.Id);
and in the callback method you'll again add the event in events array:
var getSingleEventSuccessCallback = function (event) {
$scope.events.push(event);
};
Was stuck on this for a while.
What seems to be working for me turned out to be quite simple
I fetch my calendar from google and run an event formatter function. In this I define a new array and at the end of the function I set my eventSource equal to the new array. Then I call $scope.$apply
x = []
for e in events
x.push(
title: e.summary
etc...
)
$scope.eventSource = x
$scope.$apply()
Try this. It worked for me.
function flushEvents()
{
$scope.events.splice(0,$scope.events.length);
for(var i=0; i<$scope.events.length; i++)
{
$scope.events.splice(i,1);
}
}
I found this and it helped me:
$(id_your_div).fullCalendar('removeEvents');
$(id_your_div).fullCalendar('refetchEvents');

How to get only deleted records from an Ext js Grid store?

I have a grid store and I am able to get modified data using
var modifiedData = store.getModifiedData();
Now I want to get deleted records (I am using ExtJs 3).
I tried using var deletedData = store.getRemovedRecords(); but I guess this property is available in ExtJs 4.
I just want to fetch the records that are deleted from the grid.
Any help would be appreciated.
By default this is not possible.
ExtJS 3.x is only capable of tracking modified records (out of the box). Deleted (removed) records get removed completely. But there is one thing you can do; The store will fire the remove event for each record with the record itself as second argument. You may use this to create your own array of removed records. The implementation would be really simple I guess. You can do it per instance or create a whole new storetype by extending. But I guess the later is not really needed here.
Here is a example. Note that you might need take care of other events to clear the removedList.
var myStore = new Ext.data.Store({
removedList: [],
listeners: {
clear: function(store) {
store.removedList = [];
},
load: function(store) {
store.removedList = [];
},
remove: function(store, record, index) {
store.removedList.push(record);
}
}
});
Check these two links out. They might be helpful.
http://www.sencha.com/forum/showthread.php?58888-Store-find-deleted-added-records
mi
http://www.sencha.com/forum/showthread.php?15671-Ext.data.Store-with-persistence

updating a field on contact whenever a new event is created?

Here is the problem I'm trying to solve:
When a new TASK/EVENT is created, if the user is a certain profile - we want to update a field on CONTACT with the day the T/E was created.
I tried doing a workflow rule and field update- but I couldn't get it to work... I think since this is a Standard -Standard object relationship via lookup, it might have a problem doing a field update. Any other ideas?? I'd prefer to use the platform for this one...
HALP!
Thanks
We have done exactly what you are talking about via an Apex trigger. Something like this...
//I'm sure this doesn't compile, but it gives you the idea
trigger taskTrigger on Task( after insert, after update ){
Task t = trigger.new ;
Contact contact = [Select Id from Contact where Id = :t.whoId] ;
contact.yourfield = t.AcitivityDate ;
update contact ;
}

Update Multi-Picklist on updating custom object

I have custom object KeywordAccountAssociation__c. This object has three fields
Account__c - Master-Detail(Account)
Keyword__c - Master-Detail(Keyword)
Compositecp__c - Text(255) (External ID) (Unique Case Sensitive)
I have a custom field in Account
DD_Segment__c - multi-picklist
Now I want to update (Insert is fine too) values of DD_Segment__c whenever KeywordAccountAssociation__c is updated. I could write trigger for this but I don't know how? I am new to Salesforce Development and my background is ruby (so getting accustomed to apex is bit difficult for me).
KeywordAccountAssociation__c has multiple rows of Account__c which has same account_id and those account_id are related to a record of custom object Keyword__c. I want to get all keywords related to one account and update in its (account's) multi-picklist. How can I achieve this? If you have doubts about this please do ask. Thanks!
One issue is related to learning to work with triggers in general, which can be started with Salesforce Apex Developer Documents on Triggers
but to answer your actual question, you would essentially need to build a trigger against your custom object that would update the related account. It might look something like this:
trigger keywordAccountUpdate on KeywordAccountAssociation__c (after insert, after update){
set<id> = new set<id>();
for (KeywordAccountAssociation__c a : Trigger.new)
accountIds.put(a.Account__c);
map<id,Account> accountMap = new map<id,Account>([select id, DD_Segment__c from Account where id in :accountIds]);
for (KeywordAccountAssociation__c kaa : Trigger.new){
if (AccountMap.containskey(kaa.Account__c)){
Account thisAccount = AccountMap.get(kaa.Account__c);
String s = thisAccount.DD_Segment__c + ';new value'; // Always add value
if ((thisAccount.DD_Segment__c).contains('second value')
s += ';second value';
AccountsToUpdate.add(new Account(id=thisAccount.id, DD_Segment__c = s));
}
}
}
Please keep in mind that I don't have the structure to test this trigger, I just free handded it, so YMMV.

Resources