I need two editable items to be mutually exclusive, such that when an admin user goes in to the edit interface and edits one, the other becomes greyed out (and blanked out).
Let's say I have something like this:
public sealed class MyPart : PartBase
{
[EditableTextBox]
public string Text1
{
get { return GetDetail(Names.Text1); }
set { SetDetail(Names.Text1, value); }
}
[EditableTextBox]
public string Text2
{
get { return GetDetail(Names.Text2); }
set { SetDetail(Names.Text2, value); }
}
}
In code, I can quite easily make these two text items mutually exclusive via the get/set functions, but how do I make them mutually exclusive in the N2 edit interface? Such that when the user types text into the Text1 box, the Text2 box becomes greyed out and read only and/or blanked out?
Is this achieved via a decoration/attribute in the code, or do I have to implement custom javascript?
If it is custom javascript, where and how do I plug my script into N2?
Thanks.
Got an answer on the N2 forums here: http://n2cms.codeplex.com/discussions/277768
Related
In my previous protractor JS project (This new one I will do it with TS) I created one class for all my elements and another one for my functions, something like this:
specs
|_reportPage
|_lib
|_pageElements.js
|_pageFunctions.js
Then I was importing the files as necessary, in this way was easy to find the info since the element list was long.
So far all examples online for protractor TS projects are short pageObject files with a couple of elements and methods, but I would like to know how to correctly proceed when the page requires a lot of elements and functions/methods.
For example, lets say we have 5 specs under the same folder that test the same page and this page is full of fields and tables.
What would be the best practice here? create 1 pageobject for each spec, create one long class with all the elements and functions...?
Thanks for your time!
To Extend my answer you can add additional layer as a service which can execute several actions from the flow in different pages.
Code example:
export class E2EService {
mainPage: MainPage = new MainPage();
innerPage: InnerPage = new InnerPage();
doSomethingE2E() {
this.mainPage.headerPage.weDoSomething();
this.mainPage.contentPage.weDoSomething()
this.innerPage.somethingComplicated();
}
}
export class MainPage {
public readonly headerPage: HeaderPage;
public readonly contentPage: ContentPage;
}
export class InnerPage {
headerPage: InnerHeaderPage;
contentPage: InnerContentPage;
public somethingComplicated() {
this.headerPage.weDoSomething();
this.contentPage.weDoSomething();
}
}
export class ContentPage {
private readonly elements = {
// elements
};
public weDoSomething() {
// code
}
public getElements() {
return this.elements;
}
}
export class HeaderPage {
private readonly elements = {
btn1: element(by.id('')),
div: element(by.id('')),
h2: element(by.id(''))
};
public weDoSomething() {
// code
}
public getElements() {
return this.elements;
}
}
Based on Infern0's answer, I did dependency injection to the classes:
class HeaderElements {
foo = element(by.id("foo"));
//List goes on...
}
class HomePageElements {
foo = element(by.id("foo"));
//List goes on...
}
export class MainCommonElementsPage {
headerElements: HeaderElements;
homePageElements: HomePageElements;
constructor() {
this.headerElements = new HeaderElements();
this.homePageElements = new HomePageElements();
}
}
Best practices, even for large Page Objects is this:
Each page should only have 1 page object class. All of the tools needed to access that page should be located here. Think of your page object as an API.
Don't break the PO into different parts, especially for large pages. You'll eventually need to modify the PO to adjust for content changes. Would you rather change 1 file or 12? This also ensures that each of your e2e tests will remain functional after you update the PO.
I have one PO that handles a page with a lengthy form. The form has 12 controls and three buttons (cancel, reset, and submit). I have about 30 functions that deal with the form. I don't like having more than 1-2 methods in my test, so if it gets more complicated, I add to the PO.
So I have this program set up for a simple dialogue tree, where I want to display a question and two options in the unity editor, and if you click one option, you either go to another level of the tree or a leaf. I want to use the composite design pattern to make separate level instances, each with different parameters for one question and two options and add them together into a list. What I'm stuck on is how do I start at the first level and traverse down the tree depending on which button I press. It seems like no matter what I do, it only displays the last level parameters added to the list. The best I can think is maybe add some sort of shifting list function during the button click events. If anyone could shoot some ideas, I would appreciate it. Thank you.
public class Level : MonoBehaviour {
bool button1Pressed;
bool button2Pressed;
private void Start()
{
Level Level1 = new Level("Hello", "Hi", "Shut Up");
Level leaf1 = new Level("Don't be Rude");
Level Level2 = new Level("What you Doing?", "Not Much", "None of your Business");
Level leaf2 = new Level("Well Excuuuuse Me");
Level Level3 = new Level("Can I do that too?", "Sure", "Go Away");
Level leaf3 = new Level("Fine. Be a Jerk");
Level Level4 = new Level("This is boring, can we do something else?", "Why not?", "You're boring");
Level leaf4 = new Level("I'll go be boring somewhere else");
Level Level5 = new Level("You want ice cream?", "Sounds Good", "I'm allergic");
Level leaf5 = new Level("ok.......");
Level leaf = new Level("I Want Chocolate");
Level1.add(Level1);
Level1.add(leaf1);
Level2.add(Level3);
Level2.add(leaf2);
Level3.add(Level4);
Level3.add(leaf3);
Level4.add(Level5);
Level4.add(leaf4);
Level5.add(leaf5);
Level5.add(leaf);
}
public static Text Textbox;
public static Button Button1;
public static Button Button2;
public string OptionA;
public string OptionB;
public string Question;
public string Leaf;
private List<Level> levels;
public Level(string question, string optionA, string optionB)
{
this.Question = question;
this.OptionA = optionA;
this.OptionB = optionB;
GameObject.FindGameObjectWithTag("Level").GetComponentInChildren<Text>().text = Question;
GameObject.FindGameObjectWithTag("OptionA").GetComponentInChildren<Text>().text = OptionA;
GameObject.FindGameObjectWithTag("OptionB").GetComponentInChildren<Text>().text = OptionB;
levels = new List<Level>();
}
public Level(string leaf)
{
this.Leaf = leaf;
Textbox.text = leaf;
}
public void add(Level lvl)
{
levels.Add(lvl);
}
public List<Level> getLevels()
{
return levels;
}
public void Button1Pressed()
{
}
public void Button2Pressed()
{
}
}
public class Initializer : MonoBehaviour {
public Text Textbox;
public Button Button1;
public Button Button2;
void Awake()
{
Level.Textbox = this.Textbox;
Level.Button1 = this.Button1;
Level.Button2 = this.Button2;
}
}
The short answer: All nodes know their parent and children.
There are a few ways to approach this problem. I'll explain an approach using a tree structure with multiple node classes. First we can examine the interactions with the player as you've outlined:
Making a dialog choice (speaking)
Observing a dialog response (listening)
There's also some important conditions we need to consider:
User terminated conversations
AI terminated conversations
From this outline we can build our tree with some classes. I've scratched out some examples but I haven't tested them. Hopefully it conveys the idea and you can build your own solution. It may also be more useful for you to make a single Node class that just knows which type it is. Another improvement would be using an interface or some way to generalize the parent/child relationship which would allow for more complicated tree structures.
class ChoiceNode
{
public ChoiceNode(ResponseNode myParent)
{
parent = myParent;
}
ResponseNode parent = null;
List<ResponseNode> children = new List<ResponseNode>;
bool canSayGoodbye = true;
}
class ResponseNode
{
public ResponseNode(ChoiceNode myParent, string myMessage)
{
parent = myParent;
parent.children.Add(this);
response = myMessage;
}
ChoiceNode parent;
ChoiceNode child;
string response;
}
We should now be able to use a method to display dialog choice by simply enumerating the ResponseNode.children. Then whenever we make a dialog choice we want to display the ResponseNode.response and then move to the ResponseNode.child to find the next set of dialog choices. When parent == null we're at the root branch. When child == null we display some termination text.
I hope this is helpful and gives you some ideas.
Ok, I tried to understand the logic of your code but there are several things that I don't understand, maybe it's better if I try to explain my "Unity dialogue tree".
First:
We need to create a Tree object. If you just want a binary tree you just need tree variables:
public class BinaryTree{
private string root;
private BinaryTree left;
private BinaryTree right
getters/setters
}
this object don't even need to be a Unity component.
root is "dialog"
left is "optionA"
right is "optionB"
if you don't need multiple answer just make left=right.
If you need a way to identify multiple answer I suggest you to create an object like this:
public class Tree{
private Dictionary<string,string> root;
private List<Tree> next;
getters/setters
}
root is again your dialog. A key (that identify your answer) and a value that is the actual dialog.
next is a List of Tree (you identify your answer by making a loop in your next and by checking the key of the dictionary).
Now in the Start method you need to create a new Tree object and set your nexts.
Example
Start(){
BinaryTree bn = new BinaryTree();
bn.Root = "Is this a question?";
BinaryTree left = new BinaryTree();
left.Root = "Nope";
bn.Left = left;
BinaryTree right = new BinaryTree();
right.Root = "Yes it is...";
bn.Right = right;
}
Almost the same for the Tree version:
Start(){
Tree bn = new Tree();
bn.Root = new Dictionary<string, string>();
bn.Root.Add("key1", "Do you need something?");
bn.Next = new List<Tree>();
Tree answer1 = new Tree();
answer1.Root = new Dictionary<string, string>();
answer1.Root.Add("key2", "Yes");
bn.Next.Add();
... iterate...
}
Of course this is just a basic example. The best way to initialize that is to add your dialog to an array and iterate.
Anyway.
Now you can (for example) create a button. In the Start you can Initialize it's text value to your root. In the PointerDown/Clicked method you can make an array of the possible answer keys and, for example, you can decide to generate multiple buttons for multiple answers (or just use 2 static button answer with the BinaryTree) and change the text based on the answer root value (or left/right value). each of the answer button should, in the PointerDown/Clicked method, send the key value (or left/right object) of your user selection (in practice the next value that will display in the main question button).
Of course the "question button" once clicked again should display the next of your answer (and maybe you can decide to add a bool question variable to your object... or maybe you can just decide to use only the left side for a question... or maybe you can decide that if there is only 1 value in the next List that value is a question and it should just show it in the main button text value).
And if the next is null of course you can end the conversation.
There are multiple ways to do this.
I would like to go through all fields in form data.
I know that in form I could do something like this :
// Go through all fields with IFormFieldVisitor
box.visitFields(new IFormFieldVisitor() {
#Override
public boolean visitField(IFormField field, int level, int fieldIndex) {
if (field instanceof MyClass) {
...
}
return true;
}
}, 0);
but form data doesn't have this options. How to do this in form data.
You can obtain them using
AbstractFormData.getFields to obtain the top-level fields. If you need nested fields as well, have a look at the more complex AbstractFormData.getAllFieldsRec().
AbstractFormData.getAllProperties to obtain properties that you have defined by annotating the getters and setters with #FormData
That was the simple case.
Now, if you are using the Scout Extension mechanism to add new elements to an existing form (and it's formdata), you will have to take those contributions into account.
If you need to do this, you can refer to the source code of the AbstractForm.importFormData to see how Scout implements this.
In my Form, I have Two TabPages, with the same DataSource for each.
In TabPageA I have all records from my DataSource. I select records in TabPageA and I want to only show, in TabPageB, those records I selected previously in TabPageA.
If I don't select anything in TabPageA I don't see anything in TabPageB
For example if in my GridA I selected Record#4 , in GridB I see only Record#4.
I don't think you'll be able to filter TabPageB differently from TabPageA easily due to the fact they share the same DataSource.
There are two quick ways to modify this.
Keep it as one DataSource:
Create a global QueryBuildRange to easily update the range on selected records.
public class FormRun extends ObjectRun
{
QueryBuildRange qbr;
}
Override the init of your datasource. Here you will set the qbr:
public void init()
{
super();
qbr = this.queryBuildDataSource().addRange(fieldNum(SalesTable, RecId));
}
Then override the pageActivated TabPage method for each to set the range and re-execute the query to your liking:
TabPageA:
public void pageActivated()
{
super();
qbr.value('');
SalesTable_ds.executeQuery();
}
TabPageB
public void pageActivated()
{
super();
element.createRange();
SalesTable_ds.executeQuery();
}
Method to update range:
public void createRange()
{
MultiSelectionHelper multiSelectionHelper = MultiSelectionHelper::createFromCaller(this);
common common;
Set set = new Set(Types::Int64);
// Necessary if the datasource you want is not the header datasource.
multiSelectionHelper.parmDatasource(SalesTable_ds);
common = multiSelectionHelper.getFirst();
while (common)
{
// Use of set not necessary, but a little cleaner.
set.add(common.RecId);
common = MultiSelectionHelper.getNext();
}
qbr.value(strRem(set.toString(), '"{}'));
}
This is somewhat dirty, but you get the idea and could clean it up as needed.
Two Datasources:
Update the range on the second datasource (used by TabPageB) based on the multi-selection of the first datasource. But I'm assuming you only want one datasource for some reason.
In Kentico 7, I'm trying to perform actions based on what alternative form is being submitted.
public partial class CMSModuleLoader
{
private class ObjectEventsAttribute : CMSLoaderAttribute
{
public override void Init()
{
ObjectEvents.Insert.Before += My_Create_Account_Page;
}
private void My_Create_Account_Page(object sender, ObjectEventArgs e)
{
if (e.Object is BizFormItem && e.Object != null)
{
BizFormItem formEntry = (BizFormItem)e.Object;
BizFormInfo form = formEntry.BizFormInfo;
if (form.FormName == "MyOpenAccount")
{
// somehow determine which alternative form this is
// do stuff with the fields in that alternative form
}
}
}
}
}
I've been up and down the documentation and found no solution. I could add a field that I would give a default value of the alt. form name, but that opens me up to editors deleting that field, and it still doesn't tell me what other fields are in the alternative form. Any other ideas?
I'm afraid that alternative form name is not accessible if you use ObjectEvents approach. But the information is known by the "Online form" (BizForm) webpart. So you probably have to customize it or create a copy. Bizform control has AlternativeFormFullName property and you can hook on one of its events like OnAfterSave.