Skip to content

Commit

Permalink
Feature toggle for invite migration
Browse files Browse the repository at this point in the history
  • Loading branch information
oharsta committed May 17, 2024
1 parent f4ef2f7 commit 6b55a05
Show file tree
Hide file tree
Showing 13 changed files with 182 additions and 115 deletions.
2 changes: 1 addition & 1 deletion pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@

<groupId>org.openconext</groupId>
<artifactId>teams</artifactId>
<version>9.2.0</version>
<version>9.3.0-SNAPSHOT</version>
<packaging>pom</packaging>

<name>teams</name>
Expand Down
2 changes: 1 addition & 1 deletion teams-gui/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
<parent>
<groupId>org.openconext</groupId>
<artifactId>teams</artifactId>
<version>9.2.0</version>
<version>9.3.0-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>

Expand Down
15 changes: 15 additions & 0 deletions teams-gui/src/icons/no-entry-symbol-svgrepo-com.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
4 changes: 3 additions & 1 deletion teams-gui/src/locale/en.js
Original file line number Diff line number Diff line change
Expand Up @@ -373,7 +373,9 @@ I18n.translations.en = {
urn: "Unspecified urn",
schacHome: "SchacHome organization",
email: "Email"
}
},
migrationOn: "SURF is working on the migration from Teams to <a href='https://wiki.surfnet.nl/display/surfconextdev/Upgrade+Guide%3A+Teams+naar+Invite' target='_blank' rel='noreferrer'>SURFconext Invite</a>" +
" - please <a href='mailto:[email protected]'>contact support</a> if you want to make a new team."
}
};

Expand Down
6 changes: 3 additions & 3 deletions teams-gui/src/locale/nl.js
Original file line number Diff line number Diff line change
Expand Up @@ -373,10 +373,10 @@ I18n.translations.nl = {
urn: "Unspecified urn",
schacHome: "SchacHome organization",
email: "Email"
}
},
migrationOn: "SURF werkt aan de migratie van Teams naar <a href='https://wiki.surfnet.nl/display/surfconextdev/Upgrade+Guide%3A+Teams+naar+Invite' target='_blank' rel='noreferrer'>SURFconext Invite</a>" +
" - neem <a href='mailto:[email protected]'>contact</a> op als je een nieuw team wilt maken"
}


};

export default I18n.translations.nl;
224 changes: 121 additions & 103 deletions teams-gui/src/pages/NewTeam.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import TooltipIcon from "../components/Tooltip";
import InputField from "../components/InputField";
import ErrorIndicator from "../components/ErrorIndicator";
import {isEmpty, stopEvent} from "../utils/utils";
import {ReactComponent as StopIcon} from "../icons/no-entry-symbol-svgrepo-com.svg";
import {ReactComponent as privateTeam} from "../icons/allowances-no-talking.svg";
import {ReactComponent as publicTeam} from "../icons/human-resources-offer-employee-1.svg";
import {ButtonContainer} from "../components/ButtonContainer";
Expand Down Expand Up @@ -94,121 +95,138 @@ const NewTeam = ({user}) => {
}
breadCrumbs.push({name: I18n.t(`breadcrumbs.${team.id ? "editTeam" : "newTeam"}`, {name: ""})});

return (
<>
<SubHeader>
<BreadCrumb paths={breadCrumbs}/>
</SubHeader>
<SubHeader child={true}>
<h1>{I18n.t(`breadcrumbs.${team.id ? "editTeam" : "newTeam"}`, {name: team.name})}</h1>
</SubHeader>
<Page>
<form className="new-team" method={"POST"} onSubmit={stopEvent}>
<InputField value={team.name || ""}
onChange={e => {
setTeam({...team, name: e.target.value.replace(/[^\w\s-]/gi, "")});
setNameExists(false)
}}
toolTip={team.id ? I18n.t("newTeam.tooltips.immutableName") : ""}
aria-describedby={"team-name"}
disabled={team.id}
placeholder={I18n.t("newTeam.placeholders.name")}
onBlur={e => teamExistsByName(e.target.value).then(exists => setNameExists(exists))}
error={nameExist || (!initial && isEmpty(team.name))}
name={I18n.t("newTeam.name")}/>
{(!initial && isEmpty(team.name)) &&
const renderInviteMigration = () => {
return (
<div className="invite-migration-on-container">
<div className="invite-migration-on">
<StopIcon/>
<span dangerouslySetInnerHTML={{__html: I18n.t("migrateTeam.migrationOn")}}/>
</div>
</div>
);
}

const renderTeamForm = () => {
return (
<form className="new-team" method={"POST"} onSubmit={stopEvent}>
<InputField value={team.name || ""}
onChange={e => {
setTeam({...team, name: e.target.value.replace(/[^\w\s-]/gi, "")});
setNameExists(false)
}}
toolTip={team.id ? I18n.t("newTeam.tooltips.immutableName") : ""}
aria-describedby={"team-name"}
disabled={team.id}
placeholder={I18n.t("newTeam.placeholders.name")}
onBlur={e => teamExistsByName(e.target.value).then(exists => setNameExists(exists))}
error={nameExist || (!initial && isEmpty(team.name))}
name={I18n.t("newTeam.name")}/>
{(!initial && isEmpty(team.name)) &&
<ErrorIndicator describedBy={"team-name"} msg={I18n.t("forms.required", {
attribute: I18n.t("newTeam.name")
})}/>}

{nameExist &&
{nameExist &&
<ErrorIndicator describedBy={"team-name"} msg={I18n.t("forms.alreadyExists", {
object: I18n.t("newTeam.object").toLowerCase(),
attribute: I18n.t("newTeam.name").toLowerCase(),
value: team.name
})}/>}

<div className="input-field">
<TooltipIcon tooltip={I18n.t("newTeam.tooltips.description")} name="description"
label={I18n.t("newTeam.description")}/>
<MarkDown markdown={team.description || ""}
onChange={val => setTeam({...team, description: val})}/>
</div>

<InputField value={team.personalNote || ""}
onChange={e => {
setTeam({...team, personalNote: e.target.value});
}}
placeholder={I18n.t("teamDetails.personalNotesPlaceholder")}
multiline={true}
toolTip={I18n.t("newTeam.tooltips.personalNote")}
name={I18n.t("newTeam.personalNote")}/>

<div className="input-field ">
<CheckBox name={"public-link"}
onChange={() => setTeam({...team, publicLinkDisabled: !team.publicLinkDisabled})}
readOnly={!team.viewable}
info={I18n.t("newTeam.publicLinkDisabled")}
toolTip={I18n.t("newTeam.tooltips.publicLinkDisabled")}
value={!team.publicLinkDisabled}/>
</div>

<div className="input-field ">
<CheckBox name={"hide-members"}
onChange={() => setTeam({...team, hideMembers: !team.hideMembers})}
info={I18n.t("newTeam.hideMembers")}
toolTip={I18n.t("newTeam.tooltips.hideMembers")}
value={team.hideMembers || false}/>
<div className="input-field">
<TooltipIcon tooltip={I18n.t("newTeam.tooltips.description")} name="description"
label={I18n.t("newTeam.description")}/>
<MarkDown markdown={team.description || ""}
onChange={val => setTeam({...team, description: val})}/>
</div>

<InputField value={team.personalNote || ""}
onChange={e => {
setTeam({...team, personalNote: e.target.value});
}}
placeholder={I18n.t("teamDetails.personalNotesPlaceholder")}
multiline={true}
toolTip={I18n.t("newTeam.tooltips.personalNote")}
name={I18n.t("newTeam.personalNote")}/>

<div className="input-field ">
<CheckBox name={"public-link"}
onChange={() => setTeam({...team, publicLinkDisabled: !team.publicLinkDisabled})}
readOnly={!team.viewable}
info={I18n.t("newTeam.publicLinkDisabled")}
toolTip={I18n.t("newTeam.tooltips.publicLinkDisabled")}
value={!team.publicLinkDisabled}/>
</div>

<div className="input-field ">
<CheckBox name={"hide-members"}
onChange={() => setTeam({...team, hideMembers: !team.hideMembers})}
info={I18n.t("newTeam.hideMembers")}
toolTip={I18n.t("newTeam.tooltips.hideMembers")}
value={team.hideMembers || false}/>
</div>

<div className="input-field ">
<label>{I18n.t("newTeam.visibility")}</label>
<div className="team-visibilities">
{visibilities.map((visibility, i) =>
<button key={i}
className={`visibility ${viewableActive(visibility.name) ? "active" : ""}`}
onClick={() => !viewableActive(visibility.name) && setTeam({
...team,
viewable: !team.viewable,
publicLinkDisabled: team.viewable || team.publicLinkDisabled
})}>
<section className="header">
<visibility.icon/>
<span className={"visibility"}>{I18n.t(`newTeam.${visibility.name}`)}</span>
</section>
<p>{I18n.t(`newTeam.${visibility.name}Info`)}</p>
</button>)}
</div>
</div>
{!team.id && <EmailField emails={backupEmails}
addEmail={addEmail}
singleEmail={true}
removeMail={removeMail}
name={I18n.t("newTeam.backupEmail")}
placeHolder={I18n.t("newTeam.placeholders.backupEmail")}
pinnedEmails={[user.person.email]}
/>}

{!team.id && <InputField value={team.invitationMessage || ""}
onChange={e => {
setTeam({...team, invitationMessage: e.target.value});
}}
id={"invitation-messsage"}
multiline={true}
placeholder={I18n.t("newTeam.placeholders.invitationMessage")}
name={I18n.t("newTeam.invitationMessage")}/>}

<ButtonContainer>
<Button cancelButton={true}
onClick={() => navigate(team.id ? `/team-details/${team.id}` : "/my-teams")}
txt={I18n.t("forms.cancel")}/>
<Button
onClick={submit}
disabled={!initial && !isValid()}
txt={I18n.t(`${team.id ? "forms.save" : "newTeam.create"}`)}/>
</ButtonContainer>
</form>
);
}

<div className="input-field ">
<label>{I18n.t("newTeam.visibility")}</label>
<div className="team-visibilities">
{visibilities.map((visibility, i) =>
<button key={i}
className={`visibility ${viewableActive(visibility.name) ? "active" : ""}`}
onClick={() => !viewableActive(visibility.name) && setTeam({
...team,
viewable: !team.viewable,
publicLinkDisabled: team.viewable || team.publicLinkDisabled
})}>
<section className="header">
<visibility.icon/>
<span className={"visibility"}>{I18n.t(`newTeam.${visibility.name}`)}</span>
</section>
<p>{I18n.t(`newTeam.${visibility.name}Info`)}</p>
</button>)}
</div>
</div>
{!team.id && <EmailField emails={backupEmails}
addEmail={addEmail}
singleEmail={true}
removeMail={removeMail}
name={I18n.t("newTeam.backupEmail")}
placeHolder={I18n.t("newTeam.placeholders.backupEmail")}
pinnedEmails={[user.person.email]}
/>}

{!team.id && <InputField value={team.invitationMessage || ""}
onChange={e => {
setTeam({...team, invitationMessage: e.target.value});
}}
id={"invitation-messsage"}
multiline={true}
placeholder={I18n.t("newTeam.placeholders.invitationMessage")}
name={I18n.t("newTeam.invitationMessage")}/>}

<ButtonContainer>
<Button cancelButton={true}
onClick={() => navigate(team.id ? `/team-details/${team.id}` : "/my-teams")}
txt={I18n.t("forms.cancel")}/>
<Button
onClick={submit}
disabled={!initial && !isValid()}
txt={I18n.t(`${team.id ? "forms.save" : "newTeam.create"}`)}/>
</ButtonContainer>

</form>
return (
<>
<SubHeader>
<BreadCrumb paths={breadCrumbs}/>
</SubHeader>
<SubHeader child={true}>
<h1>{I18n.t(`breadcrumbs.${team.id ? "editTeam" : "newTeam"}`, {name: team.name})}</h1>
</SubHeader>
{user.featureToggles.inviteMigrationOn && renderInviteMigration()}
<Page>
{!user.featureToggles.inviteMigrationOn && renderTeamForm()}
</Page>
</>
);
Expand Down
25 changes: 25 additions & 0 deletions teams-gui/src/pages/NewTeam.scss
Original file line number Diff line number Diff line change
Expand Up @@ -103,3 +103,28 @@ form.new-team {
}
}
}

.invite-migration-on-container {
display: flex;
margin-top: 40px;
background-color: #e9ecef;

.invite-migration-on {
max-width: 1086px;
width: 100%;
margin: 0 auto;
display: flex;
align-items: center;

padding: 20px;


svg {
color: darkred;
width: 44px;
height: auto;
margin-right: 15px;
}
}

}
2 changes: 1 addition & 1 deletion teams-server/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
<parent>
<groupId>org.openconext</groupId>
<artifactId>teams</artifactId>
<version>9.2.0</version>
<version>9.3.0-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>

Expand Down
2 changes: 1 addition & 1 deletion teams-server/src/main/java/teams/domain/Feature.java
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
package teams.domain;

public enum Feature {

inviteMigrationOn
}
4 changes: 2 additions & 2 deletions teams-server/src/main/java/teams/domain/FederatedUser.java
Original file line number Diff line number Diff line change
Expand Up @@ -16,9 +16,9 @@
public class FederatedUser extends User {

private final Map<String, String> config;
private final String productName;
private final Person person;
private String groupNameContext;
private String productName;
private Person person;
private List<ExternalTeam> externalTeams;
private Map<Feature, Boolean> featureToggles;

Expand Down
6 changes: 4 additions & 2 deletions teams-server/src/main/java/teams/security/SecurityConfig.java
Original file line number Diff line number Diff line change
Expand Up @@ -232,6 +232,9 @@ public static class SecurityConfigurationAdapter extends WebSecurityConfigurerAd
@Value("${config.supported_language_codes}")
private String supportedLanguageCodes;

@Value("${features.invite-migration-on}")
private boolean inviteMigrationOn;

@Override
protected void configure(AuthenticationManagerBuilder auth) {
PreAuthenticatedAuthenticationProvider authenticationProvider = new PreAuthenticatedAuthenticationProvider();
Expand Down Expand Up @@ -264,8 +267,7 @@ public Map<String, String> config() {
}

private Map<Feature, Boolean> featureToggles() {
Map<Feature, Boolean> toggles = new HashMap<>();
return toggles;
return Map.of(Feature.inviteMigrationOn, inviteMigrationOn);
}

@Override
Expand Down
3 changes: 3 additions & 0 deletions teams-server/src/main/resources/application.yml
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,9 @@ config:
sponsor: SURF
supported_language_codes: en,nl

features:
invite-migration-on: true

security:
user:
name: user
Expand Down
Loading

0 comments on commit 6b55a05

Please sign in to comment.