diff --git a/app/imports/api/creature/CreatureProperties.js b/app/imports/api/creature/CreatureProperties.js index 95d78db3..92d9a167 100644 --- a/app/imports/api/creature/CreatureProperties.js +++ b/app/imports/api/creature/CreatureProperties.js @@ -17,6 +17,7 @@ import { renewDocIds } from '/imports/api/parenting/parenting.js'; import {setDocToLastOrder} from '/imports/api/parenting/order.js'; +import { storedIconsSchema } from '/imports/api/icons/Icons.js'; let CreatureProperties = new Mongo.Collection('creatureProperties'); @@ -36,6 +37,10 @@ let CreaturePropertySchema = new SimpleSchema({ type: Boolean, optional: true, }, + icon: { + type: storedIconsSchema, + optional: true, + } }); for (let key in propertySchemasIndex){ diff --git a/app/imports/api/icons/Icons.js b/app/imports/api/icons/Icons.js index 6ce17817..7a6df2b3 100644 --- a/app/imports/api/icons/Icons.js +++ b/app/imports/api/icons/Icons.js @@ -1,8 +1,10 @@ import SimpleSchema from 'simpl-schema'; +import { ValidatedMethod } from 'meteor/mdg:validated-method'; +import { assertAdmin } from '/imports/api/sharing/sharingPermissions.js'; let Icons = new Mongo.Collection('icons'); -iconsSchema = new SimpleSchema({ +let iconsSchema = new SimpleSchema({ name: { type: String, unique: true, @@ -33,21 +35,57 @@ if (Meteor.isServer) { }); } +const storedIconsSchema = new SimpleSchema({ + name: { + type: String, + }, + shape: { + type: String, + }, +}); + Icons.attachSchema(iconsSchema); -/* -console.warn("Write Icons is not secure, disable before deployment") +// This method does not validate icons against the schema, use wisely; const writeIcons = new ValidatedMethod({ - name: 'writeIcons', + name: 'icons.methods.write', validate: null, run(icons){ + assertAdmin(this.userId); if (Meteor.isServer){ this.unblock(); Icons.rawCollection().insert(icons, {ordered: false}); } } }); -*/ -export { writeIcons }; +const findIcons = new ValidatedMethod({ + name: 'icons.methods.find', + validate: new SimpleSchema({ + search: { + type: String, + max: 30, + optional: true, + }, + }).validator(), + run({search}){ + if (!search) return []; + if (!Meteor.isServer) return; + return Icons.find( + { $text: {$search: search} }, + { + // relevant documents have a higher score. + fields: { + score: { $meta: 'textScore' } + }, + // `score` property specified in the projection fields above. + sort: { + score: { $meta: 'textScore' } + } + } + ).fetch(); + } +}) + +export { writeIcons, findIcons, storedIconsSchema }; export default Icons; diff --git a/app/imports/api/sharing/sharingPermissions.js b/app/imports/api/sharing/sharingPermissions.js index 00deca12..806e8f4c 100644 --- a/app/imports/api/sharing/sharingPermissions.js +++ b/app/imports/api/sharing/sharingPermissions.js @@ -113,3 +113,17 @@ export function assertDocViewPermission(doc, userId){ let root = getRoot(doc); assertViewPermission(root, userId); } + +export function assertAdmin(userId){ + assertIdValid(userId); + let user = Meteor.users.findOne(userId, {fields: {roles: 1}}); + if (!user){ + throw new Meteor.Error('Permission denied', + 'UserId does not match any existing user'); + } + let isAdmin = user.roles && user.roles.includes('admin') + if (!isAdmin){ + throw new Meteor.Error('Permission denied', + 'User does not have the admin role'); + } +} diff --git a/app/imports/ui/components/global/IconPicker.vue b/app/imports/ui/components/global/IconPicker.vue new file mode 100644 index 00000000..f525773d --- /dev/null +++ b/app/imports/ui/components/global/IconPicker.vue @@ -0,0 +1,120 @@ + + + + + diff --git a/app/imports/ui/components/global/SmartInputMixin.js b/app/imports/ui/components/global/SmartInputMixin.js index e4b30e5a..b39b0c5f 100644 --- a/app/imports/ui/components/global/SmartInputMixin.js +++ b/app/imports/ui/components/global/SmartInputMixin.js @@ -23,7 +23,7 @@ export default { inputValue: this.value, };}, props: { - value: [String, Number, Date, Array, Boolean], + value: [String, Number, Date, Array, Object, Boolean], errorMessages: [String, Array], disabled: Boolean, }, diff --git a/app/imports/ui/components/global/SvgIcon.vue b/app/imports/ui/components/global/SvgIcon.vue new file mode 100644 index 00000000..0c8789d2 --- /dev/null +++ b/app/imports/ui/components/global/SvgIcon.vue @@ -0,0 +1,89 @@ + + + + + diff --git a/app/imports/ui/components/global/globalIndex.js b/app/imports/ui/components/global/globalIndex.js index 9c81a6cf..05992177 100644 --- a/app/imports/ui/components/global/globalIndex.js +++ b/app/imports/ui/components/global/globalIndex.js @@ -1,17 +1,21 @@ import Vue from 'vue'; // Global components import DatePicker from '/imports/ui/components/global/DatePicker.vue'; +import IconPicker from '/imports/ui/components/global/IconPicker.vue'; import TextField from '/imports/ui/components/global/TextField.vue'; import TextArea from '/imports/ui/components/global/TextArea.vue'; import SmartSelect from '/imports/ui/components/global/SmartSelect.vue'; import SmartCombobox from '/imports/ui/components/global/SmartCombobox.vue'; import SmartCheckbox from '/imports/ui/components/global/SmartCheckbox.vue'; import SmartSwitch from '/imports/ui/components/global/SmartSwitch.vue'; +import SvgIcon from '/imports/ui/components/global/SvgIcon.vue'; Vue.component('DatePicker', DatePicker); +Vue.component('IconPicker', IconPicker); Vue.component('TextField', TextField); Vue.component('TextArea', TextArea); Vue.component('SmartSelect', SmartSelect); Vue.component('SmartCombobox', SmartCombobox); Vue.component('SmartCheckbox', SmartCheckbox); Vue.component('SmartSwitch', SmartSwitch); +Vue.component('SvgIcon', SvgIcon); diff --git a/app/imports/ui/components/propertyToolbar.vue b/app/imports/ui/components/propertyToolbar.vue index 96898829..49ab6876 100644 --- a/app/imports/ui/components/propertyToolbar.vue +++ b/app/imports/ui/components/propertyToolbar.vue @@ -5,7 +5,7 @@ :flat="flat" > diff --git a/app/imports/ui/icons/IconAdmin.vue b/app/imports/ui/icons/IconAdmin.vue index 67e01fa9..fa605ae1 100644 --- a/app/imports/ui/icons/IconAdmin.vue +++ b/app/imports/ui/icons/IconAdmin.vue @@ -8,47 +8,17 @@ align-center > - + - - - - - - {{ icon.name }} - - - - - - - - @@ -57,29 +27,31 @@