Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Release/v3.32.0 #3761

Merged
merged 62 commits into from
Jan 24, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
62 commits
Select commit Hold shift + click to select a range
1630bae
Add components for auth in online-survey-app
May 16, 2024
8971b53
Add online survey get and save form response from case event form id
May 23, 2024
d4a713c
Working version of online-survey login routing
May 29, 2024
bc72f19
Fix release online survey script copying of form directory
May 30, 2024
aaf3809
Add server-side APIs to create Case, Event and Form
May 30, 2024
9752ad5
Fix unfinished case-api function
May 31, 2024
a92d271
Fix bugs in case-api and add user-profile create api
May 31, 2024
69fce19
Add app config 'requireAccessCode' for users to lock access to online…
May 31, 2024
d852178
Reorder span items in release online survey component
Jun 3, 2024
1ef7c00
Include custom-scripts in online-survey releases
Jun 3, 2024
c4b6c68
Online Survey - save case variables to local storage
Jun 3, 2024
4d8c5b6
Add case module to online-survey-app
Jun 4, 2024
69a5108
Allow online survey link for case event form; online-survey custom-lo…
Jun 4, 2024
0b075e2
Ensure auth works for case in online surveys
Jun 4, 2024
c565cec
Editor event form list items get a copy link button
Jun 4, 2024
0360347
Add menu to event form list for survey link copy, qr, and open
Jun 5, 2024
1e0077c
Add getOnlineSurvey and getCaseEventFormSurveyLinks APIs
Jun 7, 2024
5837be6
Fix up case create api
Jun 8, 2024
1853c34
Update /case/createParticipant API
Jun 8, 2024
901ea4c
Add case service set context and caseSErvice window var for online su…
Jun 8, 2024
3640ef6
Fix bugs in case-api and case class
Jun 8, 2024
78c20ac
Add eventFormRedirect to online surveys
Jun 8, 2024
992c76f
case api getEventFormSurveyLinks outputs a JSON object with list of l…
Jun 10, 2024
dca0786
Fix setting event id in case creation api
Jun 11, 2024
eaa0bbb
Fix typo in content-set.md
Jun 11, 2024
26e197f
Event form link copy not allowed with http
Jun 11, 2024
e65a88d
Add case, event and form info to response
Jun 11, 2024
9f40001
Build custom-scripts when releasing online app
Jun 11, 2024
eaebe51
use surveyLinkUrl in event form list item
Jun 12, 2024
ac3d58b
Create participant event forms when adding participant through api
Jun 12, 2024
697a9f1
Set event and form name from definition in creation api
Jun 12, 2024
45ed8ee
Add helpLink config for online-survey to enable a custom link from an…
Jun 12, 2024
ed615fc
Fix creation of event forms for participant role in case api
Jun 12, 2024
175787e
Revert addition of all client/app-config.json; only set helpLink
Jun 12, 2024
8c2dbc6
Fix GROUP_ID in release online survey app
Jun 15, 2024
981d49b
Add timeout and logout to online survey
Jun 15, 2024
526f837
Add logout to editor app after warning
Jun 15, 2024
88071e5
Add process monitor dialogs to publish/unpublish online survey
Jun 15, 2024
59d05b7
Add expiry logout in online survey after warning
Jun 15, 2024
3eaa97a
Save-as-you-go in online surveys with case
Jun 15, 2024
a0011c2
Remove online survey getResponse API
Jun 15, 2024
21f922e
Add online-survey-app logout button and return to case url
Jun 25, 2024
cc68399
Merge branch 'online-surey-with-event-form-and-throttled-save' into r…
Jun 25, 2024
ab92d86
Fix online survey extend session call
Jul 1, 2024
621a751
Remove excessive logging in CSV generation scripts that causes ERR_ST…
Jul 3, 2024
f635d85
Await throttled save response to ensure response is saved after-submit
Jul 10, 2024
5944289
Online-survey-app logout on unload
Jul 10, 2024
5e363fb
Save caseUrlHash earlier in online-survey-app
Jul 12, 2024
5beca6b
Don't show case forms in online-survey publish list
Jul 15, 2024
f82f70c
Revert logout on window unload in online survey app
Jul 16, 2024
ced1ce5
Save online survey app response directly on after-submit
Jul 17, 2024
ac8078b
Show link to survey in case event form with form response id
Jul 26, 2024
1b488c9
Fix survey link copied in case event form
Jul 26, 2024
f6054f2
Add startDatetime and startUnixtime to user-profile when created with…
Aug 1, 2024
75cda62
Store caseUrlHash in sessionStorage instead of localStorage
Aug 1, 2024
30b61d3
Storage auth token vars in sessionStorage in online survey app
Aug 1, 2024
9341fbd
Merge branch 'release/v3.31.2' into release/v3.32.0
Jan 10, 2025
a792d55
Remove unused auth components in online-survey-app
Jan 10, 2025
9ac8e2d
Updates and fixes for locked and unlocked online-survey-app
Jan 10, 2025
b2e2f9e
Remove auth directives from online-survey-app
Jan 10, 2025
5813f6e
Update CHANGELOG and fixes to case-api
Jan 13, 2025
269f2d7
Merge branch 'main' into release/v3.32.0
Jan 24, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
60 changes: 60 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,65 @@
# What's new

## v3.32.0

Tangerine v3.32.0 contains a major update to the deployment and fuctionality of Online Surveys including the introduction of authentication and case association.

__Online Survey Authentication__

Online Surveys can now be released for public access or require authentication. When authentication is required, data collectors will be required to provide a device user Access Code to access the survey. The survey will be associated with the device user who provided the short code.

To deploy an Online Survey and require authentication:
1. Create the form
2. Add the `"requireAccessCode": true` property to the `app-config.json` file
3. Navigate to Deploy > Release Online Survey for the group
4. In the Unpublished Surveys list, click the <svg xmlns="http://www.w3.org/2000/svg" height="24px" viewBox="0 -960 960 960" width="24px" fill="rgb(255, 149, 34)"><path d="M240-80q-33 0-56.5-23.5T160-160v-400q0-33 23.5-56.5T240-640h40v-80q0-83 58.5-141.5T480-920q83 0 141.5 58.5T680-720v80h40q33 0 56.5 23.5T800-560v400q0 33-23.5 56.5T720-80H240Zm0-80h480v-400H240v400Zm240-120q33 0 56.5-23.5T560-360q0-33-23.5-56.5T480-440q-33 0-56.5 23.5T400-360q0 33 23.5 56.5T480-280ZM360-640h240v-80q0-50-35-85t-85-35q-50 0-85 35t-35 85v80ZM240-160v-400 400Z"/></svg> icon to deploy a survey and require an access code.

__Custom Markdown for Online Survey Login Page__

Custom styling (text, logos, formatting etc.) can be added to the login page for Online Surveys. In the content set repository, create a file called `client/custom-login-markup.html`. The file can contain any valid html. Any images can be added to the `media` folder and used in the html. Example [Custom Login Markup](content-sets/case-module/client/custom-login-markup.html).

__Online Survey Case Form Authentication__

Online Surveys with authentication can also be configured when using the Case Module. This is useful for studies who want to deploy secure forms without needing to install a full PWA or APK for one form associated with the case. For example, a mother-child cohort study deploys Tangerine to track the health of the mother and child after birth. Labs are collected in the field and sent for analysis. Instead of requiring the lab to install a tablet or PWA with the Tangerine app, the lab forms can be deployed online to simplify the completion process.

To configure forms for secure deployment online in Case, add the `"allowOnline": true` property to the `eventFormDefinitions` section in the case definition file. See the form definition for `form-allowed-online-survey` in [case-type-1](./content-sets/case-module/client/case-type-1.json).

__Online Survey Help Link__

A help link can be added to the web pages for Online Surveys and will appear with the <svg xmlns="http://www.w3.org/2000/svg" height="24px" viewBox="0 -960 960 960" width="24px" fill="#5f6368"><path d="M478-240q21 0 35.5-14.5T528-290q0-21-14.5-35.5T478-340q-21 0-35.5 14.5T428-290q0 21 14.5 35.5T478-240Zm-36-154h74q0-33 7.5-52t42.5-52q26-26 41-49.5t15-56.5q0-56-41-86t-97-30q-57 0-92.5 30T342-618l66 26q5-18 22.5-39t53.5-21q32 0 48 17.5t16 38.5q0 20-12 37.5T506-526q-44 39-54 59t-10 73Zm38 314q-83 0-156-31.5T197-197q-54-54-85.5-127T80-480q0-83 31.5-156T197-763q54-54 127-85.5T480-880q83 0 156 31.5T763-763q54 54 85.5 127T880-480q0 83-31.5 156T763-197q-54 54-127 85.5T480-80Zm0-80q134 0 227-93t93-227q0-134-93-227t-227-93q-134 0-227 93t-93 227q0 134 93 227t227 93Zm0-320Z"/></svg> icon in the header. Define the link url by adding the `"helpLink": "https://www.tangerinecentral.org"` property to the `app-config.json` file. When clicked, the link will open in a new browser window.

__Tangerine Case APIs__

- `/userProfile/createUserProfile/:groupId`
- Creates a user-profile document in the group
- Additional body data in json format will add properties to the user-profile: `{ "age": "7", "dob": "1/1/2018"}`
- `/case/createCase/:groupId/:caseDefinitionId`
- Creates a case in the group with the case type defined by the case definition id
- Additional body data in json format will add properties to the case: `{ "age": "7", "dob": "1/1/2018"}`
- `/case/readCase/:groupId/:caseId`
- Read a case from the group with the case id
- `/case/createCaseEvent/:groupId/:caseId/:caseDefinitionId/:caseEventDefinitionId`
- Creates an event with the event type as defined in the case definition
- `/case/createParticipant/:groupId/:caseId/:caseDefinitionId/:caseRoleId`
- Creates a participant with the case role as defined in the case definition
- `/case/getCaseEventFormSurveyLinks/:groupId/:caseId/`
Returns a JSON document with the urls for all case forms with active online surveys in the format:
```json
{
"eventDefinitionId": event.caseEventDefinitionId,
"eventFormDefinitionId": eventForm.eventFormDefinitionId,
"formId": formId,
"url": url
}
```

__Server upgrade instructions__

See the [Server Upgrade Insturctions](https://docs.tangerinecentral.org/system-administrator/upgrade-instructions).

*Special Instructions for this release:* N/A


## v3.31.2

__General Updates__
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@ export class SyncingService {
doc['items'][0]['inputs'].push({ name: 'tabletUserName', value: username });
// Redact any fields marked as private.
doc['items'].forEach(item => {
item['inputs'].forEach(input => {
item['inputs'].forEach(input => {
if (input.private) {
input.value = '';
}
Expand Down
12 changes: 12 additions & 0 deletions content-sets/case-module/client/case-type-1.json
Original file line number Diff line number Diff line change
Expand Up @@ -137,6 +137,18 @@
"allowDeleteIfFormNotStarted": true,
"required": false,
"repeatable": true
},
{
"id": "event-form-definition-h9d3hd9",
"formId": "form-allowed-online-survey",
"forCaseRole": "role-2",
"name": "A form allowed to be filled online",
"autoPopulate": true,
"allowDeleteIfFormNotCompleted": true,
"allowDeleteIfFormNotStarted": true,
"required": false,
"repeatable": true,
"allowOnline": true
}
]
},
Expand Down
10 changes: 10 additions & 0 deletions content-sets/case-module/client/custom-login-markup.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
<div>
<img id="logo" src="./assets/media/logo.png" width="100%">
</div>
<div>
<center><strong>Online Survey for Tangerine Study</strong></center>
<br>
<p>
<small>Enter the provided Access Code below.</small>
</p>
</div>
9 changes: 5 additions & 4 deletions develop.sh
Original file line number Diff line number Diff line change
Expand Up @@ -75,13 +75,13 @@ fi

if echo "$T_MODULES" | grep mysql; then
./mysql-create-dirs.sh
fi

if echo "$T_USE_MYSQL_CONTAINER" | grep "true"; then
if echo "$T_USE_MYSQL_CONTAINER" | grep "true"; then
./mysql-start-container.sh
echo "Waiting 20 seconds for mysql container to start..."
sleep 20
./mysql-setup.sh
sleep 20
./mysql-setup.sh
fi
fi

if echo "$T_MYSQL_PHPMYADMIN" | grep "TRUE"; then
Expand Down Expand Up @@ -231,6 +231,7 @@ OPTIONS="--link $T_COUCHDB_CONTAINER_NAME:couchdb \
--volume $(pwd)/editor/src:/tangerine/editor/src:delegated \
--volume $(pwd)/translations:/tangerine/translations:delegated \
--volume $(pwd)/online-survey-app/src:/tangerine/online-survey-app/src:delegated \
--volume $(pwd)/online-survey-app/dist:/tangerine/online-survey-app/dist:delegated \
--volume $(pwd)/tangy-form-editor:/tangerine/tangy-form-editor:delegated \
--volume $(pwd)/tangy-form:/tangerine/tangy-form:delegated \
tangerine/tangerine:local
Expand Down
2 changes: 1 addition & 1 deletion docs/editor/content-sets.md
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ Be sure to update any cron jobs to include the new build commands if they are us

``
git pull
npm rn install-server
npm run install-server
npm build
``

Expand Down
5 changes: 4 additions & 1 deletion editor/src/app/app.component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -171,7 +171,10 @@ export class AppComponent implements OnInit, OnDestroy {
this.isConfirmDialogActive = false;
} else {
await this.logout();
}
}
} else if (Date.now() > expiryTimeInMs && this.isConfirmDialogActive) {
// the token expired, and we warned them. Time to log out.
await this.logout();
}
}

Expand Down
1 change: 1 addition & 0 deletions editor/src/app/case/classes/event-form-definition.class.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ export class EventFormDefinition {
allowDeleteIfFormNotStarted:string
onEventFormOpen?:string
onEventFormClose?:string
allowOnline?:boolean

constructor() {
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,22 @@
<div [innerHTML]="renderedTemplateListItemPrimary|unsanitizeHtml"></div>
<div [innerHTML]="renderedTemplateListItemSecondary|unsanitizeHtml" secondary></div>
</div>
<span *ngIf="!eventFormArchived && canLinkToOnlineSurvey">
<button class="tangy-small-list-icon" [matMenuTriggerFor]="linkMenu" (click)="showLinkMenu(); $event.stopPropagation()">
<mwc-icon>share</mwc-icon>
</button>
<mat-menu #linkMenu="matMenu">
<button mat-menu-item tangy-small-list-icon (click)="onCopyLinkClick()">
<mwc-icon>link</mwc-icon>
</button>
<button mat-menu-item tangy-small-list-icon (click)="onQRCodeLinkClick()">
<mwc-icon>qr_code</mwc-icon>
</button>
<button mat-menu-item tangy-small-list-icon (click)="$event.stopPropagation()">
<a href="{{surveyLinkUrl}}" target="_new"><mwc-icon>open_in_new</mwc-icon></a>
</button>
</mat-menu>
</span>
<span *ngIf="canUserDeleteForms">
<button (click)="onDeleteFormClick(); $event.stopPropagation()" class="tangy-small-list-icon">
<mwc-icon>delete</mwc-icon>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,9 @@ import { TangyFormService } from 'src/app/tangy-forms/tangy-form.service';
import { CaseService } from '../../services/case.service';
import { AppConfigService } from 'src/app/shared/_services/app-config.service';
import { t } from 'tangy-form/util/t.js'


import { GroupsService } from 'src/app/groups/services/groups.service';
import { TangyErrorHandler } from 'src/app/shared/_services/tangy-error-handler.service';
import * as qrcode from 'qrcode-generator-es6';

@Component({
selector: 'app-event-form-list-item',
Expand Down Expand Up @@ -47,20 +48,37 @@ export class EventFormListItemComponent implements OnInit {
canUserDeleteForms: boolean;
groupId:string;
eventFormArchived: boolean = false;
canLinkToOnlineSurvey: boolean = false;
response:any
surveyLinkUrl:string;

constructor(
private formService: TangyFormService,
private ref: ChangeDetectorRef,
private router:Router,
private caseService: CaseService
private caseService: CaseService,
private groupsService: GroupsService,
private tangyErrorHandler: TangyErrorHandler
) {
ref.detach();
}

async ngOnInit() {
this.groupId = window.location.pathname.split('/')[2]

const group = await this.groupsService.getGroupInfo(this.groupId);
const groupOnlineSurveys = group?.onlineSurveys ?? [];

this.canLinkToOnlineSurvey = groupOnlineSurveys.some(survey =>
survey.published &&
survey.formId === this.eventFormDefinition.formId &&
this.eventFormDefinition.allowOnline
);

if (this.canLinkToOnlineSurvey) {
this.surveyLinkUrl = `/releases/prod/online-survey-apps/${this.groupId}/${this.eventFormDefinition.formId}/#/case/event/form/${this.case._id}/${this.caseEvent.id}/${this.eventForm.id}`;
}

this.canUserDeleteForms = ((this.eventFormDefinition.allowDeleteIfFormNotCompleted && !this.eventForm.complete)
|| (this.eventFormDefinition.allowDeleteIfFormNotStarted && !this.eventForm.formResponseId));
const response = await this.formService.getResponse(this.eventForm.formResponseId);
Expand Down Expand Up @@ -131,4 +149,35 @@ export class EventFormListItemComponent implements OnInit {
onUnarchiveFormClick() {
this.formUnarchivedEvent.emit(this.eventForm.id);
}

showLinkMenu() {
this.ref.detectChanges()
}

onCopyLinkClick() {
const url = `${window.location.origin}${this.surveyLinkUrl}`;
try {
navigator.clipboard.writeText(url);

this.tangyErrorHandler.handleError('Online Survey link copied to clipboard');
} catch (err) {
let errMsg = 'Failed to copy link to clipboard';
if (window.location.origin.startsWith('http://')) {
errMsg = 'Copy to clipboard is not supported with http://.';
}
this.tangyErrorHandler.handleError(errMsg);
}
}

onQRCodeLinkClick() {
const url = `${window.location.origin}${this.surveyLinkUrl}`;

const qr = new qrcode.default(0, 'H')
qr.addData(`${url}`)
qr.make()
window['dialog'].innerHTML = `<div style="width:${Math.round((window.innerWidth > window.innerHeight ? window.innerHeight : window.innerWidth) *.6)}px" id="qr"></div>`
window['dialog'].open()
window['dialog'].querySelector('#qr').innerHTML = qr.createSvgTag({cellSize:500, margin:0,cellColor:(c, r) =>''})
}

}
4 changes: 2 additions & 2 deletions editor/src/app/core/auth/_components/login/login.component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -41,10 +41,10 @@ export class LoginComponent implements OnInit {
if (await this.authenticationService.login(this.user.username, this.user.password)) {
this.router.navigate(['/projects']);
} else {
this.errorMessage = _TRANSLATE('Login Unsuccesful');
this.errorMessage = _TRANSLATE('Login Unsuccessful');
}
} catch (error) {
this.errorMessage = _TRANSLATE('Login Unsuccesful');
this.errorMessage = _TRANSLATE('Login Unsuccessful');
console.error(error);
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,15 +3,7 @@
<h2>Instructions: </h2>

<div>
In the Unpublished Surveys section, click the
<button class="info-button">
<i class="material-icons tangy-location-list-icon">published_with_changes</i>
</button>
button to "Publish" an online survey. It will then be listed in the Published Surveys section.
</div>

<div>
In the Published Surveys section, use the link
In the <strong>Published Surveys</strong> section, use the link
<button class="info-button">
<i class="material-icons tangy-location-list-icon">link</i>
</button>
Expand All @@ -21,14 +13,40 @@ <h2>Instructions: </h2>
<i class="material-icons tangy-location-list-icon">unpublished</i>
</button>
button to "Un-publish" an online survey.
The
<button class="info-button">
<i class="material-icons tangy-location-list-icon">lock</i>
</button> button appears for surveys that require a Device User and Access Code.
</div>

<div>
In the <strong>Unpublish Surveys</strong> section, click the
<button class="info-button">
<i class="material-icons tangy-location-list-icon">published_with_changes</i>
</button>
button to "Publish" an online survey. It will then be listed in the Published Surveys section.
To 'Publish' a survey and require a Device User to provide an Access Code, click the
<button class="info-button">
<i class="material-icons tangy-location-list-icon">lock</i>
</button> button.
</div>

<div>

</div>

</div>
<h2 class="tangy-foreground-secondary">{{'Published Surveys'|translate}}</h2>
<mat-list class="drag-list">
<mat-list-item class="drag-item" *ngFor="let form of publishedSurveys; let index=index">
<span>{{index+1}}</span>
<span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span>
<span>&nbsp;&nbsp;</span>
<span class="tangy-spacer" [innerHTML]="form.title|unsanitizeHtml"></span>
<span *ngIf="form.locked">
<button mat-icon-button class="lock-button">
<i class="material-icons mat-32 tangy-location-list-icon">lock</i>
</button>
</span>
<span>{{form.updatedOn|date :'medium'}}
</span>

Expand All @@ -47,14 +65,15 @@ <h2 class="tangy-foreground-secondary">{{'Unpublished Surveys'|translate}}</h2>
<span>{{index+1}}</span>
<span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span>
<span class="tangy-spacer" [innerHTML]="form.title|unsanitizeHtml"></span>

<span >{{form.updatedOn|date :'medium'}}
<span *appHasAPermission="let i;group:group._id; permission:'can_manage_forms'">
<button mat-icon-button (click)="publishSurvey(form.id, form.title)">
<button mat-icon-button (click)="publishSurvey(form.id, form.title, false)">
<i class="material-icons mat-32 tangy-location-list-icon">published_with_changes</i>
</button>
<button mat-icon-button (click)="publishSurvey(form.id, form.title, true)">
<i class="material-icons mat-32 tangy-location-list-icon">lock</i>
</button>
</span>

<span >{{form.updatedOn|date :'medium'}}
</span>
</mat-list-item>
</mat-list>
Loading