mirror of
https://github.com/TryGhost/Ghost-Admin.git
synced 2023-12-14 02:33:04 +01:00
Added csv import/export for members
no issue - Adds action view with import/export csv options
This commit is contained in:
parent
b01bb02534
commit
6cd12401c1
43
app/components/modal-import-members.js
Normal file
43
app/components/modal-import-members.js
Normal file
|
@ -0,0 +1,43 @@
|
||||||
|
import ModalComponent from 'ghost-admin/components/modal-base';
|
||||||
|
import ghostPaths from 'ghost-admin/utils/ghost-paths';
|
||||||
|
import {computed} from '@ember/object';
|
||||||
|
|
||||||
|
export default ModalComponent.extend({
|
||||||
|
labelText: 'Select or drag-and-drop a CSV File',
|
||||||
|
|
||||||
|
response: null,
|
||||||
|
closeDisabled: false,
|
||||||
|
|
||||||
|
// Allowed actions
|
||||||
|
confirm: () => {},
|
||||||
|
|
||||||
|
uploadUrl: computed(function () {
|
||||||
|
return `${ghostPaths().apiRoot}/members/csv/`;
|
||||||
|
}),
|
||||||
|
|
||||||
|
actions: {
|
||||||
|
uploadStarted() {
|
||||||
|
this.set('closeDisabled', true);
|
||||||
|
},
|
||||||
|
|
||||||
|
uploadFinished() {
|
||||||
|
this.set('closeDisabled', false);
|
||||||
|
},
|
||||||
|
|
||||||
|
uploadSuccess(response) {
|
||||||
|
this.set('response', response.meta.stats);
|
||||||
|
// invoke the passed in confirm action
|
||||||
|
this.confirm();
|
||||||
|
},
|
||||||
|
|
||||||
|
confirm() {
|
||||||
|
// noop - we don't want the enter key doing anything
|
||||||
|
},
|
||||||
|
|
||||||
|
closeModal() {
|
||||||
|
if (!this.closeDisabled) {
|
||||||
|
this._super(...arguments);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
|
@ -1,4 +1,5 @@
|
||||||
import Controller from '@ember/controller';
|
import Controller from '@ember/controller';
|
||||||
|
import ghostPaths from 'ghost-admin/utils/ghost-paths';
|
||||||
import moment from 'moment';
|
import moment from 'moment';
|
||||||
import {computed} from '@ember/object';
|
import {computed} from '@ember/object';
|
||||||
import {inject as service} from '@ember/service';
|
import {inject as service} from '@ember/service';
|
||||||
|
@ -32,6 +33,22 @@ export default Controller.extend({
|
||||||
|
|
||||||
return filtered;
|
return filtered;
|
||||||
}),
|
}),
|
||||||
|
|
||||||
|
actions: {
|
||||||
|
exportData() {
|
||||||
|
let exportUrl = ghostPaths().url.api('members/csv');
|
||||||
|
let downloadURL = `${exportUrl}`;
|
||||||
|
let iframe = document.getElementById('iframeDownload');
|
||||||
|
|
||||||
|
if (!iframe) {
|
||||||
|
iframe = document.createElement('iframe');
|
||||||
|
iframe.id = 'iframeDownload';
|
||||||
|
iframe.style.display = 'none';
|
||||||
|
document.body.append(iframe);
|
||||||
|
}
|
||||||
|
iframe.setAttribute('src', downloadURL);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
fetchMembers: task(function* () {
|
fetchMembers: task(function* () {
|
||||||
let newFetchDate = new Date();
|
let newFetchDate = new Date();
|
||||||
|
|
19
app/controllers/members/import.js
Normal file
19
app/controllers/members/import.js
Normal file
|
@ -0,0 +1,19 @@
|
||||||
|
import Controller from '@ember/controller';
|
||||||
|
import {inject as controller} from '@ember/controller';
|
||||||
|
import {inject as service} from '@ember/service';
|
||||||
|
|
||||||
|
/* eslint-disable ghost/ember/alias-model-in-controller */
|
||||||
|
export default Controller.extend({
|
||||||
|
members: controller(),
|
||||||
|
router: service(),
|
||||||
|
|
||||||
|
actions: {
|
||||||
|
fetchNewMembers() {
|
||||||
|
this.members.fetchMembers.perform();
|
||||||
|
},
|
||||||
|
|
||||||
|
close() {
|
||||||
|
this.router.transitionTo('members');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
|
@ -58,7 +58,9 @@ Router.map(function () {
|
||||||
this.route('settings.integrations.unsplash', {path: '/settings/integrations/unsplash'});
|
this.route('settings.integrations.unsplash', {path: '/settings/integrations/unsplash'});
|
||||||
this.route('settings.integrations.zapier', {path: '/settings/integrations/zapier'});
|
this.route('settings.integrations.zapier', {path: '/settings/integrations/zapier'});
|
||||||
|
|
||||||
this.route('members');
|
this.route('members', function () {
|
||||||
|
this.route('import');
|
||||||
|
});
|
||||||
this.route('member', {path: '/members/:member_id'});
|
this.route('member', {path: '/members/:member_id'});
|
||||||
|
|
||||||
this.route('subscribers', function () {
|
this.route('subscribers', function () {
|
||||||
|
|
4
app/routes/members/import.js
Normal file
4
app/routes/members/import.js
Normal file
|
@ -0,0 +1,4 @@
|
||||||
|
import Route from '@ember/routing/route';
|
||||||
|
|
||||||
|
export default Route.extend({
|
||||||
|
});
|
|
@ -27,4 +27,12 @@ p.gh-members-list-email {
|
||||||
textarea.gh-member-details-textarea {
|
textarea.gh-member-details-textarea {
|
||||||
max-width: 100%;
|
max-width: 100%;
|
||||||
height: 123px;
|
height: 123px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Import modal
|
||||||
|
/* ---------------------------------------------------------- */
|
||||||
|
|
||||||
|
.members-import-results {
|
||||||
|
margin: 0;
|
||||||
|
width: auto;
|
||||||
}
|
}
|
47
app/templates/components/modal-import-members.hbs
Normal file
47
app/templates/components/modal-import-members.hbs
Normal file
|
@ -0,0 +1,47 @@
|
||||||
|
<header class="modal-header" data-test-modal="import-members">
|
||||||
|
<h1>
|
||||||
|
{{#if response}}
|
||||||
|
Import Successful
|
||||||
|
{{else}}
|
||||||
|
Import Members
|
||||||
|
{{/if}}
|
||||||
|
</h1>
|
||||||
|
</header>
|
||||||
|
<a class="close" href="" title="Close" {{action "closeModal"}}>{{svg-jar "close"}}<span class="hidden">Close</span></a>
|
||||||
|
|
||||||
|
<div class="modal-body">
|
||||||
|
{{#if response}}
|
||||||
|
<table class="members-import-results">
|
||||||
|
<tr>
|
||||||
|
<td>Imported:</td>
|
||||||
|
<td align="left" data-test-text="import-members-imported">{{response.imported}}</td>
|
||||||
|
</tr>
|
||||||
|
{{#if response.duplicates}}
|
||||||
|
<tr>
|
||||||
|
<td>Duplicates:</td>
|
||||||
|
<td align="left" data-test-text="import-members-duplicates">{{response.duplicates}}</td>
|
||||||
|
</tr>
|
||||||
|
{{/if}}
|
||||||
|
{{#if response.invalid}}
|
||||||
|
<tr>
|
||||||
|
<td>Invalid:</td>
|
||||||
|
<td align="left" data-test-text="import-members-invalid">{{response.invalid}}</td>
|
||||||
|
</tr>
|
||||||
|
{{/if}}
|
||||||
|
</table>
|
||||||
|
{{else}}
|
||||||
|
{{gh-file-uploader
|
||||||
|
url=uploadUrl
|
||||||
|
paramName="membersfile"
|
||||||
|
labelText="Select or drag-and-drop a CSV file."
|
||||||
|
uploadStarted=(action "uploadStarted")
|
||||||
|
uploadFinished=(action "uploadFinished")
|
||||||
|
uploadSuccess=(action "uploadSuccess")}}
|
||||||
|
{{/if}}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="modal-footer">
|
||||||
|
<button {{action "closeModal"}} disabled={{closeDisabled}} class="gh-btn" data-test-button="close-import-members">
|
||||||
|
<span>{{#if response}}Close{{else}}Cancel{{/if}}</span>
|
||||||
|
</button>
|
||||||
|
</div>
|
|
@ -6,6 +6,28 @@
|
||||||
{{svg-jar "search" class="absolute top-2 left-2 w4 h4 fill-midlightgrey-l2"}}
|
{{svg-jar "search" class="absolute top-2 left-2 w4 h4 fill-midlightgrey-l2"}}
|
||||||
<GhTextInput placeholder="Search members..." @value={{this.searchText}} @input={{action (mut this.searchText) value="target.value"}} class="gh-members-list-searchfield" />
|
<GhTextInput placeholder="Search members..." @value={{this.searchText}} @input={{action (mut this.searchText) value="target.value"}} class="gh-members-list-searchfield" />
|
||||||
</div>
|
</div>
|
||||||
|
<section class="view-actions">
|
||||||
|
<span class="dropdown">
|
||||||
|
{{#gh-dropdown-button dropdownName="members-actions-menu" classNames="gh-btn gh-btn-white gh-btn-icon only-has-icon user-actions-cog" title="Members Actions" data-test-user-actions=true}}
|
||||||
|
<span>
|
||||||
|
{{svg-jar "settings"}}
|
||||||
|
<span class="hidden">Members Actions</span>
|
||||||
|
</span>
|
||||||
|
{{/gh-dropdown-button}}
|
||||||
|
{{#gh-dropdown name="members-actions-menu" tagName="ul" classNames="user-actions-menu dropdown-menu dropdown-triangle-top-right"}}
|
||||||
|
<li>
|
||||||
|
{{#link-to "members.import" class="mr2" data-test-link="import-csv"}}
|
||||||
|
<span>Import CSV </span>
|
||||||
|
{{/link-to}}
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<a href="#" {{action 'exportData'}} class="mr2">
|
||||||
|
<span>Export CSV </span>
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
{{/gh-dropdown}}
|
||||||
|
</span>
|
||||||
|
</section>
|
||||||
</section>
|
</section>
|
||||||
</GhCanvasHeader>
|
</GhCanvasHeader>
|
||||||
<section class="view-container">
|
<section class="view-container">
|
||||||
|
@ -64,4 +86,5 @@
|
||||||
</ol>
|
</ol>
|
||||||
</section>
|
</section>
|
||||||
</section>
|
</section>
|
||||||
</section>
|
</section>
|
||||||
|
{{outlet}}
|
4
app/templates/members/import.hbs
Normal file
4
app/templates/members/import.hbs
Normal file
|
@ -0,0 +1,4 @@
|
||||||
|
{{gh-fullscreen-modal "import-members"
|
||||||
|
close=(action "close")
|
||||||
|
confirm=(action "fetchNewMembers")
|
||||||
|
modifier="action wide"}}
|
Loading…
Reference in a new issue