apply custom ownCloud desgin

This commit is contained in:
David Christofas
2022-06-29 18:10:56 +02:00
parent aba3b75f15
commit 0dcb90ee9d
15 changed files with 358 additions and 143 deletions

View File

@@ -30,9 +30,7 @@ ci-go-generate: # CI runs ci-node-generate automatically before this target
ci-node-generate: assets
.PHONY: assets
assets: i18n \
ui-images \
yarn-build \
assets: yarn-build \
assets/identifier/static \
assets/identifier/static/logo.svg \
assets/identifier/static/favicon.ico

View File

@@ -23,6 +23,7 @@
"i18next-browser-languagedetector": "^6.1.3",
"i18next-http-backend": "^1.3.2",
"i18next-resources-to-backend": "^1.0.0",
"kpop": "https://download.kopano.io/community/kapp:/kpop-2.2.0.tgz",
"query-string": "^7.1.1",
"react": "^17.0.2",
"react-dom": "^17.0.2",

View File

@@ -3,18 +3,16 @@
<head data-kopano-build="%REACT_APP_KOPANO_BUILD%">
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
<meta name="theme-color" content="#ffffff">
<meta name="theme-color" content="#1b223d">
<link rel="shortcut icon" href="%PUBLIC_URL%/static/favicon.ico" type="image/x-icon">
<link href="https://fonts.googleapis.com/css2?family=Open+Sans:wght@300;400;600&display=swap" rel="stylesheet">
<meta property="csp-nonce" content="__CSP_NONCE__">
<title>Sign in to your account</title>
<title>Sign in - ownCloud</title>
</head>
<body>
<noscript>
You need to enable JavaScript to run this app.
</noscript>
<div id="bg">
<div></div>
</div>
<div id="root" data-path-prefix="__PATH_PREFIX__"></div>
<main id="root" class="oc-login-bg" data-path-prefix="__PATH_PREFIX__" passwort-reset-link="__PASSWORD_RESET_LINK__"></main>
</body>
</html>

View File

@@ -1,15 +1,14 @@
import React, { Suspense, lazy } from 'react';
import { MuiThemeProvider } from '@material-ui/core/styles';
import {
CssBaseline,
} from '@material-ui/core';
import { defaultTheme as theme } from 'kpop/es/theme';
import 'kpop/static/css/base.css';
import 'kpop/static/css/scrollbar.css';
import './App.css';
import './fancy-background.css';
import Spinner from './components/Spinner';
import * as version from './version';
import theme from './theme';
const LazyMain = lazy(() => import(/* webpackChunkName: "identifier-main" */ './Main'));
@@ -18,7 +17,6 @@ console.info(`Kopano Identifier build version: ${version.build}`); // eslint-dis
const App = () => {
return (
<MuiThemeProvider theme={theme}>
<CssBaseline/>
<Suspense fallback={<Spinner/>}>
<LazyMain />
</Suspense>

View File

@@ -8,24 +8,14 @@ import { withStyles } from '@material-ui/core/styles';
import Routes from './Routes';
const styles = () => ({
root: {
position: 'relative',
display: 'flex',
flex: 1
}
});
class Main extends PureComponent {
render() {
const { classes, hello, pathPrefix } = this.props;
return (
<div className={classes.root}>
<BrowserRouter basename={pathPrefix}>
<Routes hello={hello}/>
</BrowserRouter>
</div>
<BrowserRouter basename={pathPrefix}>
<Routes hello={hello}/>
</BrowserRouter>
);
}
@@ -54,4 +44,4 @@ const mapStateToProps = (state) => {
};
};
export default connect(mapStateToProps)(withStyles(styles)(Main));
export default connect(mapStateToProps)(Main);

157
services/idp/ui/src/app.css Normal file
View File

@@ -0,0 +1,157 @@
/* additional css on top of kpop */
body {
font-family: 'Open Sans', sans-serif;
}
strong {
font-weight: 600;
}
.oc-login-bg {
background-image: url(./images/background.jpg);
background-size: cover;
background-repeat: no-repeat;
background-position: center;
z-index: 0;
}
#loader {
/* NOTE(longsleep): White here needed because of the background image */
color: white;
text-shadow: #000 0 0 1px;
}
.oc-logo {
position: absolute;
top: -130px;
left: 50%;
height: 80px;
transform: translateX(-50%);
}
.oc-progress {
/* Needs to be important to overwrite material-ui */
background-color: rgba(78, 133, 200, 0.8) !important;
height: 4px;
width: 100px;
}
.oc-progress > div {
/* Needs to be important to overwrite material-ui */
background-color: #4a76ac !important;
}
.oc-input {
background-color: #042047;
border: 1px solid rgba(78, 133, 200, 0.8);
border-radius: 3px;
color: rgba(255, 255, 255, 0.8);
height: 40px;
width: 300px;
padding: 16px;
box-sizing: border-box;
font-size: 1rem;
}
.oc-label {
color: #fff;
display: inline-block;
margin-bottom: 5px;
}
.oc-input.error {
outline: none;
border: 1px solid #fe4600;
}
.MuiTypography-colorError {
color: #fe4600 !important;
}
.oc-input:focus {
outline: none;
border: 1px solid #fff;
}
.oc-input::placeholder {
color: rgba(78, 133, 200, 0.8);
}
.oc-input + .oc-input {
margin-top: 15px;
}
.MuiTouchRipple-root {
display: none !important;
}
.oc-button {
/* Needs to be important to overwrite material-ui */
font-size: 1.0625rem !important;
}
.oc-button-primary {
/* Needs to be important to overwrite material-ui */
background-color: #4a76ac !important;
border: 1px solid transparent !important;
}
.oc-button-primary:hover,
.oc-button-primary:focus {
/* Needs to be important to overwrite material-ui */
background-color: #4a76ac !important;
border: 1px solid white !important;
}
.oc-checkbox-dark svg {
/* Needs to be important to overwrite material-ui */
fill: white !important;
}
.oc-footer-message {
color: white;
padding: 10px;
font-size: 0.8rem;
}
@media only screen and (max-width: 768px) {
.oc-logo {
height: 60px;
top: -90px;
}
}
/* Helpers */
.oc-mt-l {
margin-top: 30px !important;
}
.oc-mb-m {
margin-bottom: 20px !important;
}
.oc-light {
color: #fff !important;
}
.oc-login-form div:not(:last-of-type) {
margin-bottom: 15px;
}
/*
* Special SR classes
* Used to hide an element visually, but keeping it accessible for accessibility tools.
*/
.oc-invisible-sr {
border: 0 !important;
clip: rect(1px, 1px, 1px, 1px) !important;
height: 1px !important;
overflow: hidden !important;
padding: 0 !important;
/* Need to make sure we override any existing styles. */
position: absolute !important;
top: 0;
white-space: nowrap;
width: 1px !important;
}

View File

@@ -4,7 +4,6 @@ import { connect } from 'react-redux';
import { withTranslation } from 'react-i18next';
import { withStyles } from '@material-ui/core/styles';
import LinearProgress from '@material-ui/core/LinearProgress';
import Grid from '@material-ui/core/Grid';
import Typography from '@material-ui/core/Typography';
@@ -14,47 +13,28 @@ import renderIf from 'render-if';
import { retryHello } from '../actions/common';
import { ErrorMessage } from '../errors';
const styles = theme => ({
root: {
flexGrow: 1,
position: 'absolute',
top: 0,
bottom: 0,
left: 0,
right: 0
},
progress: {
height: '4px',
width: '100px'
},
button: {
marginTop: theme.spacing(5)
}
});
class Loading extends React.PureComponent {
render() {
const { classes, error, t } = this.props;
return (
<Grid container direction="column" alignItems="center" justifyContent="center" spacing={0} className={classes.root}>
<Grid item align="center">
{renderIf(error === null)(() => (
<LinearProgress className={classes.progress} />
<LinearProgress className="oc-progress" />
))}
{renderIf(error !== null)(() => (
<div>
<Typography variant="h5" gutterBottom align="center">
<Typography className="oc-light" variant="h5" gutterBottom align="center">
{t("konnect.loading.error.headline", "Failed to connect to server")}
</Typography>
<Typography gutterBottom align="center" color="error">
<Typography align="center" color="error">
<ErrorMessage error={error}></ErrorMessage>
</Typography>
<Button
autoFocus
color="primary"
variant="outlined"
className={classes.button}
className="oc-button-primary oc-mt-l"
onClick={(event) => this.retry(event)}
>
{t("konnect.login.retryButton.label", "Retry")}
@@ -62,7 +42,6 @@ class Loading extends React.PureComponent {
</div>
))}
</Grid>
</Grid>
);
}
@@ -90,4 +69,4 @@ const mapStateToProps = (state) => {
};
};
export default connect(mapStateToProps)(withTranslation()(withStyles(styles)(Loading)));
export default connect(mapStateToProps)(withTranslation()(Loading));

View File

@@ -2,6 +2,8 @@ import React from 'react';
import PropTypes from 'prop-types';
import classNames from 'classnames';
import { Trans } from 'react-i18next';
import { withStyles } from '@material-ui/core/styles';
import Grid from '@material-ui/core/Grid';
import DialogActions from '@material-ui/core/DialogActions';
@@ -18,25 +20,21 @@ const styles = theme => ({
flex: 1,
},
content: {
paddingTop: 24,
paddingBottom: 12,
minHeight: 350,
paddingLeft: theme.spacing(2),
paddingRight: theme.spacing(2),
position: 'relative'
},
dialog: {
maxWidth: 440,
},
logo: {
height: 24,
position: 'relative',
width: '100%'
},
actions: {
marginTop: -40,
minHeight: 45,
justifyContent: 'flex-start',
paddingLeft: theme.spacing(3),
paddingRight: theme.spacing(3)
},
wrapper: {
width: '100%',
maxWidth: 300,
display: 'flex',
flex: 1,
alignItems: 'center'
}
});
@@ -56,26 +54,22 @@ const ResponsiveScreen = (props) => {
const bannerLogoSrc = branding?.bannerLogo ? branding.bannerLogo : Logo;
const logo = withoutLogo ? null :
<DialogContent><img src={bannerLogoSrc} className={classes.logo} alt=""/></DialogContent>;
<img src={process.env.PUBLIC_URL + '/static/logo.svg'} className="oc-logo" alt="ownCloud Logo"/>;
const content = loading ? <Loading/> : (withoutPadding ? children : <DialogContent>{children}</DialogContent>);
return (
<Grid container justifyContent="center" alignItems="center" spacing={0}
<Grid container justifyContent="center" alignItems="center" direction="column" spacing={0}
className={classNames(classes.root, className)} {...other}>
<ResponsiveDialog open fullWidth maxWidth="sm" disableEscapeKeyDown hideBackdrop
{...DialogProps}
PaperProps={{elevation: 4, ...PaperProps}}
classes={{
paperWidthSm: classes.dialog,
}}
>
<div className={classes.content}>
{logo}
{content}
<div className={classes.wrapper}>
<div className={classes.content}>
{logo}
{content}
</div>
</div>
{!loading && <DialogActions className={classes.actions} disableSpacing><LocaleSelect disableUnderline locales={branding?.locales}/></DialogActions>}
</ResponsiveDialog>
<footer className="oc-footer-message">
<Trans i18nKey="konnect.footer.slogan"><strong>ownCloud</strong> - a safe home for all your data</Trans>
</footer>
</Grid>
);
};

View File

@@ -0,0 +1,28 @@
import React from 'react';
import PropTypes from 'prop-types';
const TextInput = (props) => {
const label = props.label;
const extraClassName = props.extraClassName;
delete props.label;
delete props.extraClassName;
return (
<div>
<label className="oc-label"
htmlFor={props.id}>{label}</label>
<input className={`oc-input ${extraClassName ? extraClassName : ''}`} {...props}
placeholder={props.placeholder ? props.placeholder : null}/>
</div>);
};
TextInput.propTypes = {
placeholder: PropTypes.object,
label: PropTypes.object,
id: PropTypes.string,
extraClassName: props.string,
}
export default TextInput;

View File

@@ -15,14 +15,12 @@ import Typography from '@material-ui/core/Typography';
import DialogActions from '@material-ui/core/DialogActions';
import DialogContent from '@material-ui/core/DialogContent';
import TextInput from '../../components/TextInput'
import { updateInput, executeLogonIfFormValid, advanceLogonFlow } from '../../actions/login';
import { ErrorMessage } from '../../errors';
const styles = theme => ({
button: {
margin: theme.spacing(1),
minWidth: 100
},
buttonProgress: {
color: green[500],
position: 'absolute',
@@ -36,14 +34,12 @@ const styles = theme => ({
},
wrapper: {
position: 'relative',
display: 'inline-block'
},
slideContainer: {
overflowX: 'hidden',
width: '100%',
textAlign: 'center'
},
message: {
marginTop: theme.spacing(2),
marginBottom: theme.spacing(2)
marginTop: 5,
marginBottom: 5
}
});
@@ -61,6 +57,29 @@ function Login(props) {
} = props;
const { t } = useTranslation();
const loginFailed = errors.http;
const hasError = errors.http || errors.username || errors.password;
const errorMessage = errors.http
? <ErrorMessage error={errors.http}></ErrorMessage>
: (errors.username
? <ErrorMessage error={errors.username}></ErrorMessage>
: <ErrorMessage error={errors.password}></ErrorMessage>);
const extraPropsUsername = {
"aria-invalid" : (errors.username || errors.http) ? 'true' : 'false'
};
const extraPropsPassword = {
"aria-invalid" : (errors.password || errors.http) ? 'true' : 'false',
};
if(errors.username || errors.http){
extraPropsUsername['extraClassName'] = 'error';
extraPropsUsername['aria-describedby'] = 'oc-login-error-message';
}
if(errors.password || errors.http){
extraPropsPassword['extraClassName'] = 'error';
extraPropsPassword['aria-describedby'] = 'oc-login-error-message';
}
useEffect(() => {
if (hello && hello.state && history.action !== 'PUSH') {
@@ -106,62 +125,48 @@ function Login(props) {
}, [hello, t]);
return (
<DialogContent>
<Typography variant="h5" component="h3" gutterBottom>
{t("konnect.login.headline", "Sign in")}
</Typography>
<form action="" onSubmit={(event) => this.logon(event)}>
<TextField
placeholder={usernamePlaceHolder}
error={!!errors.username}
helperText={<ErrorMessage error={errors.username} values={{what: usernamePlaceHolder}}></ErrorMessage>}
fullWidth
margin="dense"
autoFocus
inputProps={{
autoCapitalize: 'off',
spellCheck: 'false'
}}
value={username}
onChange={handleChange('username')}
autoComplete="kopano-account username"
/>
<TextField
type="password"
placeholder={t("konnect.login.passwordField.label", "Password")}
error={!!errors.password}
helperText={<ErrorMessage error={errors.password}></ErrorMessage>}
fullWidth
margin="dense"
onChange={handleChange('password')}
autoComplete="kopano-account current-password"
/>
<DialogActions>
<div>
<h1 className="oc-invisible-sr"> Login </h1>
<form action="" className="oc-login-form" onSubmit={(event) => handleNextClick(event)}>
<TextInput
autoFocus
autoCapitalize="off"
spellCheck="false"
value={username}
onChange={handleChange('username')}
autoComplete="kopano-account username"
placeholder={t("konnect.login.usernameField.label", "Username")}
label={t("konnect.login.usernameField.label", "Username")}
id="oc-login-username"
{...extraPropsUsername}
/>
<TextInput
type="password"
margin="normal"
onChange={handleChange('password')}
autoComplete="kopano-account current-password"
placeholder={t("konnect.login.usernameField.label", "Password")}
label={t("konnect.login.usernameField.label", "Password")}
id="oc-login-password"
{...extraPropsPassword}
/>
{hasError && <Typography id="oc-login-error-message" variant="subtitle2" component="span" color="error" className={classes.message}>{errorMessage}</Typography>}
<div className={classes.wrapper}>
<br />
<Button
type="submit"
color="primary"
variant="contained"
className={classes.button}
className="oc-button-primary oc-mt-l"
disabled={!!loading}
onClick={handleNextClick}
>
{t("konnect.login.nextButton.label", "Next")}
{t("konnect.login.nextButton.label", "Log in")}
</Button>
{loading && <CircularProgress size={24} className={classes.buttonProgress} />}
</div>
</DialogActions>
{renderIf(errors.http)(() => (
<Typography variant="subtitle2" color="error" className={classes.message}>
<ErrorMessage error={errors.http}></ErrorMessage>
</Typography>
))}
{hello?.details?.branding?.signinPageText && <Typography variant="body2">{hello.details.branding.signinPageText}</Typography>}
</form>
</DialogContent>
</div>
);
}

View File

@@ -12,6 +12,6 @@
background-size: cover;
background-repeat: no-repeat;
background-position: center;
background-image: url(./images/loginscreen-bg.svg);
background-image: url(./images/background.jpg);
z-index: 0;
}

View File

Binary file not shown.

After

Width:  |  Height:  |  Size: 86 KiB

View File

@@ -2,16 +2,13 @@ import React from 'react';
import ReactDOM from 'react-dom';
import { Provider } from 'react-redux';
import '@fontsource/roboto/300.css';
import '@fontsource/roboto/400.css';
import '@fontsource/roboto/500.css';
import '@fontsource/roboto/700.css';
import './i18n';
import App from './App';
import store from './store';
import './app.css';
ReactDOM.render(
<React.StrictMode>
<Provider store={store as any}>

View File

@@ -28,7 +28,7 @@ const theme = createMuiTheme({
tonalOffset: 0.2,
},
typography: {
fontSize: 12,
fontSize: 16,
useNextVariants: true,
button: {
textTransform: 'none',

View File

@@ -6133,6 +6133,23 @@ __metadata:
languageName: node
linkType: hard
"cldr@npm:^5.2.0":
version: 5.8.0
resolution: "cldr@npm:5.8.0"
dependencies:
escodegen: ^2.0.0
esprima: ^4.0.1
memoizeasync: ^1.1.0
passerror: ^1.1.1
pegjs: ^0.10.0
seq: ^0.3.5
unicoderegexp: ^0.4.1
xmldom: ^0.4.0
xpath: ^0.0.32
checksum: 614f7867382c6958b8440523911dac8f4f664c64916da391ee666060af03e17d664f5adff93dd3ec1cf7386be2bf1cbb1e934fce896d71243efe2229139a1aa3
languageName: node
linkType: hard
"cldr@npm:^7.1.1":
version: 7.2.0
resolution: "cldr@npm:7.2.0"
@@ -6672,6 +6689,15 @@ __metadata:
languageName: node
linkType: hard
"crc32@npm:^0.2.2":
version: 0.2.2
resolution: "crc32@npm:0.2.2"
bin:
crc32: ./bin/runner.js
checksum: e4301b6bfbc081dc44a02246657581a8df27c42528bfb4879d0c93afca68040ffd4d87eb8b483df04ee8dd4ad6538844512169156be69b1a4cec23c48b81881d
languageName: node
linkType: hard
"create-ecdh@npm:^4.0.0":
version: 4.0.4
resolution: "create-ecdh@npm:4.0.4"
@@ -9697,6 +9723,13 @@ __metadata:
languageName: node
linkType: hard
"hsv-rgb@npm:^1.0.0":
version: 1.0.0
resolution: "hsv-rgb@npm:1.0.0"
checksum: eb1f616bd43dceb9ed322167d8007747c075e9429cfbf81721a524d0454c68140b4593a5f97d0b215d61e2ddca722edc856734fcc3e10f37fc4b770ca526eb38
languageName: node
linkType: hard
"html-encoding-sniffer@npm:^2.0.1":
version: 2.0.1
resolution: "html-encoding-sniffer@npm:2.0.1"
@@ -10056,6 +10089,7 @@ __metadata:
i18next-http-backend: ^1.3.2
i18next-parser: ^5.4.0
i18next-resources-to-backend: ^1.0.0
kpop: "https://download.kopano.io/community/kapp:/kpop-2.2.0.tgz"
query-string: ^7.1.1
react: ^17.0.2
react-dev-utils: ^11.0.4
@@ -10903,6 +10937,13 @@ __metadata:
languageName: node
linkType: hard
"iso-639-1@npm:^2.0.5":
version: 2.1.15
resolution: "iso-639-1@npm:2.1.15"
checksum: a201530819d33e9ce077b02c786d67b35be5d823be27b5aacc18d880e580e559e39ec5055f8e2abcdc210f46618a643bf3c74e6fe6e8255fe62059682750e595
languageName: node
linkType: hard
"isobject@npm:^2.0.0":
version: 2.1.0
resolution: "isobject@npm:2.1.0"
@@ -11893,6 +11934,28 @@ __metadata:
languageName: node
linkType: hard
"kpop@https://download.kopano.io/community/kapp:/kpop-2.2.0.tgz":
version: 2.2.0
resolution: "kpop@https://download.kopano.io/community/kapp:/kpop-2.2.0.tgz"
dependencies:
cldr: ^5.2.0
crc32: ^0.2.2
hsv-rgb: ^1.0.0
iso-639-1: ^2.0.5
peerDependencies:
"@gluejs/glue": ^0.3.0
"@material-ui/core": ^4.8.0
"@material-ui/icons": ^4.5.1
notistack: ^0.8.8
oidc-client: ^1.9.1
react: ^16.8.0
react-dom: ^16.8.0
react-intl: ^2.6.0
render-if: ^0.1.1
checksum: 37e981756f26f396f0d1a6e9acb87eb4a4dc6ad6272f84860a236da27cea152e029daffb81e04307eba366cf4279d18a19cba50ddfdcc38d66d060593caa50ce
languageName: node
linkType: hard
"language-subtag-registry@npm:~0.3.2":
version: 0.3.21
resolution: "language-subtag-registry@npm:0.3.21"
@@ -19060,6 +19123,13 @@ __metadata:
languageName: node
linkType: hard
"xmldom@npm:^0.4.0":
version: 0.4.0
resolution: "xmldom@npm:0.4.0"
checksum: ea975a7bd87109542ce24b19a70e50a3d64d20c757affa1a6c8cf906cbb39f0071a1bea9016beb5532388bc61dcbefddb92f3db2197dceee2b4c46343348ab3c
languageName: node
linkType: hard
"xpath@npm:^0.0.32":
version: 0.0.32
resolution: "xpath@npm:0.0.32"