abmp-npm 10.3.23 → 10.3.24
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
// cannot drift on what "expired" means. Deliberately free of Wix imports.
|
|
3
3
|
|
|
4
4
|
const ASSOCIATION_EXPIRATION_FIELD = 'associationExpiration';
|
|
5
|
+
const PAC_STAFF_MEMBERTYPE = 'PAC STAFF';
|
|
5
6
|
const ASSOCIATION_TIME_ZONE = 'America/Denver';
|
|
6
7
|
|
|
7
8
|
const EXPIRATION_DATE_PATTERN = /^(\d{4})-(\d{2})-(\d{2})/;
|
|
@@ -128,7 +129,20 @@ const isAssociationExpirationCurrent = (member, now) => {
|
|
|
128
129
|
return expiration.getTime() >= getTodayInAssociationTimeZone(now).getTime();
|
|
129
130
|
};
|
|
130
131
|
|
|
132
|
+
// PAC staff accounts carry a rolling "expiration" equal to the date of the feed that last mentioned
|
|
133
|
+
// them, so the association gate would lock a staff member out on any day the feed did not resend
|
|
134
|
+
// them (ticket 13031107600). Staff are already excluded from the directory and have no public
|
|
135
|
+
// profile; login is the one place the gate reaches them, and it should not.
|
|
136
|
+
const isPacStaff = member =>
|
|
137
|
+
Array.isArray(member?.memberships) &&
|
|
138
|
+
member.memberships.some(membership => membership?.membertype === PAC_STAFF_MEMBERTYPE);
|
|
139
|
+
|
|
140
|
+
const isLoginAllowedByExpiration = (member, now) =>
|
|
141
|
+
isPacStaff(member) || isAssociationExpirationCurrent(member, now);
|
|
142
|
+
|
|
131
143
|
module.exports = {
|
|
144
|
+
isPacStaff,
|
|
145
|
+
isLoginAllowedByExpiration,
|
|
132
146
|
parseExpirationToUtcDate,
|
|
133
147
|
classifyAssociationExpiration,
|
|
134
148
|
resolveAssociationExpiration,
|
|
@@ -3,7 +3,7 @@ const { createHmac } = require('crypto');
|
|
|
3
3
|
const axios = require('axios');
|
|
4
4
|
const { decode } = require('jwt-js-decode');
|
|
5
5
|
|
|
6
|
-
const {
|
|
6
|
+
const { isLoginAllowedByExpiration } = require('../association-expiry');
|
|
7
7
|
const { CONFIG_KEYS, SSO_TOKEN_AUTH_API_URL, LOGIN_REFUSAL_REASONS } = require('../consts');
|
|
8
8
|
const { MEMBER_ACTIONS } = require('../daily-pull/consts');
|
|
9
9
|
const { getCurrentMember } = require('../members-area-methods');
|
|
@@ -80,7 +80,7 @@ async function validateMemberToken(memberIdInput) {
|
|
|
80
80
|
return invalidTokenResponse;
|
|
81
81
|
}
|
|
82
82
|
|
|
83
|
-
if (!
|
|
83
|
+
if (!isLoginAllowedByExpiration(memberData)) {
|
|
84
84
|
console.log(
|
|
85
85
|
`[validateMemberToken] association membership expired for memberId ${memberData.memberId}`
|
|
86
86
|
);
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
const { COLLECTIONS } = require('../public/consts');
|
|
2
2
|
const { isWixHostedImage, emailsMatch, normalizeEmail } = require('../public/Utils/sharedUtils');
|
|
3
3
|
|
|
4
|
-
const {
|
|
4
|
+
const { isLoginAllowedByExpiration } = require('./association-expiry');
|
|
5
5
|
const { MEMBERSHIPS_TYPES, LOGIN_REFUSAL_REASONS } = require('./consts');
|
|
6
6
|
const { createSiteContact } = require('./contacts-methods');
|
|
7
7
|
const { MEMBER_ACTIONS } = require('./daily-pull/consts');
|
|
@@ -701,7 +701,7 @@ async function prepareMemberForSSOLogin(data) {
|
|
|
701
701
|
if (!memberData) {
|
|
702
702
|
throw new Error(`Member data not found for memberId ${memberId}`);
|
|
703
703
|
}
|
|
704
|
-
if (!
|
|
704
|
+
if (!isLoginAllowedByExpiration(memberData)) {
|
|
705
705
|
console.log(
|
|
706
706
|
`[prepareMemberForSSOLogin] refusing login, association membership expired for memberId ${memberId}`
|
|
707
707
|
);
|
package/package.json
CHANGED
package/pages/Profile.js
CHANGED
|
@@ -2,6 +2,7 @@ const { location: wixLocation } = require('@wix/site-location');
|
|
|
2
2
|
const { window: wixWindow } = require('@wix/site-window');
|
|
3
3
|
|
|
4
4
|
const { LIGHTBOX_NAMES } = require('../public/consts');
|
|
5
|
+
const { hasCopyrightYear, withCurrentCopyrightYear } = require('../public/Utils/copyrightYear');
|
|
5
6
|
const {
|
|
6
7
|
generateId,
|
|
7
8
|
formatPracticeAreasForDisplay,
|
|
@@ -20,6 +21,26 @@ const BREAKPOINTS = {
|
|
|
20
21
|
TABLET: 750,
|
|
21
22
|
};
|
|
22
23
|
|
|
24
|
+
// The footer reads "© <year> <name>. All rights reserved.": the name goes into #fullNameTextFoter,
|
|
25
|
+
// but the year was typed into each site's editor and has no nickname of its own, so ABMP stayed on
|
|
26
|
+
// 2025 while ASCP and AHP said 2026 (PAC ticket 12914830766). The three profile pages declare the
|
|
27
|
+
// same plain text elements; the copyright notice is found among them by content and its year kept
|
|
28
|
+
// current, so no editor needs touching now or next January.
|
|
29
|
+
const PLAIN_TEXT_ELEMENT_IDS = [
|
|
30
|
+
'#text389',
|
|
31
|
+
'#text178',
|
|
32
|
+
'#text150',
|
|
33
|
+
'#text155',
|
|
34
|
+
'#text157',
|
|
35
|
+
'#text158',
|
|
36
|
+
'#text164',
|
|
37
|
+
'#text169',
|
|
38
|
+
'#text171',
|
|
39
|
+
'#text175',
|
|
40
|
+
'#text177',
|
|
41
|
+
'#text179',
|
|
42
|
+
];
|
|
43
|
+
|
|
23
44
|
async function profileOnReady({ $w: _$w }) {
|
|
24
45
|
const profileData = await wixWindow.getRouterData();
|
|
25
46
|
console.log('profileData', profileData);
|
|
@@ -97,6 +118,23 @@ async function profileOnReady({ $w: _$w }) {
|
|
|
97
118
|
bindStudentBadge();
|
|
98
119
|
bindProfileImages();
|
|
99
120
|
bindFullName();
|
|
121
|
+
bindCopyrightYear();
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
function bindCopyrightYear() {
|
|
125
|
+
PLAIN_TEXT_ELEMENT_IDS.forEach(id => {
|
|
126
|
+
try {
|
|
127
|
+
const element = _$w(id);
|
|
128
|
+
// .html keeps the element's formatting; fall back to .text for elements without markup.
|
|
129
|
+
if (hasCopyrightYear(element.html)) {
|
|
130
|
+
element.html = withCurrentCopyrightYear(element.html);
|
|
131
|
+
} else if (hasCopyrightYear(element.text)) {
|
|
132
|
+
element.text = withCurrentCopyrightYear(element.text);
|
|
133
|
+
}
|
|
134
|
+
} catch {
|
|
135
|
+
// The element does not exist on this site's page: nothing to update.
|
|
136
|
+
}
|
|
137
|
+
});
|
|
100
138
|
}
|
|
101
139
|
|
|
102
140
|
function bindMemberSince() {
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
// The member website footer reads "© <year> <member name>. All rights reserved." The year was typed
|
|
2
|
+
// into each site's editor, so ABMP stayed on 2025 while ASCP and AHP said 2026 (PAC ticket
|
|
3
|
+
// 12914830766). This keeps the year current without anyone touching the editors again.
|
|
4
|
+
// Deliberately free of Wix imports so it can be unit-tested.
|
|
5
|
+
|
|
6
|
+
const ASSOCIATION_TIME_ZONE = 'America/Denver';
|
|
7
|
+
// Matches the year in "© 2025", "© 2025" (the .html of a text element), "(c) 2025" and
|
|
8
|
+
// "Copyright 2025", and nothing else — "Member since 2025" is left alone.
|
|
9
|
+
const COPYRIGHT_YEAR_PATTERN = /(©|©|\(c\)|copyright)(\s*| )(\d{4})/i;
|
|
10
|
+
|
|
11
|
+
// PAC operates in Denver; the year should roll over at their midnight, not the visitor's.
|
|
12
|
+
const getCurrentYear = (now = new Date()) => {
|
|
13
|
+
try {
|
|
14
|
+
return Number(
|
|
15
|
+
new Intl.DateTimeFormat('en-US', { timeZone: ASSOCIATION_TIME_ZONE, year: 'numeric' }).format(
|
|
16
|
+
now
|
|
17
|
+
)
|
|
18
|
+
);
|
|
19
|
+
} catch {
|
|
20
|
+
return now.getUTCFullYear();
|
|
21
|
+
}
|
|
22
|
+
};
|
|
23
|
+
|
|
24
|
+
/** Returns the text with the year after the copyright mark set to the current year, or unchanged. */
|
|
25
|
+
const withCurrentCopyrightYear = (text, now = new Date()) => {
|
|
26
|
+
if (typeof text !== 'string') return text;
|
|
27
|
+
return text.replace(
|
|
28
|
+
COPYRIGHT_YEAR_PATTERN,
|
|
29
|
+
(_, mark, space) => `${mark}${space}${getCurrentYear(now)}`
|
|
30
|
+
);
|
|
31
|
+
};
|
|
32
|
+
|
|
33
|
+
const hasCopyrightYear = text => typeof text === 'string' && COPYRIGHT_YEAR_PATTERN.test(text);
|
|
34
|
+
|
|
35
|
+
module.exports = { getCurrentYear, withCurrentCopyrightYear, hasCopyrightYear };
|