Skip to content

Commit 83f34ea

Browse files
committed
Merge remote-tracking branch 'origin/master' into delete-project-data
2 parents 91037d5 + f1f5ff5 commit 83f34ea

File tree

151 files changed

+4031
-9445
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

151 files changed

+4031
-9445
lines changed

.gitignore

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -151,3 +151,8 @@ src/go
151151

152152
# Python virtual environment
153153
venv
154+
155+
# frontend/i18n files, which are created during build or updating the languages
156+
src/packages/frontend/i18n/extracted.json
157+
src/packages/frontend/i18n/*.compiled.json
158+

src/packages/backend/package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@
3333
"@cocalc/backend": "workspace:*",
3434
"@cocalc/util": "workspace:*",
3535
"@types/debug": "^4.1.12",
36+
"@types/watchpack": "^2.4.4",
3637
"awaiting": "^3.0.0",
3738
"chokidar": "^3.6.0",
3839
"debug": "^4.3.2",

src/packages/backend/path-watcher.ts

Lines changed: 6 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,8 @@
44
*/
55

66
/*
7-
Watch A DIRECTORY for changes. Use ./watcher.ts for a single file.
8-
7+
Watch A DIRECTORY for changes of the files in *that* directory only (not recursive).
8+
Use ./watcher.ts for a single file.
99
1010
Slightly generalized fs.watch that works even when the directory doesn't exist,
1111
but also doesn't provide any information about what changed.
@@ -54,7 +54,7 @@ const logger = getLogger("backend:path-watcher");
5454
const POLLING = true;
5555

5656
const DEFAULT_POLL_MS = parseInt(
57-
process.env.COCALC_FS_WATCHER_POLL_INTERVAL_MS ?? "1500",
57+
process.env.COCALC_FS_WATCHER_POLL_INTERVAL_MS ?? "3000",
5858
);
5959

6060
const ChokidarOpts: WatchOptions = {
@@ -79,17 +79,13 @@ export class Watcher extends EventEmitter {
7979
private exists: boolean;
8080
private watchContents?: FSWatcher;
8181
private watchExistence?: FSWatcher;
82-
private interval_ms: number;
8382
private debounce_ms: number;
8483
private debouncedChange: any;
8584
private log: Function;
8685

8786
constructor(
8887
path: string,
89-
{
90-
debounce: debounce_ms = 0,
91-
interval: interval_ms,
92-
}: { debounce?: number; interval?: number } = {},
88+
{ debounce: debounce_ms = DEFAULT_POLL_MS }: { debounce?: number } = {},
9389
) {
9490
super();
9591
this.log = logger.extend(path).debug;
@@ -99,7 +95,6 @@ export class Watcher extends EventEmitter {
9995
}
10096
this.path = path.startsWith("/") ? path : join(process.env.HOME, path);
10197
this.debounce_ms = debounce_ms;
102-
this.interval_ms = interval_ms ?? DEFAULT_POLL_MS;
10398
this.debouncedChange = this.debounce_ms
10499
? debounce(this.change, this.debounce_ms, {
105100
leading: true,
@@ -122,16 +117,8 @@ export class Watcher extends EventEmitter {
122117
}
123118
}
124119

125-
private chokidarOptions = () => {
126-
return {
127-
...ChokidarOpts,
128-
interval: this.interval_ms,
129-
binaryInterval: this.interval_ms,
130-
};
131-
};
132-
133120
private initWatchContents(): void {
134-
this.watchContents = watch(this.path, this.chokidarOptions());
121+
this.watchContents = watch(this.path, ChokidarOpts);
135122
this.watchContents.on("all", this.debouncedChange);
136123
this.watchContents.on("error", (err) => {
137124
this.log(`error watching listings -- ${err}`);
@@ -140,7 +127,7 @@ export class Watcher extends EventEmitter {
140127

141128
private async initWatchExistence(): Promise<void> {
142129
const containing_path = path_split(this.path).head;
143-
this.watchExistence = watch(containing_path, this.chokidarOptions());
130+
this.watchExistence = watch(containing_path, ChokidarOpts);
144131
this.watchExistence.on("all", this.watchExistenceChange(containing_path));
145132
this.watchExistence.on("error", (err) => {
146133
this.log(`error watching for existence of ${this.path} -- ${err}`);

src/packages/backend/watcher.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@ export class Watcher extends EventEmitter {
4040

4141
constructor(
4242
path: string,
43-
{ debounce, interval = 300 }: { debounce?: number; interval?: number } = {},
43+
{ debounce, interval = 750 }: { debounce?: number; interval?: number } = {},
4444
) {
4545
super();
4646
this.path = path;

src/packages/database/postgres-server-queries.coffee

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -619,12 +619,14 @@ exports.extend_PostgreSQL = (ext) -> class PostgreSQL extends ext
619619
'account_id = $::UUID' : opts.account_id
620620
cb : opts.cb
621621

622+
# DEPRECATED: use import accountCreationActions from "@cocalc/server/accounts/account-creation-actions"; instead!!!!
622623
do_account_creation_actions: (opts) =>
623624
opts = defaults opts,
624625
email_address : required
625626
account_id : required
626627
cb : required
627628
dbg = @_dbg("do_account_creation_actions(email_address='#{opts.email_address}')")
629+
dbg("**DEPRECATED!** This will miss doing important things, e.g., creating initial project.")
628630
@account_creation_actions
629631
email_address : opts.email_address
630632
cb : (err, actions) =>

src/packages/frontend/account/account-page.tsx

Lines changed: 13 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -10,10 +10,10 @@ for different account related information
1010
and configuration.
1111
*/
1212

13+
import { useState } from "react";
1314
import { DownOutlined } from "@ant-design/icons";
1415
import { Button, Dropdown, MenuProps, Modal, Space, Tooltip } from "antd";
1516
import { useIntl } from "react-intl";
16-
1717
import { SignOut } from "@cocalc/frontend/account/sign-out";
1818
import { AntdTabItem, Col, Row, Tabs } from "@cocalc/frontend/antd-bootstrap";
1919
import {
@@ -86,6 +86,7 @@ export const AccountPage: React.FC = () => {
8686
const is_commercial = useTypedRedux("customize", "is_commercial");
8787
const get_api_key = useTypedRedux("page", "get_api_key");
8888
const i18n_enabled = useTypedRedux("customize", "i18n");
89+
const [langOpen, setLangOpen] = useState<boolean>(false);
8990

9091
// for each exclusive domain, tell the user which strategy to use
9192
const exclusive_sso_domains = React.useMemo(() => {
@@ -351,13 +352,21 @@ Thank you for your patience and understanding as we work to make our application
351352
{cur}
352353
<br />
353354
{msg}
354-
<br />({labels.account_language_tooltip.defaultMessage})
355+
{labels.account_language_tooltip.defaultMessage != msg ? (
356+
<>
357+
<br />({labels.account_language_tooltip.defaultMessage})
358+
</>
359+
) : undefined}
355360
</>
356361
);
357362

358363
return (
359-
<Tooltip title={tooltip} trigger={["hover"]}>
360-
<Dropdown menu={menu} trigger={["click"]}>
364+
<Tooltip title={langOpen ? undefined : tooltip} trigger={["hover"]}>
365+
<Dropdown
366+
menu={menu}
367+
trigger={["click"]}
368+
onOpenChange={(open) => setLangOpen(open)}
369+
>
361370
<Button>
362371
<Space>
363372
{lang_icon}

src/packages/frontend/admin/users/impersonate.tsx

Lines changed: 43 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -3,55 +3,54 @@
33
* License: MS-RSL – see LICENSE.md for details
44
*/
55

6-
import { Component, Rendered } from "@cocalc/frontend/app-framework";
7-
import { Loading } from "@cocalc/frontend/components";
8-
import { webapp_client } from "@cocalc/frontend/webapp-client";
6+
import { Alert } from "antd";
97
import { join } from "path";
8+
9+
import { Rendered, useEffect, useState } from "@cocalc/frontend/app-framework";
10+
import { Loading } from "@cocalc/frontend/components";
1011
import { appBasePath } from "@cocalc/frontend/customize/app-base-path";
11-
import { Alert } from "antd";
12+
import { webapp_client } from "@cocalc/frontend/webapp-client";
1213

1314
interface Props {
1415
account_id: string;
1516
first_name: string;
1617
last_name: string;
1718
}
1819

19-
interface State {
20-
auth_token?: string;
21-
err?: string;
22-
}
20+
export function Impersonate(props: Readonly<Props>) {
21+
const { first_name, last_name, account_id } = props;
2322

24-
export class Impersonate extends Component<Props, State> {
25-
constructor(props, state) {
26-
super(props, state);
27-
this.state = {};
28-
}
23+
const [auth_token, set_auth_token] = useState<string | null>(null);
24+
const [err, set_err] = useState<string | null>(null);
2925

30-
async get_token(): Promise<void> {
26+
async function get_token(): Promise<void> {
3127
try {
3228
const auth_token = await webapp_client.admin_client.get_user_auth_token(
33-
this.props.account_id
29+
account_id,
3430
);
35-
this.setState({ auth_token });
31+
set_auth_token(auth_token);
32+
set_err(null);
3633
} catch (err) {
37-
this.setState({ err: err.toString() });
34+
set_err(err.toString());
35+
set_auth_token(null);
3836
}
3937
}
4038

41-
componentDidMount(): void {
42-
this.get_token();
43-
}
39+
useEffect(() => {
40+
get_token();
41+
}, []);
4442

45-
render_link(): Rendered {
46-
if (this.state.auth_token == null) {
43+
function render_link(): Rendered {
44+
if (auth_token == null) {
4745
return <Loading />;
4846
}
49-
const link = join(appBasePath, `app?auth_token=${this.state.auth_token}`);
47+
// lang_temp: https://github.com/sagemathinc/cocalc/issues/7782
48+
const link = join(appBasePath, `app?auth_token=${auth_token}&lang_temp=en`);
5049
return (
5150
<div>
5251
<a href={link} target="_blank" rel="noopener noreferrer">
5352
Right click and open this link in a new incognito window, where you
54-
will be signed in as {this.props.first_name} {this.props.last_name}...
53+
will be signed in as {first_name} {last_name}...
5554
</a>
5655
<br />
5756
The actual link:
@@ -64,35 +63,31 @@ export class Impersonate extends Component<Props, State> {
6463
);
6564
}
6665

67-
render_err(): Rendered {
68-
if (this.state.err != null) {
66+
function render_err(): Rendered {
67+
if (err != null) {
6968
return (
7069
<div>
71-
<b>ERROR</b> {this.state.err}
70+
<b>ERROR</b> {err}
7271
</div>
7372
);
7473
}
7574
}
7675

77-
render(): Rendered {
78-
return (
79-
<Alert
80-
type="warning"
81-
style={{
82-
margin: "15px",
83-
}}
84-
message={
85-
<b>
86-
Impersonate user "{this.props.first_name} {this.props.last_name}"
87-
</b>
88-
}
89-
description={
90-
<>
91-
{this.render_err()}
92-
{this.render_link()}
93-
</>
94-
}
95-
/>
96-
);
97-
}
76+
return (
77+
<Alert
78+
type="warning"
79+
style={{ margin: "15px" }}
80+
message={
81+
<b>
82+
Impersonate user "{first_name} {last_name}"
83+
</b>
84+
}
85+
description={
86+
<>
87+
{render_err()}
88+
{render_link()}
89+
</>
90+
}
91+
/>
92+
);
9893
}

src/packages/frontend/app/localize.tsx

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -140,6 +140,12 @@ function loadAntdLocale(locale: Locale): Promise<AntdLocale> {
140140
return import("antd/locale/zh_CN");
141141
case "es":
142142
return import("antd/locale/es_ES");
143+
case "ru":
144+
return import("antd/locale/ru_RU");
145+
case "fr":
146+
return import("antd/locale/fr_FR");
147+
case "it":
148+
return import("antd/locale/it_IT");
143149
default:
144150
unreachable(locale);
145151
throw new Error(`Unknown locale '${locale}.`);

src/packages/frontend/app/render.tsx

Lines changed: 27 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -29,29 +29,43 @@ function App({ children }) {
2929

3030
// setting via ?lang=[locale] takes precedece over account settings
3131
useAsyncEffect(async () => {
32-
const lang = QueryParams.get("lang");
32+
const lang_set = QueryParams.get("lang");
33+
// lang_temp sets the language *temporarily*, i.e. without changing the account settings and it is sticky
34+
// this is useful for impersonation – https://github.com/sagemathinc/cocalc/issues/7782
35+
const lang_temp = QueryParams.get("lang_temp");
36+
const temp = lang_temp != null;
37+
const lang = temp ? lang_temp : lang_set;
3338
if (lang != null) {
3439
if (lang in LOCALIZATIONS) {
3540
console.warn(
36-
`URL query parameter 'lang=${lang}' – overriding user configuration.`,
41+
`URL query parameter 'lang=${lang}' – overriding user configuration ${
42+
temp ? "temporary" : "permanent"
43+
}.`,
3744
);
38-
const store = redux.getStore("account");
39-
// we have to ensure the account store is available, because this code runs very early
40-
await store.async_wait({
41-
until: () => store.get_account_id() != null,
42-
});
43-
redux
44-
.getActions("account")
45-
.set_other_settings(OTHER_SETTINGS_LOCALE_KEY, lang);
45+
if (!temp) {
46+
const store = redux.getStore("account");
47+
// we have to ensure the account store is available, because this code runs very early
48+
await store.async_wait({
49+
until: () => store.get_account_id() != null,
50+
});
51+
redux
52+
.getActions("account")
53+
.set_other_settings(OTHER_SETTINGS_LOCALE_KEY, lang);
54+
}
4655
setLocale(lang);
4756
} else {
4857
console.warn(
49-
`URL query parameter 'lang=${lang}' provided, but not a valid locale.`,
58+
`URL query parameter '${JSON.stringify({
59+
lang_set,
60+
lang_temp,
61+
})}' provided, but not a valid locale.`,
5062
`Known values: ${Object.keys(LOCALIZATIONS)}`,
5163
);
5264
}
53-
// removing the parameter, otherwise this conflicts with further changes of account settings
54-
QueryParams.remove("lang");
65+
if (!temp) {
66+
// removing the parameter, otherwise this conflicts with further changes of account settings
67+
QueryParams.remove("lang");
68+
}
5569
} else {
5670
setLocale(getLocale(other_settings));
5771
}

src/packages/frontend/chat/actions.ts

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -297,16 +297,16 @@ export class ChatActions extends Actions<ChatState> {
297297
search: "",
298298
});
299299
} else {
300-
// TODO: but until we fix search, do this:
300+
// TODO: but until we improve search to be by thread (instead of by message), do this:
301301
this.setState({
302302
search: "",
303303
});
304304
}
305305
this.ensureDraftStartsWithHashtags(false);
306306

307-
if (this.store) {
308-
const project_id = this.store.get("project_id");
309-
const path = this.store.get("path");
307+
if (this.store != null) {
308+
const project_id = this.store?.get("project_id");
309+
const path = this.store?.get("path");
310310
// set notification saying that we sent an actual chat
311311
let action;
312312
if (
@@ -570,7 +570,6 @@ export class ChatActions extends Actions<ChatState> {
570570
}
571571

572572
public set_uploading(is_uploading: boolean): void {
573-
console.log("set_uploading", is_uploading);
574573
this.setState({ is_uploading });
575574
}
576575

0 commit comments

Comments
 (0)