Custom field that a member can only view from a Wild Apricot restricted webpage using the API from JavaScript
I would like the ability to create a membership database field that a logged-in member can view from a Wild Apricot restricted webpage using the API from JavaScript. Administrators with Membership manager access would be able to view and edit this field.
-
Robin Sapiro commented
Hi Curtis,
And the change access setting is resolved as well.
From the Settings page - select Privacy under Members.
Then click Edit and next to the field that you do not want users to change the privacy of - check the Lock checkbox. Now user cannot make the field visible to other users. -
Robin Sapiro commented
Hi Curtis,
The loop concept worked great - but just wondering if you actually had it working as you typed it or if it was just a concept. I had to make a few syntax changes to actually get it to work. Following is what I eventually built in order to evaluate how the various field attributes effected what I was able to see with different viewing options:
<html>
<body>
<p id="demo3"></p>
<script>
$(document).ready(function(){
$.ajax(
{
url: "/sys/api/publicview/v1/accounts/1111111/contacts/me?includeDetails=true",
type: "GET",
dataType: "json",
cache: false,
async: true,
headers: { "clientId": "1z1z1z1z1z1z1" },
success: function (data, textStatus, jqXhr) {
var d = new Date()
var textblock = "Me - "
var newline = (d + "<br>")
var textblock = (textblock + newline)
var i;
for (i= 0; i < data.FieldValues.length; i++) {
var fname = data.FieldValues[i].FieldName
var fvalue = data.FieldValues[i].Value
var newline = (i + ": " + fname + " - " + fvalue + "<br>")
var textblock = (textblock + newline)
};document.getElementById("demo3").innerHTML = textblock;},
error: function (jqXHR, textStatus, errorThrown) {
alert(textStatus + " (" + jqXHR.status + ") : " + errorThrown);}
});
});
</script></body>
</html>Using the above gave me a list on my page of the various field names and their content.
I also set up a specific set of fields covering all the combinations of access to a field (both for the member to view/edit their own data and also for other members to view/edit a members data).
I then looked at the data for a specific set of members (in a member level that used these specific fields only) and observed the following:
The only problem that exists with this is that if a member views their profile, then selects Edit and then Privacy is that they can then make this field visible to other members. I would even think that this is a defect in WA in that if a member has only view access to a field (which means that they cannot edit the content) that they should also not be able to edit the access to that field either. -
Robin Sapiro commented
Hi Curtis,
Thanks for the loop suggestion - that will actually work great for me. Certainly if I am only looking to extract a single variable. If I need a few variables however it will need some tweaking to pick them all up with a single pass through the data (as opposed to looping through once for each variable needed).
Re your 'hidden' fields - something interesting that I noticed was that when I run the API for /me or if I run it with /12345 (where 12345 is my Contact ID) - I get back a different number of fields in the FieldValues array.
I have not looked yet to see what the differences in the field lists are and therefore obviously what the attributes are of any fields that appear in 1 list versus the other.
But it might just be possible that 1 version of the API returns fields that otherwise would be hidden with the other version.
So speculating that if the /12345 version of the API call brings back fields that are hidden with the /me version - then first make a call with /me - retrieve the actual Contact ID and then call with /12345
It is worth looking into.Your comment about international members is also interesting.
Do you have multiple chapters?
How do you manage the collection of membership dues and also possible multiple currencies?
How do you manage administrators of 1 chapter not accessing membership data of other chapters?
These are all things that I am working on as we also are an international organization. At the moment we have 6 chapters in Canad and the USA each with their own WA site. It makes it a whole lot more expensive than having everyone on a single site. With the current WA pricing structure we could bring everyone onto 1 site at a lesser cost than we have today combined and still have capacity to bring on board our remaining chapters in the USA, UK, South Africa and Israel.
From a membership perspective, I already have a solution based on the WA database using Integromat and the Wave App.
I am also looking into using this API to be able to manage the access of the membership records such that each chapter membership admin can only access their own membership records. -
Robin Sapiro commented
Hi Curtis,
Thanks for the loop suggestion - that will actually work great for me. Certainly if I am only looking to extract a single variable. If I need a few variables however it will need some tweaking to pick them all up with a single pass through the data (as opposed to looping through once for each variable needed).
Re your 'hidden' fields - something interesting that I noticed was that when I run the API for /me or if I run it with /12345 (where 12345 is my Contact ID) - I get back a different number of fields in the FieldValues array.
I have not looked yet to see what the differences in the field lists are and therefore obviously what the attributes are of any fields that appear in 1 list versus the other.
But it might just be possible that 1 version of the API returns fields that otherwise would be hidden with the other version.
So speculating that if the /12345 version of the API call brings back fields that are hidden with the /me version - then first make a call with /me - retrieve the actual Contact ID and then call with /12345
It is worth looking into.Your comment about international members is also interesting.
Do you have multiple chapters?
How do you manage the collection of membership dues and also possible multiple currencies?
How do you manage administrators of 1 chapter not accessing membership data of other chapters?
These are all things that I am working on as we also are an international organization. At the moment we have 6 chapters in Canad and the USA each with their own WA site. It makes it a whole lot more expensive than having everyone on a single site. With the current WA pricing structure we could bring everyone onto 1 site at a lesser cost than we have today combined and still have capacity to bring on board our remaining chapters in the USA, UK, South Africa and Israel.
From a membership perspective, I already have a solution based on the WA database using Integromat and the Wave App.
I am also looking into using this API to be able to manage the access of the membership records such that each chapter membership admin can only access their own membership records. -
Curtis Minns commented
Hi Robin,
You're right, I could probably get away with security by obscurity for the scenario I mentioned. I had started designing something like that last year but didn't get around to implementing it. It's less desirable since the javascript code is visible to any member and we have an international community.
I also have another case where I want to store a custom dates that display when a member has last received specific annual gifts. I don't mind if the member can see the date fields but the organization doesn't want this information shared with other members.
For you, I'd recommend looping through the array returned by data.FieldValues and search for the SystemCode that matches your custom Home Phone field.
for (var i in data.FieldValues) {
if (data.FieldValues[i].SystemCode == "custom-3107385") {
var homePhone = data.FieldValues[i].Value;
// value for Home Phone is now stored as homePhone
break;
// break out of for loop once Home Phone field is found
} else {
// Do something if Home Phone field is not found
}
} -
Robin Sapiro commented
Hi Curtis,
So I think that I am closing in on my solution (which is slightly different from yours in that I do not need to hide the specific field contents from other members or stop the member from editing the field).
By using the API: url: "/sys/api/publicview/v1/accounts/NNNNN/contacts/me?includeDetails=true",
I get back all the visible fields inside the JSON string.
Following is still theory - but based on the details at: https://www.w3schools.com/js/js_string_methods.asp
(I have found that w3schools.com is a great source for any information on HTML, JavaScript and any other cool web stuff).That assuming I am looking for a Home Phone in the return results - which in the JSON string looks like this:
{"FieldName":"Home Phone","FieldType":"String","FieldOrder":13,"SystemCode":"custom-3107385","Value":"+1 416 123 1234"},
I would first use the 'indexof" function to locate the position of the value: {"FieldName":"Home Phone", in the JSON string.
Then from that offset locate the position of the value: "Value":
These 2 allow me to calculate the start position of my actual data: +1 416 123 1234
Then again from the last offset locate the position of the value: "},
This allows me to determine the length of my data
The using the data start offset and the data lengthThen use 1 of
slice(start, end)
substring(start, end)
substr(start, length)
to actually extract the data that I want - and so we are off to the races.For you given the need to make the 1 field in particular inaccessible to all other than a membership admin - you will have to play around with the field attributes such that you can restrict standard access but still retrieve with the API - night be a challenge.
So some possible solutions for you might be what is known as 'Security by Obscurity' in that you do something like splitting the key across multiple fields and with obscure field names and then concatenate these fields in your java script, or insert some dummy characters into the field which you then remove as you retrieve the field. Obviously to do this - the administrators will have to know how to do this.
I am also considering to use this function as a form of 'Site Macro' where I will be able to dynamically display certain information that can change frequently - like 'Next Member Meeting Date' or 'Next Board Meeting Date' on the site by simply updating the values of these fields in a specific 'SiteMacro' membership record and then use the API on the form of: url: "/sys/api/publicview/v1/accounts/NNNNN/contacts/12345678?includeDetails=true",
Where 12345678 is the membership ID of my 'Macro' record.Seen as that we keep our meeting dates in a GMail Calendar - I will just run a scenario in Integromat say once a week to pull this data from GMail and update the WA Member record.
-
Curtis Minns commented
I need application credentials (ID + secret) for each member so they can authenticate against an external public web application. This is what I am envisioning:
• WA app gets member ID --> sends to external app --> external app sets secret key for member using the WA admin API --> sends challenge response back to WA app
• WA app gets member ID + secret key --> sends to external app --> external app verifies ID + secret key (completes authentication) --> sends session key + expiration date back to WA app
• WA app is now authorized to send member specific data to external app using session key until expiration date
• Once expiration date is reached, authentication process must repeat to obtain new session key
-
Robin Sapiro commented
Just out of curiosity what sort of data do you want to display that is so private?
-
Robin Sapiro commented
Hi Curtis,
No time for any API testing today.
But for the membership field access - I have been considering some process of replicating all the WA fields that I am willing to allow a member to edit to a Google Sheet and then from a member page use the API to retrieve their ID and use that to load the Google Sheet data to a form where they can view/edit whatever.
Then the updated row is replicated back to WA. All this replication with Integromat.My underlying for an approach like this is that I want to move to a multi chapter environment and only want to allow the chapter admin to only be able to edit their own chapter member data.
With an approach like that I then have to do something to disable the view my profile option out of the sign on gadget.
-
Curtis Minns commented
Hi Robin,
I think I may have found the answer for accessing the current user's custom fields here: https://app.swaggerhub.com/apis-docs/WildApricot/wild-apricot_api_for_non_administrative_access/1.0.0#/Contacts/get_accounts__accountId__contacts_me
If you make a call using the following:
/sys/api/publicview/v1/accounts/{accountId}/contacts/me?includeDetails=true(note: without ?includeDetails=true, it will not return custom fields)
This should get back all the fields and values to which the current user has view only or edit access. If the custom field is set to "No access - Internal use", it will not be returned with this call. The only issue is they're all under the "FieldValues" array so you'll need to loop through this to find the one you need.
Not sure if this resolves your problem. It gets me much closer but I still have the issue that if a field has member access permissions of "edit" or "view only", each member has the ability to change the default setting for how others can access that field. I need a "view only - internal use" option where the user can never change the value (only set/changed by an admin) and no one else can ever view the field.
Let me know if it works for you.
-
Robin Sapiro commented
Hi Curtis,
So some good progress from one of those flashes of inspiration that one sometimes has as you wake up.
When you call the API and specify 'me' (which is the current user), you only get back in the JSON string (per console dump) the rather short set of data as you previously mentioned (and that I was able to recreate).
However if instead of 'me' you run the API with an explicit Contact ID number - eg 12345678 - then the JSON string dump shows ALL the fields and their related data.
So this is a step forward. I would think that to implement this for the current user, that you would first have to call the API with 'me' - then extract the ID data and call the API again using the explicit ID.
So that will at least bring back everything and now we just need to figure out how to extract the custom field content from the JSON string.
I will let you know how I progress with that (but due to workload of my day time job - probably will not move on that until tonight).
Let me know if you make any progress.
-
Robin Sapiro commented
Might be easier to switch to FB Messenger - looked for you and found 9 of you.
If interested - look for me - I am unique. -
Robin Sapiro commented
So on my web page - I also dumped the JASON string to the console and agree with your earlier comments from yesterday.
However when I run the exact same API from: https://api.wildapricot.org/ui/home/RequestOAuthToken - I get back ALL the fields in my membership record.
On the page above - just enter your API key and click Submit. Then on the next screen enter the API and click GO.
Just remember that you have to use an actual ID # as opposed to 'me' when testing as above.
It would be interesting to be able to dump the JASON string from there as well - but no real idea how to do that. -
Curtis Minns commented
No worries! I was hoping maybe I had missed something. Hopefully Dmitry or one of the other product owners can provide an update. It's been almost a year since I initially posed this.
-
Robin Sapiro commented
I just pulled a list of the System Codes for a few of the System fields. Cannot say that I actually tried to retrieve more than a few of them.
Sorry if I misled you. -
Curtis Minns commented
I have been dumping out the entire data object to the browser console: console.log(JSON.stringify(data))
The only values I can retrieve are:
"AdministrativeRoleTypes"
"FirstName"
"LastName"
"Email"
"DisplayName"
"MembershipLevel"
"Status"
"Id"
"Url"
"IsAccountAdministrator"
"TermsOfUseAccepted"I'm not sure why the Phone field does not appear. I just tried a couple of test accounts and haven't been able to get this to work. I also don't have Organization or PendingRenewal.
-
Robin Sapiro commented
I see what you mean by the restrictions of the System fields.
If you make them Admin only - then the user will not be able to see their own data either and if you let the user see that data, then any other user can see when viewing their profile - assuming you provide profile access at all to other users.The Phone field is a System field so it can be retrieved using the code you referenced. The code that I am using is based on that anyway.
I just changed the Alert.... string to instead be 'document.getElementById' instead of 'Alert' and then it appears as text on the page instead of as the popup.
For the data.values you can replace 'values' with any of these:
"Email"
"FirstName"
"LastName"
"Organization"
"Id"
"Phone"
"PendingRenewal"This will build you a clickable link based on the Contact ID
document.getElementById("demo").innerHTML =("<a href=https://www.w3schools.com/" + data.Id + ">Click here to go somewhere</a>");}, -
Curtis Minns commented
The issue with reusing one of the current systems fields is that members have the ability to modify this data under their profile and make it viewable to others. I really need a field that cannot be modified by anyone other than an Administrator, is view only to the member, and can never be viewed by others (member cannot change this).
It might be helpful to see the custom code. I wasn't aware that fields like phone number could be pulled using JavaScript. I'm using the code as described here: https://gethelp.wildapricot.com/en/articles/1705-working-with-api-from-javascript
-
Robin Sapiro commented
Hi Curtis,
For whatever data that you want to retrieve and display, would it be a viable option for you at all to re-purpose 1 of the current System fields. I would suspect that other than ID, email, First and Last Names you could always use any of the remaining fields for whatever you want. phone number for example could be recreated as a custom field.
If that works for you, I can give you my custom code (based on a sample from Dmitri) that I have so far. Still trying however to figure out how to also do this with the custom fields.
-
Curtis Minns commented
Hi Robin,
No, I haven't received any update from WA about retrieving custom fields using calls to the API from JavaScript. If there's any way of retrieving custom field data from within a WA site page, that would be really useful.