Skip to content

Commit 2e36ef9

Browse files
authored
Merge branch 'main' into dependabot/npm_and_yarn/orcid-web/src/main/webapp/static/javascript/ng1Orcid/babel/traverse-7.23.2
2 parents 80adb53 + b971310 commit 2e36ef9

File tree

7 files changed

+144
-10
lines changed

7 files changed

+144
-10
lines changed

CHANGELOG.md

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,19 @@
1+
## v2.43.5 - 2023-10-30
2+
3+
[Full Changelog](https://github.com/ORCID/ORCID-Source/compare/v2.43.4...v2.43.5)
4+
5+
## v2.43.4 - 2023-10-30
6+
7+
[Full Changelog](https://github.com/ORCID/ORCID-Source/compare/v2.43.3...v2.43.4)
8+
9+
- [#6915](https://github.com/ORCID/ORCID-Source/pull/6915): 8875 update the email verification schedule to send emails 2 7 and 28 days after email creation
10+
- [#6917](https://github.com/ORCID/ORCID-Source/pull/6917): fix: Add indexes to event table
11+
- [#6919](https://github.com/ORCID/ORCID-Source/pull/6919): Bump browserify-sign from 4.2.1 to 4.2.2 in /orcid-web/src/main/webapp/static/javascript/ng1Orcid
12+
13+
### Fix
14+
15+
- Add indexes to event table
16+
117
## v2.43.3 - 2023-10-30
218

319
[Full Changelog](https://github.com/ORCID/ORCID-Source/compare/v2.43.2...v2.43.3)

orcid-core/src/main/java/org/orcid/core/togglz/Features.java

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -150,7 +150,10 @@ public enum Features implements Feature {
150150
ACCOUNT_LOCKOUT_SIMULATION,
151151

152152
@Label("Enable the new 100M ID's range")
153-
ENABLE_NEW_IDS;
153+
ENABLE_NEW_IDS,
154+
155+
@Label("Send verification emails for 2, 7 and 28 days. If disabled 2 days only verification emails will be sent.")
156+
SEND_ALL_VERIFICATION_EMAILS;
154157

155158
public boolean isActive() {
156159
return FeatureContext.getFeatureManager().isActive(this);

orcid-persistence/src/main/java/org/orcid/persistence/dao/ProfileDao.java

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
import java.util.List;
66

77
import org.apache.commons.lang3.tuple.Pair;
8+
import org.orcid.persistence.jpa.entities.EmailEventType;
89
import org.orcid.persistence.jpa.entities.IndexingStatus;
910
import org.orcid.persistence.jpa.entities.OrcidGrantedAuthority;
1011
import org.orcid.persistence.jpa.entities.ProfileEntity;
@@ -76,6 +77,8 @@ public interface ProfileDao extends GenericDao<ProfileEntity, String> {
7677
void updateLastModifiedDateAndIndexingStatusWithoutResult(String orcid, Date lastModified, IndexingStatus indexingStatus);
7778

7879
public List<Pair<String, Date>> findEmailsUnverfiedDays(int daysUnverified, int maxResults);
80+
81+
public List<Pair<String, Date>> findEmailsUnverifiedDaysAndEventType(int daysUnverified, int maxResults, List<EmailEventType> eventTypes);
7982

8083
String retrieveOrcidType(String orcid);
8184

orcid-persistence/src/main/java/org/orcid/persistence/dao/impl/ProfileDaoImpl.java

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,9 @@
55
import java.util.Collections;
66
import java.util.Date;
77
import java.util.List;
8+
import java.util.Objects;
9+
import java.util.Optional;
10+
import java.util.stream.Collectors;
811

912
import javax.persistence.NoResultException;
1013
import javax.persistence.Query;
@@ -13,13 +16,15 @@
1316
import org.apache.commons.lang3.tuple.Pair;
1417
import org.orcid.persistence.aop.UpdateProfileLastModifiedAndIndexingStatus;
1518
import org.orcid.persistence.dao.ProfileDao;
19+
import org.orcid.persistence.jpa.entities.EmailEventType;
1620
import org.orcid.persistence.jpa.entities.IndexingStatus;
1721
import org.orcid.persistence.jpa.entities.OrcidGrantedAuthority;
1822
import org.orcid.persistence.jpa.entities.ProfileEntity;
1923
import org.orcid.persistence.jpa.entities.ProfileEventEntity;
2024
import org.orcid.persistence.jpa.entities.ProfileEventType;
2125
import org.springframework.beans.factory.annotation.Value;
2226
import org.springframework.transaction.annotation.Transactional;
27+
import org.springframework.util.StringUtils;
2328

2429
public class ProfileDaoImpl extends GenericDaoImpl<ProfileEntity, String> implements ProfileDao {
2530

@@ -151,6 +156,36 @@ public List<Pair<String, Date>> findEmailsUnverfiedDays(int daysUnverified, int
151156
});
152157
return results;
153158
}
159+
160+
@SuppressWarnings("unchecked")
161+
@Override
162+
public List<Pair<String, Date>> findEmailsUnverifiedDaysAndEventType(int daysUnverified, int maxResults, List<EmailEventType> eventTypes) {
163+
List<String> stringList = eventTypes.stream().map(Optional::ofNullable) //Stream<Optional<..>>
164+
.map(opt -> opt.orElse(null))
165+
.map(Objects::toString)
166+
.map(opt -> "'"+opt+"'")
167+
.collect(Collectors.toList());
168+
169+
String eventsTypeStr = StringUtils.collectionToCommaDelimitedString(stringList);
170+
StringBuilder queryString = new StringBuilder("SELECT e.email, e.date_created FROM email e ");
171+
queryString.append("LEFT JOIN email_event ev ON e.email = ev.email ");
172+
queryString.append("JOIN profile p on p.orcid = e.orcid and p.claimed = true ");
173+
queryString.append("AND p.deprecated_date is null AND p.profile_deactivation_date is null AND p.account_expiry is null ");
174+
queryString.append("where (ev.email IS NULL or email_event_type NOT IN ("+ eventsTypeStr+"))" + "and e.is_verified = false ");
175+
queryString.append("and e.date_created < (now() - CAST('").append(daysUnverified).append("' AS INTERVAL DAY)) ");
176+
queryString.append("and (e.source_id = e.orcid OR e.source_id is null)");
177+
queryString.append(" GROUP BY e.email, e.date_created, e.last_modified");
178+
queryString.append(" ORDER BY e.last_modified");
179+
Query query = entityManager.createNativeQuery(queryString.toString());
180+
query.setMaxResults(maxResults);
181+
List<Object[]> dbInfo = query.getResultList();
182+
List<Pair<String, Date>> results = new ArrayList<Pair<String, Date>>();
183+
dbInfo.stream().forEach(element -> {
184+
Pair<String, Date> pair = Pair.of((String) element[0], (Date) element[1]);
185+
results.add(pair);
186+
});
187+
return results;
188+
}
154189

155190
@SuppressWarnings("unchecked")
156191
@Override

orcid-persistence/src/main/java/org/orcid/persistence/jpa/entities/EmailEventType.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,8 @@
66
*
77
*/
88
public enum EmailEventType {
9-
9+
VERIFY_EMAIL_28_DAYS_SENT,
10+
VERIFY_EMAIL_28_DAYS_SENT_SKIPPED,
1011
VERIFY_EMAIL_7_DAYS_SENT,
1112
VERIFY_EMAIL_7_DAYS_SENT_SKIPPED, /* we are going to skip notifying email address that where already in the system before launching this */
1213
VERIFY_EMAIL_2_DAYS_SENT,

orcid-scheduler-web/src/main/java/org/orcid/scheduler/email/cli/manager/EmailMessageSenderImpl.java

Lines changed: 82 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@
3636
import org.orcid.core.manager.v3.NotificationManager;
3737
import org.orcid.core.manager.v3.RecordNameManager;
3838
import org.orcid.core.manager.v3.read_only.EmailManagerReadOnly;
39+
import org.orcid.core.togglz.Features;
3940
import org.orcid.core.utils.VerifyEmailUtils;
4041
import org.orcid.jaxb.model.common.ActionType;
4142
import org.orcid.jaxb.model.common.AvailableLocales;
@@ -82,7 +83,11 @@ public class EmailMessageSenderImpl implements EmailMessageSender {
8283

8384
ExecutorService pool;
8485

85-
private int verifyReminderAfterDays = 2;
86+
private int verifyReminderAfterTwoDays = 2;
87+
88+
private int verifyReminderAfterSevenDays = 7;
89+
90+
private int verifyReminderAfterTwentyEightDays = 28;
8691

8792
@Resource
8893
private NotificationDao notificationDao;
@@ -141,8 +146,10 @@ public class EmailMessageSenderImpl implements EmailMessageSender {
141146
@Value("${org.notifications.max_elements_to_show:20}")
142147
private Integer maxNotificationsToShowPerClient;
143148

144-
@Value("${org.orcid.core.email.verify.tooOld:15}")
145-
private int emailTooOld;
149+
@Value("${org.orcid.core.email.verify.tooOld:45}")
150+
private int emailTooOld;
151+
152+
private int emailTooOldLegacy = 15;
146153

147154
public EmailMessageSenderImpl(@Value("${org.notifications.service_announcements.maxThreads:8}") Integer maxThreads,
148155
@Value("${org.notifications.service_announcements.maxRetry:3}") Integer maxRetry) {
@@ -563,13 +570,17 @@ private String getHtmlBody(NotificationAdministrative notificationAdministrative
563570

564571
@Override
565572
synchronized public void processUnverifiedEmails2Days() {
566-
LOGGER.info("About to process unverIfied emails for reminder");
573+
LOGGER.info("About to process unverIfied emails for 2 days reminder");
567574
List<Pair<String, Date>> elements = Collections.<Pair<String, Date>> emptyList();
568575
do {
569-
elements = profileDaoReadOnly.findEmailsUnverfiedDays(verifyReminderAfterDays, 100);
570-
LOGGER.info("Got batch of {} profiles with unverified emails for reminder", elements.size());
576+
elements = profileDaoReadOnly.findEmailsUnverfiedDays(verifyReminderAfterTwoDays, 100);
577+
LOGGER.info("Got batch of {} profiles with unverified emails for 2 days reminder", elements.size());
571578
LocalDateTime now = LocalDateTime.now();
572-
Date tooOld = now.minusDays(emailTooOld).toDate();
579+
//togglz here
580+
Date tooOld = now.minusDays(emailTooOldLegacy).toDate();
581+
if(Features.SEND_ALL_VERIFICATION_EMAILS.isActive()) {
582+
tooOld = now.minusDays(emailTooOld).toDate();
583+
}
573584
for (Pair<String, Date> element : elements) {
574585
if(element.getRight() == null || element.getRight().after(tooOld)) {
575586
processUnverifiedEmails2DaysInTransaction(element.getLeft());
@@ -580,7 +591,70 @@ synchronized public void processUnverifiedEmails2Days() {
580591
}
581592
} while (!elements.isEmpty());
582593
}
583-
594+
595+
596+
synchronized public void processUnverifiedEmails7Days() {
597+
if(Features.SEND_ALL_VERIFICATION_EMAILS.isActive()) {
598+
LOGGER.info("About to process unverIfied emails for 7 days reminder");
599+
List<Pair<String, Date>> elements = Collections.<Pair<String, Date>> emptyList();
600+
do {
601+
elements = profileDaoReadOnly.findEmailsUnverfiedDays(verifyReminderAfterSevenDays, 100);
602+
LOGGER.info("Got batch of {} profiles with unverified emails for 7 days reminder", elements.size());
603+
LocalDateTime now = LocalDateTime.now();
604+
Date tooOld = now.minusDays(emailTooOld).toDate();
605+
for (Pair<String, Date> element : elements) {
606+
if(element.getRight() == null || element.getRight().after(tooOld)) {
607+
processUnverifiedEmailsInTransaction(element.getLeft(), EmailEventType.VERIFY_EMAIL_7_DAYS_SENT, EmailEventType.VERIFY_EMAIL_7_DAYS_SENT_SKIPPED);
608+
} else {
609+
// Mark is as too old to send the verification email
610+
markUnverifiedEmailAsTooOld(element.getLeft());
611+
}
612+
}
613+
} while (!elements.isEmpty());
614+
}
615+
}
616+
617+
618+
synchronized public void processUnverifiedEmails28Days() {
619+
if(Features.SEND_ALL_VERIFICATION_EMAILS.isActive()) {
620+
LOGGER.info("About to process unverIfied emails for 28 days reminder");
621+
List<Pair<String, Date>> elements = Collections.<Pair<String, Date>> emptyList();
622+
do {
623+
elements = profileDaoReadOnly.findEmailsUnverfiedDays(verifyReminderAfterTwentyEightDays, 100);
624+
LOGGER.info("Got batch of {} profiles with unverified emails for 28 days reminder", elements.size());
625+
LocalDateTime now = LocalDateTime.now();
626+
Date tooOld = now.minusDays(emailTooOld).toDate();
627+
for (Pair<String, Date> element : elements) {
628+
if(element.getRight() == null || element.getRight().after(tooOld)) {
629+
processUnverifiedEmailsInTransaction(element.getLeft(),EmailEventType.VERIFY_EMAIL_28_DAYS_SENT, EmailEventType.VERIFY_EMAIL_28_DAYS_SENT_SKIPPED);
630+
} else {
631+
// Mark is as too old to send the verification email
632+
markUnverifiedEmailAsTooOld(element.getLeft());
633+
}
634+
}
635+
} while (!elements.isEmpty());
636+
}
637+
}
638+
639+
private void processUnverifiedEmailsInTransaction(final String email, EmailEventType eventSent, EmailEventType eventSkipped) {
640+
transactionTemplate.execute(new TransactionCallbackWithoutResult() {
641+
@Override
642+
@Transactional
643+
protected void doInTransactionWithoutResult(TransactionStatus status) {
644+
try {
645+
String userOrcid = emailManagerReadOnly.findOrcidIdByEmail(email);
646+
sendVerificationReminderEmail(userOrcid, email);
647+
emailEventDao.persist(new EmailEventEntity(email, eventSent));
648+
emailEventDao.flush();
649+
} catch (Exception e) {
650+
LOGGER.error("Unable to send unverified email reminder to email: " + email, e);
651+
emailEventDao.persist(new EmailEventEntity(email, eventSkipped));
652+
emailEventDao.flush();
653+
}
654+
}
655+
});
656+
}
657+
584658
private void processUnverifiedEmails2DaysInTransaction(final String email) {
585659
transactionTemplate.execute(new TransactionCallbackWithoutResult() {
586660
@Override

orcid-scheduler-web/src/main/resources/orcid-scheduler-context.xml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,8 @@
2323
<task:scheduled ref="cleanOldClientKeysCronJob" method="cleanOldClientKeys" cron="${org.orcid.scheduler.web.cleanOldClientKeys:0 0 0/1 * * ?}" />
2424
<task:scheduled ref="emailMessageSender" method="sendEmailMessages" cron="${org.orcid.scheduler.web.sendEmailMessages:35 */5 * * * *}" />
2525
<task:scheduled ref="emailMessageSender" method="processUnverifiedEmails2Days" cron="${org.orcid.scheduler.web.processUnverifiedEmails2Days:0 10 * * * *}"/>
26+
<task:scheduled ref="emailMessageSender" method="processUnverifiedEmails7Days" cron="${org.orcid.scheduler.web.processUnverifiedEmails7Days:0 0 0 * * FRI}"/>
27+
<task:scheduled ref="emailMessageSender" method="processUnverifiedEmails28Days" cron="${org.orcid.scheduler.web.processUnverifiedEmails28Days:0 0 0 */28 * * }"/>
2628
<task:scheduled ref="identityProviderLoader" method="loadIdentityProviders" cron="${org.orcid.scheduler.web.loadIdentityProviders:05 05 0-2 * * *}"/>
2729
<task:scheduled ref="notificationManager" method="processOldNotificationsToAutoArchive" cron="${org.orcid.scheduler.web.processOldNotificationsToAutoArchive:06 06 1 * * *}"/>
2830
<task:scheduled ref="notificationManager" method="processOldNotificationsToAutoDelete" cron="${org.orcid.scheduler.web.processOldNotificationsToAutoDelete:07 07 2 * * *}"/>

0 commit comments

Comments
 (0)