Skip to content

Commit

Permalink
Implemented download buttons
Browse files Browse the repository at this point in the history
  • Loading branch information
rmathis committed Aug 31, 2022
1 parent 0e7c7aa commit b108413
Show file tree
Hide file tree
Showing 12 changed files with 123 additions and 31 deletions.
16 changes: 16 additions & 0 deletions backend/src/main/resources/i18n/messages.properties
Original file line number Diff line number Diff line change
Expand Up @@ -757,6 +757,22 @@ tooltip.role-description=A description of the purpose of the role.

tooltip.contact-information=Add a contact to organization information. Contacts provide information about how to contact the organization responsible for standing up the entity.

tooltip.download-single-config=Putting all the properties in one file can make it easier for deploying or moving among environments.
tooltip.download-multi-config=Putting the properties into individual files will follow the distribution layout and more closely align with the Shibboleth wiki page sections describing each property.
action.download-single-config=Single file
action.download-multi-config=Separated files
label.download-config=Downloads
message.configurations-none=No configurations defined.
label.configuration-name=Name
label.configuration-name-placeholder=Enter name
label.configuration-property=Property
label.configuration-category=Category
label.configuration-type=Type
label.configuration-value=Value
label.configuration-action=Action
message.delete-property-title=Delete Configuration?
message.delete-property-body=You are requesting to delete a configuration set. If you complete this process the set will be removed. This cannot be undone. Do you wish to continue?

label.external-description=Description

tooltip.external-description=A brief description of the purpose of this filter.
Expand Down
36 changes: 21 additions & 15 deletions ui/src/app/admin/component/ConfigurationForm.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import { useProperties } from '../hoc/PropertiesProvider';

import Form from 'react-bootstrap/Form';
import FloatingLabel from 'react-bootstrap/FloatingLabel';
import { useTranslator } from '../../i18n/hooks';

export function ConfigurationForm({ configuration = {}, loading, onSave, onCancel }) {

Expand All @@ -20,7 +21,7 @@ export function ConfigurationForm({ configuration = {}, loading, onSave, onCance
}
});

const { fields, prepend, remove } = useFieldArray({
const { fields, append, remove } = useFieldArray({
control,
name: "properties",
});
Expand All @@ -36,22 +37,24 @@ export function ConfigurationForm({ configuration = {}, loading, onSave, onCance
}
}, []);

prepend(parsed);
append(parsed);
};

const saveConfig = (formValues) => {
const parsed = formValues.properties.map(p => ({
propertyName: p.propertyName,
propertyValue: p.propertyValue,
configFile: p.configFile,
category: p.category,
displayType: p.displayType
}));
onSave({
...formValues,
properties: parsed
});
};

React.useEffect(() => console.log(configuration), [configuration]);
const translator = useTranslator();

return (<>
<div className="container-fluid">
Expand Down Expand Up @@ -79,8 +82,8 @@ export function ConfigurationForm({ configuration = {}, loading, onSave, onCance
<div className="row">
<div className="col-12 col-lg-5">
<Form.Group className="mb-3" controlId="formName">
<Form.Label>Name</Form.Label>
<Form.Control type="text" placeholder="Enter name" required {...register(`name`)} />
<Form.Label><Translate value="label.configuration-name">Name</Translate></Form.Label>
<Form.Control type="text" placeholder={translator('label.configuration-name-placeholder')} required {...register(`name`)} />
</Form.Group>
</div>
</div>
Expand All @@ -97,11 +100,11 @@ export function ConfigurationForm({ configuration = {}, loading, onSave, onCance
<table className='w-100 table align-middle'>
<thead>
<tr>
<th>Property</th>
<th>Category</th>
<th>Type</th>
<th>Value</th>
<th>Action</th>
<th><Translate value="label.configuration-property">Property</Translate></th>
<th><Translate value="label.configuration-category">Category</Translate></th>
<th><Translate value="label.configuration-type">Type</Translate></th>
<th><Translate value="label.configuration-value">Value</Translate></th>
<th className="text-end"><Translate value="label.configuration-action">Action</Translate></th>
</tr>
</thead>
<tbody>
Expand All @@ -114,20 +117,23 @@ export function ConfigurationForm({ configuration = {}, loading, onSave, onCance
{p.displayType !== 'boolean' ?
<FloatingLabel
controlId={`valueInput-${p.propertyName}`}
label="property value">
<Form.Control type={p.displayType === 'number' ? 'number' : 'text'} placeholder="Value" {...register(`properties.${idx}.propertyValue`)} />
label={translator('label.configuration-value')}>
<Form.Control
type={p.displayType === 'number' ? 'number' : 'text'}
placeholder="value"
{...register(`properties.${idx}.propertyValue`)} />
</FloatingLabel>
:
<Form.Check type="switch"
label={ watch(`properties.${idx}.propertyValue`) === true ? 'True' : 'False' }
label={ watch(`properties.${idx}.propertyValue`) === true ? translator('value.true') : translator('value.false') }
reverse={'true'} {...register(`properties.${idx}.propertyValue`)}
className="my-3" />
}
</td>
<td>
<td className="text-end">
<Button variant="danger" onClick={() => remove(idx)}>
<FontAwesomeIcon icon={faTrash} size="lg" />
Remove
&nbsp; <Translate value="action.remove">Remove</Translate>
</Button>
</td>
</tr>
Expand Down
2 changes: 1 addition & 1 deletion ui/src/app/admin/component/PropertySelector.js
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,7 @@ export function PropertySelector ({ properties, options, onAddProperties }) {
</Typeahead>
</div>
<Button type="button"
variant="outline-secondary"
variant="success"
className="ms-2"
onClick={() => add(selected)}>Add</Button>
</Fragment>
Expand Down
71 changes: 64 additions & 7 deletions ui/src/app/admin/container/ConfigurationList.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,18 +4,43 @@ import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';

import Button from 'react-bootstrap/Button';
import ButtonGroup from 'react-bootstrap/ButtonGroup';
import Popover from 'react-bootstrap/Popover';
import { Link } from 'react-router-dom';

import { Translate } from '../../i18n/components/translate';

import { DeleteConfirmation } from '../../core/components/DeleteConfirmation';
import OverlayTrigger from 'react-bootstrap/esm/OverlayTrigger';
import { useTranslator } from '../../i18n/hooks';
import useFetch from 'use-http';
import API_BASE_PATH from '../../App.constant';
import { downloadAsZip } from '../../core/utility/download_as';

export function ConfigurationList({ configurations, onDelete, loading }) {

const remove = (id) => {
onDelete(id);
}

const translate = useTranslator();

const downloader = useFetch(`${API_BASE_PATH}/shib/property/set`, {
cachePolicy: 'no-cache',
headers: {
'Content-Type': 'application/zip',
'Accept': 'application/zip'
}
});

const download = async (id, type) => {
await downloader.get(`/${id}${ type === 'single' ? '/onefile' : '' }`);
const file = await downloader.response.blob();
if (downloader.response.ok) {
downloadAsZip('configuration', file);
console.log(file);
}
};

return (
<DeleteConfirmation title={`message.delete-property-title`} body={`message.delete-property-body`}>
{(block) =>
Expand Down Expand Up @@ -46,7 +71,12 @@ export function ConfigurationList({ configurations, onDelete, loading }) {
<th>
<Translate value="label.configuration-name">Configuration Name (label)</Translate>
</th>
<th><span className="sr-only"><Translate value="label.actions">Actions</Translate></span></th>
<th className="text-center">
<Translate value="label.download-config">Download</Translate>
</th>
<th className="text-end">
<Translate value="label.actions">Actions</Translate>
</th>
</tr>
</thead>
<tbody>
Expand All @@ -57,25 +87,52 @@ export function ConfigurationList({ configurations, onDelete, loading }) {
{c.name}
</Link>
</td>
<td className="text-center">
<div className="d-flex justify-content-center">

<OverlayTrigger trigger={['hover', 'focus']} overlay={(
<Popover variant="info">
<Popover.Body><Translate value={'tooltip.download-single-config'} /></Popover.Body>
</Popover>
)}
aria-label={translate('')}>
<Button onClick={() => download(c.resourceId, 'single')} variant="link" disabled={downloader.loading}>
<FontAwesomeIcon icon={faDownload} />
&nbsp; <Translate value="action.download-single-config">Single file</Translate>
</Button>
</OverlayTrigger>
<div className="vr"></div>
<OverlayTrigger trigger={['hover', 'focus']} overlay={(
<Popover variant="info">
<Popover.Body><Translate value={'tooltip.download-multi-config'} /></Popover.Body>
</Popover>
)}
aria-label={translate('')}>
<Button onClick={() => download(c.resourceId, 'multi')} variant="link" disabled={downloader.loading}>
<FontAwesomeIcon icon={faDownload} />
&nbsp; <Translate value="action.download-multi-config">Multi file</Translate>
</Button>
</OverlayTrigger>
{downloader.loading && <FontAwesomeIcon icon={faSpinner} pulse={true} />}
</div>
</td>
<td className="text-end">
<Button onClick={() => console.log('clicked')} className={`btn btn-success`}>
<FontAwesomeIcon icon={faDownload} size="lg" />
&nbsp; <Translate value="action.download">Download single file</Translate>
</Button>
<ButtonGroup aria-label="Actions" className="ms-4" >
<Link className="btn btn-primary" to={`../configurations/${c.resourceId}/edit`}>
<FontAwesomeIcon icon={faEdit} size="lg" />
&nbsp; Edit
</Link>
<Button variant="danger"onClick={() => block(() => remove(c.resourceId))}>
<Button variant="danger" onClick={() => block(() => remove(c.resourceId))}>
<FontAwesomeIcon icon={faTrash} size="lg" />
&nbsp; <Translate value="action.delete">Delete</Translate>
</Button>
</ButtonGroup>
</td>
</tr>
) : <tr>
<td colSpan="3">No configurations.</td>
<td colSpan="3">
<Translate value="message.configurations-none">No configurations.</Translate>
</td>
</tr>}
</tbody>
</table>
Expand Down
2 changes: 0 additions & 2 deletions ui/src/app/admin/container/EditConfiguration.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,10 @@ import React from 'react';
import { Prompt, useHistory, useParams } from 'react-router-dom';
import Translate from '../../i18n/components/translate';
import { useConfiguration } from '../hooks';
import { Schema } from '../../form/Schema';
import { ConfigurationForm } from '../component/ConfigurationForm';

import { createNotificationAction, NotificationTypes, useNotificationDispatcher } from '../../notifications/hoc/Notifications';
import { useTranslator } from '../../i18n/hooks';
import { BASE_PATH } from '../../App.constant';
import { PropertiesProvider } from '../hoc/PropertiesProvider';
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
import { faSpinner } from '@fortawesome/free-solid-svg-icons';
Expand Down
2 changes: 1 addition & 1 deletion ui/src/app/admin/hoc/ConfigurationsProvider.js
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ export function ConfigurationsProvider({ children, cache = 'no-cache' }) {

async function removeConfiguration(id) {
let toast;
const resp = await del(`/${id}`);
const resp = await del(`shib/property/set/${id}`);
if (response.ok) {
loadConfigurations();
toast = createNotificationAction(`Deleted property successfully.`, NotificationTypes.SUCCESS);
Expand Down
8 changes: 7 additions & 1 deletion ui/src/app/admin/hooks.js
Original file line number Diff line number Diff line change
Expand Up @@ -61,4 +61,10 @@ export function useConfigurationUiSchema () {
'ui:widget': 'textarea'
}
};
}
}

export function useConfigDownload () {
return useFetch(`${API_BASE_PATH}/shib/property/set`, {
cachePolicy: 'no-cache'
});
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,11 @@
import * as FileSaver from 'file-saver';

export const downloadAsZip = (fileName, data) => {
// const blob = new Blob([data], { type: 'text/zip;charset=utf-8' });
FileSaver.saveAs(data, `${fileName}.zip`);
}

export const downloadAsXml = (fileName, xml) => {
const blob = new Blob([xml], { type: 'text/xml;charset=utf-8' });
FileSaver.saveAs(blob, `${fileName}.xml`);
}
}
2 changes: 1 addition & 1 deletion ui/src/app/core/utility/download_as_xml.test.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import * as FileSaver from 'file-saver';
import { downloadAsXml } from './download_as_xml';
import { downloadAsXml } from './download_as';
jest.mock('file-saver');

it('attempts to save the provided content', () => {
Expand Down
2 changes: 1 addition & 1 deletion ui/src/app/metadata/hoc/FilterTargetPreview.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import { useFetch } from 'use-http';
import Modal from 'react-bootstrap/Modal';
import Button from 'react-bootstrap/Button';
import Translate from '../../i18n/components/translate';
import { downloadAsXml } from '../../core/utility/download_as_xml';
import { downloadAsXml } from '../../core/utility/download_as';

export function FilterTargetPreview ({ entityId, children }) {

Expand Down
2 changes: 1 addition & 1 deletion ui/src/app/metadata/view/MetadataXml.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import { MetadataObjectContext } from '../hoc/MetadataSelector';

import { MetadataXmlContext } from '../hoc/MetadataXmlLoader';
import { MetadataViewToggle } from '../component/MetadataViewToggle';
import { downloadAsXml } from '../../core/utility/download_as_xml';
import { downloadAsXml } from '../../core/utility/download_as';

export function MetadataXml () {
const { xml, reload } = React.useContext(MetadataXmlContext);
Expand Down
4 changes: 4 additions & 0 deletions ui/src/theme/project/forms.scss
Original file line number Diff line number Diff line change
Expand Up @@ -124,6 +124,10 @@ mark {
}
}

.form-floating > label {
color:#9299A0;
}

@media only screen and (max-width: 1200px) {
.form-section:not(:first-child) {
border-left: 0px;
Expand Down

0 comments on commit b108413

Please sign in to comment.