I am designing a SwiftUI App to enable reporting of daily revenues/activities of a particular centre. (Using Realm Flexible Sync)
Not all App users can report the data - Sync permissions set on "RevenueReport" collection for read/ write if user.customdata.isCentreAdmin = true.
I have a function to save the data from the App. I want the user to be intimated if he tries to submit report that he does not have write permissions using app.syncmanager.errorhandler.
if accApp.syncManager.errorHandler == nil {
alertMessage = "report submitted"
showAlertToggle.toggle()
} else {
accApp.syncManager.errorHandler = { error, session in
alertMessage = "You are not authorised to submit reports /n Please Log In, again"
showAlertToggle.toggle()
}
This always shows up "report submitted" even when write permission error is seen in the debug area.
My function to save the report
func saveRevRep() {
let revIPDouble = Double(revenueIPD) ?? 0.0
let revOPDouble = Double(revenueOPD) ?? 0.0
let revenueTot = revIPDouble + revOPDouble
let collectAmtDouble = Double(collectAmt) ?? 0.0
let reps = users.first
revenueOfCentre = reps!.centreName
let rep = RevenueReport()
rep.revenueReportedBy = revenueReportedBy!
rep.revenueReportedById = revenueReportedById!
rep.centreName = revenueOfCentre
rep.revenueDate = revenueDate
rep.revenueIPD = revIPDouble
rep.revenueOPD = revOPDouble
rep.revenueTot = revenueTot
rep.collectAmt = collectAmtDouble
$revReps.append(rep)
accApp.syncManager.errorHandler = { error, session in
alertTitle = "Artemis Cardiac Care Alert!"
alertMessage = "You are not authorised to submit reports \n Please Log In, again"
showAlertToggle.toggle()
accApp.currentUser?.logOut { _ in
}
}
alertTitle = "Artemis Cardiac Care Alert!"
alertMessage = "Report submitted"
showAlertToggle.toggle()
}
The syncManager.errorhandler correctly displays the alert on wrong submission without permissions. But, I also want to display confirmation to authorised user that report has been submitted.
If I add the second alert "Report submitted". That is the one that shows up regardless of the error or not. The syncManager.errorhandler block does not execute.
Related
I am supposed to generate drive activity report so we can track what type of file users are using and where is the file being created (My Drive/shared drive).
I used the GAM command to pull drive activity report which has various fields except for the root path.
Does anyone know a way i can manipulate that so i can get a field that shows folder path as well.
Thanks!
You can try these particular GAM commands so you can edit them later to gather information of the folders and root folders:
gam user <User Email Address> print filetree depth 0 showmimetype gfolder excludetrashed todrive
You can edit the depth, for example orphaned folders when using -1. I am not familiar with which command you use, but you might need to mix or add some fields so it shows the root folder or path.
gam user <User Email Address> print filelist todrive select 1Yvxxxxxxxxxxxxxxxxxxxxxxjif9 showmimetype gfolder fields id
You might need to add over your command something like "print filetree" or "show filepath"
Reference:
https://github.com/taers232c/GAMADV-XTD3/wiki/Users-Drive-Files-Display
I have created a custom menu that iterates through a table of data, the data must have a column with the file IDs of interest and 2 additional columns for owner and path, since the file can be owned by either a user or a shared drive. The user running the function must have Super Admin rights to access files owned by other users and the user in question must be a member of a shared drive for the file to be located. My previous implementation as a custom function failed to address a limitation of this feature where advanced services are inaccessible.
The custom menu is created as explained in this documentation article https://developers.google.com/apps-script/guides/menus. There must be a trigger that executes when the sheet opens the menu is created.
In addition to that the code requires the use of Advanced Services, Google Drive must be added following the steps of this other article https://developers.google.com/apps-script/guides/services/advanced#enable_advanced_services. The advanced service will ask for authorization but the first time the code is executed. You may expedite the process by creating an empty function and running it.
function onOpen() {
var ui = SpreadsheetApp.getUi();
ui.createMenu('File ownership').addItem('Read data', 'readData').addToUi();
}
function readData() {
var sheetData = SpreadsheetApp.getActiveSheet().getDataRange().getValues();
var i = 0;
for (; i < sheetData.length; i++){
if (sheetData[0][i] == '')
break;
}
SpreadsheetApp.getUi().alert('There are ' + i + ' cells with data.');
for (i = 1; i < sheetData.length; i++){
var fileID = sheetData[i][0];
var owner = getFileOwner(fileID);
var path = getFilePath(fileID);
SpreadsheetApp.getActiveSheet().getRange(i + 1,2).setValue(owner);
SpreadsheetApp.getActiveSheet().getRange(i + 1,3).setValue(path );
}
SpreadsheetApp.getUi().alert('The owner and file path have been populated');
}
function getFilePath(fileID, filePath = ""){
try {
var file = Drive.Files.get(fileID,{
supportsAllDrives: true
});
if (!file.parents[0])
return "/" + filePath;
var parent = file.parents[0];
var parentFile = Drive.Files.get(parent.id,{ supportsAllDrives: true });
var parentPath = parentFile.title;
if (parent.isRoot || parentFile.parents.length == 0)
return "/" + filePath;
else {
return getFilePath(
parentFile.id,
parentPath + "/" + filePath);
}
}
catch (GoogleJsonResponseException){
return "File inaccesible"
}
}
function getFileOwner(fileID){
try {
var file = Drive.Files.get(
fileID,
{
supportsAllDrives: true
});
var driveId = file.driveId;
if (driveId){
var driveName = Drive.Drives.get(driveId).name;
return driveName + "(" + driveId + ")";
}
var ownerEmailAddress = file.owners[0].emailAddress;
return ownerEmailAddress;
}
catch (GoogleJsonResponseException){
return "File inaccesible"
}
}
After executing the function, it will take significantly longer the more files IDs it has, the cells will be updated with their respective owner and path.
Note: with a Super Admin account you can programmatically create a view permission for shared drives you don't have access to using APIs or Apps Script, you may submit a separate question for more details or read the documentation in the developer page at https://developers.google.com/drive/api/v2/reference/permissions.
Trying to add workflow step to pause the workflow for approval for second signer.
Please note I was able to add the pause when creating envelopes with recipients added.
But was not able to add the workflow step when trying to create envelope from templates. The templates are created in docusign with only roles added.
Later when creating the envelope through API we added TemplateRoles to add signers against each role.
In this case when we tried to add workflow step to pause the workflow it didn’t pause and sent invitation for the second signer. Can you please help us on this and correct us if we are doing any thing wrong.
Please find the snip of the code below:
var workflowStep = new WorkflowStep()
{
Action ="pause_before",
TriggerOnItem = "routing_order",
ItemId = "2"
};
var workflowsteps = new List<WorkflowStep>();
workflowsteps.Add(workflowStep);
TemplateRole signer = new TemplateRole();
signer.Email = "test#example.com";
signer.Name = "Test";
signer.RoleName = "QA";
signer.RoutingOrder = "1";
TemplateRole signer1 = new TemplateRole();
signer1.Email = "test123#example.com";
signer1.Name = "Test123";
signer1.RoleName = "RS";
signer1.RoutingOrder = "2";
var tempRoles = new List<TemplateRole>();
tempRoles.Add(signer);
tempRoles.Add(signer1);
EnvelopesApi envelopesApi = new EnvelopesApi(apiclient);
EnvelopeDefinition envDef = new EnvelopeDefinition();
envDef.TemplateId = templateId;
envDef.Status = "Sent";
envDef.TemplateRoles = tempRoles;
envDef.Workflow = new Workflow { WorkflowSteps = workflowsteps };
EnvelopeSummary envelopeSummary = envelopesApi.CreateEnvelope(accountId, envDef);
I hesitate to add this because I haven't tested it but I'll mention it in case it unblocks you and see if I can verify later...
I believe if you create the envelope/template with status = paused rather than sent, you should be able to update the template with ARR.
The problem is that when you get to the 'pause' in the above code, it's already 'too late'.
I want to save and retrieve the password and userAccount in keychain. I found a solution in stackoverflow and I use the code there. Save and Load from KeyChain | Swift. But this code only saves and load the password not the accountName. I think I figured out how to save the accountName but I need to figure out how to load it along with the password. Here is the code to save you some time from going into that link.
var userAccount = "AuthenticatedUser"
let accessGroup = "SecurityService"
// Mark: User defined keys for new entry
// Note: add new keys for new secure item and use them in load and save methods
let accountKey = "KeyForAccount"
let passwordKey = "KeyForPassword"
private class func save(service: String, data: String) {
let dataFromString: NSData = data.data(using: String.Encoding(rawValue: String.Encoding.utf8.rawValue), allowLossyConversion: false)! as NSData
//Instantiate a new default keychain query
let keychainQuery: NSMutableDictionary = NSMutableDictionary(objects: [kSecClassGenericPasswordValue, service, userAccount, dataFromString], forKeys: [kSecClassValue, kSecAttrServiceValue as NSCopying, kSecAttrAccountValue as NSCopying, kSecValueDataValue as NSCopying])
//Add new keychain item
let status = SecItemAdd(keychainQuery as CFDictionary, nil)
if status != errSecSuccess { //Always check status
print("Write failed. Attempting update.")
//updatePassword(token: data)
}
}
private class func load(service: String) -> String? {
//Instantiate a new default keychain query
//Tell the query to return a result
//Limit our results to one item
let keychainQuery: NSMutableDictionary = NSMutableDictionary(objects: [kSecClassGenericPasswordValue, service, userAccount, kCFBooleanTrue, kSecMatchLimitOneValue], forKeys: [kSecClassValue, kSecAttrServiceValue as NSCopying, kSecAttrAccountValue as NSCopying, kSecReturnDataValue as NSCopying, kSecMatchLimitValue as NSCopying])
var dataTypeRef: AnyObject?
//Search for the keychain items
let status : OSStatus = SecItemCopyMatching(keychainQuery, &dataTypeRef)
var contentsOfKeychain: String? = nil
if status == errSecSuccess {
if let retrieveData = dataTypeRef as? Data {
contentsOfKeychain = String(data: retrieveData as Data, encoding: String.Encoding(rawValue: String.Encoding.utf8.rawValue))
}
} else {
print("Nothing was retrieved from the keychain. Status code \(status)")
}
print(contentsOfKeychain ?? "none found")
return contentsOfKeychain
}
Complete code is inside the link. Thanks all.
But this code only saves and load the password not the accountName.
The code you show doesn't save or load the password or the account name.
What you have are two functions, one to save a key/value pair, one to load a key's value. It is how you call these two functions that determines what gets saved/loaded.
Somewhere in your code you call save to save the password, you need to call save (using a different key) to save the account name as well. Likewise for load. Your code does define the two keys to use (accountKey & passwordKey).
HTH
I have a booking program in Google Sheets where a user can pick their name (uses data verification to provide a list of emails) and then the cell is then placed under protection so no other user can change the booking.
The strange thing that happens is that a person can enter in another person's email and then the cell is protected by the entered email not the user. The user can enter in a non-email string and it does not protect the cell.
The desired result would be that if the user's email and the data entered is the same, protect the cells otherwise it is free to be edited.
Any help would be appreciated!
function onEdit(e){
var CurrentUser = Session.getEffectiveUser().getEmail()
var range_DataEntered = SpreadsheetApp.getActiveRange();
var DataEntered = range_DataEntered.getValue();
var ss = SpreadsheetApp.getActiveSpreadsheet();
var SheetName = SpreadsheetApp.getActiveSheet();
var Cell = ss.getActiveCell();
if (CurrentUser = DataEntered) {
var protection = range_DataEntered.protect()
// Ensure the current user is an editor before removing others. Otherwise, if the user's edit
// permission comes from a group, the script will throw an exception upon removing the group.
protection.addEditor(CurrentUser);
if (protection.canDomainEdit()) {
protection.setDomainEdit(false);
}
}
else {
//remove protection, set everyone to edit
range_DataEntered.protect().setDomainEdit(true);
}
}
if (CurrentUser = DataEntered) needs to be
if(CurrentUser === DataEntered)
A single = will assign a value not check for equivalency.
I'm creating a Dashcode project that allows me to mount remote drives. I have a Scroll Area named "statusArea" defined to contain status text of the mounting progress, kind of a command output area.
The first time through testing I'll force the "ERROR: Must enter " text to be written. Then I'll go to the back to provide a username and password and try again, but the box is never cleared for new text.
// get configuration fields from back panel
var usernameField = document.getElementById("username");
var username = usernameField.value;
var passwordField = document.getElementById("password");
var password = passwordField.value;
// clear status
var statusArea = document.getElementById("statusArea");
var status = document.getElementById("content");
status.innerText = "";
statusArea.object.refresh();
if ( !username || !password) {
status.innerText = "ERROR: Must enter username and password on back panel!";
alert("Must enter username and password on back panel!");
}
if (username && password) {
status.innerText = "Connecting as "+username;
Am I going about this the wrong way?
Thanks,
Bill