Import users from CSV
Change-Id: Id05363ecc39aee4fdc4ac6afbcb039558b2a17ed
This commit is contained in:
		
							parent
							
								
									7b5c0e2845
								
							
						
					
					
						commit
						1aaa137afe
					
				| @ -40,6 +40,7 @@ const App = () => ( | |||||||
|     dataProvider={dataProvider} |     dataProvider={dataProvider} | ||||||
|     i18nProvider={i18nProvider} |     i18nProvider={i18nProvider} | ||||||
|     customRoutes={[ |     customRoutes={[ | ||||||
|  |       <Route key="csvImport" path="/importcsv" component={ImportFeature} />, | ||||||
|       <Route key="showpdf" path="/showpdf" component={ShowUserPdf} />, |       <Route key="showpdf" path="/showpdf" component={ShowUserPdf} />, | ||||||
|     ]} |     ]} | ||||||
|   > |   > | ||||||
|  | |||||||
| @ -20,6 +20,7 @@ import { | |||||||
| import { useTranslate } from "ra-core"; | import { useTranslate } from "ra-core"; | ||||||
| import Container from "@material-ui/core/Container/Container"; | import Container from "@material-ui/core/Container/Container"; | ||||||
| import { generateRandomUser } from "./users"; | import { generateRandomUser } from "./users"; | ||||||
|  | import ShowUserPdf from "./ShowUserPdf"; | ||||||
| 
 | 
 | ||||||
| const LOGGING = true; | const LOGGING = true; | ||||||
| 
 | 
 | ||||||
| @ -59,6 +60,8 @@ const FilePicker = props => { | |||||||
| 
 | 
 | ||||||
|   const [progress, setProgress] = useState(null); |   const [progress, setProgress] = useState(null); | ||||||
| 
 | 
 | ||||||
|  |   const [pdfRecords, setPdfRecords] = useState(null); | ||||||
|  | 
 | ||||||
|   const [importResults, setImportResults] = useState(null); |   const [importResults, setImportResults] = useState(null); | ||||||
|   const [skippedRecords, setSkippedRecords] = useState(null); |   const [skippedRecords, setSkippedRecords] = useState(null); | ||||||
| 
 | 
 | ||||||
| @ -66,17 +69,23 @@ const FilePicker = props => { | |||||||
|   const [passwordMode, setPasswordMode] = useState(true); |   const [passwordMode, setPasswordMode] = useState(true); | ||||||
|   const [useridMode, setUseridMode] = useState("ignore"); |   const [useridMode, setUseridMode] = useState("ignore"); | ||||||
| 
 | 
 | ||||||
|  |   const [showingPdf, setShowingPdf] = useState(false); | ||||||
|  | 
 | ||||||
|   const translate = useTranslate(); |   const translate = useTranslate(); | ||||||
|   const notify = useNotify(); |   const notify = useNotify(); | ||||||
| 
 | 
 | ||||||
|   const dataProvider = useDataProvider(); |   const dataProvider = useDataProvider(); | ||||||
| 
 | 
 | ||||||
|   const onFileChange = async e => { |   const onFileChange = async e => { | ||||||
|     if (progress !== null) return; |     if (progress !== null) { | ||||||
| 
 |       return; | ||||||
|  |     } | ||||||
|  |     if (LOGGING) console.log("onFileChange was called"); | ||||||
|     setValues(null); |     setValues(null); | ||||||
|     setError(null); |     setError(null); | ||||||
|     setStats(null); |     setStats(null); | ||||||
|  |     setPdfRecords(null); | ||||||
|  | 
 | ||||||
|     setImportResults(null); |     setImportResults(null); | ||||||
|     const file = e.target.files ? e.target.files[0] : null; |     const file = e.target.files ? e.target.files[0] : null; | ||||||
|     /* Let's refuse some unreasonably big files instead of freezing |     /* Let's refuse some unreasonably big files instead of freezing | ||||||
| @ -126,6 +135,11 @@ const FilePicker = props => { | |||||||
|     }); |     }); | ||||||
| 
 | 
 | ||||||
|     if (eF.length !== 0) { |     if (eF.length !== 0) { | ||||||
|  |       if (LOGGING) { | ||||||
|  |         console.log(meta.fields); | ||||||
|  |         console.log(eF); | ||||||
|  |         console.log(oF); | ||||||
|  |       } | ||||||
|       setError( |       setError( | ||||||
|         translate("import_users.error.required_field", { field: eF[0] }) |         translate("import_users.error.required_field", { field: eF[0] }) | ||||||
|       ); |       ); | ||||||
| @ -226,6 +240,9 @@ const FilePicker = props => { | |||||||
|       setProgress, |       setProgress, | ||||||
|       setError |       setError | ||||||
|     ); |     ); | ||||||
|  | 
 | ||||||
|  |     setPdfRecords(results.recordsForPdf); | ||||||
|  | 
 | ||||||
|     setImportResults(results); |     setImportResults(results); | ||||||
|     // offer CSV download of skipped or errored records
 |     // offer CSV download of skipped or errored records
 | ||||||
|     // (so that the user doesn't have to filter out successful
 |     // (so that the user doesn't have to filter out successful
 | ||||||
| @ -251,6 +268,8 @@ const FilePicker = props => { | |||||||
|     let skippedRecords = []; |     let skippedRecords = []; | ||||||
|     let erroredRecords = []; |     let erroredRecords = []; | ||||||
|     let succeededRecords = []; |     let succeededRecords = []; | ||||||
|  |     let recordsForPdf = []; | ||||||
|  | 
 | ||||||
|     let changeStats = { |     let changeStats = { | ||||||
|       toAdmin: 0, |       toAdmin: 0, | ||||||
|       toGuest: 0, |       toGuest: 0, | ||||||
| @ -365,6 +384,14 @@ const FilePicker = props => { | |||||||
|                 await dataProvider.create("users", { data: recordData }); |                 await dataProvider.create("users", { data: recordData }); | ||||||
|               } |               } | ||||||
|               succeededRecords.push(recordData); |               succeededRecords.push(recordData); | ||||||
|  | 
 | ||||||
|  |               if (recordData.password !== undefined) { | ||||||
|  |                 recordsForPdf.push({ | ||||||
|  |                   id: recordData.id, | ||||||
|  |                   password: recordData.password, | ||||||
|  |                   displayname: recordData.displayname, | ||||||
|  |                 }); | ||||||
|  |               } | ||||||
|             } |             } | ||||||
|           ); |           ); | ||||||
|         }; |         }; | ||||||
| @ -389,6 +416,7 @@ const FilePicker = props => { | |||||||
|       erroredRecords, |       erroredRecords, | ||||||
|       succeededRecords, |       succeededRecords, | ||||||
|       totalRecordCount: entriesCount, |       totalRecordCount: entriesCount, | ||||||
|  |       recordsForPdf, | ||||||
|       changeStats, |       changeStats, | ||||||
|       wasDryRun: dryRun, |       wasDryRun: dryRun, | ||||||
|     }; |     }; | ||||||
| @ -618,6 +646,10 @@ const FilePicker = props => { | |||||||
|               <br />, |               <br />, | ||||||
|             ] |             ] | ||||||
|           : ""} |           : ""} | ||||||
|  |         {translate( | ||||||
|  |           "import_users.cards.results.for_print", | ||||||
|  |           importResults.recordsForPdf.length | ||||||
|  |         )} | ||||||
|         <br /> |         <br /> | ||||||
|         {importResults.wasDryRun && [ |         {importResults.wasDryRun && [ | ||||||
|           translate("import_users.cards.results.simulated_only"), |           translate("import_users.cards.results.simulated_only"), | ||||||
| @ -655,6 +687,27 @@ const FilePicker = props => { | |||||||
|       </CardActions> |       </CardActions> | ||||||
|     ); |     ); | ||||||
| 
 | 
 | ||||||
|  |   let pdfDisplay = | ||||||
|  |     pdfRecords && showingPdf && pdfRecords.length ? ( | ||||||
|  |       <ShowUserPdf records={pdfRecords} /> | ||||||
|  |     ) : null; | ||||||
|  | 
 | ||||||
|  |   let pdfActions = pdfRecords ? ( | ||||||
|  |     <CardActions> | ||||||
|  |       <Button | ||||||
|  |         size="large" | ||||||
|  |         onClick={e => { | ||||||
|  |           setShowingPdf(true); | ||||||
|  |         }} | ||||||
|  |       > | ||||||
|  |         {translate("import_users.goToPdf")} | ||||||
|  |       </Button> | ||||||
|  |     </CardActions> | ||||||
|  |   ) : null; | ||||||
|  | 
 | ||||||
|  |   if (pdfRecords && showingPdf) { | ||||||
|  |     return <Card>{pdfDisplay}</Card>; | ||||||
|  |   } else { | ||||||
|     let allCards = []; |     let allCards = []; | ||||||
|     if (uploadCard) allCards.push(uploadCard); |     if (uploadCard) allCards.push(uploadCard); | ||||||
|     if (errorCards) allCards.push(errorCards); |     if (errorCards) allCards.push(errorCards); | ||||||
| @ -662,6 +715,7 @@ const FilePicker = props => { | |||||||
|     if (statsCards) allCards.push(...statsCards); |     if (statsCards) allCards.push(...statsCards); | ||||||
|     if (startImportCard) allCards.push(startImportCard); |     if (startImportCard) allCards.push(startImportCard); | ||||||
|     if (resultsCard) allCards.push(resultsCard); |     if (resultsCard) allCards.push(resultsCard); | ||||||
|  |     if (pdfActions) allCards.push(pdfActions); | ||||||
| 
 | 
 | ||||||
|     let cardContainer = <Card>{allCards}</Card>; |     let cardContainer = <Card>{allCards}</Card>; | ||||||
| 
 | 
 | ||||||
| @ -669,6 +723,7 @@ const FilePicker = props => { | |||||||
|       <Title defaultTitle={translate("import_users.title")} />, |       <Title defaultTitle={translate("import_users.title")} />, | ||||||
|       cardContainer, |       cardContainer, | ||||||
|     ]; |     ]; | ||||||
|  |   } | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| export const ImportFeature = FilePicker; | export const ImportFeature = FilePicker; | ||||||
|  | |||||||
| @ -1,8 +1,9 @@ | |||||||
| import React from "react"; | import React, { useRef } from "react"; | ||||||
| import { Title, Button } from "react-admin"; | import { Title, Button } from "react-admin"; | ||||||
| import { makeStyles } from "@material-ui/core/styles"; | import { makeStyles } from "@material-ui/core/styles"; | ||||||
| import { PDFExport } from "@progress/kendo-react-pdf"; | import { PDFExport } from "@progress/kendo-react-pdf"; | ||||||
| import QRCode from "qrcode.react"; | import QRCode from "qrcode.react"; | ||||||
|  | import { string, any } from "prop-types"; | ||||||
| 
 | 
 | ||||||
| function xor(a, b) { | function xor(a, b) { | ||||||
|   var res = ""; |   var res = ""; | ||||||
| @ -14,15 +15,113 @@ function xor(a, b) { | |||||||
| 
 | 
 | ||||||
| function calculateQrString(serverUrl, username, password) { | function calculateQrString(serverUrl, username, password) { | ||||||
|   const magicString = "wo9k5tep252qxsa5yde7366kugy6c01w7oeeya9hrmpf0t7ii7"; |   const magicString = "wo9k5tep252qxsa5yde7366kugy6c01w7oeeya9hrmpf0t7ii7"; | ||||||
|   var urlString = "user=" + username + "&password=" + password; |   const origUrlString = "user=" + username + "&password=" + password; | ||||||
| 
 | 
 | ||||||
|   urlString = xor(urlString, magicString); // xor with magic string
 |   var urlString = xor(origUrlString, magicString); // xor with magic string
 | ||||||
|  |   if (origUrlString !== xor(urlString, magicString)) { | ||||||
|  |     console.error( | ||||||
|  |       "xoring this url string with magicString twice gave different results:", | ||||||
|  |       origUrlString, | ||||||
|  |       urlString, | ||||||
|  |       xor(urlString, magicString) | ||||||
|  |     ); | ||||||
|  |   } | ||||||
|   urlString = btoa(urlString); // to base64
 |   urlString = btoa(urlString); // to base64
 | ||||||
| 
 | 
 | ||||||
|   return serverUrl + "/#" + urlString; |   return serverUrl + "/#" + urlString; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| const ShowUserPdf = props => { | UserPdfPage.propTypes = { | ||||||
|  |   classes: any, | ||||||
|  |   displayname: string, | ||||||
|  |   qrCode: any, | ||||||
|  |   serverUrl: string, | ||||||
|  |   username: string, | ||||||
|  |   password: string, | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | function UserPdfPage({ | ||||||
|  |   classes, | ||||||
|  |   displayname, | ||||||
|  |   qrCode, | ||||||
|  |   serverUrl, | ||||||
|  |   username, | ||||||
|  |   password, | ||||||
|  | }) { | ||||||
|  |   return ( | ||||||
|  |     <div className={classes.page}> | ||||||
|  |       <div className={classes.header}> | ||||||
|  |         <div className={classes.name}>{displayname}</div> | ||||||
|  |         <img className={classes.logo} alt="Logo" src="images/logo.png" /> | ||||||
|  |       </div> | ||||||
|  |       <div className={classes.body}> | ||||||
|  |         <table> | ||||||
|  |           <tbody> | ||||||
|  |             <tr> | ||||||
|  |               <td width="200px"> | ||||||
|  |                 <div className={classes.code_note}> | ||||||
|  |                   Ihr persönlicher Anmeldecode: | ||||||
|  |                 </div> | ||||||
|  |               </td> | ||||||
|  |               <td className={classes.table_cell}> | ||||||
|  |                 <div className={classes.credentials_note}> | ||||||
|  |                   Ihre persönlichen Zugangsdaten: | ||||||
|  |                 </div> | ||||||
|  |               </td> | ||||||
|  |             </tr> | ||||||
|  |             <tr> | ||||||
|  |               <td> | ||||||
|  |                 <div className={classes.qr}>{qrCode}</div> | ||||||
|  |               </td> | ||||||
|  |               <td className={classes.table_cell}> | ||||||
|  |                 <div className={classes.credentials_text}> | ||||||
|  |                   <br /> | ||||||
|  |                   <table> | ||||||
|  |                     <tbody> | ||||||
|  |                       <tr> | ||||||
|  |                         <td>Heimserver:</td> | ||||||
|  |                         <td> | ||||||
|  |                           <span className={classes.credentials}> | ||||||
|  |                             {serverUrl} | ||||||
|  |                           </span> | ||||||
|  |                         </td> | ||||||
|  |                       </tr> | ||||||
|  |                       <tr> | ||||||
|  |                         <td>Benutzername:</td> | ||||||
|  |                         <td> | ||||||
|  |                           <span className={classes.credentials}> | ||||||
|  |                             {username} | ||||||
|  |                           </span> | ||||||
|  |                         </td> | ||||||
|  |                       </tr> | ||||||
|  |                       <tr> | ||||||
|  |                         <td>Passwort:</td> | ||||||
|  |                         <td> | ||||||
|  |                           <span className={classes.credentials}> | ||||||
|  |                             {password} | ||||||
|  |                           </span> | ||||||
|  |                         </td> | ||||||
|  |                       </tr> | ||||||
|  |                     </tbody> | ||||||
|  |                   </table> | ||||||
|  |                 </div> | ||||||
|  |               </td> | ||||||
|  |             </tr> | ||||||
|  |           </tbody> | ||||||
|  |         </table> | ||||||
|  |         <div className={classes.note}> | ||||||
|  |           Hier können Sie Ihre selbst gewählte Schlüsselsicherungs-Passphrase | ||||||
|  |           notieren: | ||||||
|  |           <br /> | ||||||
|  |           <br /> | ||||||
|  |           <br /> | ||||||
|  |           <hr /> | ||||||
|  |         </div> | ||||||
|  |       </div> | ||||||
|  |     </div> | ||||||
|  |   ); | ||||||
|  | } | ||||||
|  | 
 | ||||||
| const useStyles = makeStyles(theme => ({ | const useStyles = makeStyles(theme => ({ | ||||||
|   page: { |   page: { | ||||||
|     height: 800, |     height: 800, | ||||||
| @ -87,36 +186,33 @@ const ShowUserPdf = props => { | |||||||
|   }, |   }, | ||||||
| })); | })); | ||||||
| 
 | 
 | ||||||
|  | const ShowUserPdf = props => { | ||||||
|   const classes = useStyles(); |   const classes = useStyles(); | ||||||
| 
 |   const userPdf = useRef(null); | ||||||
|   var resume; |  | ||||||
| 
 | 
 | ||||||
|   const exportPDF = () => { |   const exportPDF = () => { | ||||||
|     resume.save(); |     userPdf.current.save(); | ||||||
|   }; |   }; | ||||||
| 
 | 
 | ||||||
|   var qrCode = ""; |   let userRecords; | ||||||
|   var displayname = ""; | 
 | ||||||
|   var id = ""; |   if (props.records) { | ||||||
|   var password = ""; |     userRecords = props.records; | ||||||
|   var username = ""; |   } | ||||||
|   var serverUrl = ""; |  | ||||||
| 
 | 
 | ||||||
|   if ( |   if ( | ||||||
|  |     props.location && | ||||||
|     props.location.state && |     props.location.state && | ||||||
|     props.location.state.id && |     props.location.state.id && | ||||||
|     props.location.state.password |     props.location.state.password | ||||||
|   ) { |   ) { | ||||||
|     id = props.location.state.id; |     userRecords = [ | ||||||
|     password = props.location.state.password; |       { | ||||||
| 
 |         id: props.location.state.id, | ||||||
|     username = id.substring(1, id.indexOf(":")); |         password: props.location.state.password, | ||||||
|     serverUrl = "https://" + id.substring(id.indexOf(":") + 1); |         displayname: props.location.state.displayname, | ||||||
| 
 |       }, | ||||||
|     const qrString = calculateQrString(serverUrl, username, password); |     ]; | ||||||
| 
 |  | ||||||
|     qrCode = <QRCode value={qrString} size={128} />; |  | ||||||
|     displayname = props.location.state.displayname; |  | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   return ( |   return ( | ||||||
| @ -126,82 +222,39 @@ const ShowUserPdf = props => { | |||||||
| 
 | 
 | ||||||
|       <PDFExport |       <PDFExport | ||||||
|         paperSize={"A4"} |         paperSize={"A4"} | ||||||
|         fileName="User.pdf" |         fileName="Users.pdf" | ||||||
|         title="" |         title="" | ||||||
|         subject="" |         subject="" | ||||||
|         keywords="" |         keywords="" | ||||||
|         ref={r => (resume = r)} |         ref={userPdf} | ||||||
|  |         //ref={r => (resume = r)}
 | ||||||
|       > |       > | ||||||
|         <div className={classes.page}> |         {userRecords.map(record => { | ||||||
|           <div className={classes.header}> |           if (record.id && record.password) { | ||||||
|             <div className={classes.name}>{displayname}</div> |             const username = record.id.substring(1, record.id.indexOf(":")); | ||||||
|             <img className={classes.logo} alt="Logo" src="images/logo.png" /> |             const serverUrl = | ||||||
|           </div> |               "https://" + record.id.substring(record.id.indexOf(":") + 1); | ||||||
|           <div className={classes.body}> |             const qrString = calculateQrString( | ||||||
|             <table> |               serverUrl, | ||||||
|               <tbody> |               username, | ||||||
|                 <tr> |               record.password | ||||||
|                   <td width="200px"> |             ); | ||||||
|                     <div className={classes.code_note}> |             const qrCode = <QRCode value={qrString} size={128} />; | ||||||
|                       Ihr persönlicher Anmeldecode: |             return ( | ||||||
|                     </div> |               <UserPdfPage | ||||||
|                   </td> |                 classes={classes} | ||||||
|                   <td className={classes.table_cell}> |                 displayname={record.displayname} | ||||||
|                     <div className={classes.credentials_note}> |                 qrCode={qrCode} | ||||||
|                       Ihre persönlichen Zugangsdaten: |                 serverUrl={serverUrl} | ||||||
|                     </div> |                 username={username} | ||||||
|                   </td> |                 password={record.password} | ||||||
|                 </tr> |               /> | ||||||
|                 <tr> |             ); | ||||||
|                   <td> |           } else { | ||||||
|                     <div className={classes.qr}>{qrCode}</div> |             /* Skip empty PDF pages */ | ||||||
|                   </td> |             return null; | ||||||
|                   <td className={classes.table_cell}> |           } | ||||||
|                     <div className={classes.credentials_text}> |         })} | ||||||
|                       <br /> |  | ||||||
|                       <table> |  | ||||||
|                         <tbody> |  | ||||||
|                           <tr> |  | ||||||
|                             <td>Heimserver:</td> |  | ||||||
|                             <td> |  | ||||||
|                               <span className={classes.credentials}> |  | ||||||
|                                 {serverUrl} |  | ||||||
|                               </span> |  | ||||||
|                             </td> |  | ||||||
|                           </tr> |  | ||||||
|                           <tr> |  | ||||||
|                             <td>Benutzername:</td> |  | ||||||
|                             <td> |  | ||||||
|                               <span className={classes.credentials}> |  | ||||||
|                                 {username} |  | ||||||
|                               </span> |  | ||||||
|                             </td> |  | ||||||
|                           </tr> |  | ||||||
|                           <tr> |  | ||||||
|                             <td>Passwort:</td> |  | ||||||
|                             <td> |  | ||||||
|                               <span className={classes.credentials}> |  | ||||||
|                                 {password} |  | ||||||
|                               </span> |  | ||||||
|                             </td> |  | ||||||
|                           </tr> |  | ||||||
|                         </tbody> |  | ||||||
|                       </table> |  | ||||||
|                     </div> |  | ||||||
|                   </td> |  | ||||||
|                 </tr> |  | ||||||
|               </tbody> |  | ||||||
|             </table> |  | ||||||
|             <div className={classes.note}> |  | ||||||
|               Hier können Sie Ihre selbst gewählte |  | ||||||
|               Schlüsselsicherungs-Passphrase notieren: |  | ||||||
|               <br /> |  | ||||||
|               <br /> |  | ||||||
|               <br /> |  | ||||||
|               <hr /> |  | ||||||
|             </div> |  | ||||||
|           </div> |  | ||||||
|         </div> |  | ||||||
|       </PDFExport> |       </PDFExport> | ||||||
|     </div> |     </div> | ||||||
|   ); |   ); | ||||||
|  | |||||||
| @ -12,10 +12,12 @@ import { | |||||||
|   ArrayInput, |   ArrayInput, | ||||||
|   ArrayField, |   ArrayField, | ||||||
|   Button, |   Button, | ||||||
|  |   CreateButton, | ||||||
|   Datagrid, |   Datagrid, | ||||||
|   DateField, |   DateField, | ||||||
|   Create, |   Create, | ||||||
|   Edit, |   Edit, | ||||||
|  |   ExportButton, | ||||||
|   List, |   List, | ||||||
|   Filter, |   Filter, | ||||||
|   Toolbar, |   Toolbar, | ||||||
| @ -37,11 +39,8 @@ import { | |||||||
|   DeleteButton, |   DeleteButton, | ||||||
|   SaveButton, |   SaveButton, | ||||||
|   regex, |   regex, | ||||||
|   useRedirect, |  | ||||||
|   useTranslate, |   useTranslate, | ||||||
|   Pagination, |   Pagination, | ||||||
|   CreateButton, |  | ||||||
|   ExportButton, |  | ||||||
|   TopToolbar, |   TopToolbar, | ||||||
|   sanitizeListRestProps, |   sanitizeListRestProps, | ||||||
|   NumberField, |   NumberField, | ||||||
| @ -50,6 +49,13 @@ import SaveQrButton from "./SaveQrButton"; | |||||||
| import { ServerNoticeButton, ServerNoticeBulkButton } from "./ServerNotices"; | import { ServerNoticeButton, ServerNoticeBulkButton } from "./ServerNotices"; | ||||||
| import { DeviceRemoveButton } from "./devices"; | import { DeviceRemoveButton } from "./devices"; | ||||||
| import { makeStyles } from "@material-ui/core/styles"; | import { makeStyles } from "@material-ui/core/styles"; | ||||||
|  | import { Link } from "react-router-dom"; | ||||||
|  | 
 | ||||||
|  | const redirect = (basePath, id, data) => { | ||||||
|  |   return { | ||||||
|  |     pathname: "/importcsv", | ||||||
|  |   }; | ||||||
|  | }; | ||||||
| 
 | 
 | ||||||
| const useStyles = makeStyles({ | const useStyles = makeStyles({ | ||||||
|   small: { |   small: { | ||||||
| @ -81,7 +87,6 @@ const UserListActions = ({ | |||||||
|   total, |   total, | ||||||
|   ...rest |   ...rest | ||||||
| }) => { | }) => { | ||||||
|   const redirectTo = useRedirect(); |  | ||||||
|   return ( |   return ( | ||||||
|     <TopToolbar className={className} {...sanitizeListRestProps(rest)}> |     <TopToolbar className={className} {...sanitizeListRestProps(rest)}> | ||||||
|       {filters && |       {filters && | ||||||
| @ -101,6 +106,10 @@ const UserListActions = ({ | |||||||
|         exporter={exporter} |         exporter={exporter} | ||||||
|         maxResults={maxResults} |         maxResults={maxResults} | ||||||
|       /> |       /> | ||||||
|  |       {/* Add your custom actions */} | ||||||
|  |       <Button component={Link} to={redirect} label="CSV Import"> | ||||||
|  |         <GetAppIcon style={{ transform: "rotate(180deg)", fontSize: "20" }} /> | ||||||
|  |       </Button> | ||||||
|     </TopToolbar> |     </TopToolbar> | ||||||
|   ); |   ); | ||||||
| }; | }; | ||||||
| @ -178,7 +187,7 @@ export const UserList = props => { | |||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| // redirect to the related Author show page
 | // redirect to the related Author show page
 | ||||||
| const redirect = (basePath, id, data) => { | const redirectToPdf = (basePath, id, data) => { | ||||||
|   return { |   return { | ||||||
|     pathname: "/showpdf", |     pathname: "/showpdf", | ||||||
|     state: { |     state: { | ||||||
| @ -193,7 +202,7 @@ const UserCreateToolbar = props => ( | |||||||
|   <Toolbar {...props}> |   <Toolbar {...props}> | ||||||
|     <SaveQrButton |     <SaveQrButton | ||||||
|       label="synapseadmin.action.save_and_show" |       label="synapseadmin.action.save_and_show" | ||||||
|       redirect={redirect} |       redirect={redirectToPdf} | ||||||
|       submitOnEnter={true} |       submitOnEnter={true} | ||||||
|     /> |     /> | ||||||
|     <SaveButton |     <SaveButton | ||||||
|  | |||||||
| @ -104,6 +104,8 @@ export default { | |||||||
|         with_error: |         with_error: | ||||||
|           "%{smart_count} Eintrag mit Fehlern ||| %{smart_count} Einträge mit Fehlern", |           "%{smart_count} Eintrag mit Fehlern ||| %{smart_count} Einträge mit Fehlern", | ||||||
|         simulated_only: "Import-Vorgang war nur simuliert", |         simulated_only: "Import-Vorgang war nur simuliert", | ||||||
|  |         for_print: | ||||||
|  |           "%{smart_count} Eintrag zum Drucken verfügbar |||| %{smart_count} Einträge zum Drucken verfügbar", | ||||||
|       }, |       }, | ||||||
|     }, |     }, | ||||||
|   }, |   }, | ||||||
|  | |||||||
| @ -104,6 +104,8 @@ export default { | |||||||
|         with_error: |         with_error: | ||||||
|           "%{smart_count} entry with errors ||| %{smart_count} entries with errors", |           "%{smart_count} entry with errors ||| %{smart_count} entries with errors", | ||||||
|         simulated_only: "Run was only simulated", |         simulated_only: "Run was only simulated", | ||||||
|  |         for_print: | ||||||
|  |           "%{smart_count} entry available for printing |||| %{smart_count} entries available for printing", | ||||||
|       }, |       }, | ||||||
|     }, |     }, | ||||||
|   }, |   }, | ||||||
|  | |||||||
| @ -25,9 +25,6 @@ const mxcUrlToHttp = mxcUrl => { | |||||||
|   return `${homeserver}/_matrix/media/r0/thumbnail/${serverName}/${mediaId}?width=24&height=24&method=scale`; |   return `${homeserver}/_matrix/media/r0/thumbnail/${serverName}/${mediaId}?width=24&height=24&method=scale`; | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| const powerLevelToRole = powerLevel => |  | ||||||
|   powerLevel < 100 ? (powerLevel < 50 ? "user" : "mod") : "admin"; |  | ||||||
| 
 |  | ||||||
| const POWER_LEVELS = { | const POWER_LEVELS = { | ||||||
|   admin: 100, |   admin: 100, | ||||||
|   mod: 50, |   mod: 50, | ||||||
|  | |||||||
		Loading…
	
		Reference in New Issue
	
	Block a user
	 Timo Paulssen
						Timo Paulssen