Skip to content

Commit 6b3542d

Browse files
committed
require license
1 parent 7896d9c commit 6b3542d

File tree

5 files changed

+66
-26
lines changed

5 files changed

+66
-26
lines changed

src/packages/frontend/customize.tsx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -127,6 +127,7 @@ export interface CustomizeState {
127127
max_upgrades: TypedMap<Partial<Upgrades>>;
128128
nonfree_countries?: List<string>;
129129
limit_free_project_uptime: number; // minutes
130+
require_license_to_create_project?: boolean;
130131
onprem_quota_heading: string;
131132
organization_email: string;
132133
organization_name: string;

src/packages/frontend/projects/create-project.tsx

Lines changed: 29 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ Create a new project
99

1010
import { Button, Card, Col, Form, Input, Row } from "antd";
1111
import { delay } from "awaiting";
12-
12+
import { BuyLicenseForProject } from "@cocalc/frontend/site-licenses/purchase/buy-license-for-project";
1313
import { Alert, Well } from "@cocalc/frontend/antd-bootstrap";
1414
import {
1515
CSS,
@@ -61,7 +61,6 @@ export const NewProjectCreator: React.FC<Props> = ({
6161
const [title_text, set_title_text] = useState<string>(default_value ?? "");
6262
const [error, set_error] = useState<string>("");
6363
const [show_advanced, set_show_advanced] = useState<boolean>(false);
64-
const [show_add_license, set_show_add_license] = useState<boolean>(false);
6564
const [title_prefill, set_title_prefill] = useState<boolean>(false);
6665
const [license_id, set_license_id] = useState<string>("");
6766
const [warnBoost, setWarnBoost] = useState<boolean>(false);
@@ -73,15 +72,19 @@ export const NewProjectCreator: React.FC<Props> = ({
7372

7473
const is_anonymous = useTypedRedux("account", "is_anonymous");
7574
const customize_kucalc = useTypedRedux("customize", "kucalc");
75+
const requireLicense = !!useTypedRedux(
76+
"customize",
77+
"require_license_to_create_project",
78+
);
79+
const [show_add_license, set_show_add_license] =
80+
useState<boolean>(requireLicense);
7681

7782
// onprem and cocalc.com use licenses to adjust quota configs – but only cocalc.com has custom software images
7883
const show = useMemo(
7984
() => [KUCALC_COCALC_COM, KUCALC_ON_PREMISES].includes(customize_kucalc),
8085
[customize_kucalc],
8186
);
8287

83-
//const requireLicense = customize_kucalc == KUCALC_COCALC_COM;
84-
8588
const [form] = Form.useForm();
8689

8790
useEffect(() => {
@@ -113,7 +116,7 @@ export const NewProjectCreator: React.FC<Props> = ({
113116
set_error("");
114117
set_custom_software({});
115118
set_show_advanced(false);
116-
set_show_add_license(false);
119+
set_show_add_license(requireLicense);
117120
set_title_prefill(true);
118121
set_license_id("");
119122
}
@@ -227,7 +230,10 @@ export const NewProjectCreator: React.FC<Props> = ({
227230
);
228231
}
229232

230-
function create_disabled() {
233+
function isDisabled() {
234+
if (requireLicense && !license_id) {
235+
return true;
236+
}
231237
return (
232238
// no name of new project
233239
!title_text?.trim() ||
@@ -286,8 +292,20 @@ export const NewProjectCreator: React.FC<Props> = ({
286292
function render_add_license() {
287293
if (!show_add_license) return;
288294
return (
289-
<Card size="small" title="Select license" style={CARD_STYLE}>
295+
<Card
296+
size="small"
297+
title={
298+
<>
299+
<div style={{ float: "right" }}>
300+
<BuyLicenseForProject size="small" />
301+
</div>
302+
<Icon name="key" /> Select License
303+
</>
304+
}
305+
style={CARD_STYLE}
306+
>
290307
<SiteLicenseInput
308+
requireValid
291309
confirmLabel={"Add this license"}
292310
onChange={addSiteLicense}
293311
/>
@@ -325,6 +343,7 @@ export const NewProjectCreator: React.FC<Props> = ({
325343
return (
326344
<div style={TOGGLE_STYLE}>
327345
<Button
346+
disabled={requireLicense}
328347
onClick={() => set_show_add_license(true)}
329348
type="link"
330349
style={TOGGLE_BUTTON_STYLE}
@@ -348,7 +367,7 @@ export const NewProjectCreator: React.FC<Props> = ({
348367
>
349368
add/remove licenses
350369
</A>{" "}
351-
in the project settings later on.
370+
in project settings later.
352371
</div>
353372
);
354373
}
@@ -419,12 +438,12 @@ export const NewProjectCreator: React.FC<Props> = ({
419438
Cancel
420439
</Button>
421440
<Button
422-
disabled={create_disabled()}
441+
disabled={isDisabled()}
423442
onClick={() => create_project()}
424443
type="primary"
425444
>
426445
Create Project
427-
{create_disabled() ? " (enter a title above!)" : ""}
446+
{requireLicense && !license_id && <> (select license above)</>}
428447
</Button>
429448
</Col>
430449
</Row>

src/packages/frontend/site-licenses/input.tsx

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ interface Props {
3434
style?: CSS;
3535
extra?: React.ReactNode;
3636
extraButtons?: React.ReactNode;
37+
requireValid?: boolean;
3738
}
3839

3940
export const SiteLicenseInput: React.FC<Props> = (props: Props) => {
@@ -46,6 +47,7 @@ export const SiteLicenseInput: React.FC<Props> = (props: Props) => {
4647
confirmLabel = "Apply License",
4748
extra,
4849
extraButtons,
50+
requireValid,
4951
} = props;
5052

5153
const managedLicenses = useManagedLicenses();
@@ -63,6 +65,7 @@ export const SiteLicenseInput: React.FC<Props> = (props: Props) => {
6365
style={style}
6466
extra={extra}
6567
extraButtons={extraButtons}
68+
requireValid={requireValid}
6669
/>
6770
);
6871
};

src/packages/frontend/site-licenses/select-license.tsx

Lines changed: 24 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@ interface Props {
3535
style?: CSS;
3636
extra?: ReactNode; // plain-text node is ok
3737
extraButtons?: ReactNode;
38+
requireValid?: boolean;
3839
}
3940

4041
export default function SelectLicense(props: Props) {
@@ -49,6 +50,7 @@ export default function SelectLicense(props: Props) {
4950
style,
5051
extra,
5152
extraButtons,
53+
requireValid,
5254
} = props;
5355
const isBlurredRef = useRef<boolean>(true);
5456
const [licenseId, setLicenseId] = useState<string>(defaultLicenseId ?? "");
@@ -154,24 +156,30 @@ export default function SelectLicense(props: Props) {
154156
style={{ width: "100%", ...style }}
155157
>
156158
<div>
157-
{(showAll || licenseIds.length < len(managedLicenses)) && (
158-
<Checkbox
159-
style={{
160-
flex: "1 0 0",
161-
margin: "5px 0",
162-
color: COLORS.GRAY_M,
163-
whiteSpace: "nowrap",
164-
}}
165-
checked={showAll}
166-
onChange={() => setShowAll(!showAll)}
167-
>
168-
Show expired
169-
</Checkbox>
170-
)}{" "}
159+
{!requireValid &&
160+
(showAll || licenseIds.length < len(managedLicenses)) && (
161+
<Checkbox
162+
style={{
163+
flex: "1 0 0",
164+
margin: "5px 0",
165+
color: COLORS.GRAY_M,
166+
whiteSpace: "nowrap",
167+
}}
168+
checked={showAll}
169+
onChange={() => setShowAll(!showAll)}
170+
>
171+
Show expired
172+
</Checkbox>
173+
)}{" "}
171174
<Select
172-
style={{ width: "100%", flex: "1 1 0", marginRight: "10px" }}
175+
style={{
176+
width: "100%",
177+
height: licenseId ? "100px" : undefined,
178+
flex: "1 1 0",
179+
marginRight: "10px",
180+
}}
173181
placeholder={
174-
"Enter license code " +
182+
`Enter${requireValid ? " valid " : " "}license code ` +
175183
(options.length > 0
176184
? `or select from the ${options.length} licenses you manage`
177185
: "")

src/packages/util/db-schema/site-defaults.ts

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,7 @@ export type SiteSettingsKeys =
7777
| "max_trial_projects"
7878
| "nonfree_countries"
7979
| "limit_free_project_uptime"
80+
| "require_license_to_create_project"
8081
| "google_analytics"
8182
| "kucalc"
8283
| "dns"
@@ -576,6 +577,14 @@ export const site_settings_conf: SiteSettings = {
576577
show: only_cocalc_com,
577578
to_display: (val) => `${val} minutes`,
578579
},
580+
require_license_to_create_project: {
581+
name: "Require License to Create Project",
582+
desc: "If yes the 'New Project' creation form on the projects page requires the user to enter a valid license. This has no other impact and only impacts the frontend UI. Users can circumvent this via the API or a course.",
583+
default: "no",
584+
valid: only_booleans,
585+
show: only_cocalc_com,
586+
to_val: to_bool,
587+
},
579588
datastore: {
580589
name: "Datastore",
581590
desc: `Show the '${DATASTORE_TITLE}' panel in the project settings`,

0 commit comments

Comments
 (0)