I have four date fields on a form and I need to alert the user when they have entered repetitive dates. I have already created an array that generates and checks the dates, but throwing the alert is not working for me. This is what I have so far.
else if ((dateRow1.text== dateRow2.text) || (dateRow1.text == dateRow3.text) || (dateRow1.text == dateRow4.text)) {
alert.show("You must have unique dates for each field");
Thanks.
You can check if your text fields are all distinct from each other:
var t:Array = [dateRow1, dateRow2, dateRow3, dateRow4];
var l:Number = t.length;
var i:Number;
var j:Number;
for (i = 0; i < l; i++) {
var ti = t[i]; // dateRow1... dateRow4
for (j in t) {
if (ti != t[j] && ti.text == t[j].text) trace(ti + "==" + t[j]);
}
}
Related
I'm trying to design a template that generates a document based on the following Diagram.
For each System (1 or 2) there is a Chapter, which will be further populated with subchapters for every "Element type C".
Example of chapter structure
The template I'm using is this one:
And it calls a table:
While scripting the fragment of this, for each element type C, I'm checking the connection type ( connection Y) to see if the element is related and can be added to the bottom half of the table. If so, the ID of element Type B is added to an XML structure.
The code I'm using is the following:
function arrange_data_in_xml(objectid) {
//Get the repository type
var repotype = Repository.RepositoryType();
//Create the xml structure
var xmlDOM = new COMObject("MSXML2.DOMDocument.6.0");
xmlDOM.validateOnParse = false;
xmlDOM.async = false;
var node = xmlDOM.createProcessingInstruction("xml", "version='1.0' encoding='ISO-8859-1'");
xmlDOM.appendChild(node);
var xmlRoot = xmlDOM.createElement("EADATA");
xmlDOM.appendChild(xmlRoot);
var xmlDataSet = xmlDOM.createElement("Dataset_0");
xmlRoot.appendChild(xmlDataSet);
var xmlData = xmlDOM.createElement("Data");
xmlDataSet.appendChild(xmlData);
var part1A = Repository.GetElementSet(sqlquery, 2);
//Session.Output(part1A.Count)
//This cycle will iterate over each Element Type B
for (var i = 0; i < part1A.Count; i++) {
var ElementTypeBrow = ""
var countElementTypeB = 0;
Session.Output("DEBUG 0 " + part1A.GetAt(i).ElementID + " -- " + part1A.GetAt(i).Name + " -- " + objectid)
var xmlRow = xmlDOM.createElement("Row");
xmlData.appendChild(xmlRow);
var xmlName = xmlDOM.createElement("Object_ID");
xmlName.text = part1A.GetAt(i).ElementID;
xmlRow.appendChild(xmlName);
var xmlName = xmlDOM.createElement("ElementTypeCName");
xmlName.text = part1A.GetAt(i).Name;
xmlRow.appendChild(xmlName);
for (var k = 0; k < part1A.GetAt(i).TaggedValues.Count; k++) {
var tv = part1A.GetAt(i).TaggedValues.GetAt(k);
if (tv.Name == 'Proprety X') {
var xmlName = xmlDOM.createElement("ElementTypeC.TagValue1");
xmlName.text = tv.Value;
xmlRow.appendChild(xmlName);
} else if (tv.Name == 'Proprety Y') {
var xmlName = xmlDOM.createElement("ElementTypeC.TagValue2");
xmlName.text = tv.Value;
xmlRow.appendChild(xmlName);
}
}
for (var j = 0; j < part1A.GetAt(i).Connectors.Count; j++) {
var connector = part1A.GetAt(i).Connectors.GetAt(j);
if (connector.Stereotype == 'Connection type Z') {
var xmlName = xmlDOM.createElement("ElementTypeC.TagValue3");
xmlName.text = Repository.GetElementByID(connector.SupplierID).Name;
xmlRow.appendChild(xmlName);
} else if (connector.Stereotype == 'Connection type Y') {
var xmlName = xmlDOM.createElement("ElementTypeC.TagValue4");
xmlName.text = Repository.GetElementByID(connector.ClientID).Name;
xmlRow.appendChild(xmlName);
} else if (connector.Stereotype == 'Connection type X'') {
var ElementTypeB = Repository.GetElementByID(connector.SupplierID)
//Check if the ElementTypeB is connected to the current interface
if (check_interface(ElementTypeB.ElementID) == objectid) {
//Session.Output("DEBUG 1 " + part1A.GetAt(i).Name + " - " + ElementTypeB.Name)
var ElementTypeBalias = ""
if (ElementTypeB.Alias != "") {ElementTypeBalias = " (" + ElementTypeB.Alias + ")"}
ElementTypeBrow = ElementTypeB.Name + ElementTypeBalias + " \n" + ElementTypeBrow
countElementTypeB = countElementTypeB + 1
}
}
}
//Session.Output("DEBUG 1 " +ElementTypeBrow + " - " + countElementTypeBs + " - " + ElementTypeB.Name)
var xmlName = xmlDOM.createElement("ApplicableElementTypeB-Hyperlink");
if (countElementTypeBs > 1) {
xmlName.text = ElementTypeBrow.trimStart();
} else {
xmlName.text = ElementTypeB.ElementGUID+ElementTypeBrow;
}
xmlRow.appendChild(xmlName);
}
return xmlDOM.xml;
}
The problem that I'm having now is the result list of elements is taken as one, ie all of the elements of the bottom half of the table are as one.
Is there a way to manipulate each element individually (for example create a hyperlink for each entry)?
I've tried to rearrange the XML stucture. But the problem remains.
Fragments like this expect there contents in the form of a table, with rows and columns.
Currently you are only returning a single row, but somehow you would like to split the different values for ElementTypeBName-Hyperlink into different rows.
I think you have two possibilities here:
Return each ElementTypeBName-Hyperlink element with a different name e.g. ElementTypeBName-Hyperlink1, ElementTypeBName-Hyperlink2,... and then use each of those in your table template.
Downside of this approach is that it isn't flexible at all, and only useful if you have more or less the same number of ElementB names.
Split your template into two templates. One template for the upper part, and one for the lower part.
Another observation is that you are taking the complicated (and slow) route. For requirements like this, it's much easier to create an SQL template instead of a script template.
The script below places a static time stamp into column A whenever column B is edited.
function onEdit() {
var s = SpreadsheetApp.getActiveSheet();
if (s.getName() == "Sheet1") {
var r = s.getActiveCell();
if (r.getColumn() == 2) {
var nextCell = r.offset(0, -1);
if (nextCell.getValue() === '')
nextCell.setValue(new Date());
}
}
}
However, I would like the timestamp to occur when columns B,C,D,E, etc. are edited.
How do I specify that Column A (or 1) is where I want the date to be placed?
The documentation knows a Method on a Sheet getRange(row, column)
So I guess, this should work:
var firstCell = s.getRange(r.getRow(), 1);
You can use Event object to get information about the cell that was edited. No need to use getActiveSheet, getActiveCell and offset:
function onEdit(e) {
var range = e.range; // Range that was edited
var sheet = range.getSheet(); // Sheet that was edited
var editedRow = range.getRow(); // Row that was edited
var editedCol = range.getColumn(); // Column that was edited
if (sheet.getName() == "Sheet1" && editedCol > 1) { // Check that edited sheet is "Sheet1" and edited column is not A
var colA = sheet.getRange(editedRow, 1); // Get corresponding cell in column A
if (colA.getValue() === "") colA.setValue(new Date()); // Set date if cell is empty
}
}
Reference:
Event objects: onEdit
Sheet.getRange(row, column)
I want to make a script that send me mail whith the content of what I've enterd in a form some days after the form is submitted. As a help for students to study. In the form enter what to study, then 1 day, 7 days and 28 days later get that in a mail.
I've made a form that collect time, recipient adress, subject and body for the mail. These are saved in a Google spreadsheet.
The code kind of work. But it send all of the mail from my test input in the sheet. I've added one 6 days ago, one yesterday and one today.
Today I should only get one mail and tomorrow two. But I get all of them today.
I think it's this line:
if (diffDays == 1 || diffDays == 7 || diffDays == 28) continue;
I've trided to change it, searched other ways of writing it, as array for example.
Here's the full code:
function createTimeDrivenTriggers() {
ScriptApp.newTrigger('reminder')
.timeBased()
.everyDays(1)
.atHour(16) // Change time of day here
.inTimezone("Europe/Stockholm")
.create();
}
function reminder() {
var today = new Date(); // Today
var sheet = SpreadsheetApp.getActiveSheet();
var range = sheet.getDataRange();
var data = range.getValues();
var headerRows = 1;
for (i=0; i<data.length; i++){
if (i < headerRows) continue;
var row = data[i];
var time = row[0];
// Get time difference
var timeDiff = Math.abs(today.getTime() - time.getTime());
var diffDays = Math.ceil((timeDiff) / (1000 * 3600 * 24)-1);
if (diffDays == 1 || diffDays == 7 || diffDays == 28) continue;
var recipient = row[1];
var subject = row[2];
var body = row[3];
// Send mail
GmailApp.sendEmail(recipient, subject, body)
}
}
Thanks
Make sure to execute the code only when your if statement is true.
Include all code after if statement in the statement itself, only executing it when diffDays = 1, 2 or 28.
function reminder() {
var today = new Date(); // Today
var sheet = SpreadsheetApp.getActiveSheet();
var range = sheet.getDataRange();
var data = range.getValues();
var headerRows = 1;
for (i=0; i<data.length; i++){
if (i < headerRows) continue;
var row = data[i];
var time = row[0];
// Get time difference
var timeDiff = Math.abs(today.getTime() - time.getTime());
var diffDays = Math.ceil((timeDiff) / (1000 * 3600 * 24)-1);
if (diffDays == 1 || diffDays == 7 || diffDays == 28) {
var recipient = row[1];
var subject = row[2];
var body = row[3];
// Send mail
GmailApp.sendEmail(recipient, subject, body)
}
}
}
Note that continue means "stop processing this iteration of whatever loop is getting executed, and begin the next iteration." So your script sends reminders on every day except the days you want to remind them.
Changing your test to an "AND NOT", moving the code for reminding inside the existing check, or using a more expressive syntax like switch will satisfy your intent.
switch (diffDays) {
case 1:
case 7:
case 28:
sendReminder(your, Args, Here);
break;
case 0:
sendWelcome(some, other, Args);
break;
...
default:
break;
}
...
function sendReminder(your, Args, Here) {
/* Code that uses function arguments to build and send an email */
}
"And not" meaning if (diffDay != 1 && diffDay != ...), i.e. "if it's not this, and not that, and not this other thing, and not..."
i couldnt do aautocopmlete edit box. i want to take names from another database. i wrote my code to typeahead's value list. but it dont work. i am using same server but different database.anybody help me ? here is my code:
//Getting the view containing a document for each of the employees
var searchView:NotesView = session.getDatabase("servername","test/application name.nsf")
.getView("viewname");
// Creating a Lotus Notes search query. Notice the reference to lupkey!
var query = "(FIELD Ad Soyad CONTAINS *" + lupkey +"*)";
// Creating an array to store hits in
var searchOutput:Array = ["å","åå"];
// Doing the actual search
var hits = searchView.FTSearch(query);
var entries = searchView.getAllEntries();
var entry = entries.getFirstEntry();
//Sort the array manually, since Notes doesn't want to sort them alphabetically
for (i=0; i<hits; i++) {
searchOutput.push(entry.getColumnValues()[0]);
entry = entries.getNextEntry();
}
searchOutput.sort();
// Build the resulting output HTML code
var result = "<ul><li><span class='informal'>Suggestions:</span></li></ul>";
var limit = Math.min(hits,20);
for (j=0; j<limit; j++) {
var name = searchOutput[j].toString();
var start = name.indexOfIgnoreCase(lupkey)
var stop = start + lupkey.length;
//Make the matching part of the name bold
name = name.insert("</b>",stop).insert("<b>",start);
result += "<li>" + name + "</li>";
}
result += "</ul>";
return result;
There are plenty of issues with your code:
the query can't return any result since your field has a space in it
Do you really need an FTSearch to return values and not a sorted view?
the typeahead -as the name suggest- presents values that match left to right and not somewhere substring. If you need that you need to roll your own typeahead function using Ajax
The typeahead function doesn't take a parameter, so your lupkey doesn't go anywhere. The function needs to return all values and XPages will do the matching
Instead of copying one by one into an array for sorting, copy the returning Vector() into a TreeSet(). This is one line, sorts it and removes duplicates
To get it working check this example based on dojo, previously asked here. You will need the REST control
i do it like that
var directoryTypeahead = function (searchValue:string) {
// update the following line to point to your real directory
//var directory:NotesDatabase = session.getDatabase(database.getServer(), "names.nsf");
var directory:NotesDatabase = session.getDatabase(database.getServer(), "org/test.nsf");
var allUsers:NotesView = directory.getView("SVFHP2");
var matches = {};
var includeForm = {
Person: true,
Group: true
}
searchValue = searchValue.replace("I","i")
var matchingEntries:NotesViewEntryCollection = allUsers.getAllEntriesByKey(searchValue, false);
var entry:NotesViewEntry = matchingEntries.getFirstEntry();
var resultCount:int = 0;
while (entry != null) {
var matchDoc:NotesDocument = entry.getDocument();
var matchType:string = matchDoc.getItemValueString("Form");
//if (includeForm[matchType]) { // ignore if not person or group
var fullName:string = matchDoc.getItemValue("Name").elementAt(0) + " " + matchDoc.getItemValue("Title").elementAt(0);
if (!(matches[fullName])) { // skip if already stored
resultCount++;
var matchName:NotesName = session.createName(fullName);
matches[fullName] = {
cn: matchName.getCommon(),
photo: matchDoc.getItemValueString("Photo"),
job: matchDoc.getItemValueString("sum"),
email: matchDoc.getItemValueString("email"),
}
}
// }
/*if (resultCount > 15) {
entry = null; // limit the results to first 10 found
}
else {*/
entry = matchingEntries.getNextEntry(entry);
//}
};
}
Here is what I want to do:
I got a date range from e.g. 03.04.2013 to 23.04.2013 - that's my main range.
Now I have the possibility to create some own time ranges (e.g. 04.04.2013 to 09.04.2013 and 11.04.2013 to 23.04.2013). Those have to cover the whole main range, so every day of the main range (excluding weekens) needs an corresponding day in my own time ranges.
My plan would be to create an Array for the main range. Then I check each day of my own time ranges against the main range. If there is an accordance, I would remove the day from the main range.
So in the end, if everything is ok, there would be an emtpy array, because all days are covered by my own time ranges. If not, then the days not covered would still be in the main range and I could work with them (in this example: 03.04.2013, 10.04.2013)
Does anybody have an better idea to solve this problem? NotesDateTimeRanges?
I would add the dates into a sorted collection and then a "pirate algorithm". Look left, look right and if any of the looks fails you can stop (unless you want to find all missing dates).
Off my head (you might need to massage the final list to store the value back):
var AbsenctSince:NotesDateTime; //Start Date - stored in the NotesItem
var endDate:NotesDateTime; // Return, could be in Notes or Today
var wfDoc:NotesDocument = docApplication.getDocument();
var responseCDs:NotesDocumentCollection = wfDoc.getResponses();
var docResponse:NotesDocument;
var nextResponse:NotesDocument;
//Get the date, which limits the function - if there is a return information, then this is the limit, else today
AbsenctSince = wfDoc.getDateTimeValue("AbsentSince") ;
if (wfDoc.hasItem("ReturnInformationDat")) {
endDate = wfDoc.getDateTimeValue("ReturnInformationDat");
} else {
endDate = session.createDateTime("Today");
}
//Get all days between two dates - as pure Java!
var dateList:java.util.List = getWorkDayList(AbsenctSince.toJavaDate(), endDate.toJavaDate());
// Looping once through the reponse documents
var docResponse = responseCDs.getFirstDocument();
while (docResponse != null) {
nextResponse = responseCDs.getNextDocument(docResponse);
var CDValidSince:NotesDateTime = docResponse.getDateTimeValue("CDValidSince");
var CDValidTill:NotesDateTime = docResponse.getDateTimeValue("CDValidTill");
// Now we need get all days in this range
var removeDates:java.util.List = getWorkDayList(CDValidSince.toJavaDate(),CDValidTill.toJavaDate());
dateList.removeAll(removeDates);
docResponse.recycle();
docResponse = nextResponse;
}
// Both docs are null - nothing to recycle left
// Now we only have uncovered dates left in dateList
docApplication.replaceItemValue("openDates", dateList);
// Cleanup
try {
AbsenctSince.recycle();
endDate.recyle();
wfDoc.recycle();
responseCDs.recycle();
} catch (e) {
dBar.error(e);
}
function getWorkDayList(startDate, endDate) {
var dates:java.util.List = new java.util.ArrayList();
var calendar:java.util.Calendar = new java.util.GregorianCalendar();
calendar.setTime(startDate);
while (calendar.getTime().before(endDate)) {
var workDay = calendar.get(calendar.DAY_OF_WEEK);
if (workDay != calendar.SATURDAY && workDay != calendar.SUNDAY) {
var result = calendar.getTime();
dates.add(result);
}
calendar.add(java.util.Calendar.DATE, 1);
}
return dates;
}
I've done it this way now (seems to work so far):
var dateArray = new Array();
var responseCDs:NotesDocumentCollection = docApplication.getDocument().getResponses();
var dt:NotesDateTime = session.createDateTime("Today");
var wfDoc = docApplication.getDocument();
dt.setNow();
//Get the date, which limits the function - if there is a return information, then this is the limit, else today
var AbsenctSince:NotesDateTime = session.createDateTime(wfDoc.getItemValue("AbsentSince").toString().substr(0,19));
if (wfDoc.hasItem("ReturnInformationDat")) {
var endDate:NotesDateTime = session.createDateTime(wfDoc.getItemValue("ReturnInformationDat").toString().substr(0,19));
} else {
var endDate:NotesDateTime = session.createDateTime("Today");
}
//Get all days between two dates
dateArray = getDates(AbsenctSince, endDate);
for (var i=dateArray.length-1; i >= 0 ; i--) {
var checkDate:NotesDateTime = session.createDateTime(dateArray[i].toString().substr(0,19));
var day = checkDate.toJavaDate().getDay();
//Remove weekends first
if ((day == 6) || (day == 0)) { //6 = Saturday, 0 = Sunday
dBar.info("splice: " + dateArray[i]);
dateArray = dateArray.splice(i,1);
} else {
var docResponse = responseCDs.getFirstDocument();
//Work through all response docs to check if any date is covered
while (docResponse != null) {
var CDValidSince:NotesDateTime = session.createDateTime(docResponse.getItemValue("CDValidSince").toString().substr(0,19));
var CDValidTill:NotesDateTime = session.createDateTime(docResponse.getItemValue("CDValidTill").toString().substr(0,19));
//checkDate covered? If yes, it will be removed
if (checkDate.timeDifference(CDValidSince)/86400 >= 0 && checkDate.timeDifference(CDValidTill)/86400 <= 0 ) {
dBar.info("splice: " + dateArray[i]);
dateArray = dateArray.splice(i,1);
}
docResponse = responseCDs.getNextDocument();
}
}
}
docApplication.replaceItemValue("openDates", dateArray);
And I'm using this function (adopted from this question here):
function getDates(startDate:NotesDateTime, endDate:NotesDateTime) {
var dateArray = new Array();
var currentDate:NotesDateTime = startDate;
while (endDate.timeDifference(currentDate) > 0) {
dateArray.push( currentDate.getDateOnly() );
currentDate.adjustDay(1);
}
return dateArray;
}