Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Re-factor admin UI into its own package and allow custom field types #2390

Closed
wants to merge 15 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
2 changes: 1 addition & 1 deletion .eslintignore
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,6 @@ admin/public/js/packages.js
bundles/*
coverage/*
docs/*
fields/types/**/lib/*
fields/**/lib/*
node_modules/*
test/*
3 changes: 3 additions & 0 deletions .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,9 @@ node_js:
- "5"
- "6"

before_install:
- if [[ `npm -v` != 3* ]]; then npm i -g npm@3; fi

before_script:
- npm run lint
- sleep 15
Expand Down
40 changes: 0 additions & 40 deletions admin/client/fields/columns.js

This file was deleted.

36 changes: 0 additions & 36 deletions admin/client/fields/filters.js

This file was deleted.

36 changes: 0 additions & 36 deletions admin/client/fields/types.js

This file was deleted.

File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import async from 'async';
import Field from '../Field';
import listsByKey from '../../../admin/client/utils/lists';
import listsByKey from 'keystone-admin/client/utils/lists';
import React from 'react';
import Select from 'react-select';
import xhr from 'xhr';
Expand Down Expand Up @@ -192,10 +192,10 @@ module.exports = Field.create({

renderInputGroup () {
// TODO: find better solution
// when importing the CreateForm using: import CreateForm from '../../../admin/client/App/shared/CreateForm';
// when importing the CreateForm using: import CreateForm from 'keystone-admin/client/App/shared/CreateForm';
// CreateForm was imported as a blank object. This stack overflow post suggested lazilly requiring it:
// http://stackoverflow.com/questions/29807664/cyclic-dependency-returns-empty-object-in-react-native
const CreateForm = require('../../../admin/client/App/shared/CreateForm');
const CreateForm = require('keystone-admin/client/App/shared/CreateForm');
return (
<InputGroup>
<InputGroup.Section grow>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import xhr from 'xhr';

import { FormField, FormInput, SegmentedControl } from 'elemental';

import PopoutList from '../../../admin/client/App/shared/Popout/PopoutList';
import PopoutList from 'keystone-admin/client/App/shared/Popout/PopoutList';

const INVERTED_OPTIONS = [
{ label: 'Linked To', value: false },
Expand Down
File renamed without changes.
File renamed without changes.
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import React from 'react';
import { Checkbox, FormField, SegmentedControl } from 'elemental';
import PopoutList from '../../../admin/client/App/shared/Popout/PopoutList';
import PopoutList from 'keystone-admin/client/App/shared/Popout/PopoutList';

const INVERTED_OPTIONS = [
{ label: 'Matches', value: false },
Expand Down
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
2 changes: 1 addition & 1 deletion fields/components/Checkbox.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import React from 'react';
import blacklist from 'blacklist';
import classnames from 'classnames';
import Color from 'color';
import E from '../../admin/client/constants';
import E from 'keystone-admin/client/constants';

var Checkbox = React.createClass({
displayName: 'Checkbox',
Expand Down
2 changes: 1 addition & 1 deletion fields/components/DateInput.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import moment from 'moment';
import DayPicker from 'react-day-picker';
import React from 'react';
import Popout from '../../admin/client/App/shared/Popout';
import Popout from 'keystone-admin/client/App/shared/Popout';
import { FormInput } from 'elemental';

let lastId = 0;
Expand Down
2 changes: 1 addition & 1 deletion fields/components/Lightbox.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import React from 'react';
import blacklist from 'blacklist';
import Portal from '../../admin/client/App/shared/Portal';
import Portal from 'keystone-admin/client/App/shared/Portal';
import Transition from 'react-addons-css-transition-group';

const BODY = document.getElementsByTagName('body')[0];
Expand Down
24 changes: 20 additions & 4 deletions index.js
Original file line number Diff line number Diff line change
Expand Up @@ -120,7 +120,7 @@ Keystone.prototype.list = require('./lib/core/list');
Keystone.prototype.openDatabaseConnection = require('./lib/core/openDatabaseConnection');
Keystone.prototype.populateRelated = require('./lib/core/populateRelated');
Keystone.prototype.redirect = require('./lib/core/redirect');
Keystone.prototype.render = require('./lib/core/render');
// Keystone.prototype.render = require('./lib/core/render'); //FIXME: this is ONLY used in the admin UI, moved to the admin folder -- LW March 8, 2016
Keystone.prototype.start = require('./lib/core/start');
Keystone.prototype.wrapHTMLError = require('./lib/core/wrapHTMLError');

Expand All @@ -133,12 +133,28 @@ Keystone.prototype.wrapHTMLError = require('./lib/core/wrapHTMLError');
var keystone = module.exports = new Keystone();

// Expose modules and Classes
keystone.Admin = {
Server: require('./admin/server'),
};
try {
keystone.Admin = {
Server: require('keystone-admin/server'),
};
}
catch (e) {
if (e.code !== 'MODULE_NOT_FOUND') {
throw e;
}
if (e.message.indexOf('keystone-admin') === -1) {
throw e;
}

console.log('Note: Optional package keystone-admin is not installed. Keystone will run in\n'
+ ' headless mode. run `npm install keystone-admin` to install the KeystoneJS\n'
+ ' admin UI.');
keystone.set('headless', true);
}
keystone.Email = require('./lib/email');
keystone.Field = require('./fields/types/Type');
keystone.Field.Types = require('./lib/fieldTypes');
keystone.Field.Registry = require('./lib/fieldRegistry');
keystone.Keystone = Keystone;
keystone.List = require('./lib/list');
keystone.View = require('./lib/view');
Expand Down
15 changes: 14 additions & 1 deletion lib/core/wrapHTMLError.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,22 @@
* @api public
*/

var path = require('path');
var fs = require('fs');
var style = '';

// read style async into memory, if it is not ready or errors out, at least we have an unstyled error
fs.readFile(path.join(path.dirname(require.resolve('keystone')), 'templates/helpers/errors/styles/error.css'),
function (err, css) {
if (!err) {
style = css;
}
});


function wrapHTMLError (title, err) {
return '<html><head><meta charset=\'utf-8\'><title>Error</title>'
+ '<link rel=\'stylesheet\' href=\'/' + this.get('admin path') + '/styles/error.css\'>'
+ '<style>' + style + '</style>'
+ '</head><body><div class=\'error\'><h1 class=\'error-title\'>' + title + '</h1>'
+ '<div class="error-message">' + (err || '') + '</div></div></body></html>';
}
Expand Down
113 changes: 113 additions & 0 deletions lib/fieldRegistry.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
var fs = require('fs');
var path = require('path');
var EventEmitter = require('events');

function FieldRegistry () {
}

FieldRegistry.prototype = new EventEmitter();

FieldRegistry.prototype.register = function register (name, componentName, requireSpec) {
if (typeof this[name] === 'undefined') {
this[name] = {};
}
this[name][componentName] = requireSpec;
this.emit('added', { name: name, componentName: componentName, path: requireSpec });
};

FieldRegistry.prototype.registerDirectory = function registerDirectory (name, dirPath) {
var files = fs.readdirSync(dirPath);
files.forEach(function (file) {
var basename = path.basename(file, '.js');
if (basename.substr(0, name.length) === name) {
this.register(name, basename.substr(name.length), path.join(dirPath, file));
}
}.bind(this));
};

Object.defineProperty(FieldRegistry.prototype, 'fieldNames', {
get: function getFieldNames () {
return Object.keys(this);
},
});

FieldRegistry.prototype.generateJSForComponentType = function generateJSForComponentType (type, transformKey) {
if (typeof transformKey !== 'function') transformKey = function (x) { return x; };
var registry = this;
var fields = Object.keys(this);
var code = 'module.exports = {\n';
fields.forEach(function (field) {
var components = registry[field];
if (typeof components[type] !== 'undefined') {
code += ' "' + transformKey(field) + '": require("' + components[type] + '"),\n';
}
});
code += '};';
return code;
};

var registry = module.exports = new FieldRegistry();

// Register components for built-in types

registry.registerDirectory('AzureFile', path.resolve(__dirname, '../fields/admin/azurefile/'));
registry.registerDirectory('Boolean', path.resolve(__dirname, '../fields/admin/boolean/'));
registry.registerDirectory('CloudinaryImage', path.resolve(__dirname, '../fields/admin/cloudinaryimage/'));
registry.registerDirectory('CloudinaryImages', path.resolve(__dirname, '../fields/admin/cloudinaryimages/'));
registry.registerDirectory('Code', path.resolve(__dirname, '../fields/admin/code/'));
registry.registerDirectory('Color', path.resolve(__dirname, '../fields/admin/color/'));
registry.registerDirectory('Date', path.resolve(__dirname, '../fields/admin/date/'));
registry.registerDirectory('DateArray', path.resolve(__dirname, '../fields/admin/datearray/'));
registry.registerDirectory('Datetime', path.resolve(__dirname, '../fields/admin/datetime/'));
registry.registerDirectory('Email', path.resolve(__dirname, '../fields/admin/email/'));
registry.registerDirectory('Embedly', path.resolve(__dirname, '../fields/admin/embedly/'));
// registry.registerDirectory('GeoPoint', path.resolve(__dirname, '../fields/admin/geopoint/'));
registry.registerDirectory('Html', path.resolve(__dirname, '../fields/admin/html/'));
registry.registerDirectory('Key', path.resolve(__dirname, '../fields/admin/key/'));
registry.registerDirectory('LocalFile', path.resolve(__dirname, '../fields/admin/localfile/'));
registry.registerDirectory('LocalFiles', path.resolve(__dirname, '../fields/admin/localfiles/'));
registry.registerDirectory('Location', path.resolve(__dirname, '../fields/admin/location/'));
registry.registerDirectory('Markdown', path.resolve(__dirname, '../fields/admin/markdown/'));
registry.registerDirectory('Money', path.resolve(__dirname, '../fields/admin/money/'));
registry.registerDirectory('Name', path.resolve(__dirname, '../fields/admin/name/'));
registry.registerDirectory('Number', path.resolve(__dirname, '../fields/admin/number/'));
registry.registerDirectory('NumberArray', path.resolve(__dirname, '../fields/admin/numberarray/'));
registry.registerDirectory('Password', path.resolve(__dirname, '../fields/admin/password/'));
registry.registerDirectory('Relationship', path.resolve(__dirname, '../fields/admin/relationship/'));
registry.registerDirectory('S3File', path.resolve(__dirname, '../fields/admin/s3file/'));
registry.registerDirectory('Select', path.resolve(__dirname, '../fields/admin/select/'));
registry.registerDirectory('Text', path.resolve(__dirname, '../fields/admin/text/'));
registry.registerDirectory('TextArray', path.resolve(__dirname, '../fields/admin/textarray/'));
registry.registerDirectory('Textarea', path.resolve(__dirname, '../fields/admin/textarea/'));
registry.registerDirectory('Url', path.resolve(__dirname, '../fields/admin/url/'));

registry.registerDirectory('AzureFile', path.resolve(__dirname, '../fields/types/azurefile/'));
registry.registerDirectory('Boolean', path.resolve(__dirname, '../fields/types/boolean/'));
registry.registerDirectory('CloudinaryImage', path.resolve(__dirname, '../fields/types/cloudinaryimage/'));
registry.registerDirectory('CloudinaryImages', path.resolve(__dirname, '../fields/types/cloudinaryimages/'));
registry.registerDirectory('Code', path.resolve(__dirname, '../fields/types/code/'));
registry.registerDirectory('Color', path.resolve(__dirname, '../fields/types/color/'));
registry.registerDirectory('Date', path.resolve(__dirname, '../fields/types/date/'));
registry.registerDirectory('DateArray', path.resolve(__dirname, '../fields/types/datearray/'));
registry.registerDirectory('Datetime', path.resolve(__dirname, '../fields/types/datetime/'));
registry.registerDirectory('Email', path.resolve(__dirname, '../fields/types/email/'));
registry.registerDirectory('Embedly', path.resolve(__dirname, '../fields/types/embedly/'));
// registry.registerDirectory('GeoPoint', path.resolve(__dirname, '../fields/types/geopoint/'));
registry.registerDirectory('Html', path.resolve(__dirname, '../fields/types/html/'));
registry.registerDirectory('Key', path.resolve(__dirname, '../fields/types/key/'));
registry.registerDirectory('LocalFile', path.resolve(__dirname, '../fields/types/localfile/'));
registry.registerDirectory('LocalFiles', path.resolve(__dirname, '../fields/types/localfiles/'));
registry.registerDirectory('Location', path.resolve(__dirname, '../fields/types/location/'));
registry.registerDirectory('Markdown', path.resolve(__dirname, '../fields/types/markdown/'));
registry.registerDirectory('Money', path.resolve(__dirname, '../fields/types/money/'));
registry.registerDirectory('Name', path.resolve(__dirname, '../fields/types/name/'));
registry.registerDirectory('Number', path.resolve(__dirname, '../fields/types/number/'));
registry.registerDirectory('NumberArray', path.resolve(__dirname, '../fields/types/numberarray/'));
registry.registerDirectory('Password', path.resolve(__dirname, '../fields/types/password/'));
registry.registerDirectory('Relationship', path.resolve(__dirname, '../fields/types/relationship/'));
registry.registerDirectory('S3File', path.resolve(__dirname, '../fields/types/s3file/'));
registry.registerDirectory('Select', path.resolve(__dirname, '../fields/types/select/'));
registry.registerDirectory('Text', path.resolve(__dirname, '../fields/types/text/'));
registry.registerDirectory('TextArray', path.resolve(__dirname, '../fields/types/textarray/'));
registry.registerDirectory('Textarea', path.resolve(__dirname, '../fields/types/textarea/'));
registry.registerDirectory('Url', path.resolve(__dirname, '../fields/types/url/'));
1 change: 1 addition & 0 deletions node_modules/keystone

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

File renamed without changes.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

File renamed without changes.
File renamed without changes.
9 changes: 9 additions & 0 deletions node_modules/keystone-admin/client/fields/columns.js

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading