Keystone.js nested categories - keystonejs

I am trying to implement nested categories for Post model.
What I have:
Post.add({
title: { type: String, required: true },
state: { type: Types.Select, options: 'draft, published, archived', default: 'draft', index: true },
author: { type: Types.Relationship, ref: 'User', index: true },
publishedDate: { type: Types.Date, index: true, dependsOn: { state: 'published' } },
content: {
extended: { type: Types.Html, wysiwyg: true, height: 300 },
},
categories: { type: Types.Relationship, ref: 'PostCategory', index: true }
});
And category
PostCategory.add({
name: { type: String, required: true },
subCategories: { type: Types.TextArray }
});
Now I can add a list of subcategories to each category.
What I can't do is to display subcategories while creating a post. Also if I change category I need to load sub categories related to selected category.
My plan was to achieve that with watch functionality but it seems only works on save.
Another thing I was thinking about was to add subcategories as relationship, see ref:
categories: { type: Types.Relationship, ref: 'PostCategory.subCategories', index: true }
But it isn't working as well.
So, if anybody has any ideas how to achieve that, please share.
Thanks.
P.S. Don't hesitate to ask any additional information.

I created nested categories by creating a new model 'PostSubCategory' that allows the user to assign the parent category to the child category when they create the child category:
var keystone = require('keystone');
var Types = keystone.Field.Types;
/**
* PostSubCategory Model
* ==================
*/
var PostSubCategory = new keystone.List('PostSubCategory', {
autokey: { from: 'name', path: 'key', unique: true },
});
PostSubCategory.add({
name: {
type: String,
required: true
},
parentCategory: {
type: Types.Relationship,
ref: 'PostCategory',
required: true,
initial: true
}
});
PostSubCategory.relationship({ ref: 'Post', path: 'subcategories' });
PostSubCategory.register();
Then in my Post.js, I add a field to choose a subcategory with a filter on that field to only select from subcategories that are children of the parent category selected:
subcategory: {
type: Types.Relationship,
ref: 'PostSubCategory',
many: false,
filters: { parentCategory: ':categories' }
}
I'm not sure how well this would work for deeper nesting, and I do have an issue in the edit Post admin ui where changing the parent category for a post doesn't update the available subcategories to choose from until you save and refresh. But it got me far enough along for getting parent/child categories to work.

Related

Set Keystone List "noedit" based on field value?

I would like to make my Keystone List object editable only if the object is not published. Here's my reduced List definition:
var Campaign = new keystone.List('Campaign', {
nodelete: true,
track: {
createdAt: true,
},
});
Campaign.add({
...,
publish: {
type: Types.Boolean,
required: false,
initial: false,
dependsOn: {
publishedOn: '',
},
},
publishedOn: {
type: Types.Datetime,
label: 'Published On',
hidden: true,
},
});
Is it possible to set noedit only if publishedOn is not null? I'm trying to prevent the object from being modified after it's "published", and am coming up short on examples.
The noedit property for a list or field is part of the model definition, and not part of the definition of an individual item. There is no way to mark a single item as uneditable in the admin UI unless you want to apply it to the field or model as a whole.
If you are less worried about clarity, and more worried about ensuring it cannot be edited, you could try the following:
Campaign.schema.post('init', function () {
if (this.published) this.invalidate('Published items cannot be edited');
});
This will cause an error to be thrown if you attempt to save the item once it is published.
While you could use dependsOn: { published: { $exists: true } } to filter fields, this would hide the information.
Interestingly, I was able to make the publish field simply check itself:
Campaign.add({
...,
publish: {
type: Types.Boolean,
required: false,
initial: false,
dependsOn: {
publish: false,
},
},
...,
});
and now it only shows if publish is false, which works exactly as I hoped it would.

set 'dependsOn' on a Relationship field in KeystoneJS

I wonder if there is a way to achieve dependsOn option for a Types.Relationship field in KeystoneJS.
Example:
Event.add({
category: { type: Types.Relationship, ref: 'EventCategory' },
singer: { type: String, dependsOn: { category: { type: 'concerts' } } },
});
Currently, I can only walk around this by manually set dependsOn to the id of the related field.

How to add custom data in a story board?

Question 1: Is it possible to add custom data (Ex: testcase count : 5) to each card in a story board? If so, how? I couldn't find an example or specific information in documentation.
Question 2: Is it possible to get testcase count (including child story testcases)for a highlevel story in one query?
Please let me know. Here's my code
Ext.define('Rally.Story.View', {
extend: 'Rally.app.App',
launch: function() {
this.add({
xtype: 'rallyfieldvaluecombobox',
fieldLabel: 'Filter by Target Release:',
model: 'UserStory',
field: 'c_TargetRelease',
value: '15.0',
listeners: {
select: this._onSelect,
ready: this._onLoad,
scope: this
}
});
},
_onLoad: function() {
this.add({
xtype: 'rallycardboard',
types: ['User Story'],
attribute: 'ScheduleState',
readOnly: true,
fetch: ['Name', 'TestCases', 'c_StoryType', 'c_TargetRelease', 'PlanEstimate', 'Priority', 'TaskEstimateTotal', 'TaskRemainingTotal'],
context: this.getContext(),
cardConfig: {
editable: false,
showIconsAndHighlightBorder: false,
fields: ['Name', 'c_StoryType', 'c_TargetRelease', 'PlanEstimate', 'c_PriorityBin', 'Parent', 'TestCases', 'TaskEstimateTotal', 'TaskRemainingTotal']
},
storeConfig: {
filters: [
{
property: 'c_StoryType',
value: 'SAGA Feature'
},
{
property: 'c_TargetRelease',
operator: '=',
value: this.down('rallyfieldvaluecombobox').getValue()
}
]
}
});
},
_onSelect: function() {
var board = this.down('rallycardboard');
board.refresh({
storeConfig: {
filters: [
{
property: 'c_StoryType',
value: 'SAGA Feature'
},
{
property: 'c_TargetRelease',
operator: '=',
value: this.down('rallyfieldvaluecombobox').getValue()
}
]
}
});
},
});
Here's a sample card I made that contains the test case count:
You can add a field by simply including an object with some rendering information in it instead of just a simple string in the fields array in the cardConfig:
cardConfig: {
fields: [
'Name', //simple string field to show
{
name: 'TCCount', //field name
hasValue: function() {return true;}, //always show this field
renderTpl: Ext.create('Rally.ui.renderer.template.LabeledFieldTemplate', {
fieldLabel: 'Test Case Count', //the field label
valueTemplate: Ext.create('Ext.XTemplate',
['{[this.getTestCaseCount(values)]}',
{getTestCaseCount: function(data) { return data.Summary.TestCases.Count;}}])
})
},
//additional string fields
'PlanEstimate', 'Parent', 'TestCases', 'TaskEstimateTotal', 'TaskRemainingTotal']
}
This ended up being less straightforward than I thought it might be, but at least it is doable. The key part is using the LabeledFieldTemplate, specifying a field label and a value template to actually render the content.
You'll also notice the little beaker status icon in the footer which is automatically rendered because TestCases was included in the fields list.
As for your second question there is no roll up field on story for the total number of test cases included on child stories.

keystoneJS relationship to self

I want to create a Category model that can hold another category, but having a problem with reference field that I can set my current category to it self
Any suggestions how to achieve hierarchical categories?
Does KeystoneJS have filter like 'not equal'?
In other hand, maybe I can set default reference field to it self and it will be like a root...
My current code below:
var keystone = require('keystone'),
Types = keystone.Field.Types;
var PageCategory = keystone.List('PageCategory', {
map: { name: 'name' },
autokey : { from: 'name', path: 'key'}
});
PageCategory.add({
name: { type: String, required: true, unique: true},
image: { type: Types.CloudinaryImage, label: "Category Image"},
description : { type: Types.Html, wysiwyg: true},
parent: { type: Types.Relationship, ref: "PageCategory", label: "Parent category"}
});
PageCategory.relationship({ ref: "PageCategory", path: "parent"});
PageCategory.register();
I think you have misunderstood how Model.relationship() works.
It has three options:
path, this is the "virtual" field name that will hold the values
ref, this is the model that we reference
refPath, this is the field in the referenced model that we populate path with
I think something in line with this will work for you
PageCategory.relationship({ ref: "PageCategory", path: "children", refPath: "parent"});

ExtJs4 Model with different poxy url's

I have defined a model which I want to use twice but with a different url int he proxy (in fact only the id differs) But how can I manage this?
Ext.define('TesterModel', {
extend: 'Ext.data.Model',
autoLoad: false,
fields: [
{ name: 'prename', type: 'string' },
{ name: 'lastname', type: 'string' },
{ name: 'dept', type: 'string' },
{ name: 'rackName', type: 'string' },
{ name: 'rackIP' , vtype:'IPAddress'}],
proxy: {
type: 'ajax',
url: 'php/getData_db.php?id=',
reader: {
type: 'json',
messageProperty: 'message',
root: 'data',
}
},
constructor: function() {
UrlParams=document.URL.split("?");
if(UrlParams.length > 1) {
SingleUrlParams=Ext.Object.fromQueryString(UrlParams[1]);
this.proxy.url = this.proxy.url + SingleUrlParams.right;
console.log(this.proxy.url);
}
return this;
}});
Ext.ModelMgr.getModel('TesterModel').load(0, { // load user with ID of "0"
success: function(tester) {
var rightPanel=Ext.getCmp('rightTester');
rightPanel.loadRecord(tester); // when tester is loaded successfully, load the data into the form
}
});
I thought that the constructor will be done before loading, but nope, it is done after. It's weired to me.
Any hints, please?
(the main URL it's like: .../index.html?left=xx&right=yy )
so I want to fill up a panel on the left with the one id, and a panel on the right window side eith th right id.
Thanks!
Try .getProxy().url = "what/ever.php"