https://wiki.mdriven.net/api.php?action=feedcontributions&user=Hans&feedformat=atomMDrivenWiki - User contributions [en]2024-03-28T09:38:43ZUser contributionsMediaWiki 1.39.4https://wiki.mdriven.net/index.php?title=Documentation:PopUp_action&diff=17652Documentation:PopUp action2024-03-22T12:18:56Z<p>Hans: </p>
<hr />
<div>Actions can bring up Views. A modifier of the view shown is the "Is Modal" modifier which brings up the view in a modal overlay that must be closed by "Ok" or "Cancel" before continuing with other actions on the original page.<br />
<br />
Yet another variation is the new "Is PopUp" modifier. <br />
[[File:2021-01-10 11h37 09.png|none|thumb|523x523px]] <br />
<br />
The PopUp is equal to the Modal in many ways but differs from the Modal:<br />
* By not having a header<br />
* By not having the "ok" and "cancel" buttons<br />
* By allowing you to dismiss the PopUp by clicking outside the popup view<br />
The PopUp is always submitting changes to the underlying view. Any cancel action you would like to perform must be done on the underlying (or starting) view.<br />
[[File:2021-01-10 11h34 01.gif|none|thumb|1136x1136px]]<br />
[[Category:WebUI]]<br />
[[Category:WPF]]<br />
[[Category:MDriven Designer]]<br />
[[Category:Actions]]<br />
<br />
You can also do a lightweight InPlacePopup that has everything defined in just one viewmodel. You do this by placing a button - and add a nesting to the button. The content from the nesting will render as a popup when the button is pressed.<br />
<br />
{{Edited|July|12|2024}}</div>Hanshttps://wiki.mdriven.net/index.php?title=SQL_Server&diff=17636SQL Server2024-03-20T19:29:51Z<p>Hans: </p>
<hr />
<div>SQLServer is the persistence mapper most frequently used by us at MDriven.<br />
A snapshot of its implementation is available here :<br />
[[File:SQLServerPMapper.zip|SQLServerPMapper.zip]]</div>Hanshttps://wiki.mdriven.net/index.php?title=SQL_Server&diff=17635SQL Server2024-03-20T19:29:32Z<p>Hans: </p>
<hr />
<div>SQLServer is the persistence mapper most frequently used by us at MDriven.<br />
[[File:SQLServerPMapper.zip|SQLServerPMapper.zip]]<br />
A snapshot of its implementation is available here :</div>Hanshttps://wiki.mdriven.net/index.php?title=SQL_Server&diff=17634SQL Server2024-03-20T19:29:10Z<p>Hans: Created page with "SQLServer is the persistence mapper most frequently used by us at MDriven. x A snapshot of its implementation is available here :"</p>
<hr />
<div>SQLServer is the persistence mapper most frequently used by us at MDriven.<br />
[[File:SQLServerPMapper.zip|none|thumb|x]]<br />
A snapshot of its implementation is available here :</div>Hanshttps://wiki.mdriven.net/index.php?title=File:SQLServerPMapper.zip&diff=17633File:SQLServerPMapper.zip2024-03-20T19:28:50Z<p>Hans: </p>
<hr />
<div>x</div>Hanshttps://wiki.mdriven.net/index.php?title=Persistence_mappers&diff=17632Persistence mappers2024-03-20T19:27:29Z<p>Hans: /* SQL Server */</p>
<hr />
<div>=== Background ===<br />
Persistence mapping, also known as object-relational mapping (ORM), is a technique used in MDriven to map objects in an object-oriented programming language to relational database tables. The goal of persistence mapping is to make it easier for programmers to work with data stored in databases by allowing them to use familiar object-oriented programming concepts to access and manipulate that data.<br />
<br />
Persistence mapping works by creating a mapping between each class in the programming language and a corresponding table in the database. The attributes of each class are mapped to columns in the table, and the methods of the class are mapped to the SQL statements used to manipulate the data in the table.<br />
<br />
MDriven provides an abstraction layer between the application and the database, allowing programmers to work with the database using familiar object-oriented programming concepts like classes, objects, and inheritance. The MDriven Framework takes care of translating the object-oriented programming constructs into SQL statements that are executed by the database.<br />
<br />
=== Benefits ===<br />
Persistence mapping provides several benefits, including:<br />
# '''Simplified data access''': By providing a simple and more intuitive interface for accessing and manipulating data in databases, Persistence mapping makes it easier for developers to work with databases and reduces the amount of boilerplate code required to interact with them.<br />
# '''Abstraction from database implementation details''': Persistence mapping provides an abstraction layer between the application and the database, allowing developers to work with data using object-oriented programming concepts without worrying about the underlying implementation details of the database.<br />
# '''Improved portability''': By abstracting away the implementation details of the database, persistence mapping makes it easier to switch between different database platforms or even different data storage mechanisms without having to change the application code.<br />
Overall, Persistence mapping is a powerful technique that simplifies data access, making it easier to work with databases in object-oriented programming languages. Persistence mapping helps developers write more maintainable, portable, and scalable code by providing an abstraction layer between the application and the database.<br />
<br />
=== MDriven Build-In Persistence Mappers ===<br />
<br />
==== As XML file ====<br />
Saves all of the objects in the database by serializing the objects to XML<br />
<br />
==== [[SQL Server]] ====<br />
<br />
==== [[MySQL–notes to support the use of MySQL with MDriven|MySQL]] ====<br />
<br />
==== [[SQLite]] ====<br />
{{Edited|July|12|2024}}</div>Hanshttps://wiki.mdriven.net/index.php?title=WebAssembly_2020&diff=17610WebAssembly 20202024-03-19T10:01:32Z<p>Hans: </p>
<hr />
<div>'''Update and notes on WebAssembly (WASM) Status.'''<br />
<br />
The WASM and MONO linkers strip stuff not discovered as important (used), and this has been an issue for MDriven since we do some reflection to determine what to use in runtime (after linker). This has caused exceptions with "missing method" information.<br />
<br />
If you add a tagged value to the package "CodeGen.GenerateConstructorReferences", we will generate the use of certain important things that the linker before has stripped. You must add a reference to this new method to tie your code to this and avoid stripping by linker: '''YourPackage.ConstructorReferatorToAvoidLinkerStripping'''() .<br />
<br />
One mitigation is that we now generate an extra Attribute with codegen called Preserve: The Preserve attribute is recongnised by Mono linker and instructs it to NOT prune away even is no usage is seen.<br />
<br />
Furthermore, the same issue exists for code-generated ViewModels, and you must call: '''ClassLibrary1.ViewModelCodeGen_ViewModel1.ViewModel1.ReferenceToAllConstructorsToHelpWasmLinker();'''<br />
<br />
We will try to make use of the UNO framework together with MDriven to bring model-driven benefits and one source to all the most common platforms:<br />
[[File:Unodiagram.png|none|thumb]]<br />
[[Category:Beta Testing]]<br />
{{Edited|July|12|2024}}</div>Hanshttps://wiki.mdriven.net/index.php?title=Documentation:OCLOperators_format&diff=17519Documentation:OCLOperators format2024-03-15T10:58:55Z<p>Hans: </p>
<hr />
<div>The format is a static method of the type String:<br />
String.format('thetemplate {0} - {1} that can have any number of replacers, in this case {2}','hello','there',3)<br />
The sample above will produce:<br />
{| class="wikitable"<br />
|thetemplate hello - there that can have any number of replacers 3,1<br />
|}<br />
[[Category:OCL General Operators]]<br />
For more details see [https://learn.microsoft.com/en-us/dotnet/api/system.string.format?view=net-8.0 Microsofts page]<br />
<br />
{{Edited|July|12|2024}}</div>Hanshttps://wiki.mdriven.net/index.php?title=Documentation:OCLOperators_format&diff=17518Documentation:OCLOperators format2024-03-15T10:57:25Z<p>Hans: </p>
<hr />
<div>The format is a static method of the type String:<br />
String.format('thetemplate {0} - {1} that can have any number of replacers, in this case {2}','hello','there',3)<br />
The sample above will produce:<br />
{| class="wikitable"<br />
|thetemplate hello - there that can have any number of replacers 3,1<br />
|}<br />
[[Category:OCL General Operators]]<br />
{{Edited|July|12|2024}}</div>Hanshttps://wiki.mdriven.net/index.php?title=Per_viewmodel_ReadOnly_mode&diff=17460Per viewmodel ReadOnly mode2024-03-14T11:48:07Z<p>Hans: </p>
<hr />
<div>Some systems want to make use of distinction between ''opening a page for viewing'' - and ''intending to edit the data within''.<br />
<br />
The reasons for this may be many but one that was top of mind during our design is that we have inexperienced users that are afraid to touch or update things they do not intend to update. These users can be made to feel safer if there is a switch on the page you need to flip in order to edit anything.<br />
<br />
Of course users still want to search and navigate the system - so the readonly logic should function much like AccessGroup ViewEnable expression - that only locks down actions and controls that actually change persistent data.<br />
<br />
Resources and abilities available now for a framework backed per page readonly model:<br />
<br />
# vSysReadOnlyMode:Boolean : a Special ViewModel variable will be detected and applied<br />
## When seen it will be "or"ed into the ViewModel.ExternalReadOnlyExpression - sharing this expression with any AccessGroups.ViewEnableExpressions.<br />
# WillEffectPersistedDataOverrideValue : a tagged value to be applied to columns, actions and reverse-derived-attributes to be used when you want to exempt from the automatic expression deduction if it changes persistent data or not.<br />
# vSysReadOnlyMode is set to true on open of View<br />
# : vSysReadOnlyMode is set reset from false to true after save and after cancel<br />
# A special button placed along Save/Cancel called Lock/Unlock that toggles vSysReadOnlyMode<br />
## The default text in Turnkey will be "Write" as in "enter write mode" - to influence/translate the text create a global action of with Framework action: ExecuteFrameworkRuntimeActionRT.ReadOnlyModeToogle<br />
# A way to set global readonly mode for the whole application<br />
## Once the global readonly is in effect you can skip for certain ViewModels by setting taggedvalue SysReadOnlyModeSkip=true on ViewModel<br />
## To turn on global readonly set [[SysMDrivenMiscSettingsSingleton]].GlobalReadOnlyMode = true<br />
{{Edited|July|12|2024}}</div>Hanshttps://wiki.mdriven.net/index.php?title=Documentation:Column.StringFormat&diff=17436Documentation:Column.StringFormat2024-03-13T10:36:26Z<p>Hans: </p>
<hr />
<div>{| class="wikitable"<br />
!#TargetType.TaggedValue<br />
!CommaSeparatedExampleValues<br />
!Short Description<br />
|-<br />
|#Column.StringFormat<br />
|<identifier><br />
|WPF Only: TaggedValue StringFormat on ViewModel column has precedence for finding a binding Stringformat - normally the stringFormat is taken from <StyleRef>.StringFormat, but if this TV has value - <value>.StringFormat is used. See: [[Text_formatting]] <br />
For angularjs see [[Documentation:Column.StringFormatAngular|StringFormatAngular]]<br />
|}<br />
[[Category:Tagged Values]]<br />
{{Edited|July|12|2024}}</div>Hanshttps://wiki.mdriven.net/index.php?title=Documentation:Turnkey_debug&diff=17435Documentation:Turnkey debug2024-03-13T08:44:25Z<p>Hans: </p>
<hr />
<div>=== The Different Scenarios for Debugging Turnkey Generic Code ===<br />
# You have the MDrivenServer stable and you want to step through Turnkey-generic code<br />
# Debug CodeDress code from the model '''''or''''' you have the MDrivenServer stable and want to step through Turnkey-generic code <br />
# You want to debug toward local prototyping XML-based persistence, cutting MDrivenServer out of the equation<br />
A walk-through article complementing this Wiki post is available here: https://blog.mdriven.net/debug-codedress-turnkey-locally/<br />
<br />
=== Starting Turnkey Locally ===<br />
Please look [[Development in Visual Studio|here]] for instructions on downloading, unpacking, and opening Turnkey as a Web site in Visual Studio.<br />
<br />
=== Debugging ===<br />
<br />
==== 1: MDrivenServer is stable and you want to step through Turnkey-generic code ====<br />
Use MDrivenServerOverride.xml with the setting:<br />
<MDrivenServerOverride MDrivenServerPWD="pwd for mdrivenserver user a">https://&#x3C;urltomdrivenserver usually ending with __MDrivenServer></MDrivenServerOverride><br />
<br />
==== 2: Debug CodeDress code from the model '''''or''''' you have the MDrivenServer stable and you want to step through Turnkey-generic code ====<br />
Make sure you have the same model version in the MDrivenServer as you have locally in Visual Studio. Use MDrivenServerOverride.xml with the setting:<br />
<MDrivenServerOverride MDrivenServerPWD="pwd for mdrivenserver user a" '''CodeDressOverride'''="C:\PathToCodeDress_bin_Debug_assemblies" >https://<urltomdrivenserver usually ending with __MDrivenServer></MDrivenServerOverride><br />
<br />
==== 3: You want to debug toward local prototyping XML-based persistence, cutting MDrivenServer out of the equation ====<br />
Note that for this option CodeDress will always try to find assemblies close to the model rather than in ModelCodeAssemblies. The tried paths for assemblies are "bin/debug" and "../bin/debug" (relative to the model path): Use MDrivenServerOverride.xml with the setting:<br />
<MDrivenServerOverride '''PrototypeWithMDrivenDesignerInPath'''="C:\PathToLocalModel modlr or ecomdl"></MDrivenServerOverride><br />
When you debug and build components ([[Documentation:EXT Components|EXT_Components]]) its practical to have a very small test-case-model - but that would force you to copy component files from the main model. Use the setting '''ExternalAssetsTK''' to stear you test model to read assets from your main model<br />
<MDrivenServerOverride PrototypeWithMDrivenDesignerInPath="C:\temp\DocComp\DocComp.modlr" MDrivenServerPWD="somepwd" '''ExternalAssetsTK'''="C:\Users\...\SomeOther_AssetsTK\"></MDrivenServerOverride><br />
All steps require Turnkey generic code access to work fully but can be used partially by opening Turnkey local installations in Visual Studio as a website.<br />
<br />
If you want to see the status of a running Turnkey app, look here: [[Serverinfo]]<br />
<br />
=== Problem-solving ===<br />
If you run into problems, here are some possible situations and how to correct them:<br />
* Make sure you have the same installed version of TK and the installed Framework in VS. If you uninstall the Framework, try [[Downgrade MDriven Framework version|Clean GAC]] to be sure you don't load old versions.<br />
* Exception in VS with [[No suitable constructor found]]<br />
* Old versions in the GAC get loaded from your CodeDress. Uninstall Framework and then [[Downgrade MDriven Framework version|Clean GAC]]<br />
<br />
=== Other Tips and Tricks ===<br />
* There are many Chrome plugins to intercept where files are loaded from -> great when you want your app to grab stuff from your development folder to avoid reloading the CSS all the time.<br />
* There are Chrome plugins for intercepting redirects -> great when you have an OpenID provider set up for production but need to debug locally - then catch the OpenID redirect back to your localhost.<br />
<br />
=== .net Framework vs .net Core ===<br />
The binaries for .net Core and .net Framework are different and CodeDress from a core-built-server with a Framework-built model will not work. Create a new ModelProject in VS that targets .net Core and build the model with that to get CodeDress to load.<br />
[[Category:MDriven Turnkey]]<br />
[[Category:Debugging]]<br />
{{Edited|July|12|2024}}</div>Hanshttps://wiki.mdriven.net/index.php?title=MDrivenDesignerTaggedValueHints&diff=17353MDrivenDesignerTaggedValueHints2024-03-10T12:12:24Z<p>Hans: </p>
<hr />
<div>The intention of this information is to be downloaded by MDrivenDesigner at startup to guide what tagged values can be used where.<br />
<br />
'''All valid Tagged values must be added here.'''<br />
#TargetType.<Modernity.>Function<br />
We deliberately skipped the ones that have Designer properties already.<br />
{| class="wikitable"<br />
!#TargetType.TaggedValue<br />
!CommaSeparatedExampleValues<br />
!Short Description<br />
|-<br />
|#Column.DataIsImageUrl<br />
|True,False<br />
|When set on a ViewModelColumn, the data string is treated as an Image URL by Turnkey. See: [[Column.DataIsImageUrl]]<br />
|-<br />
|#Column.DataIsLink<br />
|True,False<br />
|When set on viewmodelColumn, the string data within is treated as a hyperlink. See: [[Column.DataIsLink]]<br />
|-<br />
|#Column.BlobDownloadLink<br />
|True,False<br />
|When set on a ViewModelColumn, the Blob content can be downloaded by a hyperlink rather than rendered. See: [[Column.BlobDownloadLink]]<br />
|-<br />
|#Nesting.Editable<br />
|True,False<br />
|Nesting that represents the grid will render web grid cells as possibly editable (depending on read-only expression). See also: [[Nesting.Editable]]<br />
|-<br />
|#Column.MaxFetch<br />
|<int><br />
|Declare "MaxFetch=<int>" on the ViewModelColumn action that initiates the search to change from the default (100). See also: [[Column.MaxFetch]], [[MaxFetch]]<br />
|-<br />
|#Attribute.ExternalBlobStorage<br />
|True,False<br />
|Large files can be kept out of the granular model database. This is a turnkey functionality only - and the files will be stored (with the simplest out-of-the-box solution) on disk App_Data/BlobStorage. See: [[Attribute.ExternalBlobStorage]]<br />
|-<br />
|#Column.StringFormat<br />
|<identifier><br />
|WPF Only: TaggedValue StringFormat on ViewModel column has precedence for finding a binding Stringformat - normally the stringFormat is taken from <StyleRef>.StringFormat, but if this TV has value - <value>.StringFormat is used. See [[Column.StringFormat]], [[Text_formatting]]<br />
|-<br />
|#Span.MVC<br />
|True,False<br />
|The Turnkey WebApplication uses MVC for index and login pages - it defaults to angular js for all other pages. To get MVC on other pages you must set the tagged value MVC=true on ViewModel. See: [[Span.MVC]]<br />
|-<br />
|#Column.Angular_Ext_Component<br />
|<Angular component name><br />
|Set UIOverride on ViewModelColum - assign this tagged value - turnkey will now look for a component definition in EXT_Components folder. See: [[Column.Angular Ext Component]]<br />
|-<br />
|#Span.DoNotSearchOnEnter<br />
|Existence<br />
|UI's in WPF and Turnkey will search on enter as it has seek expressions - you can turn this ff by defining this TV on the ViewModel level. See also: [[Span.DoNotSearchOnEnter]]<br />
|-<br />
|#Span.Bootstrap.ClassPrefix<br />
|col-xs-<br />
|Alter the default Bootstrap column prefix from col-sm- to something else. See: [[Bootstrap]]<br />
|-<br />
|#Span.Bootstrap.StaticSections<br />
|Existence<br />
|The view parts above the topmost splitter and below the bottom-most splitters will be static in the browser. The middle section scrolls. See: [[Bootstrap]]<br />
|-<br />
|#Method.Eco.ExternalLateBound<br />
|Existence<br />
|If TV is set the ExternalLateBound logic is searched for implementation of this method. See also: [[Method.Eco.ExternalLateBound]]<br />
|-<br />
|#Span.CSSGrid<br />
|True,False<br />
|If this is true then MVC and angular views will Render with CSSGrid instead of with Bootstrap. See: [[Span.CSSGrid]]<br />
|-<br />
|#Nesting.MultiSelect<br />
|True,False<br />
|Allows multi-select - you can then expect vSelected_<NameOfNesting> to be maintained. See: [[Nesting.MultiSelect]]<br />
|-<br />
|#Column.DataIsHtml<br />
|True,False<br />
|When True static fields treat the data as markup and display potential HTML in the browser. See: [[Column.DataIsHtml]]<br />
|-<br />
|#Span.Eco.RestAllowed<br />
|True,False<br />
|Enables a ViewModel to be accessed with REST API (default False). See: [[Span.Eco.RestAllowed]]<br />
|-<br />
|#Span.Eco.UIAllowed<br />
|True,False<br />
|Disables a ViewModel to be accessed within the UI (default True). See: [[Span.Eco.UIAllowed]]<br />
|-<br />
|#Span.Eco.AutoSave<br />
|True,False<br />
|Enables a viewModel to automatically save all changes without showing the Save/Cancel buttons. See: [[Span.Eco.AutoSave]]<br />
|-<br />
|#Column.XmlAttribute<br />
|True,False<br />
|When using ViewModelAsXml, ViewModel column will be rendered as an XML Attribute. See: [[OCLOperators ViewModelAsXml|ViewModelAsXML]], [[OCLOperators ViewModelAsJSon|ViewModelAsJSon]]<br />
|-<br />
|#Column.XmlChildnode<br />
|True,False<br />
|When using ViewModelAsXml, ViewModel single link column will be rendered as an XML element tree node. See: [[OCLOperators ViewModelAsXml|ViewModelAsXML]], [[OCLOperators ViewModelAsJSon|ViewModelAsJSon]]<br />
|-<br />
|#Column.NodeName<br />
|<name of node><br />
|Used to override the XML och JSON node name usually given by the ViewModel column. Used for example to input a - (dash) as a part of the name of an attribute. See: [[OCLOperators ViewModelAsXml|ViewModelAsXML]], [[OCLOperators ViewModelAsJSon|ViewModelAsJSon]]<br />
|-<br />
|#Column.MaxFileSizeInBytes<br />
|10000000<br />
|Limits Blobs and Images to a maximum size of the file (primarily web). See: [[Column.MaxFileSizeInBytes]]<br />
|-<br />
|#Column.AcceptFiles<br />
|image/*,.pdf<br />
|Rendered in HTML on the <input> file tag as the "accept" attribute. For more info, search for the "accept" attribute. See also: [[Upload/Download files and images in MDriven Turnkey]]<br />
|-<br />
|#Span.HideSidebar<br />
|True,False<br />
|Hide the sidebar on the page - Note: Use the new HideSidebar checkbox on ViewModel. See: [[Span.HideSidebar]]<br />
|-<br />
|#Span.HideMenubar<br />
|True,False<br />
|Hides Menubar for page - Note: Use the new HideMenubar checkbox on ViewModel. See: [[Span.HideMenubar]]<br />
|-<br />
|#Span.FixColSize<br />
|True,False<br />
|Set equal columns size on the page. See: [[Span.FixColSize]]<br />
|-<br />
|#Column.FixColSize<br />
|True,False<br />
|Set equal columns size inside the groupboxes. See: [[Column.FixColSize]]<br />
|-<br />
|#Column.HideHeading<br />
|True,False<br />
|Hide the heading of the groupbox. See: [[Column.HideHeading]]<br />
|-<br />
|#Column.OpenInNewTab<br />
|True,False<br />
|Open the link in the new browser tab<br />
|-<br />
|#Nesting.Eco.WECPOFDoubleClickAction<br />
|None,DefaultAction,ModalOkPrecedence<br />
|Implemented for WPF but should follow in Turnkey. Controls what should happen on Grid double click.<br />
|-<br />
|#Column.Switch<br />
|True,False<br />
|Change the checkbox appearance to the switch control. See: [[Column.Switch]]<br />
|-<br />
|#Nesting.CellSelect<br />
|True,False<br />
|In WPF, this controls if the grid should get CellSelect from start rather than row select. See also: [[Nesting.CellSelect]]<br />
|-<br />
|#Column.Placeholder<br />
|Placeholder text<br />
|Add placeholder text for text and number fields. See: [[Column.Placeholder]]<br />
|-<br />
|#Column.autofocus<br />
|True,False<br />
|Focuses the control when it appears on the screen. Might conflict with other focus events. See: [[Column.autofocus]] <br />
|-<br />
|#Column.autocomplete<br />
|True, False<br />
|Hint for form autofill feature (disabled by default for every input field), See: [[Column.autocomplete]]<br />
|-<br />
|#Nesting.FrozenColumns<br />
|1,2<br />
|In WPF DataGrid can freeze leftmost columns from scrolling (aka fixedcolumns). See: [[Nesting.FrozenColumns]]<br />
|-<br />
|#Attribute.CheckIdNameConflict<br />
|True,False<br />
|Avoids warning about naming conflicts between a table and the automatically named ID columns when generating a SQL database. See also: [[Attribute.CheckIdNameConflict]]<br />
|-<br />
|#Column.XmlParentValue<br />
|True,False<br />
|Makes the attribute's value appear as the parent element's value instead of an element. Note: if used on more than one attribute on the same ViewModel class, the result is undefined. See: [[OCLOperators ViewModelAsXml|ViewModelAsXML]]<br />
|-<br />
|#Column.Resizable<br />
|True,False<br />
|Make the text area element resizable. See: [[Column.Resizable]]<br />
|-<br />
|#Column.Eco.ImageWidth<br />
|20,30,40<br />
|Controls WPF Images in Grids default is 20. <br />
|-<br />
|#Column.Eco.ImageHeight<br />
|20,30,40<br />
|Controls WPF Images in Grids default is 20<br />
|-<br />
|#Column.FormatAttr<br />
|<br />
|Sets the angular format attribute that controls text rendering. Use [[Column.StringFormatAngular|StringFormatAngular]] instead. See: [[Column.FormatAttr]]<br />
|-<br />
|#Column.StringFormatAngular<br />
|<br />
|Sets the angular format attribute that controls text rendering. Replaces the FormatAttr to resemble WPF and Razor. See also: [[Column.StringFormatAngular]], [[Text_formatting]]<br />
|-<br />
|#Column.StringFormatRazor<br />
|<br />
|Sets the Razor format attribute that controls text rendering. Replaces the FormatAttr to resemble WPF and Angular. See: [[Column.StringFormatRazor]], [[Text_formatting]]<br />
|-<br />
|#Column.Texttype<br />
|password,email,tel,url,search<br />
|For inputs intended as type text, this type can be overridden with something else. See: [[Column.Texttype]]<br />
|-<br />
|#Attribute.Eco.BlobType<br />
|SVG,XamlSymbol,Image,Blob,RichText<br />
|Set on attributes of classes to influence how a binary array or Text field should be interpreted. See: [[Attribute.Eco.BlobType]]<br />
|-<br />
|#Column.Eco.BlobType<br />
|SVG,XamlSymbol,Image,Blob,RichText<br />
|Set on Column to influence how a binary array or Text field should be interpreted. See: [[Column.Eco.BlobType]]<br />
|-<br />
|#Nesting.Striped<br />
|True,False<br />
|Adds the striped style to your table. See: [[Nesting.Striped]]<br />
|-<br />
|#Span.ModalSize<br />
|narrow,large,veryLarge<br />
|Choose the size of the modal window which will contain your ViewModel. See: [[Span.ModalSize]]<br />
|-<br />
|#Nesting.IsSeekerResultGrid<br />
|True,False<br />
|Seeker page logic uses this to show buttons in the grid. See: [[Nesting.IsSeekerResultGrid]]<br />
|-<br />
|#Attribute.Realtime<br />
|True,False<br />
|If changed it will be invalidated in all clients upon saving asap - ie by an active push from the server. See also: [[SignalR and Realtime]]<br />
|-<br />
|#Column.Icon<br />
|<br />
|Set the icon to your action from the default [https://material.io/resources/icons/?style=baseline Material Design icons pack]. See: [[Material Design Icons]]<br />
|-<br />
|#Column.IconPosition<br />
|before,after<br />
|Choose the position of the icon inside your action button. By default, the icon is placed before the button text. See: [[Material Design Icons]]<br />
|-<br />
|#Column.IconButton<br />
|True,False<br />
|Displays just the icon instead of the button text. See: [[Material Design Icons]]<br />
|-<br />
|#Column.IconInInput<br />
|True,False<br />
|Enable this Tagged value to put the icon inside the input control instead of the label. See: [[Material Design Icons]]<br />
|-<br />
|#Nesting.ClientSortable<br />
|True,False<br />
|Makes all columns in the table sortable by clicking on the header. Can be overridden on each column. Defaults to True for tables that are not seeker results. See: [[Nesting.ClientSortable]]<br />
|-<br />
|#Column.ClientSortable<br />
|True,False<br />
|Makes the column sortable by clicking on the header. Default is inherited from the table. Default is False in Seekers. See: [[Column.ClientSortable]]<br />
|-<br />
|#Column.SeekerSortable<br />
|True,False<br />
|Makes the column sortable by clicking on the header in a Seeker. See: [[Column.SeekerSortable]] <br />
|-<br />
|#Nesting.Eco.SeekerDefaultOrderColumnName<br />
|<br />
|Name of the attribute in the OrderExpression ViewModel class to use when a result is found by using this search expression. See also: [[Seeker view]]<br />
|-<br />
|#Nesting.Eco.HiliteGridColumn<br />
|<br />
|Viewmodel name of the SearchResultGrid result and the Name of the attribute in that ViewModel in the format <ViewModelName>.<AttributeName>. This column will be highlighted (coloured) when the result is found by using this search expression. See also: [[Seeker view]]<br />
|-<br />
|#Span.XmlRootTag<br />
|<br />
|Influence root tag used in [[OCLOperators ViewModelAsXml|ViewModelAsXML]]. See also: [[Span.XmlRootTag]]<br />
|-<br />
|#Span.SkipWhenBacking<br />
|True,False<br />
|When pressing back into this view we skip and move to the view before - (Implemented in WPF) - Good if you have a switchboard view that automatically navigates somewhere<br />
|-<br />
|#Nesting.ShowOldContextMenu<br />
|True,False<br />
|If this option is enabled - the old context menu will appear instead of the new one.<br />
|-<br />
|#Column.TaJsonTreatListAsDynamicProperties<br />
|True,False<br />
|When this is true, we read the nesting in the ViewModel object and look for Name and Value properties - we then use the result as properties on the resulting json object. Read more: [[Tajson]]<br />
|-<br />
|#Column.TaJsonTreatListAsValues<br />
|True,False<br />
|When this is true, we generate a json array of the first column in the ViewModel nesting. This gives a json array of values rather than of objects. Read more: [[Tajson]]<br />
|-<br />
|#Column.SkipStyleLogic<br />
|True,False<br />
|To be used when _Style column is not to be interpreted as a style name in Xaml or CSS-class in HTML - instead, it is just data picked up by some other style's binding - or HTML style info. See also: [[Column.SkipStyleLogic]], [[Turnkey Styling]]<br />
|-<br />
|#Attribute.AllowMVCAccess<br />
|ok,notok,okwhenauthenticated<br />
|The GetImage of MVC - this is the default "ok" for byte[] and default not ok for strings. See: [[MVC GetImage]]<br />
|-<br />
|#Nesting.IncludeCurrentAndSelectedVariables<br />
|True,False<br />
|Default True, which includes vCurrent and vSelected as data in a streaming [[ViewModel|ViewModel]]. Used when easy access to current and selected row are not wanted when databinding. See: [[Nesting.IncludeCurrentAndSelectedVariables]], [[VCurrent and vSelected]]<br />
|-<br />
|#Column.DesignTimeColor<br />
|Red,Green,Blue,#1278FF<br />
|This sets a background color of the ViewModel column in the ViewModel tree - it is a design time help<br />
|-<br />
|#Column.RawJSon<br />
|True,False<br />
|When using AsTajson you may want to inject snippets of already formatted json into the tree. Read more: [[Tajson]]<br />
|-<br />
|#Span.Savebar<br />
|True,False<br />
|Shows a "savebar" at the top of the screen when unsaved changes exists. Also shows Undo/Redo buttons. Will also hide the left sidebar. See also: [[Span.Savebar]]<br />
|-<br />
|#Span.TurnkeyTimeoutMinutes<br />
|<br />
|Delay in minutes until user will be timed out from current [[ViewModel]]. See: [[Span.TurnkeyTimeoutMinutes]], [[Turnkey Client Timeout]]<br />
|-<br />
|#Span.MemorySeeker<br />
|True,False<br />
|If set to true Seeker logic will use OCL to search in memory rather than the standard OCLPS to search in db<br />
|-<br />
|#Span.UseAngularJSComponents<br />
|True,False<br />
|Turn on for the view the new implementation of rendering using AngularJS component based application structure.<br />
|-<br />
|#Column.Fab<br />
|True,False<br />
|Changing default button presentation to the FAB (Floating action button). Requires icon.<br />
|-<br />
|#Column.TypeToPresent<br />
|time,datetime-local,week,month<br />
|Changing the presentation of DateTime object based on chosen option.<br />
|-<br />
|#Column.WillEffectPersistedDataOverrideValue<br />
|true,false,notset<br />
|Override of the logic that deduce if an expression effects persistent data or not<br />
|-<br />
|#ContextAction.WillEffectPersistedDataOverrideValue<br />
|true,false,notset<br />
|Override of the logic that deduce if an expression effects persistent data or not<br />
|-<br />
|#ClassAction.WillEffectPersistedDataOverrideValue<br />
|true,false,notset<br />
|Override of the logic that deduce if an expression effects persistent data or not<br />
|-<br />
|#Nesting.NavigateOnSingleClickEnable <br />
|true,false<br />
|When set on Nesting displaying grid that has only 1 enabled action that is navigating - execution happens on row select<br />
|-<br />
|#Nesting.AdvancedTable<br />
|true,false<br />
|This turn on resize in tables, also see the [[Documentation:TurnkeySettings|TurnkeySetting AdvancedTables]]<br />
|}<br />
[[Category:MDriven Designer]]<br />
[[Category:Tagged Values]]<br />
{{Edited|July|12|2024}}</div>Hanshttps://wiki.mdriven.net/index.php?title=Documentation:VCurrent_and_vSelected&diff=17257Documentation:VCurrent and vSelected2024-03-01T17:07:22Z<p>Hans: </p>
<hr />
<div>[[Training:ViewModel|ViewModels]] create and maintain vCurrent_ and vSelected_ variables for each level of your ViewModel-nestings. <br />
<br />
The values maintained by the Framework reflect what the user has focused on/clicked last (vCurrent_), and when you allow multi-select in grids, the vSelected_ collection is maintained. <br />
<br />
The variables are strongly typed to match your nestings and get the postfix name from the nesting.<br />
<br />
See this article for a fuller discussion on [[Cursored or Full Tree|how to cursor your ViewModel]].<br />
<br />
See also: [[Training:How to use vCurrent and “self” correctly in viewmodels|How to use vCurrent and “self” correctly in viewmodels]]<br />
<br />
==== Update vSelected maintaince in Turnkey ====<br />
Complex issue with vSelected in Turnkey - when you have not checked the SelectedBox - you still want the vCurrent to be part of the vSelected - but then you want it removed from vSelected if the only reason for being in vSelected was that it was vCurrent.<br />
<br />
This issue is not complex in WPF because there is a natural difference between the focused and selected and UI controls automatically include the focused in the selected collection - but on the web we have the extra checkbox and we must know the reason for being in vSelected - is it only because we was vCurrent before - and thus should we be removed from vSelected<br />
<br />
{{Edited|July|12|2024}}</div>Hanshttps://wiki.mdriven.net/index.php?title=Documentation:VCurrent_and_vSelected&diff=17256Documentation:VCurrent and vSelected2024-03-01T17:06:23Z<p>Hans: </p>
<hr />
<div>[[Training:ViewModel|ViewModels]] create and maintain vCurrent_ and vSelected_ variables for each level of your ViewModel-nestings. <br />
<br />
The values maintained by the Framework reflect what the user has focused on/clicked last (vCurrent_), and when you allow multi-select in grids, the vSelected_ collection is maintained. <br />
<br />
The variables are strongly typed to match your nestings and get the postfix name from the nesting.<br />
<br />
See this article for a fuller discussion on [[Cursored or Full Tree|how to cursor your ViewModel]].<br />
<br />
See also: [[Training:How to use vCurrent and “self” correctly in viewmodels|How to use vCurrent and “self” correctly in viewmodels]]<br />
<br />
==== Update vSelected maintaince in Turnkey ====<br />
Complex issue with vSelected in Turnkey - when you have not checked the SelectedBox - you still want the vCurrent to be part of the vSelected - but then you want it removed from vSelected if the only reason for being in vSelected was that it was vCurrent.<br />
<br />
This issue is not complex in WPF because there you are a natural difference between the focused and selected and UI controls automatically include the focused in the selected collection - but on the web have the extra checkbox and we must know the reason for being in vSelected - is it only because we was vCurrent before - and thus should we be removed from vSelected<br />
<br />
{{Edited|July|12|2024}}</div>Hanshttps://wiki.mdriven.net/index.php?title=Documentation:VCurrent_and_vSelected&diff=17255Documentation:VCurrent and vSelected2024-03-01T17:03:11Z<p>Hans: </p>
<hr />
<div>[[Training:ViewModel|ViewModels]] create and maintain vCurrent_ and vSelected_ variables for each level of your ViewModel-nestings. <br />
<br />
The values maintained by the Framework reflect what the user has focused on/clicked last (vCurrent_), and when you allow multi-select in grids, the vSelected_ collection is maintained. <br />
<br />
The variables are strongly typed to match your nestings and get the postfix name from the nesting.<br />
<br />
See this article for a fuller discussion on [[Cursored or Full Tree|how to cursor your ViewModel]].<br />
<br />
See also: [[Training:How to use vCurrent and “self” correctly in viewmodels|How to use vCurrent and “self” correctly in viewmodels]]<br />
<br />
==== Update vSelected maintaince in Turnkey ====<br />
Complex issue with vSelected in Turnkey - when you have not checked the SelectedBox - you still want the vCurrent to be part of the vSelected - but then you want it removed from vSelected if the only reason for being in vSelected was that it was vCurrent.<br />
<br />
This issue is not complex in WPF because there you are a natural difference between the focused and selected and UI controls automatically include the focused in the selected collection - but on the web have the extra checkbox and we must know the reason for being in vSelected - is it only because we was vCurrent before - and thus should we be removed from vSelected<br />
[[File:Documentation VCurrent and vSelected 1709312562167.gif|none|thumb|1115x1115px]]<br />
{{Edited|July|12|2024}}</div>Hanshttps://wiki.mdriven.net/index.php?title=File:Documentation_VCurrent_and_vSelected_1709312562167.gif&diff=17254File:Documentation VCurrent and vSelected 1709312562167.gif2024-03-01T17:02:44Z<p>Hans: </p>
<hr />
<div></div>Hanshttps://wiki.mdriven.net/index.php?title=Documentation:Model_Examples&diff=17075Documentation:Model Examples2024-02-20T13:20:14Z<p>Hans: </p>
<hr />
<div><html><br />
<style><br />
.content {<br />
width: 100%;<br />
display: flex;<br />
flex-direction: column;<br />
gap: 10px;<br />
box-sizing: border-box;<br />
padding: 0 2em 5em 5em;<br />
}<br />
.heading {<br />
color: #F67A07;<br />
font-size: 2.25em;<br />
font-weight: 700;<br />
text-transform: uppercase;<br />
}<br />
.description, .sub-heading, .search-input {<br />
font-size: 16px;<br />
}<br />
@media (max-width: 767px) {<br />
.content {<br />
padding: 2em 1em;<br />
}<br />
.heading {<br />
font-size: 1.5em;<br />
}<br />
.sub-heading {<br />
font-size: 1em;<br />
}<br />
.description {<br />
font-size: 0.875em;<br />
}<br />
.content,<br />
.container-model {<br />
padding-left: 0 !important;<br />
padding-right: 0 !important;<br />
padding-bottom: 0 !important;<br />
padding-top: 0 !important;<br />
}<br />
}<br />
.container-model {<br />
display: flex;<br />
padding-left: 5em;<br />
padding-right: 5em;<br />
}<br />
<br />
.sidebar {<br />
flex: 0 0 200px;<br />
padding: 20px;<br />
display: flex;<br />
flex-direction: column;<br />
margin-top:50px;<br />
}<br />
<br />
.menu-item {<br />
color: #0060A8;<br />
font-size: 16px;<br />
font-weight: 500;<br />
margin-bottom: 10px;<br />
word-wrap: break-word;<br />
}<br />
<br />
.card-container, .card-container-3 {<br />
display: grid;<br />
grid-template-columns: repeat(3, 1fr);<br />
grid-gap: 32px;<br />
margin: 0 auto;<br />
max-width: 960px;<br />
}<br />
<br />
.cards {<br />
max-width: 296px;<br />
background: white;<br />
border-radius: 6px;<br />
border: 1px solid #E3E8EE;<br />
display: flex;<br />
flex-direction: column;<br />
align-items: center;<br />
margin-bottom: 32px;<br />
}<br />
<br />
.card img {<br />
width: 100%;<br />
border-top-left-radius: 6px;<br />
border-top-right-radius: 6px;<br />
}<br />
<br />
.contentss {<br />
padding: 24px;<br />
display: flex;<br />
flex-direction: column;<br />
align-items: flex-start;<br />
height: 100%;<br />
justify-content: space-between;<br />
}<br />
<br />
.title {<br />
color: #112B3C;<br />
font-size: 20px;<br />
font-weight: 700;<br />
margin-bottom: 10px;<br />
}<br />
<br />
.description {<br />
font-size: 14px;<br />
font-weight: 300;<br />
color: black;<br />
}<br />
<br />
.download-link {<br />
display: inline-flex;<br />
align-items: center;<br />
gap: 6px;<br />
color: #F67A07;<br />
font-size: 14px;<br />
font-weight: 600;<br />
text-decoration: none;<br />
transition: background-color 0.3s, color 0.3s;<br />
}<br />
<br />
.download-indicator {<br />
width: 14px;<br />
height: 15px;<br />
display: block;<br />
background-image: url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" width="14" height="15" viewBox="0 0 14 15" fill="none"><path d="M1 10.6094L1 11.3594C1 12.602 2.00736 13.6094 3.25 13.6094L10.75 13.6094C11.9926 13.6094 13 12.602 13 11.3594L13 10.6094M10 7.60937L7 10.6094M7 10.6094L4 7.60937M7 10.6094L7 1.60937" stroke="%23F77A07" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/></svg>');<br />
background-size: contain;<br />
background-repeat: no-repeat;<br />
background-position: center;<br />
}<br />
<br />
.sidebar a {<br />
display: block;<br />
color: #182933;<br />
text-decoration: none;<br />
margin-bottom: 10px;<br />
padding-left: 20px;<br />
position: relative;<br />
font-weight: 500;<br />
}<br />
<br />
.sidebar a.active {<br />
color: #1A50AD;<br />
}<br />
<br />
.sidebar a.active svg {<br />
position: absolute;<br />
left: 0;<br />
top: 0;<br />
}<br />
<br />
@media (max-width: 768px) {<br />
.sidebar, .search-container {<br />
display: none;<br />
}<br />
<br />
.card-container, .card-container-3 {<br />
grid-template-columns: 1fr;<br />
padding: 0 15px; <br />
grid-gap: 20px;<br />
}<br />
<br />
.cards {<br />
margin-bottom: 20px;<br />
width: auto;<br />
max-width: 296px;<br />
box-sizing: border-box;<br />
margin: 10px auto;<br />
}<br />
}<br />
}<br />
<br />
search-container {<br />
display: flex;<br />
align-items: center;<br />
border-radius: 4px;<br />
border: 1px solid #E3E8EE;<br />
background: #FFF;<br />
width: 951px;<br />
height: 36px;<br />
flex-shrink: 0;<br />
}<br />
<br />
.search-container input[type="search"] {<br />
border: none;<br />
outline: none;<br />
padding: 0 10px;<br />
height: 100%;<br />
width:100%<br />
color: #999B9E;<br />
}<br />
<br />
.search-container svg {<br />
margin: 0 10px;<br />
}<br />
<br />
.search-container input::placeholder {<br />
color: #999B9E;<br />
}<br />
<br />
.search-container input:focus {<br />
border: none;<br />
outline: none;<br />
}<br />
h2 {<br />
border-bottom: none;<br />
}<br />
.section-divider {<br />
border: 0;<br />
height: 1px;<br />
background-color: #E3E8EE;<br />
margin: 20px 0;<br />
}<br />
<br />
@media (max-width: 600px) {<br />
.cards {<br />
flex-direction: column;<br />
}<br />
<br />
.title, .description {<br />
font-size: 16px;<br />
}<br />
<br />
.download-link {<br />
font-size: 12px;<br />
padding: 8px 12px;<br />
}<br />
}<br />
<br />
<br />
</style><br />
<br />
<div class="content"><br />
<div style="color: #44546F;">Model Examples</div><br />
<div class="heading">This page is under construction - <a href='https://wiki.mdriven.net/Documentation:Model_Examples_Old'> refer to this page in the mean time</a></div><br />
<div class="heading">MODEL EXAMPLES</div><br />
<div class="description" style="color: black; font-weight: 400; word-wrap: break-word"><br />
Get started with MDriven sample models. Explore and choose what you want to build.<br />
</div><br />
<div class="description" style="color: black; font-weight: 650; word-wrap: break-word"><br />
</div><br />
</div><br />
<div class="container-model"><br />
<div class="sidebar"><br />
<!-- Menu items --><br />
<a href="#complete-models" class="menu-item">Complete models</a><br />
<a href="#another-model" class="menu-item">Patterns</a><br />
<a href="#final-model" class="menu-item">Extensions</a><br />
</div><br />
<main><br />
<div class="search-container" style="display: flex;align-items: center;border-radius: 4px; border: 1px solid #E3E8EE;background: #FFF;width: 100%;height: 36px; flex-shrink: 0;"><br />
<svg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 14 14" fill="none"><br />
<path d="M13 13L9 9L13 13ZM10.3333 5.66667C10.3333 8.244 8.244 10.3333 5.66667 10.3333C3.08934 10.3333 1 8.244 1 5.66667C1 3.08934 3.08934 1 5.66667 1C8.244 1 10.3333 3.08934 10.3333 5.66667Z" stroke="#999B9E" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/><br />
</svg><br />
<input type="search" id="searchBox" onkeyup="searchCards()" placeholder="Search here..."><br />
</div><br />
<br />
<!-- Section for Complete Models --><br />
<section id="complete-models"><br />
<h2>Complete Models</h2><br />
<div style="color: black; font-size: 16px; font-weight: 400; word-wrap: break-word">Use ready-made "complete system" (model+data) to apply to an existing turnkey site.</div><br />
<div class="card-container"><br />
</html><br />
{{#widget:Models|img=/images/model2.svg|title=Gantt Chart Interactive|description=Use this when you need to show and interact with data in a timeline.|downloadLink=https://learn.mdriven.net/Special:Redirect/file/GanttChartInteractive.modlr}}<br />
{{#widget:Models|img=/images/model2.svg|title=A Trello-like Board With Cards You Can Move Between Lists|description=Move cards between lists, click cards, add new cards and more.|downloadLink=https://learn.mdriven.net/Special:Redirect/file/TrelloLikeBoard.modlr}}<br />
{{#widget:Models|img=/images/model2.svg|title=Markup Editor TinyMCE|description=Lets your users produce rich HTML text.|downloadLink=https://learn.mdriven.net/Special:Redirect/file/HtmlEditor.modlr}}<br />
{{#widget:Models|img=/images/model2.svg|title=SVG - Move boxes around|description=Objects represented by SVG graphics that react to mouse events so that they can be moved around and saved.|downloadLink=https://learn.mdriven.net/Special:Redirect/file/SvgInteractionMoveBoxesAround.modlr}}<br />
{{#widget:Models|img=/images/model2.svg|title=Google Maps to Show and Update Positions|description=Place a marker on a Google map, track the setting of the map, and move the marker.|downloadLink=https://learn.mdriven.net/Special:Redirect/file/GoogleMapWithMarkers.modlr}}<br />
<br />
<html><br />
<br />
</div><br />
</section><br />
<hr class="section-divider" /><br />
<!-- Section for Patterns --><br />
<section id="another-model"><br />
<h2>Patterns</h2><br />
<div><span style="color: black; font-size: 16px; font-weight: 400; word-wrap: break-word">Packages primarily used by importing into an existing model. </span><a<br />
href="#" style="color: #1A50AD; font-size: 16px; font-weight: 500; text-decoration: underline; word-wrap: break-word">How to merge .modlr file?</a></div><br />
<div class="card-container"><br />
</html><br />
{{#widget:Models|img=/images/model2.svg|title=SysUserAuthentication - important architectural pattern|description=This example adds the SysSingleton and SysUser classes needed to enable login and authentication in Turnkey systems.|downloadLink=https://learn.mdriven.net/Special:Redirect/file/SysUserAuthentication.modlr}}<br />
{{#widget:Models|img=/images/model2.svg|title=Password Reset Sample - Architectural Pattern|description=Add this functionality to your model and this frees you to integrate and adapt it as needed.|downloadLink=https://learn.mdriven.net/Special:Redirect/file/PasswordResetTemplate.modlr}}<br />
{{#widget:Models|img=/images/model2.svg|title=SysAsyncTicket - Important Architectural Pattern|description=A model pattern you can easily add to your model, backed by recent MDrivenServers.|downloadLink=https://learn.mdriven.net/SysAsync_package}}<br />
{{#widget:Models|img=/images/model2.svg|title=Localization and Translation - Architectural Pattern|description=MDriven systems can adhere to a model pattern and use that to store translations to all texts in actions and ViewModels.|downloadLink=https://learn.mdriven.net/Special:Redirect/file/Translations.modlr}}<br />
{{#widget:Models|img=/images/model2.svg|title=SysTurnkeyTraceLog|description=Add a class following this pattern if you need to track what users are up to in your Turnkey app.|downloadLink=https://github.com/supportMDriven/MDrivenComponents.git}}<br />
{{#widget:Models|img=/images/model2.svg|title=BaseApp|description=A model extracted from larger models used in various projects with certain functions ready to go.|downloadLink=https://learn.mdriven.net/File:BaseApp.zip}}<br />
{{#widget:Models|img=/images/model2.svg|title=Asp.Net Identity package|description=Use this pattern for Turnkey login support.|downloadLink=https://learn.mdriven.net/File:AspNetIdentity.zip}}<br />
<br />
<html><br />
</div><br />
</section><br />
<hr class="section-divider" /><br />
<!-- Section for Extensions --><br />
<section id="final-model"><br />
<h2>Extensions</h2><br />
<div><span style="color: black; font-size: 16px; word-wrap: break-word">Ready-made solutions used by importing into an existing model. </span><a href="#" style="color: #1A50AD; font-size: 16px; font-weight: 500; text-decoration: underline; word-wrap: break-word">How to merge .modlr file?</a></div><br />
<div class="card-container"><br />
</html><br />
{{#widget:Models|img=/images/model2.svg|title=Consume and Purchase GetPaid#1|description=This model extends your system with the ability to charge the end-user for services you provide.|downloadLink=https://learn.mdriven.net/Special:Redirect/file/ChargeEndUserViaMDrivenPortalService.modlr}}<br />
{{#widget:Models|img=/images/model2.svg|title=PayPal - Checkout GetPaid#2|description=Integrate PayPal as an alternative method to get paid by users.|downloadLink=https://learn.mdriven.net/Special:Redirect/file/PayPalCheckout.modlr}}<br />
{{#widget:Models|img=/images/model2.svg|title=Theme Builder - Build Themes in Runtime|description=Set CSS variables to influence the colors and sizes of the UI.|downloadLink=https://learn.mdriven.net/Special:Redirect/file/ThemeBuilder.modlr}}<br />
{{#widget:Models|img=/images/model2.svg|title=Outgoing Email Queue|description=This is a model pattern that adds an outgoing email queue to the standard base model.|downloadLink=https://learn.mdriven.net/Special:Redirect/file/OutgoingEmailQueue.modlr}}<br />
{{#widget:Models|img=/images/model2.svg|title=Calendar Model for Linking Things to Dates, Weeks, Months, and Years|description=This model implements classes that make aggregation (for statistics, for example) easy.|downloadLink=https://learn.mdriven.net/Special:Redirect/file/CalendarPackage.modlr}}<br />
{{#widget:Models|img=/images/model2.svg|title=Store Complaint Handling|description=The complete app, model, and example data for a clothing shop, to help staff handle customer complaints professionally.|downloadLink=https://learn.mdriven.net/File:StoreComplaintHandling.zip}}<br />
{{#widget:Models|img=/images/model2.svg|title=Turnkey Sample Board Map Balls Gantt|description=This sample is a bit of a playground for html5 component development that jacks into the Turnkey.|downloadLink=}}<br />
{{#widget:Models|img=/images/model2.svg|title=Turnkey Sample SoftConflict|description=A small app that helps you be constructive about issues in your daily life.|downloadLink=https://learn.mdriven.net/File:DatabaseCompact.zip}}<br />
{{#widget:Models|img=/images/model2.svg|title=Turnkey Sample InstantPoll|description=Set up an instant poll using MDriven.|downloadLink=https://learn.mdriven.net/File:DatabaseCompact_ForInstantPoll.zip}}<br />
<br />
<br />
<br />
<html><br />
</div><br />
</div><br />
</div><br />
</section><br />
</main><br />
</div><br />
<script><br />
function searchCards() {<br />
var input, filter, cards, title, i, txtValue;<br />
input = document.getElementById("searchBox");<br />
filter = input.value.toUpperCase();<br />
cards = document.getElementsByClassName("cards");<br />
<br />
for (i = 0; i < cards.length; i++) {<br />
title = cards[i].getElementsByClassName("title")[0];<br />
if (title) {<br />
txtValue = title.textContent || title.innerText;<br />
if (txtValue.toUpperCase().indexOf(filter) > -1) {<br />
cards[i].style.display = "";<br />
} else {<br />
cards[i].style.display = "none";<br />
}<br />
} <br />
}<br />
}<br />
</script><br />
<script><br />
// JavaScript to scroll to the section smoothly when a sidebar link is clicked<br />
document.addEventListener('DOMContentLoaded', (event) => {<br />
document.querySelectorAll('.sidebar a').forEach(link => {<br />
link.addEventListener('click', function(e) {<br />
e.preventDefault();<br />
const targetId = this.getAttribute('href');<br />
const targetSection = document.querySelector(targetId);<br />
if (targetSection) {<br />
targetSection.scrollIntoView({ behavior: 'smooth', block: 'start' });<br />
}<br />
});<br />
});<br />
});<br />
</script><br />
<script><br />
<br />
document.addEventListener('DOMContentLoaded', (event) => {<br />
document.querySelectorAll('.sidebar a').forEach(link => {<br />
link.addEventListener('click', function(e) {<br />
e.preventDefault();<br />
const targetId = this.getAttribute('href');<br />
const targetSection = document.querySelector(targetId);<br />
if (targetSection) {<br />
targetSection.scrollIntoView({ behavior: 'smooth', block: 'start' });<br />
}<br />
});<br />
});<br />
});<br />
</script><br />
<script><br />
document.addEventListener('DOMContentLoaded', (event) => {<br />
const svgMarkup = `<svg xmlns="http://www.w3.org/2000/svg" width="4" height="16" viewBox="0 0 4 16" fill="none"><br />
<path d="M0 0C2.20914 0 4 1.79086 4 4L4 12C4 14.2091 2.20914 16 0 16L0 0Z" fill="#1A50AD"/><br />
</svg>`;<br />
<br />
// Function to create a new SVG element<br />
const createSvgElement = (htmlString) => {<br />
const div = document.createElement('div');<br />
div.innerHTML = htmlString.trim();<br />
return div.firstChild;<br />
};<br />
<br />
// Function to update active link style and SVG<br />
const updateActiveLink = (targetId) => {<br />
document.querySelectorAll('.sidebar a').forEach(link => {<br />
if (link.getAttribute('href') === targetId) {<br />
link.classList.add('active');<br />
// Insert SVG for active link<br />
link.prepend(createSvgElement(svgMarkup));<br />
} else {<br />
link.classList.remove('active');<br />
// Remove SVG for non-active links<br />
if (link.querySelector('svg')) {<br />
link.removeChild(link.querySelector('svg'));<br />
}<br />
}<br />
});<br />
};<br />
<br />
updateActiveLink('#complete-models');<br />
<br />
document.querySelectorAll('.sidebar a').forEach(link => {<br />
link.addEventListener('click', function(e) {<br />
e.preventDefault();<br />
const targetId = this.getAttribute('href');<br />
updateActiveLink(targetId);<br />
const targetSection = document.querySelector(targetId);<br />
if (targetSection) {<br />
targetSection.scrollIntoView({ behavior: 'smooth', block: 'start' });<br />
}<br />
});<br />
});<br />
});<br />
</script><br />
</html><br />
{{Edited|July|12|2024}}<br />
{{Class|scroll}}</div>Hanshttps://wiki.mdriven.net/index.php?title=Documentation:Model_Examples&diff=17074Documentation:Model Examples2024-02-20T13:17:11Z<p>Hans: </p>
<hr />
<div><html><br />
<style><br />
.content {<br />
width: 100%;<br />
display: flex;<br />
flex-direction: column;<br />
gap: 10px;<br />
box-sizing: border-box;<br />
padding: 0 2em 5em 5em;<br />
}<br />
.heading {<br />
color: #F67A07;<br />
font-size: 2.25em;<br />
font-weight: 700;<br />
text-transform: uppercase;<br />
}<br />
.description, .sub-heading, .search-input {<br />
font-size: 16px;<br />
}<br />
@media (max-width: 767px) {<br />
.content {<br />
padding: 2em 1em;<br />
}<br />
.heading {<br />
font-size: 1.5em;<br />
}<br />
.sub-heading {<br />
font-size: 1em;<br />
}<br />
.description {<br />
font-size: 0.875em;<br />
}<br />
.content,<br />
.container-model {<br />
padding-left: 0 !important;<br />
padding-right: 0 !important;<br />
padding-bottom: 0 !important;<br />
padding-top: 0 !important;<br />
}<br />
}<br />
.container-model {<br />
display: flex;<br />
padding-left: 5em;<br />
padding-right: 5em;<br />
}<br />
<br />
.sidebar {<br />
flex: 0 0 200px;<br />
padding: 20px;<br />
display: flex;<br />
flex-direction: column;<br />
margin-top:50px;<br />
}<br />
<br />
.menu-item {<br />
color: #0060A8;<br />
font-size: 16px;<br />
font-weight: 500;<br />
margin-bottom: 10px;<br />
word-wrap: break-word;<br />
}<br />
<br />
.card-container, .card-container-3 {<br />
display: grid;<br />
grid-template-columns: repeat(3, 1fr);<br />
grid-gap: 32px;<br />
margin: 0 auto;<br />
max-width: 960px;<br />
}<br />
<br />
.cards {<br />
max-width: 296px;<br />
background: white;<br />
border-radius: 6px;<br />
border: 1px solid #E3E8EE;<br />
display: flex;<br />
flex-direction: column;<br />
align-items: center;<br />
margin-bottom: 32px;<br />
}<br />
<br />
.card img {<br />
width: 100%;<br />
border-top-left-radius: 6px;<br />
border-top-right-radius: 6px;<br />
}<br />
<br />
.contentss {<br />
padding: 24px;<br />
display: flex;<br />
flex-direction: column;<br />
align-items: flex-start;<br />
height: 100%;<br />
justify-content: space-between;<br />
}<br />
<br />
.title {<br />
color: #112B3C;<br />
font-size: 20px;<br />
font-weight: 700;<br />
margin-bottom: 10px;<br />
}<br />
<br />
.description {<br />
font-size: 14px;<br />
font-weight: 300;<br />
color: black;<br />
}<br />
<br />
.download-link {<br />
display: inline-flex;<br />
align-items: center;<br />
gap: 6px;<br />
color: #F67A07;<br />
font-size: 14px;<br />
font-weight: 600;<br />
text-decoration: none;<br />
transition: background-color 0.3s, color 0.3s;<br />
}<br />
<br />
.download-indicator {<br />
width: 14px;<br />
height: 15px;<br />
display: block;<br />
background-image: url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" width="14" height="15" viewBox="0 0 14 15" fill="none"><path d="M1 10.6094L1 11.3594C1 12.602 2.00736 13.6094 3.25 13.6094L10.75 13.6094C11.9926 13.6094 13 12.602 13 11.3594L13 10.6094M10 7.60937L7 10.6094M7 10.6094L4 7.60937M7 10.6094L7 1.60937" stroke="%23F77A07" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/></svg>');<br />
background-size: contain;<br />
background-repeat: no-repeat;<br />
background-position: center;<br />
}<br />
<br />
.sidebar a {<br />
display: block;<br />
color: #182933;<br />
text-decoration: none;<br />
margin-bottom: 10px;<br />
padding-left: 20px;<br />
position: relative;<br />
font-weight: 500;<br />
}<br />
<br />
.sidebar a.active {<br />
color: #1A50AD;<br />
}<br />
<br />
.sidebar a.active svg {<br />
position: absolute;<br />
left: 0;<br />
top: 0;<br />
}<br />
<br />
@media (max-width: 768px) {<br />
.sidebar, .search-container {<br />
display: none;<br />
}<br />
<br />
.card-container, .card-container-3 {<br />
grid-template-columns: 1fr;<br />
padding: 0 15px; <br />
grid-gap: 20px;<br />
}<br />
<br />
.cards {<br />
margin-bottom: 20px;<br />
width: auto;<br />
max-width: 296px;<br />
box-sizing: border-box;<br />
margin: 10px auto;<br />
}<br />
}<br />
}<br />
<br />
search-container {<br />
display: flex;<br />
align-items: center;<br />
border-radius: 4px;<br />
border: 1px solid #E3E8EE;<br />
background: #FFF;<br />
width: 951px;<br />
height: 36px;<br />
flex-shrink: 0;<br />
}<br />
<br />
.search-container input[type="search"] {<br />
border: none;<br />
outline: none;<br />
padding: 0 10px;<br />
height: 100%;<br />
width:100%<br />
color: #999B9E;<br />
}<br />
<br />
.search-container svg {<br />
margin: 0 10px;<br />
}<br />
<br />
.search-container input::placeholder {<br />
color: #999B9E;<br />
}<br />
<br />
.search-container input:focus {<br />
border: none;<br />
outline: none;<br />
}<br />
h2 {<br />
border-bottom: none;<br />
}<br />
.section-divider {<br />
border: 0;<br />
height: 1px;<br />
background-color: #E3E8EE;<br />
margin: 20px 0;<br />
}<br />
<br />
@media (max-width: 600px) {<br />
.cards {<br />
flex-direction: column;<br />
}<br />
<br />
.title, .description {<br />
font-size: 16px;<br />
}<br />
<br />
.download-link {<br />
font-size: 12px;<br />
padding: 8px 12px;<br />
}<br />
}<br />
<br />
<br />
</style><br />
<br />
<div class="content"><br />
<div style="color: #44546F;">Model Examples</div><br />
This page is under construction - <a url='Documentation:Model Examples Old - MDrivenWiki'> refer to this page in the mean time</a><br />
<div class="heading">MODEL EXAMPLES</div><br />
<div class="description" style="color: black; font-weight: 400; word-wrap: break-word"><br />
Get started with MDriven sample models. Explore and choose what you want to build.<br />
</div><br />
<div class="description" style="color: black; font-weight: 650; word-wrap: break-word"><br />
</div><br />
</div><br />
<div class="container-model"><br />
<div class="sidebar"><br />
<!-- Menu items --><br />
<a href="#complete-models" class="menu-item">Complete models</a><br />
<a href="#another-model" class="menu-item">Patterns</a><br />
<a href="#final-model" class="menu-item">Extensions</a><br />
</div><br />
<main><br />
<div class="search-container" style="display: flex;align-items: center;border-radius: 4px; border: 1px solid #E3E8EE;background: #FFF;width: 100%;height: 36px; flex-shrink: 0;"><br />
<svg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 14 14" fill="none"><br />
<path d="M13 13L9 9L13 13ZM10.3333 5.66667C10.3333 8.244 8.244 10.3333 5.66667 10.3333C3.08934 10.3333 1 8.244 1 5.66667C1 3.08934 3.08934 1 5.66667 1C8.244 1 10.3333 3.08934 10.3333 5.66667Z" stroke="#999B9E" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/><br />
</svg><br />
<input type="search" id="searchBox" onkeyup="searchCards()" placeholder="Search here..."><br />
</div><br />
<br />
<!-- Section for Complete Models --><br />
<section id="complete-models"><br />
<h2>Complete Models</h2><br />
<div style="color: black; font-size: 16px; font-weight: 400; word-wrap: break-word">Use ready-made "complete system" (model+data) to apply to an existing turnkey site.</div><br />
<div class="card-container"><br />
</html><br />
{{#widget:Models|img=/images/model2.svg|title=Gantt Chart Interactive|description=Use this when you need to show and interact with data in a timeline.|downloadLink=https://learn.mdriven.net/Special:Redirect/file/GanttChartInteractive.modlr}}<br />
{{#widget:Models|img=/images/model2.svg|title=A Trello-like Board With Cards You Can Move Between Lists|description=Move cards between lists, click cards, add new cards and more.|downloadLink=https://learn.mdriven.net/Special:Redirect/file/TrelloLikeBoard.modlr}}<br />
{{#widget:Models|img=/images/model2.svg|title=Markup Editor TinyMCE|description=Lets your users produce rich HTML text.|downloadLink=https://learn.mdriven.net/Special:Redirect/file/HtmlEditor.modlr}}<br />
{{#widget:Models|img=/images/model2.svg|title=SVG - Move boxes around|description=Objects represented by SVG graphics that react to mouse events so that they can be moved around and saved.|downloadLink=https://learn.mdriven.net/Special:Redirect/file/SvgInteractionMoveBoxesAround.modlr}}<br />
{{#widget:Models|img=/images/model2.svg|title=Google Maps to Show and Update Positions|description=Place a marker on a Google map, track the setting of the map, and move the marker.|downloadLink=https://learn.mdriven.net/Special:Redirect/file/GoogleMapWithMarkers.modlr}}<br />
<br />
<html><br />
<br />
</div><br />
</section><br />
<hr class="section-divider" /><br />
<!-- Section for Patterns --><br />
<section id="another-model"><br />
<h2>Patterns</h2><br />
<div><span style="color: black; font-size: 16px; font-weight: 400; word-wrap: break-word">Packages primarily used by importing into an existing model. </span><a<br />
href="#" style="color: #1A50AD; font-size: 16px; font-weight: 500; text-decoration: underline; word-wrap: break-word">How to merge .modlr file?</a></div><br />
<div class="card-container"><br />
</html><br />
{{#widget:Models|img=/images/model2.svg|title=SysUserAuthentication - important architectural pattern|description=This example adds the SysSingleton and SysUser classes needed to enable login and authentication in Turnkey systems.|downloadLink=https://learn.mdriven.net/Special:Redirect/file/SysUserAuthentication.modlr}}<br />
{{#widget:Models|img=/images/model2.svg|title=Password Reset Sample - Architectural Pattern|description=Add this functionality to your model and this frees you to integrate and adapt it as needed.|downloadLink=https://learn.mdriven.net/Special:Redirect/file/PasswordResetTemplate.modlr}}<br />
{{#widget:Models|img=/images/model2.svg|title=SysAsyncTicket - Important Architectural Pattern|description=A model pattern you can easily add to your model, backed by recent MDrivenServers.|downloadLink=https://learn.mdriven.net/SysAsync_package}}<br />
{{#widget:Models|img=/images/model2.svg|title=Localization and Translation - Architectural Pattern|description=MDriven systems can adhere to a model pattern and use that to store translations to all texts in actions and ViewModels.|downloadLink=https://learn.mdriven.net/Special:Redirect/file/Translations.modlr}}<br />
{{#widget:Models|img=/images/model2.svg|title=SysTurnkeyTraceLog|description=Add a class following this pattern if you need to track what users are up to in your Turnkey app.|downloadLink=https://github.com/supportMDriven/MDrivenComponents.git}}<br />
{{#widget:Models|img=/images/model2.svg|title=BaseApp|description=A model extracted from larger models used in various projects with certain functions ready to go.|downloadLink=https://learn.mdriven.net/File:BaseApp.zip}}<br />
{{#widget:Models|img=/images/model2.svg|title=Asp.Net Identity package|description=Use this pattern for Turnkey login support.|downloadLink=https://learn.mdriven.net/File:AspNetIdentity.zip}}<br />
<br />
<html><br />
</div><br />
</section><br />
<hr class="section-divider" /><br />
<!-- Section for Extensions --><br />
<section id="final-model"><br />
<h2>Extensions</h2><br />
<div><span style="color: black; font-size: 16px; word-wrap: break-word">Ready-made solutions used by importing into an existing model. </span><a href="#" style="color: #1A50AD; font-size: 16px; font-weight: 500; text-decoration: underline; word-wrap: break-word">How to merge .modlr file?</a></div><br />
<div class="card-container"><br />
</html><br />
{{#widget:Models|img=/images/model2.svg|title=Consume and Purchase GetPaid#1|description=This model extends your system with the ability to charge the end-user for services you provide.|downloadLink=https://learn.mdriven.net/Special:Redirect/file/ChargeEndUserViaMDrivenPortalService.modlr}}<br />
{{#widget:Models|img=/images/model2.svg|title=PayPal - Checkout GetPaid#2|description=Integrate PayPal as an alternative method to get paid by users.|downloadLink=https://learn.mdriven.net/Special:Redirect/file/PayPalCheckout.modlr}}<br />
{{#widget:Models|img=/images/model2.svg|title=Theme Builder - Build Themes in Runtime|description=Set CSS variables to influence the colors and sizes of the UI.|downloadLink=https://learn.mdriven.net/Special:Redirect/file/ThemeBuilder.modlr}}<br />
{{#widget:Models|img=/images/model2.svg|title=Outgoing Email Queue|description=This is a model pattern that adds an outgoing email queue to the standard base model.|downloadLink=https://learn.mdriven.net/Special:Redirect/file/OutgoingEmailQueue.modlr}}<br />
{{#widget:Models|img=/images/model2.svg|title=Calendar Model for Linking Things to Dates, Weeks, Months, and Years|description=This model implements classes that make aggregation (for statistics, for example) easy.|downloadLink=https://learn.mdriven.net/Special:Redirect/file/CalendarPackage.modlr}}<br />
{{#widget:Models|img=/images/model2.svg|title=Store Complaint Handling|description=The complete app, model, and example data for a clothing shop, to help staff handle customer complaints professionally.|downloadLink=https://learn.mdriven.net/File:StoreComplaintHandling.zip}}<br />
{{#widget:Models|img=/images/model2.svg|title=Turnkey Sample Board Map Balls Gantt|description=This sample is a bit of a playground for html5 component development that jacks into the Turnkey.|downloadLink=}}<br />
{{#widget:Models|img=/images/model2.svg|title=Turnkey Sample SoftConflict|description=A small app that helps you be constructive about issues in your daily life.|downloadLink=https://learn.mdriven.net/File:DatabaseCompact.zip}}<br />
{{#widget:Models|img=/images/model2.svg|title=Turnkey Sample InstantPoll|description=Set up an instant poll using MDriven.|downloadLink=https://learn.mdriven.net/File:DatabaseCompact_ForInstantPoll.zip}}<br />
<br />
<br />
<br />
<html><br />
</div><br />
</div><br />
</div><br />
</section><br />
</main><br />
</div><br />
<script><br />
function searchCards() {<br />
var input, filter, cards, title, i, txtValue;<br />
input = document.getElementById("searchBox");<br />
filter = input.value.toUpperCase();<br />
cards = document.getElementsByClassName("cards");<br />
<br />
for (i = 0; i < cards.length; i++) {<br />
title = cards[i].getElementsByClassName("title")[0];<br />
if (title) {<br />
txtValue = title.textContent || title.innerText;<br />
if (txtValue.toUpperCase().indexOf(filter) > -1) {<br />
cards[i].style.display = "";<br />
} else {<br />
cards[i].style.display = "none";<br />
}<br />
} <br />
}<br />
}<br />
</script><br />
<script><br />
// JavaScript to scroll to the section smoothly when a sidebar link is clicked<br />
document.addEventListener('DOMContentLoaded', (event) => {<br />
document.querySelectorAll('.sidebar a').forEach(link => {<br />
link.addEventListener('click', function(e) {<br />
e.preventDefault();<br />
const targetId = this.getAttribute('href');<br />
const targetSection = document.querySelector(targetId);<br />
if (targetSection) {<br />
targetSection.scrollIntoView({ behavior: 'smooth', block: 'start' });<br />
}<br />
});<br />
});<br />
});<br />
</script><br />
<script><br />
<br />
document.addEventListener('DOMContentLoaded', (event) => {<br />
document.querySelectorAll('.sidebar a').forEach(link => {<br />
link.addEventListener('click', function(e) {<br />
e.preventDefault();<br />
const targetId = this.getAttribute('href');<br />
const targetSection = document.querySelector(targetId);<br />
if (targetSection) {<br />
targetSection.scrollIntoView({ behavior: 'smooth', block: 'start' });<br />
}<br />
});<br />
});<br />
});<br />
</script><br />
<script><br />
document.addEventListener('DOMContentLoaded', (event) => {<br />
const svgMarkup = `<svg xmlns="http://www.w3.org/2000/svg" width="4" height="16" viewBox="0 0 4 16" fill="none"><br />
<path d="M0 0C2.20914 0 4 1.79086 4 4L4 12C4 14.2091 2.20914 16 0 16L0 0Z" fill="#1A50AD"/><br />
</svg>`;<br />
<br />
// Function to create a new SVG element<br />
const createSvgElement = (htmlString) => {<br />
const div = document.createElement('div');<br />
div.innerHTML = htmlString.trim();<br />
return div.firstChild;<br />
};<br />
<br />
// Function to update active link style and SVG<br />
const updateActiveLink = (targetId) => {<br />
document.querySelectorAll('.sidebar a').forEach(link => {<br />
if (link.getAttribute('href') === targetId) {<br />
link.classList.add('active');<br />
// Insert SVG for active link<br />
link.prepend(createSvgElement(svgMarkup));<br />
} else {<br />
link.classList.remove('active');<br />
// Remove SVG for non-active links<br />
if (link.querySelector('svg')) {<br />
link.removeChild(link.querySelector('svg'));<br />
}<br />
}<br />
});<br />
};<br />
<br />
updateActiveLink('#complete-models');<br />
<br />
document.querySelectorAll('.sidebar a').forEach(link => {<br />
link.addEventListener('click', function(e) {<br />
e.preventDefault();<br />
const targetId = this.getAttribute('href');<br />
updateActiveLink(targetId);<br />
const targetSection = document.querySelector(targetId);<br />
if (targetSection) {<br />
targetSection.scrollIntoView({ behavior: 'smooth', block: 'start' });<br />
}<br />
});<br />
});<br />
});<br />
</script><br />
</html><br />
{{Edited|July|12|2024}}<br />
{{Class|scroll}}</div>Hanshttps://wiki.mdriven.net/index.php?title=MDrivenDesignerTaggedValueHints&diff=17073MDrivenDesignerTaggedValueHints2024-02-20T10:43:42Z<p>Hans: </p>
<hr />
<div>The intention of this information is to be downloaded by MDrivenDesigner at startup to guide what tagged values can be used where.<br />
<br />
'''All valid Tagged values must be added here.'''<br />
#TargetType.<Modernity.>Function<br />
We deliberately skipped the ones that have Designer properties already.<br />
{| class="wikitable"<br />
!#TargetType.TaggedValue<br />
!CommaSeparatedExampleValues<br />
!Short Description<br />
|-<br />
|#Column.DataIsImageUrl<br />
|True,False<br />
|When set on a ViewModelColumn, the data string is treated as an Image URL by Turnkey. See: [[Column.DataIsImageUrl]]<br />
|-<br />
|#Column.DataIsLink<br />
|True,False<br />
|When set on viewmodelColumn, the string data within is treated as a hyperlink. See: [[Column.DataIsLink]]<br />
|-<br />
|#Column.BlobDownloadLink<br />
|True,False<br />
|When set on a ViewModelColumn, the Blob content can be downloaded by a hyperlink rather than rendered. See: [[Column.BlobDownloadLink]]<br />
|-<br />
|#Nesting.Editable<br />
|True,False<br />
|Nesting that represents the grid will render web grid cells as possibly editable (depending on read-only expression). See also: [[Nesting.Editable]]<br />
|-<br />
|#Column.MaxFetch<br />
|<int><br />
|Declare "MaxFetch=<int>" on the ViewModelColumn action that initiates the search to change from the default (100). See also: [[Column.MaxFetch]], [[MaxFetch]]<br />
|-<br />
|#Attribute.ExternalBlobStorage<br />
|True,False<br />
|Large files can be kept out of the granular model database. This is a turnkey functionality only - and the files will be stored (with the simplest out-of-the-box solution) on disk App_Data/BlobStorage. See: [[Attribute.ExternalBlobStorage]]<br />
|-<br />
|#Column.StringFormat<br />
|<identifier><br />
|WPF Only: TaggedValue StringFormat on ViewModel column has precedence for finding a binding Stringformat - normally the stringFormat is taken from <StyleRef>.StringFormat, but if this TV has value - <value>.StringFormat is used. See [[Column.StringFormat]], [[Text_formatting]]<br />
|-<br />
|#Span.MVC<br />
|True,False<br />
|The Turnkey WebApplication uses MVC for index and login pages - it defaults to angular js for all other pages. To get MVC on other pages you must set the tagged value MVC=true on ViewModel. See: [[Span.MVC]]<br />
|-<br />
|#Column.Angular_Ext_Component<br />
|<Angular component name><br />
|Set UIOverride on ViewModelColum - assign this tagged value - turnkey will now look for a component definition in EXT_Components folder. See: [[Column.Angular Ext Component]]<br />
|-<br />
|#Span.DoNotSearchOnEnter<br />
|Existence<br />
|UI's in WPF and Turnkey will search on enter as it has seek expressions - you can turn this ff by defining this TV on the ViewModel level. See also: [[Span.DoNotSearchOnEnter]]<br />
|-<br />
|#Span.Bootstrap.ClassPrefix<br />
|col-xs-<br />
|Alter the default Bootstrap column prefix from col-sm- to something else. See: [[Bootstrap]]<br />
|-<br />
|#Span.Bootstrap.StaticSections<br />
|Existence<br />
|The view parts above the topmost splitter and below the bottom-most splitters will be static in the browser. The middle section scrolls. See: [[Bootstrap]]<br />
|-<br />
|#Method.Eco.ExternalLateBound<br />
|Existence<br />
|If TV is set the ExternalLateBound logic is searched for implementation of this method. See also: [[Method.Eco.ExternalLateBound]]<br />
|-<br />
|#Span.CSSGrid<br />
|True,False<br />
|If this is true then MVC and angular views will Render with CSSGrid instead of with Bootstrap. See: [[Span.CSSGrid]]<br />
|-<br />
|#Nesting.MultiSelect<br />
|True,False<br />
|Allows multi-select - you can then expect vSelected_<NameOfNesting> to be maintained. See: [[Nesting.MultiSelect]]<br />
|-<br />
|#Column.DataIsHtml<br />
|True,False<br />
|When True static fields treat the data as markup and display potential HTML in the browser. See: [[Column.DataIsHtml]]<br />
|-<br />
|#Span.Eco.RestAllowed<br />
|True,False<br />
|Enables a ViewModel to be accessed with REST API (default False). See: [[Span.Eco.RestAllowed]]<br />
|-<br />
|#Span.Eco.UIAllowed<br />
|True,False<br />
|Disables a ViewModel to be accessed within the UI (default True). See: [[Span.Eco.UIAllowed]]<br />
|-<br />
|#Span.Eco.AutoSave<br />
|True,False<br />
|Enables a viewModel to automatically save all changes without showing the Save/Cancel buttons. See: [[Span.Eco.AutoSave]]<br />
|-<br />
|#Column.XmlAttribute<br />
|True,False<br />
|When using ViewModelAsXml, ViewModel column will be rendered as an XML Attribute. See: [[OCLOperators ViewModelAsXml|ViewModelAsXML]], [[OCLOperators ViewModelAsJSon|ViewModelAsJSon]]<br />
|-<br />
|#Column.XmlChildnode<br />
|True,False<br />
|When using ViewModelAsXml, ViewModel single link column will be rendered as an XML element tree node. See: [[OCLOperators ViewModelAsXml|ViewModelAsXML]], [[OCLOperators ViewModelAsJSon|ViewModelAsJSon]]<br />
|-<br />
|#Column.NodeName<br />
|<name of node><br />
|Used to override the XML och JSON node name usually given by the ViewModel column. Used for example to input a - (dash) as a part of the name of an attribute. See: [[OCLOperators ViewModelAsXml|ViewModelAsXML]], [[OCLOperators ViewModelAsJSon|ViewModelAsJSon]]<br />
|-<br />
|#Column.MaxFileSizeInBytes<br />
|10000000<br />
|Limits Blobs and Images to a maximum size of the file (primarily web). See: [[Column.MaxFileSizeInBytes]]<br />
|-<br />
|#Column.AcceptFiles<br />
|image/*,.pdf<br />
|Rendered in HTML on the <input> file tag as the "accept" attribute. For more info, search for the "accept" attribute. See also: [[Upload/Download files and images in MDriven Turnkey]]<br />
|-<br />
|#Span.HideSidebar<br />
|True,False<br />
|Hide the sidebar on the page - Note: Use the new HideSidebar checkbox on ViewModel. See: [[Span.HideSidebar]]<br />
|-<br />
|#Span.HideMenubar<br />
|True,False<br />
|Hides Menubar for page - Note: Use the new HideMenubar checkbox on ViewModel. See: [[Span.HideMenubar]]<br />
|-<br />
|#Span.FixColSize<br />
|True,False<br />
|Set equal columns size on the page. See: [[Span.FixColSize]]<br />
|-<br />
|#Column.FixColSize<br />
|True,False<br />
|Set equal columns size inside the groupboxes. See: [[Column.FixColSize]]<br />
|-<br />
|#Column.HideHeading<br />
|True,False<br />
|Hide the heading of the groupbox. See: [[Column.HideHeading]]<br />
|-<br />
|#Column.OpenInNewTab<br />
|True,False<br />
|Open the link in the new browser tab<br />
|-<br />
|#Nesting.Eco.WECPOFDoubleClickAction<br />
|None,DefaultAction,ModalOkPrecedence<br />
|Implemented for WPF but should follow in Turnkey. Controls what should happen on Grid double click.<br />
|-<br />
|#Column.Switch<br />
|True,False<br />
|Change the checkbox appearance to the switch control. See: [[Column.Switch]]<br />
|-<br />
|#Nesting.CellSelect<br />
|True,False<br />
|In WPF, this controls if the grid should get CellSelect from start rather than row select. See also: [[Nesting.CellSelect]]<br />
|-<br />
|#Column.Placeholder<br />
|Placeholder text<br />
|Add placeholder text for text and number fields. See: [[Column.Placeholder]]<br />
|-<br />
|#Column.autofocus<br />
|True,False<br />
|Focuses the control when it appears on the screen. Might conflict with other focus events. See: [[Column.autofocus]] <br />
|-<br />
|#Column.autocomplete<br />
|True, False<br />
|Hint for form autofill feature (disabled by default for every input field), See: [[Column.autocomplete]]<br />
|-<br />
|#Nesting.FrozenColumns<br />
|1,2<br />
|In WPF DataGrid can freeze leftmost columns from scrolling (aka fixedcolumns). See: [[Nesting.FrozenColumns]]<br />
|-<br />
|#Attribute.CheckIdNameConflict<br />
|True,False<br />
|Avoids warning about naming conflicts between a table and the automatically named ID columns when generating a SQL database. See also: [[Attribute.CheckIdNameConflict]]<br />
|-<br />
|#Column.XmlParentValue<br />
|True,False<br />
|Makes the attribute's value appear as the parent element's value instead of an element. Note: if used on more than one attribute on the same ViewModel class, the result is undefined. See: [[OCLOperators ViewModelAsXml|ViewModelAsXML]]<br />
|-<br />
|#Column.Resizable<br />
|True,False<br />
|Make the text area element resizable. See: [[Column.Resizable]]<br />
|-<br />
|#Column.Eco.ImageWidth<br />
|20,30,40<br />
|Controls WPF Images in Grids default is 20. <br />
|-<br />
|#Column.Eco.ImageHeight<br />
|20,30,40<br />
|Controls WPF Images in Grids default is 20<br />
|-<br />
|#Column.FormatAttr<br />
|<br />
|Sets the angular format attribute that controls text rendering. Use [[Column.StringFormatAngular|StringFormatAngular]] instead. See: [[Column.FormatAttr]]<br />
|-<br />
|#Column.StringFormatAngular<br />
|<br />
|Sets the angular format attribute that controls text rendering. Replaces the FormatAttr to resemble WPF and Razor. See also: [[Column.StringFormatAngular]], [[Text_formatting]]<br />
|-<br />
|#Column.StringFormatRazor<br />
|<br />
|Sets the Razor format attribute that controls text rendering. Replaces the FormatAttr to resemble WPF and Angular. See: [[Column.StringFormatRazor]], [[Text_formatting]]<br />
|-<br />
|#Column.Texttype<br />
|password,email,tel,url,search<br />
|For inputs intended as type text, this type can be overridden with something else. See: [[Column.Texttype]]<br />
|-<br />
|#Attribute.Eco.BlobType<br />
|SVG,XamlSymbol,Image,Blob,RichText<br />
|Set on attributes of classes to influence how a binary array or Text field should be interpreted. See: [[Attribute.Eco.BlobType]]<br />
|-<br />
|#Column.Eco.BlobType<br />
|SVG,XamlSymbol,Image,Blob,RichText<br />
|Set on Column to influence how a binary array or Text field should be interpreted. See: [[Column.Eco.BlobType]]<br />
|-<br />
|#Nesting.Striped<br />
|True,False<br />
|Adds the striped style to your table. See: [[Nesting.Striped]]<br />
|-<br />
|#Span.ModalSize<br />
|narrow,large,veryLarge<br />
|Choose the size of the modal window which will contain your ViewModel. See: [[Span.ModalSize]]<br />
|-<br />
|#Nesting.IsSeekerResultGrid<br />
|True,False<br />
|Seeker page logic uses this to show buttons in the grid. See: [[Nesting.IsSeekerResultGrid]]<br />
|-<br />
|#Attribute.Realtime<br />
|True,False<br />
|If changed it will be invalidated in all clients upon saving asap - ie by an active push from the server. See also: [[SignalR and Realtime]]<br />
|-<br />
|#Column.Icon<br />
|<br />
|Set the icon to your action from the default [https://material.io/resources/icons/?style=baseline Material Design icons pack]. See: [[Material Design Icons]]<br />
|-<br />
|#Column.IconPosition<br />
|before,after<br />
|Choose the position of the icon inside your action button. By default, the icon is placed before the button text. See: [[Material Design Icons]]<br />
|-<br />
|#Column.IconButton<br />
|True,False<br />
|Displays just the icon instead of the button text. See: [[Material Design Icons]]<br />
|-<br />
|#Column.IconInInput<br />
|True,False<br />
|Enable this Tagged value to put the icon inside the input control instead of the label. See: [[Material Design Icons]]<br />
|-<br />
|#Nesting.ClientSortable<br />
|True,False<br />
|Makes all columns in the table sortable by clicking on the header. Can be overridden on each column. Defaults to True for tables that are not seeker results. See: [[Nesting.ClientSortable]]<br />
|-<br />
|#Column.ClientSortable<br />
|True,False<br />
|Makes the column sortable by clicking on the header. Default is inherited from the table. Default is False in Seekers. See: [[Column.ClientSortable]]<br />
|-<br />
|#Column.SeekerSortable<br />
|True,False<br />
|Makes the column sortable by clicking on the header in a Seeker. See: [[Column.SeekerSortable]] <br />
|-<br />
|#Nesting.Eco.SeekerDefaultOrderColumnName<br />
|<br />
|Name of the attribute in the OrderExpression ViewModel class to use when a result is found by using this search expression. See also: [[Seeker view]]<br />
|-<br />
|#Nesting.Eco.HiliteGridColumn<br />
|<br />
|Viewmodel name of the SearchResultGrid result and the Name of the attribute in that ViewModel in the format <ViewModelName>.<AttributeName>. This column will be highlighted (coloured) when the result is found by using this search expression. See also: [[Seeker view]]<br />
|-<br />
|#Span.XmlRootTag<br />
|<br />
|Influence root tag used in [[OCLOperators ViewModelAsXml|ViewModelAsXML]]. See also: [[Span.XmlRootTag]]<br />
|-<br />
|#Span.SkipWhenBacking<br />
|True,False<br />
|When pressing back into this view we skip and move to the view before - (Implemented in WPF) - Good if you have a switchboard view that automatically navigates somewhere<br />
|-<br />
|#Nesting.ShowOldContextMenu<br />
|True,False<br />
|If this option is enabled - the old context menu will appear instead of the new one.<br />
|-<br />
|#Column.TaJsonTreatListAsDynamicProperties<br />
|True,False<br />
|When this is true, we read the nesting in the ViewModel object and look for Name and Value properties - we then use the result as properties on the resulting json object. Read more: [[Tajson]]<br />
|-<br />
|#Column.TaJsonTreatListAsValues<br />
|True,False<br />
|When this is true, we generate a json array of the first column in the ViewModel nesting. This gives a json array of values rather than of objects. Read more: [[Tajson]]<br />
|-<br />
|#Column.SkipStyleLogic<br />
|True,False<br />
|To be used when _Style column is not to be interpreted as a style name in Xaml or CSS-class in HTML - instead, it is just data picked up by some other style's binding - or HTML style info. See also: [[Column.SkipStyleLogic]], [[Turnkey Styling]]<br />
|-<br />
|#Attribute.AllowMVCAccess<br />
|ok,notok,okwhenauthenticated<br />
|The GetImage of MVC - this is the default "ok" for byte[] and default not ok for strings. See: [[MVC GetImage]]<br />
|-<br />
|#Nesting.IncludeCurrentAndSelectedVariables<br />
|True,False<br />
|Default True, which includes vCurrent and vSelected as data in a streaming [[ViewModel|ViewModel]]. Used when easy access to current and selected row are not wanted when databinding. See: [[Nesting.IncludeCurrentAndSelectedVariables]], [[VCurrent and vSelected]]<br />
|-<br />
|#Column.DesignTimeColor<br />
|Red,Green,Blue,#1278FF<br />
|This sets a background color of the ViewModel column in the ViewModel tree - it is a design time help<br />
|-<br />
|#Column.RawJSon<br />
|True,False<br />
|When using AsTajson you may want to inject snippets of already formatted json into the tree. Read more: [[Tajson]]<br />
|-<br />
|#Span.Savebar<br />
|True,False<br />
|Shows a "savebar" at the top of the screen when unsaved changes exists. Also shows Undo/Redo buttons. Will also hide the left sidebar. See also: [[Span.Savebar]]<br />
|-<br />
|#Span.TurnkeyTimeoutMinutes<br />
|<br />
|Delay in minutes until user will be timed out from current [[ViewModel]]. See: [[Span.TurnkeyTimeoutMinutes]], [[Turnkey Client Timeout]]<br />
|-<br />
|#Span.MemorySeeker<br />
|True,False<br />
|If set to true Seeker logic will use OCL to search in memory rather than the standard OCLPS to search in db<br />
|-<br />
|#Span.UseAngularJSComponents<br />
|True,False<br />
|Turn on for the view the new implementation of rendering using AngularJS component based application structure.<br />
|-<br />
|#Column.Fab<br />
|True,False<br />
|Changing default button presentation to the FAB (Floating action button). Requires icon.<br />
|-<br />
|#Column.TypeToPresent<br />
|time,datetime-local,week,month<br />
|Changing the presentation of DateTime object based on chosen option.<br />
|-<br />
|#Column.WillEffectPersistedDataOverrideValue<br />
|true,false,notset<br />
|Override of the logic that deduce if an expression effects persistent data or not<br />
|-<br />
|#ContextAction.WillEffectPersistedDataOverrideValue<br />
|true,false,notset<br />
|Override of the logic that deduce if an expression effects persistent data or not<br />
|-<br />
|#ClassAction.WillEffectPersistedDataOverrideValue<br />
|true,false,notset<br />
|Override of the logic that deduce if an expression effects persistent data or not<br />
|-<br />
|#Nesting.NavigateOnSingleClickEnable <br />
|true,false<br />
|When set on Nesting displaying grid that has only 1 enabled action that is navigating - execution happens on row select<br />
|}<br />
[[Category:MDriven Designer]]<br />
[[Category:Tagged Values]]<br />
{{Edited|July|12|2024}}</div>Hanshttps://wiki.mdriven.net/index.php?title=Training:Custom_OR_Mapping&diff=17033Training:Custom OR Mapping2024-02-19T13:45:59Z<p>Hans: </p>
<hr />
<div>==== Declaring a Composite key for a reversed database ====<br />
In this video: https://youtu.be/BmmoAFjaQtc the process of defining a composite primary key is explained.<br />
<br />
Here are the steps involved:<br />
<br />
# On the class (filmactor) find the two foreign keys that together makes the row unique in the database (film_id,actor_id) <br />
# Set the both these keys as comma separated on the class (A) (filmactor) primarykey: film_id,actor_id <br />
# In the singlelink ends (film (B) and actor (C)) set the columnName (D) to film_id and actor_id respectively<br />
<br />
[[File:Training Custom OR Mapping 1708350261846.png|none|thumb|674x674px]]<br />
<br />
==== Setting the primary key to an existing attribute ====<br />
We had this issue from Rich W in the forum – he tried to do a custom OR mapping.<br />
<br />
He wanted to pick a modeled attribute to be the primary key in the database.<br />
<br />
This is how to accomplish this:<br />
<br />
[[File:Custom OR Mapping 01.png|frameless|249x249px]]<br />
<br />
In Class1, we make SomePK the primary key.<br />
<br />
On Class1, we set PrimaryKey and PrimaryKeyMapper:<br />
<br />
[[File:Custom OR Mappin 02.png|frameless]]<br />
<br />
We also set the SaveAction on the attribute SomePK to Freeze – possible other values DBAssigned and None.<br />
<br />
[[File:Custom OR Mappin 03.png|frameless]]<br />
<br />
Doing this means that we take responsibility for setting the key and we do not allow it to be changed once saved – as is normal for primary keys.<br />
<br />
If we had DBAssigned – it will not be set on save – and it will be re-read from the DB once saved. This is for scenarios where you have AutoInc fields in the database.<br />
<br />
Rich did all this correctly but could not get it to work – he got errors while trying to generate the database. At first, I could not put my finger on why but then it dawned on me that we have an issue with the DefaultSuperClass.<br />
<br />
The package may have a named DefaultSuperClass that all the other classes in the package inherit from. This still has the default primary key mapping.<br />
<br />
If you do not have a designed DefaultSuperClass, the auto-generated class EcoModelRoot will assume its place in the standard OR mapping.<br />
<br />
So the EcoModelRoot has a standard key named EcoId and the subclass Class1 has the SomePK attribute as the primary key – this is both strange and not what Rich wanted – and this is also unsupported – hence the error Rich got.<br />
<br />
To handle this, we instruct the OR mapper to do something about it. In this particular case, the best thing is to skip the EcoModelRoot table. We do this by adding a DefaultORMappingBuilder to the PersistenceMapper:<br />
<br />
[[File:Custom OR Mappin 04.png|frameless|400x400px]]<br />
<br />
We connect it to both NewMappingProvider and RunTimeMappingProvider of the PersistenceMapper (New is used in design time when doing the create and evolve and runtime is when the app executes):<br />
<br />
[[File:Custom OR Mapping 05.png|frameless|388x388px]]<br />
<br />
So far, we have changed nothing – this is what would have happened anyway – but we have exposed the defaultORMappingBuilder so that we can tweak it:<br />
<br />
[[File:Custom OR Mapping 06.png|frameless|384x384px]]<br />
<br />
The most important tweak is ChildMapRootClass – this means that EcoModelRoot, or if you have another modeled DefaultSuperClass, will not get a table of its own – it will be mapped into the child classes. The other tweaks are just for show – I do this because I personally think it looks better with primary keys that follow the pattern above (original value is <Name> and EcoKey).<br />
<br />
Having this in place, I can generate a schema and get the following result:<br />
<br />
[[File:Custom OR Mapping 07.png|frameless]]<br />
<br />
Class1 has the SomePK as the key – but Class2, which we did not give any special primary key, has the default <TableName>ID – namely Class2ID. Also, note the single link back to Class1 in the Class2 table – it is named Class1ID.<br />
[[Category:Advanced]]<br />
[[Category:Database]]</div>Hanshttps://wiki.mdriven.net/index.php?title=File:Training_Custom_OR_Mapping_1708350261846.png&diff=17032File:Training Custom OR Mapping 1708350261846.png2024-02-19T13:44:23Z<p>Hans: </p>
<hr />
<div></div>Hanshttps://wiki.mdriven.net/index.php?title=MDriven_administrative_database_tables&diff=17017MDriven administrative database tables2024-02-18T20:25:17Z<p>Hans: </p>
<hr />
<div>==== New 2024-02-18; Use MDrivenServer to create to the admin tables instead of manually as described below ====<br />
This is described in this video: https://youtu.be/BmmoAFjaQtc<br />
[[File:MDriven administrative database tables 1708287873349.png|none|thumb|667x667px]]<br />
<br />
<br />
==== Manual process ====<br />
When you create a database from a model these administrative tables will be added automatically for you.<br />
<br />
But when [[Training:Reverse engineer a database|reversing an existing database]] to a model - and then wanting to use the model just like any another MDriven Model you may find that you need to add these manually.<br />
<br />
Below is the script and explanation of what the table is for:<br />
{| class="wikitable"<br />
|+<br />
!Table name<br />
!<br />
!<br />
!<br />
!PostgreSQL<br />
|-<br />
|ECO_ID<br />
|This is the cursor of the default internal key assigned to objects saved with the default persistence mapper.<br />
This table only has 1 row holding the next free identity for a new object<br />
|<br />
|CREATE TABLE [dbo].[ECO_ID](<br />
[PK] [int] NOT NULL,<br />
<br />
[BOLD_ID] [int] NOT NULL,<br />
<br />
CONSTRAINT [PK_ECO_ID] PRIMARY KEY CLUSTERED <br />
<br />
(<br />
<br />
[PK] ASC<br />
<br />
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON, OPTIMIZE_FOR_SEQUENTIAL_KEY = OFF) ON [PRIMARY]<br />
<br />
) ON [PRIMARY]<br />
|CREATE TABLE eco_id (<br />
pk SERIAL primary key,<br />
<br />
bold_id INT NOT NULL<br />
<br />
);<br />
|-<br />
|ECO_ORMAPPING<br />
|This holds a text-blob with the OR-mapping in MDrivenFormat (xml). MDriven reads this upon evolve of database to figure out what we has changed compared to the current model. <br />
After successfull evolve MDriven writes the updated OR-mapping to this column.<br />
The Table only holds 1 row<br />
|<br />
|CREATE TABLE [dbo].[ECO_ORMAPPING](<br />
[ID] [int] NOT NULL,<br />
<br />
[ECO_ORMAPPING] [ntext] NOT NULL,<br />
<br />
PRIMARY KEY CLUSTERED <br />
<br />
(<br />
<br />
[ID] ASC<br />
<br />
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON, OPTIMIZE_FOR_SEQUENTIAL_KEY = OFF) ON [PRIMARY]<br />
<br />
) ON [PRIMARY] TEXTIMAGE_ON [PRIMARY]<br />
<br />
GO<br />
|<code>CREATE TABLE eco_ormapping (<br />
id SERIAL PRIMARY KEY,<br />
eco_ormapping TEXT NOT NULL<br />
);</code><br />
|-<br />
|ECO_TABLES<br />
|This table has one row per table that MDriven has created or taken ownership of in your database.<br />
|<br />
|CREATE TABLE [dbo].[ECO_TABLES](<br />
[TABLENAME] [nvarchar](255) NOT NULL<br />
<br />
) ON [PRIMARY]<br />
<br />
GO<br />
|<code>CREATE TABLE eco_tables (<br />
tablename VARCHAR(255) NOT NULL<br />
);</code><br />
|-<br />
|ECO_TYPE<br />
|This table holds the class names from your model along with an integer number unique to that type. If MDriven is instructed to write many types to the same table (OR-Parent-Mapping) MDriven figures out the runtime type by lookin the ECO_TYPE column of such a table - and the number will match a number in the ECO_TYPE table.<br />
|<br />
|CREATE TABLE [dbo].[ECO_TYPE](<br />
[ECO_TYPE] [smallint] NOT NULL,<br />
<br />
[CLASSNAME] [nvarchar](255) NOT NULL,<br />
<br />
PRIMARY KEY CLUSTERED <br />
<br />
(<br />
<br />
[ECO_TYPE] ASC<br />
<br />
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON, OPTIMIZE_FOR_SEQUENTIAL_KEY = OFF) ON [PRIMARY]<br />
<br />
) ON [PRIMARY]<br />
<br />
GO<br />
|<code>CREATE TABLE eco_type (<br />
eco_type SMALLINT NOT NULL,<br />
classname VARCHAR(255) NOT NULL,<br />
PRIMARY KEY (eco_type)<br />
);</code><br />
|-<br />
|<br />
|<br />
|<br />
|<br />
|<br />
|}<br />
{{Edited|July|12|2024}}</div>Hanshttps://wiki.mdriven.net/index.php?title=File:MDriven_administrative_database_tables_1708287873349.png&diff=17016File:MDriven administrative database tables 1708287873349.png2024-02-18T20:24:35Z<p>Hans: </p>
<hr />
<div></div>Hanshttps://wiki.mdriven.net/index.php?title=MDriven_administrative_database_tables&diff=17015MDriven administrative database tables2024-02-18T20:23:30Z<p>Hans: </p>
<hr />
<div>==== New 2024-02-18; Use MDrivenServer to create to the admin tables instead of manually as described below ====<br />
This is described in this video: https://youtu.be/BmmoAFjaQtc<br />
<br />
==== Manual process ====<br />
When you create a database from a model these administrative tables will be added automatically for you.<br />
<br />
But when [[Training:Reverse engineer a database|reversing an existing database]] to a model - and then wanting to use the model just like any another MDriven Model you may find that you need to add these manually.<br />
<br />
Below is the script and explanation of what the table is for:<br />
{| class="wikitable"<br />
|+<br />
!Table name<br />
!<br />
!<br />
!<br />
!PostgreSQL<br />
|-<br />
|ECO_ID<br />
|This is the cursor of the default internal key assigned to objects saved with the default persistence mapper.<br />
This table only has 1 row holding the next free identity for a new object<br />
|<br />
|CREATE TABLE [dbo].[ECO_ID](<br />
[PK] [int] NOT NULL,<br />
<br />
[BOLD_ID] [int] NOT NULL,<br />
<br />
CONSTRAINT [PK_ECO_ID] PRIMARY KEY CLUSTERED <br />
<br />
(<br />
<br />
[PK] ASC<br />
<br />
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON, OPTIMIZE_FOR_SEQUENTIAL_KEY = OFF) ON [PRIMARY]<br />
<br />
) ON [PRIMARY]<br />
|CREATE TABLE eco_id (<br />
pk SERIAL primary key,<br />
<br />
bold_id INT NOT NULL<br />
<br />
);<br />
|-<br />
|ECO_ORMAPPING<br />
|This holds a text-blob with the OR-mapping in MDrivenFormat (xml). MDriven reads this upon evolve of database to figure out what we has changed compared to the current model. <br />
After successfull evolve MDriven writes the updated OR-mapping to this column.<br />
The Table only holds 1 row<br />
|<br />
|CREATE TABLE [dbo].[ECO_ORMAPPING](<br />
[ID] [int] NOT NULL,<br />
<br />
[ECO_ORMAPPING] [ntext] NOT NULL,<br />
<br />
PRIMARY KEY CLUSTERED <br />
<br />
(<br />
<br />
[ID] ASC<br />
<br />
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON, OPTIMIZE_FOR_SEQUENTIAL_KEY = OFF) ON [PRIMARY]<br />
<br />
) ON [PRIMARY] TEXTIMAGE_ON [PRIMARY]<br />
<br />
GO<br />
|<code>CREATE TABLE eco_ormapping (<br />
id SERIAL PRIMARY KEY,<br />
eco_ormapping TEXT NOT NULL<br />
);</code><br />
|-<br />
|ECO_TABLES<br />
|This table has one row per table that MDriven has created or taken ownership of in your database.<br />
|<br />
|CREATE TABLE [dbo].[ECO_TABLES](<br />
[TABLENAME] [nvarchar](255) NOT NULL<br />
<br />
) ON [PRIMARY]<br />
<br />
GO<br />
|<code>CREATE TABLE eco_tables (<br />
tablename VARCHAR(255) NOT NULL<br />
);</code><br />
|-<br />
|ECO_TYPE<br />
|This table holds the class names from your model along with an integer number unique to that type. If MDriven is instructed to write many types to the same table (OR-Parent-Mapping) MDriven figures out the runtime type by lookin the ECO_TYPE column of such a table - and the number will match a number in the ECO_TYPE table.<br />
|<br />
|CREATE TABLE [dbo].[ECO_TYPE](<br />
[ECO_TYPE] [smallint] NOT NULL,<br />
<br />
[CLASSNAME] [nvarchar](255) NOT NULL,<br />
<br />
PRIMARY KEY CLUSTERED <br />
<br />
(<br />
<br />
[ECO_TYPE] ASC<br />
<br />
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON, OPTIMIZE_FOR_SEQUENTIAL_KEY = OFF) ON [PRIMARY]<br />
<br />
) ON [PRIMARY]<br />
<br />
GO<br />
|<code>CREATE TABLE eco_type (<br />
eco_type SMALLINT NOT NULL,<br />
classname VARCHAR(255) NOT NULL,<br />
PRIMARY KEY (eco_type)<br />
);</code><br />
|-<br />
|<br />
|<br />
|<br />
|<br />
|<br />
|}<br />
{{Edited|July|12|2024}}</div>Hanshttps://wiki.mdriven.net/index.php?title=Documentation:Making_sense_of_legacy_data%E2%80%93DB_Reverse&diff=17014Documentation:Making sense of legacy data–DB Reverse2024-02-18T20:21:01Z<p>Hans: </p>
<hr />
<div><html><br />
<h4><br />
The AutoForms in MDriven Designer make it easy and fun to get to grips with legacy data.<br />
</h4><br />
<br />
<p class="video-warn"><br />
<em>To make your experience smooth, we set the main tags mentioned in the video to the right bar menu of this mini-player. Choose an interesting subtitle on the list and immediately get to the exact theme navigation item place in the video. Now you can pick any topic to be instructed on without watching the whole video.</em><br />
</p><br />
<br />
<br />
<div class="video"><br />
<div class="video__wrapper"><br />
<iframe src="https://www.youtube.com/embed/BmmoAFjaQtc?rel=0&autoplay=0" frameborder="0" allowfullscreen></iframe><br />
</div><br />
<div class="video__navigation"><br />
<br />
</div><br />
</div><br />
<br />
<br />
<br />
<br />
<br />
<div class="video"><br />
<div class="video__wrapper"><br />
<iframe src="https://www.youtube.com/embed/A-Oo5gWTc44?rel=0&autoplay=0" frameborder="0" allowfullscreen></iframe><br />
</div><br />
<div class="video__navigation"><br />
<br />
</div><br />
</div><br />
<br />
<br />
<div class="video"><br />
<div class="video__wrapper"><br />
<iframe src="https://www.youtube.com/embed/cNzXeWclvaI?rel=0&autoplay=0" frameborder="0" allowfullscreen></iframe><br />
</div><br />
<div class="video__navigation"><br />
<br />
</div><br />
</div><br />
<br />
<br />
<div class="video"><br />
<div class="video__wrapper"><br />
<iframe src="https://www.youtube.com/embed/L1MK24VmtK0?rel=0&autoplay=0" frameborder="0" allowfullscreen></iframe><br />
</div><br />
<div class="video__navigation"><br />
<br />
</div><br />
</div><br />
<br />
<br />
</html><br />
[[Category:MDriven Designer]]<br />
{{Edited|July|12|2024}}</div>Hanshttps://wiki.mdriven.net/index.php?title=Documentation:Introduction_to_ECO&diff=17013Documentation:Introduction to ECO2024-02-17T18:46:25Z<p>Hans: </p>
<hr />
<div>ECO is short for EnterpriseCoreObjects and is the old name we used for the MDriven-technology. The ECO name was shared with Borlands/Codegears product named ECO that was a similar product targeting the Delphi development environment. Codegear discontinued their ECO-product when they switched focus to native development and no .net. <br />
<br />
MDriven has never targeted the Delphi development environment - we have always targeted Visual Studio (MDrivenFramework) and stand alone modelling (MDrivenDesigner) <br />
<br />
MDriven has honored the API and general constructs of Codegears ECO - and MDriven is a good transition path for any one wanting to stay model driven in a .net environment going forward.<br />
<br />
MDriven AB - the owner of all MDriven products - has no affiliation or ties to Codegear.<br />
[[Category:ECO]]<br />
{{Edited|July|12|2024}}</div>Hanshttps://wiki.mdriven.net/index.php?title=Documentation:Introduction_to_ECO&diff=17012Documentation:Introduction to ECO2024-02-17T18:42:46Z<p>Hans: </p>
<hr />
<div>ECO is short for EnterpriseCoreObjects and is the old name we used for the MDriven-technology. The ECO name was shared with Borlands/Codegears product named ECO that was a similar product targeting the Delphi development environment. Codegear discontinued their ECO-product when they switched focus to native development and no .net. <br />
<br />
MDriven has never targeted the Delphi development environment - we have always targeted Visual Studio (MDrivenFramework) and stand alone modelling (MDrivenDesigner) <br />
<br />
MDriven has honored the API and general constructs of Codegears ECO - and MDriven is a good transition path for any one wanting to stay model driven in a .net environment going forward.<br />
<br />
MDriven has no affiliation or ties to Codegear.<br />
[[Category:ECO]]<br />
{{Edited|July|12|2024}}</div>Hanshttps://wiki.mdriven.net/index.php?title=Documentation:TurnkeySettings&diff=17011Documentation:TurnkeySettings2024-02-17T18:19:59Z<p>Hans: </p>
<hr />
<div>=== Introduction ===<br />
The TurnkeySettings.xml is a settings file in the App_Data folder of the Turnkey application. This file holds several settings that control the behavior of your application. Consider putting your [[OpenID config]] here.<br />
<br />
=== Using the Portal ===<br />
The TurnkeySettings.xml is recreated for your application by the portal.mdriven.net when you press SendSettingsAndRestart. The TurnkeySettings is thus a usually generated file based on the settings you have given your application in portal.mdriven.net.<br />
<br />
==== Local Installation (not using the Portal) ====<br />
When you have a standalone or local installation that the Portal cannot reach, you need to supply this file yourself.<br />
<br />
==== TurnkeySettingsOverride.xml ====<br />
From October 15:th we allow for a TurnkeySettingsOverride.xml that will be '''read after the TurnkeySettings.xml''' file is read.<br />
<br />
Use case for this is to for example add your GraphAPI/OpenID config settings in this file since this file will not be overwritten when you refresh the TurnkeyCore installation.<br />
<br />
==== TurnkeySettingsExtra.xml ====<br />
From July 2019, we allow for a TurnkeySettingsExtra.xml file to be created.<br />
<br />
This file will not be touched/created by the Portal. The TurnkeySettingsExtra.xml will be '''read before the TurnkeySettings.xml''' file is read.<br />
<br />
The use-case for TurnkeySettingsExtra.xml is that you may have settings local to your application that the Portal does not know about - like experimental or new settings not available in the Portal.<br />
<br />
One such example at the time of writing is the [[ORCID]] authentication settings that we believe very few will use and thus should neither consume portal space nor increase portal complexity for the user.<br />
<br />
There is also a TurnkeySettingsOverride - same as above - read AFTER TurnkeySettings.<br />
<br />
Furthermore, the TurnkeySettingsExtra allows to set the local app name - not overwritten by SendSettings:<br />
<ApplicationNameAsMenu>false</ApplicationNameAsMenu> -- this prevent the ApplicationName to be added as a index link after the AppLogo in the menubar<br />
<br />
==== Some of the Settings Available in the TurnkeySettings.xml file ====<br />
<HidePasswordLogin>False</HidePasswordLogin> Read more: [[Documentation:Hide Password login|Hide Password login]]<br />
<br />
<HideExternalLogin>False</HideExternalLogin> Read more: [[Documentation:HideExternalLogin|Hide External Login]]<br />
<br />
<AllowUserToChooseSocialLoginEmail>True</AllowUserToChooseSocialLoginEmail><br />
<br />
<SkipExternalAccountConfirmationScreen>False</SkipExternalAccountConfirmationScreen><br />
<br />
<UseCSSGridByDefault>True</UseCSSGridByDefault><br />
<br />
<HideRegistration>True</HideRegistration><br />
<br />
<CurrentUserViewModel><br />
name of viewmodel to bring up when user click logged in name<br />
</CurrentUserViewModel><br />
<br />
<TurnOffOWINButKeepSignalR>true/false(default)</TurnOffOWINButKeepSignalR> Needed when using Windows Auth in IIS but still want to use SignalR, available from 2023-10-17<br />
<br />
<RemoveTurnkeyAppAfterMinutes>10</RemoveTurnkeyAppAfterMinutes> // this is how long a Turnkey session can be gone from net before being target for close down (user will loose unsaved changes) <br />
<br />
<NumberOfTurnkeyEcoSpacesToKeepWhenFree>10</NumberOfTurnkeyEcoSpacesToKeepWhenFree> // this controls how many EcoSpaces we prepare in advance to use, and how many we keep when they are not needed (controls memory and load speed)<br />
<br />
<TurnkeyTimeoutMinutes>1</TurnkeyTimeoutMinutes> // this controls how long a client screen will continue to show the UI if there is no user interaction<br />
<br />
<AdvancedTables>true</AdvancedTables> // Turnkey DataGrid now lets user resize column heads<br />
<br />
<TableColumnsExcludeNames>ColumnName1,ColumnName2</TableColumnsExcludeNames> // When rendering DataGrids you can filter out columns - added as fix to remove "Dummy" column used as a remedy for WPF right adjusted (numbers) columns that was last in the grid ended up at the far right of the screen and users missed it - so developer added a blank dummy column - but this confuse user when using AdvancedTables<br />
[[Category:MDriven Turnkey]]<br />
[[Category:MDriven Turnkey]]<br />
<br />
===== ShowDebugInfo =====<br />
When True collects and makes a lot more information available<br />
<br />
'''Warning:''' This will make the TK server slower and use more memory!<br />
<br />
See also [[Documentation:OpenID_config]]<br />
{{Edited|July|12|2024}}</div>Hanshttps://wiki.mdriven.net/index.php?title=Documentation:ViewModelColumnNamesWithSpecialMeaning&diff=16970Documentation:ViewModelColumnNamesWithSpecialMeaning2024-02-14T22:29:27Z<p>Hans: Created page with "Work in progress: Write up all ViewModelColumn names with special meaning {| class="wikitable" |+ !Name !Description !Area |- |OnFail |Used with selfVM.RestPost and other Rest operators to catch a fail and to try again with selfVM.ExecuteCurrentActionAgainOnce |Rest |- |TemplateUrl |The url or unc to a template (odt,ods,xml,html,odp) |Reports |- |ReportFileName |The name of the resulting report |Reports |- |ConnectionString | |SQLImport |- |Query | |SQLImport |- | | | |}"</p>
<hr />
<div>Work in progress: Write up all ViewModelColumn names with special meaning<br />
{| class="wikitable"<br />
|+<br />
!Name<br />
!Description<br />
!Area<br />
|-<br />
|OnFail<br />
|Used with selfVM.RestPost and other Rest operators to catch a fail and to try again with selfVM.ExecuteCurrentActionAgainOnce<br />
|Rest<br />
|-<br />
|TemplateUrl<br />
|The url or unc to a template (odt,ods,xml,html,odp)<br />
|Reports<br />
|-<br />
|ReportFileName<br />
|The name of the resulting report<br />
|Reports<br />
|-<br />
|ConnectionString<br />
|<br />
|SQLImport<br />
|-<br />
|Query<br />
|<br />
|SQLImport<br />
|-<br />
|<br />
|<br />
|<br />
|}</div>Hanshttps://wiki.mdriven.net/index.php?title=Documentation:SelfVM&diff=16969Documentation:SelfVM2024-02-14T22:22:20Z<p>Hans: </p>
<hr />
<div>selfVM is a variable that is available within the context of a ViewModel. You will find these functions on the selfVM object:<br />
* [[Efficient ViewModel fetch|ExecuteFetchHints]]<br />
* [https://wiki.mdriven.net/index.php/SelfVM.Save Save]<br />
* [[OCLOperators ExecuteAction|ExecuteAction]]<br />
* [[OCLOperators CanExecuteAction|CanExecuteAction]]<br />
* [[OCLOperators Refresh|Search]]<br />
* [https://wiki.mdriven.net/index.php/OCLOperators_ImportTabSepData ImportTabSepData]<br />
* [[OCLOperators Refresh|Refresh]]<br />
* [[How to use the ExecutePS function in selfVM|ExecutePS]]<br />
* [https://wiki.mdriven.net/index.php/PSExpression_,_or_how_to_do_things_in_the_DB_from_MDriven PSExpression_Refresh]<br />
* [[Rest Services In MDriven|RestGet]]<br />
* [[Rest Post|RestPost]]<br />
* [https://wiki.mdriven.net/index.php/Rest_Patch RestPatch]<br />
* [https://wiki.mdriven.net/index.php/Rest_Put RestPut]<br />
* [https://wiki.mdriven.net/index.php/Rest_Delete RestDelete]<br />
* [https://wiki.mdriven.net/index.php/OCLOperators_RestDownload RestDownload]<br />
* [[Import xml and JSon with MDriven|JSonToObjects]]<br />
* [[Import xml and JSon with MDriven|XmlToObjects]]<br />
*[[OCLOperators XsltTransformXml|XsltTransformXml]]<br />
*[[OCLOperators XmlValidateWithSchemas|XmlValidateWithSchemas]]<br />
* [[OCLOperators ViewModelAsJSon|ViewModelAsJSon]]<br />
* [https://wiki.mdriven.net/index.php/OCLOperators_GetGridAsTabSepData GetGridAsTabSepData]<br />
* [[OCLOperators SuspectExternalUpdateInvalidate|SuspectExternalUpdateInvalidate]]<br />
* [[OCLOperators RunServerSideViewModelNow|RunServerSideViewModelNow]]<br />
* [[OCLOperators Navigate|Navigate]]<br />
* ViewModelName<br />
* [[SelfVM.DirtyList|DirtyList]]<br />
* ListActions<br />
* Nestings<br />
* [[OCLOperators ShowActionMenuForCurrentInNesting|ShowActionMenuForCurrentInNesting]]<br />
* [[Documentation:OCLOperators RestPost|ExecuteCurrentActionAgainOnce]]<br />
* [[OCLOperators Styles|Styles]]<br />
[[Category:View Model]]<br />
[[Category:OCL ViewModel Operators]]<br />
{{Edited|July|12|2024}}</div>Hanshttps://wiki.mdriven.net/index.php?title=Documentation:OCLOperators_RestPost&diff=16968Documentation:OCLOperators RestPost2024-02-14T22:21:55Z<p>Hans: </p>
<hr />
<div>===== Rest =====<br />
[[OCLOperators RestGet|RestGet]], [[OCLOperators RestPost|RestPost]], RestPut, RestDelete, and [[OCLOperators RestDownload|RestDownload]] are all operators on the selfVM variable only available in ViewModels.<br />
<br />
These operators enable you to get or put information into external REST-based services.<br />
<br />
These operators work by adding a ViewModel Nesting/Class that has data and additional headers.<br />
<br />
Rest explained: https://en.wikipedia.org/wiki/Representational_state_transfer<br />
<br />
===== General Rules and How to Use Headers in HTTP and Rest =====<br />
Please read about the difference between request headers and content headers.<br />
<br />
==== Nesting Columns Starting According to ====<br />
For '''Content'''<br />
* '''STRINGCONTENT''' If there's a column named, then the request will be filled with StringContent(value as string)<br />
* '''FILENAME_'''xxx the value is used as a filename in a MultipartFormDataContent expecting a xxx column with byte data for the file content<br />
* '''HEADER_'''xxx the value is added to the content as Headers.Add(xxx,value)<br />
* '''HEADERMINUS_'''xxx the value is added to the content with the name underscores converted to dashes. I.e. as Headers.Add(xxx.Replace('_','-'),value)<br />
* '''HEADERMINUS_Content_Type ='application/x-www-form-urlencoded'''' will force the Post/Put into url-encoded-mode - all other columns are assumed to be strings that will be sent with a dictionary of name /values as FormUrlEncodedContent(theValuesForUrlEncoded)<br />
The default content is MultipartFormDataContent, (values as dictionary) for example, used in the [[Oauth2|Oauth2 implementation for server to server authentication.]]<br />
<br />
Also, note that content can only be sent in REST '''POST''' and '''PUT'''.<br />
<br />
For the '''Request'''<br />
* '''DEFAULTREQUESTHEADER_'''xxx the value is added to the request as client.DefaultRequestHeaders.Add(xxx,value)<br />
* '''DEFAULTREQUESTHEADERMINUS_'''xxx the value is added to the request with the name underscores converted to dashes. I.e. as client.DefaultRequestHeaders.Add(xxx.Replace('_','-'),value)<br />
* '''FORCEREQUESTCONTENTTYPE''' can be used to force adding a '''Content Type''' header to '''GET''' request. Some frameworks need it, but it is actually an invalid request. It's implemented as adding an empty content and adding a header to that empty content. '''''NOTE!!''''' this should be ''avoided'' if possible!!<br />
** Note also that using '''FORCEREQUESTCONTENTTYPE''' will enable you to add other headers to a Get request. Needed for several Frameworks, but again, not according to standard.<br />
For the '''returned''' string result:<br />
* '''base64ReturnStream''' - when this column is found and evaluates to Boolean true, the string returned is really as base64 of the returned stream. This is important if the returned binary stream cannot be represented with utf-8. <br />
See a further description here: [[Rest Services In MDriven]]<br />
<br />
* '''onfail''' - Action named OnFail placed in the nesting will be executed if the call that used the nesting failed. A special action suitable to execute on onfail is selfVM.[[Documentation:SelfVM|ExecuteCurrentActionAgainOnce]] - the action using the nesting will then be executed once more (only once more, if it fails again it is busted). Prior to calling selfVM.ExecuteCurrentActionAgainOnce you may want to renew any accesstoken that may have lapsed and being the reason for the failed call<br />
<br />
=== Rest Checklist ===<br />
'''The URL''' = the address of the remote service<br />
<br />
'''User & Password''' = if provided, will build up an Authentication request header of type basic ( == 'basic '+(user+':'+password).stringtobase64 ) - when user == Bearer we will assume that the password is a bearer token that will be passed in an authentication header ( == Bearer tokenincleartext) (Note: some services are case sensitive on Bearer - we use the value you give in user un-altered).<br />
<br />
'''Nesting with values''' = name of an existing Nesting (blue) in your ViewModel (green).<br />
<br />
In the Nesting, you can add additional columns to control how data is sent to the remote service.<br />
<br />
It is the remote service that defines what you need to send - but you will quickly need to decide if the remote service expects content headers, request headers, and payload in a specific format (one of MultiPartContent. StringContent or UrlEncodedContent). All values you send in the payload will be from the values in the Nesting - apart from any parameters, you can send in the URL directly (Get and Delete operate this way).<br />
<br />
There are 3 kinds of values in the payload; Content headers (usually define what the content is), Request headers (usually where authentication information goes), and plain data as payload. <br />
<br />
'''Payload:''' We separate between the 3 kinds (MultiPartContent. StringContent or UrlEncodedContent) of payload with a naming convention of the nesting columns name (begins with) - see above.<br />
<br />
'''Arrays in Payload''': You sometimes need to send arrays in this format:<br />
<br />
my_array[0] value1<br />
<br />
my_array[1] value2<br />
<br />
SubNestings from the NestingsWithParams will now be handled as such arrays and the name of the param will be repeated once per item in the list - the value will be the first ViewModel column in such subnesting.<br />
<br />
=== Read Status Code from RestPost, RestGet, etc ===<br />
Use the vReturnStatusCode:string variable name to get Status from querying others with rest - RestPost, RestGet etc.<br />
<br />
Use the vReturnMessage:string variable name to get the reasoncode from other with rest.<br />
<br />
[[Category:OCLOperators]]<br />
{{Edited|July|12|2024}}</div>Hanshttps://wiki.mdriven.net/index.php?title=Documentation:OCLOperators_RestPost&diff=16967Documentation:OCLOperators RestPost2024-02-14T22:18:44Z<p>Hans: /* Rest Checklist */</p>
<hr />
<div>===== Rest =====<br />
[[OCLOperators RestGet|RestGet]], [[OCLOperators RestPost|RestPost]], RestPut, RestDelete, and [[OCLOperators RestDownload|RestDownload]] are all operators on the selfVM variable only available in ViewModels.<br />
<br />
These operators enable you to get or put information into external REST-based services.<br />
<br />
These operators work by adding a ViewModel Nesting/Class that has data and additional headers.<br />
<br />
Rest explained: https://en.wikipedia.org/wiki/Representational_state_transfer<br />
<br />
===== General Rules and How to Use Headers in HTTP and Rest =====<br />
Please read about the difference between request headers and content headers.<br />
<br />
==== Nesting Columns Starting According to ====<br />
For '''Content'''<br />
* '''STRINGCONTENT''' If there's a column named, then the request will be filled with StringContent(value as string)<br />
* '''FILENAME_'''xxx the value is used as a filename in a MultipartFormDataContent expecting a xxx column with byte data for the file content<br />
* '''HEADER_'''xxx the value is added to the content as Headers.Add(xxx,value)<br />
* '''HEADERMINUS_'''xxx the value is added to the content with the name underscores converted to dashes. I.e. as Headers.Add(xxx.Replace('_','-'),value)<br />
* '''HEADERMINUS_Content_Type ='application/x-www-form-urlencoded'''' will force the Post/Put into url-encoded-mode - all other columns are assumed to be strings that will be sent with a dictionary of name /values as FormUrlEncodedContent(theValuesForUrlEncoded)<br />
The default content is MultipartFormDataContent, (values as dictionary) for example, used in the [[Oauth2|Oauth2 implementation for server to server authentication.]]<br />
<br />
Also, note that content can only be sent in REST '''POST''' and '''PUT'''.<br />
<br />
For the '''Request'''<br />
* '''DEFAULTREQUESTHEADER_'''xxx the value is added to the request as client.DefaultRequestHeaders.Add(xxx,value)<br />
* '''DEFAULTREQUESTHEADERMINUS_'''xxx the value is added to the request with the name underscores converted to dashes. I.e. as client.DefaultRequestHeaders.Add(xxx.Replace('_','-'),value)<br />
* '''FORCEREQUESTCONTENTTYPE''' can be used to force adding a '''Content Type''' header to '''GET''' request. Some frameworks need it, but it is actually an invalid request. It's implemented as adding an empty content and adding a header to that empty content. '''''NOTE!!''''' this should be ''avoided'' if possible!!<br />
** Note also that using '''FORCEREQUESTCONTENTTYPE''' will enable you to add other headers to a Get request. Needed for several Frameworks, but again, not according to standard.<br />
For the '''returned''' string result:<br />
* '''base64ReturnStream''' - when this column is found and evaluates to Boolean true, the string returned is really as base64 of the returned stream. This is important if the returned binary stream cannot be represented with utf-8. <br />
See a further description here: [[Rest Services In MDriven]]<br />
<br />
* '''onfail''' - Action named OnFail placed in the nesting will be executed if the call that used the nesting failed. A special action suitable to execute on onfail is selfVM.ExecuteCurrentActionAgainOnce - the action using the nesting will then be executed once more (only once more, if it fails again it is busted). Prior to calling selfVM.ExecuteCurrentActionAgainOnce you may want to renew any accesstoken that may have lapsed and being the reason for the failed call<br />
<br />
=== Rest Checklist ===<br />
'''The URL''' = the address of the remote service<br />
<br />
'''User & Password''' = if provided, will build up an Authentication request header of type basic ( == 'basic '+(user+':'+password).stringtobase64 ) - when user == Bearer we will assume that the password is a bearer token that will be passed in an authentication header ( == Bearer tokenincleartext) (Note: some services are case sensitive on Bearer - we use the value you give in user un-altered).<br />
<br />
'''Nesting with values''' = name of an existing Nesting (blue) in your ViewModel (green).<br />
<br />
In the Nesting, you can add additional columns to control how data is sent to the remote service.<br />
<br />
It is the remote service that defines what you need to send - but you will quickly need to decide if the remote service expects content headers, request headers, and payload in a specific format (one of MultiPartContent. StringContent or UrlEncodedContent). All values you send in the payload will be from the values in the Nesting - apart from any parameters, you can send in the URL directly (Get and Delete operate this way).<br />
<br />
There are 3 kinds of values in the payload; Content headers (usually define what the content is), Request headers (usually where authentication information goes), and plain data as payload. <br />
<br />
'''Payload:''' We separate between the 3 kinds (MultiPartContent. StringContent or UrlEncodedContent) of payload with a naming convention of the nesting columns name (begins with) - see above.<br />
<br />
'''Arrays in Payload''': You sometimes need to send arrays in this format:<br />
<br />
my_array[0] value1<br />
<br />
my_array[1] value2<br />
<br />
SubNestings from the NestingsWithParams will now be handled as such arrays and the name of the param will be repeated once per item in the list - the value will be the first ViewModel column in such subnesting.<br />
<br />
=== Read Status Code from RestPost, RestGet, etc ===<br />
Use the vReturnStatusCode:string variable name to get Status from querying others with rest - RestPost, RestGet etc.<br />
<br />
Use the vReturnMessage:string variable name to get the reasoncode from other with rest.<br />
<br />
[[Category:OCLOperators]]<br />
{{Edited|July|12|2024}}</div>Hanshttps://wiki.mdriven.net/index.php?title=Training:Access_control_system_in_MDriven&diff=16966Training:Access control system in MDriven2024-02-14T10:19:56Z<p>Hans: </p>
<hr />
<div>First, we must agree that the reasons for restricting access can be manifold. This may be as simple as avoiding confusing a user with too many options at the wrong time in the process – or it may be as crucial as protecting highly sensitive information from getting into the wrong hands. Any practical Access control is probably based on static information as “users belonging to group Fishermen should not have access to view Treasury”, but also dynamic information as “if a Fisherman has been granted payment but has not collected it yet, he should be able to open the view Treasury”.<br />
<br />
Another aspect is that we sometimes want to show that actions are available – but not enabled to you at this particular moment. At other times, we do not even want to show you that a given action exists in the system. These are all common requirements when dealing with access control. Since MDriven solves all user interaction through ViewModels and Actions, it is natural that these are our targets for Access Control.<br />
<br />
We can set any expression in the Enable expression of an action – this is one way to disable actions. If we have a rule that is the same for many actions, we may consolidate the rule to an AccessGroup: <br />
<br />
[[File:Access control system in MDriven 01.png|frameless|365x365px]]<br />
<br />
[[File:Accessgroups.png|frameless|429x429px]]<br />
<br />
AccessGroups are useful for static rules like checking if the logged-in user is part of the fisherman group:<br />
SysSingleton.oclSingleton.CurrentUser.SysRoles->exists(name=’fisherman’)<br />
For actions, we can set the Enable expression for more precision:<br />
<br />
[[File:Access control system in MDriven.png|frameless|426x426px]]<br />
<br />
We may also want to give the User a hint as to why the action is disabled. This ability has reduced the support requests a lot in all the projects we have done:<br />
<br />
[[File:Access control system in MDriven 03.png|frameless|413x413px]]<br />
<br />
For views, refine the access control per placed widget by setting the ReadOnly expression or/and the Visible expression:<br />
<br />
[[File:Access system in MDriven 04.png|frameless|467x467px]]<br />
<br />
But in views, you can also work on the whole view:<br />
<br />
[[File:Access system in MDriven 05.png|frameless|460x460px]]<br />
<br />
Setting the Readonly expression on this level affects all the widgets in the view. Setting the access expression will control if the view is shown at all.<br />
<br />
If the view’s Access expression evaluates to false, the system will look for a ViewModel named AccessDenied and if found, will show this instead. If no AccessDenied view is found, then a blank screen will show. To allow you to disable an action that would bring up a view with an access expression evaluating to false given a certain root object, check the result of the Access expression before bringing up the view. Do this with the OCL operator <code>canAccess(vmname):bool</code>.<br />
<br />
This enables you to disable actions that will end up showing AccessDenied and stop reports from being created based on a ViewModel and root object that is not allowed based on the Access expression of the ViewModel.<br />
<br />
Working with the described levels of expression, you can tailor a perfect-fit access control system that evaluates in the safe realm of your server. All OCL rules use a few OCL operators but mostly names from your model that are probably in a ubiquitous language shared with the security officers of your domain. The transparency and fine-grained control of this access control system is precisely what many organizations need to protect their information and still allow for fast-paced development.<br />
<br />
See also: [[AccessGroups,_InterestGroups_and_ViewModel-Enable]]<br />
<br />
[[Documentation:Access_groups]]<br />
[[Category:Advanced]]<br />
[[Category:Access groups]]<br />
[[Category:The MDriven Book]]</div>Hanshttps://wiki.mdriven.net/index.php?title=MDriven_administrative_database_tables&diff=16893MDriven administrative database tables2024-02-12T00:33:05Z<p>Hans: </p>
<hr />
<div>When you create a database from a model these administrative tables will be added automatically for you.<br />
<br />
But when [[Training:Reverse engineer a database|reversing an existing database]] to a model - and then wanting to use the model just like any another MDriven Model you may find that you need to add these manually.<br />
<br />
Below is the script and explanation of what the table is for:<br />
{| class="wikitable"<br />
|+<br />
!Table name<br />
!<br />
!<br />
!<br />
!PostgreSQL<br />
|-<br />
|ECO_ID<br />
|This is the cursor of the default internal key assigned to objects saved with the default persistence mapper.<br />
This table only has 1 row holding the next free identity for a new object<br />
|<br />
|CREATE TABLE [dbo].[ECO_ID](<br />
[PK] [int] NOT NULL,<br />
<br />
[BOLD_ID] [int] NOT NULL,<br />
<br />
CONSTRAINT [PK_ECO_ID] PRIMARY KEY CLUSTERED <br />
<br />
(<br />
<br />
[PK] ASC<br />
<br />
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON, OPTIMIZE_FOR_SEQUENTIAL_KEY = OFF) ON [PRIMARY]<br />
<br />
) ON [PRIMARY]<br />
|CREATE TABLE eco_id (<br />
pk SERIAL primary key,<br />
<br />
bold_id INT NOT NULL<br />
<br />
);<br />
|-<br />
|ECO_ORMAPPING<br />
|This holds a text-blob with the OR-mapping in MDrivenFormat (xml). MDriven reads this upon evolve of database to figure out what we has changed compared to the current model. <br />
After successfull evolve MDriven writes the updated OR-mapping to this column.<br />
The Table only holds 1 row<br />
|<br />
|CREATE TABLE [dbo].[ECO_ORMAPPING](<br />
[ID] [int] NOT NULL,<br />
<br />
[ECO_ORMAPPING] [ntext] NOT NULL,<br />
<br />
PRIMARY KEY CLUSTERED <br />
<br />
(<br />
<br />
[ID] ASC<br />
<br />
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON, OPTIMIZE_FOR_SEQUENTIAL_KEY = OFF) ON [PRIMARY]<br />
<br />
) ON [PRIMARY] TEXTIMAGE_ON [PRIMARY]<br />
<br />
GO<br />
|<code>CREATE TABLE eco_ormapping (<br />
id SERIAL PRIMARY KEY,<br />
eco_ormapping TEXT NOT NULL<br />
);</code><br />
|-<br />
|ECO_TABLES<br />
|This table has one row per table that MDriven has created or taken ownership of in your database.<br />
|<br />
|CREATE TABLE [dbo].[ECO_TABLES](<br />
[TABLENAME] [nvarchar](255) NOT NULL<br />
<br />
) ON [PRIMARY]<br />
<br />
GO<br />
|<code>CREATE TABLE eco_tables (<br />
tablename VARCHAR(255) NOT NULL<br />
);</code><br />
|-<br />
|ECO_TYPE<br />
|This table holds the class names from your model along with an integer number unique to that type. If MDriven is instructed to write many types to the same table (OR-Parent-Mapping) MDriven figures out the runtime type by lookin the ECO_TYPE column of such a table - and the number will match a number in the ECO_TYPE table.<br />
|<br />
|CREATE TABLE [dbo].[ECO_TYPE](<br />
[ECO_TYPE] [smallint] NOT NULL,<br />
<br />
[CLASSNAME] [nvarchar](255) NOT NULL,<br />
<br />
PRIMARY KEY CLUSTERED <br />
<br />
(<br />
<br />
[ECO_TYPE] ASC<br />
<br />
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON, OPTIMIZE_FOR_SEQUENTIAL_KEY = OFF) ON [PRIMARY]<br />
<br />
) ON [PRIMARY]<br />
<br />
GO<br />
|<code>CREATE TABLE eco_type (<br />
eco_type SMALLINT NOT NULL,<br />
classname VARCHAR(255) NOT NULL,<br />
PRIMARY KEY (eco_type)<br />
);</code><br />
|-<br />
|<br />
|<br />
|<br />
|<br />
|<br />
|}<br />
{{Edited|July|12|2024}}</div>Hanshttps://wiki.mdriven.net/index.php?title=PostgreSQL&diff=16892PostgreSQL2024-02-11T20:22:40Z<p>Hans: </p>
<hr />
<div>PostgreSQL<br />
# Install, remember pwd, <br />
# C:\Program Files\PostgreSQL\16\pgAdmin 4\runtime>pgAdmin4.exe<br />
#* Register server, localhost:5432 <br />
#* Create Database, First<br />
# run C:\Program Files\PostgreSQL\16\pg_env.bat <br />
# C:\Program Files\PostgreSQL\16\bin>psql.exe dbname=First (case sensitive)<br />
#* Just make sure it works...<br />
# Connectionstring takes this form: <br />
## Server=localhost;Port=5432;User Id=postgres;Password=<yourpwd>;Database=First;Include Error Detail=True<br />
Install an [[MDrivenServer Summarized|MDrivenServer]] or start MDrivenServerCore, and set Connection Type, and connection string:<br />
[[File:MDrPostgreSQL.png|none|thumb|566x566px]]<br />
{{Edited|July|12|2024}}</div>Hanshttps://wiki.mdriven.net/index.php?title=Training:Reverse_engineer_a_database&diff=16890Training:Reverse engineer a database2024-02-11T11:02:41Z<p>Hans: </p>
<hr />
<div>Reverse engineering is the process of taking a given database and creating a UML model from it.<br />
<br />
Videos showing the process of reverse engineering a database to model: [[Documentation:Making sense of legacy data–DB Reverse]]<br />
<br />
[[File:Reverse database menu option.png|frameless|376x376px]] <br />
<br />
You just need to give a valid connection string to it. <br />
<br />
In this case, we use the MusicStoreDb from a MicrosoftSample:<br />
<br />
[[File:Reod.png|frameless|342x342px]]<br />
<br />
Start by analyzing the database:<br />
[[File:Analyzing_the_database.png|none|frameless|327x327px]]<br />
<br />
And then you “Go!”. Be aware that this process will empty the package you started from and fill it with the stuff from the reverse engineer process:<br />
<br />
[[File:Erase package.png|frameless|294x294px]]<br />
<br />
Giving us this in the modlr tree:<br />
<br />
[[File:Package list.png|frameless|146x146px]]<br />
<br />
I will drag these classes out on a diagram to get this:<br />
[[File:Diagram_reod.png|none|frameless|218x218px]]<br />
<br />
That is enough information to run the model against the same database as we reversed it from:<br />
[[File:Run the model.png|none|frameless|218x218px]]<br />
<br />
Giving us a way to use the trusty AutoForms and EcoSpaceDebugger to look at the changeable data:<br />
[[File:EcoSpacedebugger.png|none|frameless|218x218px]]<br />
<br />
In this particular database, the RecordId, ArtistId, and so forth are of type int in the database and are designed with “Is Identity==true” in SqlExpress. Also the “Identity Increment” is set in SqlExpress.<br />
<br />
The Reverse algorithm gave us a model with int Attributes for the ID – we want ECO to be able to use that Key scheme instead of the standard ECO_ID:<br />
[[File:Use that Key scheme instead of the standard ECO ID.png|none|frameless|218x218px]]The PrimaryKey setting we got from the reversing process and the PrimaryKeyMapper I set to ”AutoInc” – an Eco-supplied primary key mapper with that name that handles the normal AutoIncrease behaviour of databases. (You can create your own named mappers in ECO for VisualStudio).<br />
<br />
For the Attribute, we must also say that it is DbAssigned; that forces ECO to omit setting the primary key on the first save and to read the saved row back from the database to get the actual assigned key (which we need to use as a foreign key in related objects):<br />
<br />
[[File:Attribute we must also say that it is DbAssigned; .png|none|frameless|230x230px]]<br />
This is enough to enable you to create new objects from the ecospace debugger and have them inserted in the database (or from code or WECPOF or what have you). If you follow along in this brief and try to save a new Album, you will notice 2 SQLServer exceptions. Both Genre and Artist foreign keys are marked NOT NULL in the DB – handle this by assigning an Artist and a Genre to the new Album with AutoForms drag and drop.<br />
<br />
=== Database Evolve ===<br />
[[File:Script to the Database.png|left|361x361px|frameless]]<br />
<br />
To use the Model Evolve mechanism – which calculates what needs to change in the database by comparing a new and old DB script – we have to allow ECO to write the (current) script to the database. If we do not do this, ECO will not have any “old” script to compare the new script with that ECO derives from your current model.<br />
<br />
In ECO for VisualStudio, you can choose to store the script some other way by using the ORMappingProvider components in your EcoSpace – but in MDriven, you do not have any code, so you really must allow us to store it in the database. '''Note:''' this is only if you want to use the Database Evolve mechanism.<br />
<br />
To write the Script to the Database, open up the prototyper once more by hitting “Run”:<br />
<br />
Once you have done this one-time operation, which is only necessary for reversed databases (since standard eco databases have these from the start), we can do some model changes:<br />
<br />
[[File:Release_date.png|frameless|220x220px]]<br />
<br />
And “Run” and “Evolve”:<br />
[[File:And “Run” and “Evolve”.png|none|frameless|218x218px]]<br />
Looking in the SQL Script tab in the dialog above, we can see the expected script that ECO will send to the database:<blockquote>BEGIN TRANSACTION;</blockquote><blockquote>/* Add column [Album].ReleaseDate */</blockquote><blockquote>ALTER TABLE [Album] ADD ReleaseDate DATETIME DEFAULT ’19000101′ NOT NULL;</blockquote><blockquote>COMMIT;</blockquote>That wraps up this presentation of the Reverse (engineer) database function in MDriven'''.'''<br />
<br />
=== MDriven for Massive Projects ===<br />
New products, new regulations, new technology, new clients, new business areas, new employees – in short, everything is challenged by a relentless progressive attitude. The IT department needs to be ''agile'' to avoid becoming the heavy-set ''we-cannot-do-it-that-way'' part of the company that limits and slows evolution down.<br />
<br />
Apply MDriven on the example database to see what it can do.<br />
[[File:List.png|left|frameless|218x218px]]<br />
The reversing mechanism runs through the Schema of the database and creates Classes of the Tables, Attributes of Fields, and Associations of primary and foreign key pairs. We can now draw diagrams that help us understand what the information is all about.<br />
<br />
[[File:Diagram_appcomplete_.png|frameless|218x218px]]<br />
<br />
And still have the overview that MDriven’s AutoDiagrams offer:<br />
<br />
[[File:Diagram_appcomplete_auto.png|frameless|325x325px]]<br />
<br />
Since the information in the database is now explained in a model, I can create ViewModels that run and show the real data with [http://theblog.capableobjects.com/2010/05/wecpof-icing-on-model-driven-cake.html WECPOF as explained here.]<br />
<br />
=== Real-life Experiences ===<br />
Even if everything is just dandy as far as I can see, when comparing the database schema and Model, I notice that some things are missing. I find some missing associations, classes, and attributes.<br />
<br />
Why is that? While the Reverse mechanism of MDriven uses the Schema of the database to conclude about the model – the Schema might not be a complete truth.<br />
<br />
After all, developers can always join things in SQL even if those things do not have an explicit primary/foreign key definition.<br />
<br />
Developers can use tables that have primary keys according to the Schema etc… In short, the Reverse mechanism is hampered by a crappy input.<br />
<br />
=== Defining Persistence Mapping Manually ===<br />
I suspect that the “Crappy input” fact is commonplace and MDriven must be able to adapt even in these situations.<br />
<br />
Luckily it does; you can add missing classes manually, explaining what the Primary Key is in the properties:<br />
<br />
[[File:PrimaryKey.png|frameless|260x260px]]<br />
<br />
You can also add Associations this way:<br />
<br />
[[File:Add Associations.png|frameless|223x223px]]<br />
<br />
It is hard work to look up missing details by browsing the database schema and comparing it with the model.<br />
<br />
=== Aided Definition of Persistence Mapping ===<br />
The latest versions of MDriven have a way of browsing the database Schema so you do not need to switch tools. Again, right-click the Package, choose Functions, and Reverse Database:<br />
<br />
[[File:Red to ECO.png|frameless|273x273px]]<br />
<br />
Click Analyze db:<br />
<br />
[[File:Analyze.png|frameless|274x274px]]<br />
<br />
The schema shows up – if you click “Go!”, the process will start to clear your existing package content and fill it up with the stuff found in the Schema. '''You do not want to do that if you are largely happy with your model''' – just missing some details.<br />
<br />
Instead, you can click the new tool “Reverse worker”:<br />
[[File:Reverseworkerhost.png|left|frameless|316x316px]]<br />
This UI shows all the Tables in the DB. Select one Table and see all the columns in that Table. You can use the buttons “Use as PK(primary key) or Attribute” and “Use as FK(foreign key)” to pick columns:<br />
<br />
[[File:Reverse worker (2).png|frameless|313x313px]]<br />
<br />
If you then move on to step 4 “Find possible actions in the Model”, the tool suggests what you can do with the picked columns:<br />
<br />
[[File:Schema (4.5).png|frameless|316x316px]]<br />
<br />
In this case, we can add an Association.<br />
<br />
Depending on whether the Table is used by any of your Model classes or not, you can also get the option to add the Class:<br />
<br />
[[File:Schema (4,5 2).png|frameless|322x322px]]<br />
<br />
This tool does the same thing you can do manually as explained earlier in this article – but it saves you the trouble of using some SQL-Browser, copying and pasting SQL-Names, or clicking and editing Persistence mapping attributes of the model.<br />
<br />
=== Continued work on your model ===<br />
When you are up and running with your reversed model it will probably not take long until you want to add new classes/tables to your database. To fully support standard MDriven models you will need to add a few administrative tables to your database as described here: The [[MDriven administrative database tables]]<br />
[[Category:Database]]<br />
[[Category:Advanced]]</div>Hanshttps://wiki.mdriven.net/index.php?title=Documentation:Making_sense_of_legacy_data%E2%80%93DB_Reverse&diff=16889Documentation:Making sense of legacy data–DB Reverse2024-02-11T11:00:59Z<p>Hans: </p>
<hr />
<div><html><br />
<h4><br />
The AutoForms in MDriven Designer make it easy and fun to get to grips with legacy data.<br />
</h4><br />
<br />
<p class="video-warn"><br />
<em>To make your experience smooth, we set the main tags mentioned in the video to the right bar menu of this mini-player. Choose an interesting subtitle on the list and immediately get to the exact theme navigation item place in the video. Now you can pick any topic to be instructed on without watching the whole video.</em><br />
</p><br />
<br />
<div class="video"><br />
<div class="video__wrapper"><br />
<iframe src="https://www.youtube.com/embed/L1MK24VmtK0?rel=0&autoplay=0" frameborder="0" allowfullscreen></iframe><br />
</div><br />
<div class="video__navigation"><br />
<br />
</div><br />
</div><br />
<br />
<br />
<div class="video"><br />
<div class="video__wrapper"><br />
<iframe src="https://www.youtube.com/embed/A-Oo5gWTc44?rel=0&autoplay=0" frameborder="0" allowfullscreen></iframe><br />
</div><br />
<div class="video__navigation"><br />
<br />
</div><br />
</div><br />
<br />
<br />
<div class="video"><br />
<div class="video__wrapper"><br />
<iframe src="https://www.youtube.com/embed/cNzXeWclvaI?rel=0&autoplay=0" frameborder="0" allowfullscreen></iframe><br />
</div><br />
<div class="video__navigation"><br />
<br />
</div><br />
</div><br />
<br />
<br />
</html><br />
[[Category:MDriven Designer]]<br />
{{Edited|July|12|2024}}</div>Hanshttps://wiki.mdriven.net/index.php?title=Per_viewmodel_ReadOnly_mode&diff=15907Per viewmodel ReadOnly mode2024-02-09T15:34:55Z<p>Hans: </p>
<hr />
<div>Some systems want to make use of distinction between ''opening a page for viewing'' - and ''intending to edit the data within''.<br />
<br />
The reasons for this may be many but one that was top of mind during our design is that we have inexperienced users that are afraid to touch or update things they do not intend to update. These users can be made to feel safer if there is a switch on the page you need to flip in order to edit anything.<br />
<br />
Of course users still want to search and navigate the system - so the readonly logic should function much like AccessGroup ViewEnable expression - that only locks down actions and controls that actually change persistent data.<br />
<br />
Resources and abilities available now for a framework backed per page readonly model:<br />
<br />
# vSysReadOnlyMode:Boolean : a Special ViewModel variable will be detected and applied<br />
## When seen it will be "or"ed into the ViewModel.ExternalReadOnlyExpression - sharing this expression with any AccessGroups.ViewEnableExpressions.<br />
# WillEffectPersistedDataOverrideValue : a tagged value to be applied to columns and actions that you want to exempt from the automatic expression deduction if it changes persistent data or not.<br />
# vSysReadOnlyMode is set to true on open of View<br />
# : vSysReadOnlyMode is set reset from false to true after save and after cancel<br />
# A special button placed along Save/Cancel called Lock/Unlock that toggles vSysReadOnlyMode<br />
## The default text in Turnkey will be "Write" as in "enter write mode" - to influence/translate the text create a global action of with Framework action: ExecuteFrameworkRuntimeActionRT.ReadOnlyModeToogle<br />
# A way to set global readonly mode for the whole application<br />
## Once the global readonly is in effect you can skip for certain ViewModels by setting taggedvalue SysReadOnlyModeSkip=true on ViewModel<br />
## To turn on global readonly set [[SysMDrivenMiscSettingsSingleton]].GlobalReadOnlyMode = true</div>Hanshttps://wiki.mdriven.net/index.php?title=SysMDrivenMiscSettingsSingleton&diff=15906SysMDrivenMiscSettingsSingleton2024-02-09T15:14:37Z<p>Hans: Created page with "Certain settings influencing the server behavior has been added. Add a Singleton class named SysMDrivenMiscSettingsSingleton Attributes recognized currently: GlobalReadOnlyMode: Boolean"</p>
<hr />
<div>Certain settings influencing the server behavior has been added. <br />
<br />
Add a Singleton class named SysMDrivenMiscSettingsSingleton <br />
<br />
Attributes recognized currently:<br />
<br />
GlobalReadOnlyMode: Boolean</div>Hanshttps://wiki.mdriven.net/index.php?title=Per_viewmodel_ReadOnly_mode&diff=15905Per viewmodel ReadOnly mode2024-02-09T15:12:37Z<p>Hans: </p>
<hr />
<div>Some systems want to make use of distinction between ''opening a page for viewing'' - and ''intending to edit the data within''.<br />
<br />
The reasons for this may be many but one that was top of mind during our design is that we have inexperienced users that are afraid to touch or update things they do not intend to update. These users can be made to feel safer if there is a switch on the page you need to flip in order to edit anything.<br />
<br />
Of course users still want to search and navigate the system - so the readonly logic should function much like AccessGroup ViewEnable expression - that only locks down actions and controls that actually change persistent data.<br />
<br />
Resources and abilities available now for a framework backed per page readonly model:<br />
<br />
# vSysReadOnlyMode:Boolean : a Special ViewModel variable will be detected and applied<br />
## When seen it will be "or"ed into the ViewModel.ExternalReadOnlyExpression - sharing this expression with any AccessGroups.ViewEnableExpressions.<br />
# WillEffectPersistedDataOverrideValue : a tagged value to be applied to columns and actions that you want to exempt from the automatic expression deduction if it changes persistent data or not.<br />
# vSysReadOnlyMode is set to true on open of View<br />
# : vSysReadOnlyMode is set reset from false to true after save and after cancel<br />
# A special button placed along Save/Cancel called Lock/Unlock that toggles vSysReadOnlyMode<br />
# A way to set global readonly mode for the whole application<br />
## Once the global readonly is in effect you can skip for certain ViewModels by setting taggedvalue SysReadOnlyModeSkip=true on ViewModel<br />
## To turn on global readonly set [[SysMDrivenMiscSettingsSingleton]].GlobalReadOnlyMode = true</div>Hanshttps://wiki.mdriven.net/index.php?title=Documentation:AccessGroups,_InterestGroups_and_ViewModel-Enable&diff=15904Documentation:AccessGroups, InterestGroups and ViewModel-Enable2024-02-09T15:12:03Z<p>Hans: </p>
<hr />
<div>This article describes new features introduced in January 2024.<br />
<br />
A video discussing this article is available here: https://youtu.be/A56NFLQqe54<br />
<br />
The basic [[Documentation:Access groups|AccessGroups overview is found here.]]<br />
<br />
This article has an accompanying sample found here: https://github.com/supportMDriven/Samples/tree/master/AccessGroupsInterestGroupsAndViewModelReadOnly<br />
<br />
An AccessGroup is a collection of Enable, Visible and View-Enable expressions. The expressions are not rooted - meaning they have no particular context like a user or anything. To get the context of the current user that is very common for AccessGroups, one could simply use SysSingleton.oclSingleton.CurrentUser from the SysUserAuthentication-pattern [[Documentation:Model Examples Old|described here.]]<br />
<br />
==== The InterestGroup - The Clutter Reducer ====<br />
What differentiates an InterestGroup from an AccessGroup: The InterestGroup is a softer sibling to the AccessGroup. The AccessGroup is used to control if Access is granted - and this has precedence. The InterestGroup is evaluated after AccessGroups and controls if we should bother to show the tagged action to the user or not. In a large system, the left-side menu can be filled with [[Class actions|Class-Actions]] and [[ViewModel actions|ViewModel-Actions;]]<nowiki/>a user from the SalesDepartment may not be interested in actions leading to views or having functionality specific to the Supply department - or vice versa.<br />
<br />
To avoid clutter and confusing options, you can tag your users, or have your users tag themselves in a way that you pick up on in an interestgroup. This way, users can reduce clutter of left-side menu themselves. If the action is <code>Visible-true</code> for any InterestGroup that tags the action - the action will be visible - but only if any AccessGroups on the action also agree that it should be visible.<br />
<br />
Since the whole point of an InterestGroup is to reduce clutter, the InterestGroup only offers the VisibleExpression - whether tagged actions should show or not. <br />
<br />
A particular user may be interested in both Sales and Supply and as such, the user should get <code>Visible=true</code> for both these InterestGroups - and will thus show actions tagged with any of these groups.<br />
<br />
A particular action may always be interesting to both Sales and Supply departments and as such, it can be tagged with both InterestGroups - or no InterestGroup. Until you introduce a third group, for example, the Quality-department, that will be the same. Having no groups or all groups tagged on an action will be equivalent if all users get <code>Visible=true</code> from at least one InterestGroup.<br />
<br />
==== Global apply of given accessgroup(s) ====<br />
A setting RuntimeDefaultOnViews has been added to accessgroups. If this is true all views that DOES NOT HAVE ANY accessgroups designed will be assigned these. This offers a way to make sure the new views reach a minimum requirement like maybe "IsLoggedIn" - and you will sleep a bit better at night:<br />
[[File:2024-02-09 16h11 28.png|none|thumb|535x535px]]<br />
<br />
==== The View-Enable expression - Considerable Complexity Reduced to Almost Nothing ====<br />
The View-Enable expression has been available on the AccessGroups for some time - but it was a bit clumsy (up til now) as it knocked out all fields on the tagged View when in evaluated to <code>Enable=false</code>.<br />
<br />
This was good for some use-cases, like protecting strict data entry forms - but bad in many cases since many views are not 100% data capture. Most views make use of some local variables - or maybe they change something transient - like an object of a Class marked <code>Persistent=false</code>. And in these cases, we really want the user to be able to change the local variables - but not allowing them to change anything that will need a save to the database (anything marked being <code>persistent=true</code> as is default on classes, attributes and associations).<br />
<br />
A common example is a Seeker. The Seeker may have a few actions on it to things you want to restrict - maybe a CreateNew-action. At the same time, you have actions that you want to allow - like search. You also want the user to be able to enter a search criteria in the search box - and maybe set some other search filters. The one separating thing between the "Dont allow this" and "Allow this" is (feel free to challenge this bold statement) that the "Allow this" group of actions and edits are all Transient (they do not change persistent data) - and the "Dont allow this" group DOES change persistent data. Please take a moment to think about this axiom. <br />
<br />
* Is this true for most cases? <br />
* Are there cases when it is not true? <br />
* If it is true very often would you benefit from a platform that could figure this out this on its own?<br />
<br />
===== How can we statically figure out if an expression will effect persistent data or not? =====<br />
How can we statically figure out if an expression will effect persistent data or not - this is the question we wanted to find an answer to and that we deemed important enough - because if we could, the View-Enable expression of the AccessGroups would go from - "Yes, it works and locks or opens everything" to "YES, it can read my mind and lets the user do benign things but stops the user from doing anything that risks permanently changing data".<br />
<br />
Long story short: We solved it! For OCL-expressions, we check if the resulting element is part of the model and if it is persistent. If it is, we lock the expression if View-Enable evaluates to false. For Action-Expressions, the story is a bit more complex; we deem Action-Expressions to effect persistent data if:<br />
<br />
# It uses one of these operators: Create, Delete and if so, that Class is Persistent<br />
# It includes a class member that is Persistent on the left side of any of these operators: ":=" , "add" , "insertat" , "clear" , "removeat", "remove"<br />
# It uses a Method that is a Persistent class and is not marked with IsQuery<br />
The above distinction between AffectingPersistentData and not is only possible because we are in a managed environment when it comes to data management. If you would have implemented your system in most other modern object oriented languages, the lack of available meta information on what is truly persistent, the lack of knowledge on what an association is and if it is persistent, the lack of knowing what properties belong to and if they are persistent would make this kind of static analysis very much harder. Luckily, you have chosen wisely and manage you Gist separate from your Modernity :-).<br />
<br />
2024-02-06: A tagged value WillEffectPersistedDataOverrideValue has been introduced to allow override of the logic that deduce if an expression effects persistent data or not. The TaggedValue has effect on Column,ContextAction(ViewModelAction) and ClassAction.<br />
<br />
2024-02-07: A [[per viewmodel ReadOnly mode]]<br />
[[Category:Access groups]]</div>Hanshttps://wiki.mdriven.net/index.php?title=File:2024-02-09_16h11_28.png&diff=15903File:2024-02-09 16h11 28.png2024-02-09T15:11:49Z<p>Hans: </p>
<hr />
<div></div>Hanshttps://wiki.mdriven.net/index.php?title=Per_viewmodel_ReadOnly_mode&diff=15902Per viewmodel ReadOnly mode2024-02-09T15:07:03Z<p>Hans: </p>
<hr />
<div>Some systems want to make use of distinction between ''opening a page for viewing'' - and ''intending to edit the data within''.<br />
<br />
The reasons for this may be many but one that was top of mind during our design is that we have inexperienced users that are afraid to touch or update things they do not intend to update. These users can be made to feel safer if there is a switch on the page you need to flip in order to edit anything.<br />
<br />
Of course users still want to search and navigate the system - so the readonly logic should function much like AccessGroup ViewEnable expression - that only locks down actions and controls that actually change persistent data.<br />
<br />
Resources and abilities available now for a framework backed per page readonly model:<br />
<br />
# vSysReadOnlyMode:Boolean : a Special ViewModel variable will be detected and applied<br />
## When seen it will be "or"ed into the ViewModel.ExternalReadOnlyExpression - sharing this expression with any AccessGroups.ViewEnableExpressions.<br />
# WillEffectPersistedDataOverrideValue : a tagged value to be applied to columns and actions that you want to exempt from the automatic expression deduction if it changes persistent data or not.<br />
# vSysReadOnlyMode is set to true on open of View<br />
# : vSysReadOnlyMode is set reset from false to true after save and after cancel<br />
# A special button placed along Save/Cancel called Lock/Unlock that toggles vSysReadOnlyMode<br />
# A way to set global readonly mode for the whole application<br />
## Once the global readonly is in effect you can skip for certain ViewModels by setting taggedvalue SysReadOnlyModeSkip=true on ViewModel<br />
## To turn on global readonly set SysMDrivenMiscSettingsSingleton.GlobalReadOnlyMode = true</div>Hanshttps://wiki.mdriven.net/index.php?title=Per_viewmodel_ReadOnly_mode&diff=15851Per viewmodel ReadOnly mode2024-02-07T16:07:23Z<p>Hans: </p>
<hr />
<div>Some systems want to make use of distinction between ''opening a page for viewing'' - and ''intending to edit the data within''.<br />
<br />
The reasons for this may be many but one that was top of mind during our design is that we have inexperienced users that are afraid to touch or update things they do not intend to update. These users can be made to feel safer if there is a switch on the page you need to flip in order to edit anything.<br />
<br />
Of course users still want to search and navigate the system - so the readonly logic should function much like AccessGroup ViewEnable expression - that only locks down actions and controls that actually change persistent data.<br />
<br />
Resources and abilities available now for a framework backed per page readonly model:<br />
<br />
# vSysReadOnlyMode:Boolean : a Special ViewModel variable will be detected and applied<br />
## When seen it will be "or"ed into the ViewModel.ExternalReadOnlyExpression - sharing this expression with any AccessGroups.ViewEnableExpressions.<br />
# WillEffectPersistedDataOverrideValue : a tagged value to be applied to columns and actions that you want to exempt from the automatic expression deduction if it changes persistent data or not.<br />
# Comming: vSysReadOnlyMode is set to true on open of View<br />
# Comming: vSysReadOnlyMode is set reset from false to true after save and after cancel<br />
# Maybe comming: A special button placed along Save/Cancel called Lock/Unlock that toggles vSysReadOnlyMode</div>Hanshttps://wiki.mdriven.net/index.php?title=Per_viewmodel_ReadOnly_mode&diff=15850Per viewmodel ReadOnly mode2024-02-07T16:05:43Z<p>Hans: </p>
<hr />
<div>Some systems want to make use of distinction between ''opening a page for viewing'' - and ''intending to edit the data within''.<br />
<br />
The reasons for this may be many but one that was top of mind during our design is that we have inexperienced users that are afraid to touch or update things they do not intend to update. These users can be made to feel sa lot safer if there is a switch on the page you need to flip in order to edit anything.<br />
<br />
Of course users still want to search and navigate the system - so a the readonly model should function much like AccessGroup ViewEnable expression - that only effect (locks down) actions and controls that actually change persistent data.<br />
<br />
Resources and abilities available now for a framework backed per page readonly model:<br />
<br />
# vSysReadOnlyMode:Boolean : a Special ViewModel variable will be detected and applied<br />
## When seen it will be "or"ed into the ViewModel.ExternalReadOnlyExpression - sharing this expression with any AccessGroups.ViewEnableExpressions.<br />
# WillEffectPersistedDataOverrideValue : a tagged value to be applied to columns and actions that you want to exempt from the automatic expression deduction if it changes persistent data or not.<br />
# Comming: vSysReadOnlyMode is set to true on open of View<br />
# Comming: vSysReadOnlyMode is set reset from false to true after save and after cancel<br />
# Maybe comming: A special button placed along Save/Cancel called Lock/Unlock that toggles vSysReadOnlyMode</div>Hanshttps://wiki.mdriven.net/index.php?title=Per_viewmodel_ReadOnly_mode&diff=15849Per viewmodel ReadOnly mode2024-02-07T15:59:36Z<p>Hans: </p>
<hr />
<div>Some systems want to make use of distinction between ''opening a page for viewing'' - and ''intending to edit the data within''.<br />
<br />
The reasons for this may be many but one that was top of mind during our design is that we have inexperienced users that are afraid to touch or update things they do not intend to update. These users can be made to feel sa lot safer if there is a switch on the page you need to flip in order to edit anything.<br />
<br />
Of course users still want to search and navigate the system - so a the readonly model should function much like AccessGroup ViewEnable expression - that only effect (locks down) actions and controls that actually change persistent data.<br />
<br />
Resources and abilities available now for a framework backed per page readonly model:<br />
<br />
# vSysReadOnlyMode:Boolean : a Special ViewModel variable will be detected and applied<br />
## When seen it will be "or"ed into the ViewModel.ExternalReadOnlyExpression - sharing this expression with any AccessGroups.ViewEnableExpressions.<br />
# WillEffectPersistedDataOverrideValue : a tagged value to be applied to columns and actions that you want to exempt from the automatic expression deduction if it changes persistent data or not.</div>Hanshttps://wiki.mdriven.net/index.php?title=Per_viewmodel_ReadOnly_mode&diff=15848Per viewmodel ReadOnly mode2024-02-07T15:57:14Z<p>Hans: </p>
<hr />
<div>Some systems want to make use of distinction between ''opening a page for viewing'' - and ''intending to edit the data within''.<br />
<br />
The reasons for this may be many but one that was top of mind during our design is that we have inexperienced users that are afraid to touch or update things they do not intend to update. These users can be made to feel sa lot safer if there is a switch on the page you need to flip in order to edit anything.<br />
<br />
Of course users still want to search and navigate the system - so a the readonly model should function much like AccessGroup ViewEnable expression - that only effect (locks down) actions and controls that actually change persistent data.<br />
<br />
Resources and abilities available now for a framework backed per page readonly model:<br />
<br />
# a Special ViewModel variable will be detected and applied: vSysReadOnlyMode:Boolean<br />
## When seen it will be "or"ed into the ViewModel.ExternalReadOnlyExpression - sharing this expression with any AccessGroups.ViewEnableExpressions.</div>Hanshttps://wiki.mdriven.net/index.php?title=Per_viewmodel_ReadOnly_mode&diff=15847Per viewmodel ReadOnly mode2024-02-07T15:53:47Z<p>Hans: Created page with "Some systems want to make use of distinction between ''opening a page for viewing'' - and ''intending to edit the data within''. The reasons for this may be many but one that was top of mind during our design is that we have inexperienced users that are afraid to touch or update things they do not intend to update. These users can be made to feel sa lot safer if there is a switch on the page you need to flip in order to edit anything. Of course users still want to sear..."</p>
<hr />
<div>Some systems want to make use of distinction between ''opening a page for viewing'' - and ''intending to edit the data within''.<br />
<br />
The reasons for this may be many but one that was top of mind during our design is that we have inexperienced users that are afraid to touch or update things they do not intend to update. These users can be made to feel sa lot safer if there is a switch on the page you need to flip in order to edit anything.<br />
<br />
Of course users still want to search and navigate the system - so a the readonly model should function much like AccessGroup ViewEnable expression - that only effect (locks down) actions and controls that actually change persistent data.</div>Hanshttps://wiki.mdriven.net/index.php?title=Documentation:AccessGroups,_InterestGroups_and_ViewModel-Enable&diff=15846Documentation:AccessGroups, InterestGroups and ViewModel-Enable2024-02-07T15:48:02Z<p>Hans: </p>
<hr />
<div>This article describes new features introduced in January 2024.<br />
<br />
A video discussing this article is available here: https://youtu.be/A56NFLQqe54<br />
<br />
The basic [[Documentation:Access groups|AccessGroups overview is found here.]]<br />
<br />
This article has an accompanying sample found here: https://github.com/supportMDriven/Samples/tree/master/AccessGroupsInterestGroupsAndViewModelReadOnly<br />
<br />
An AccessGroup is a collection of Enable, Visible and View-Enable expressions. The expressions are not rooted - meaning they have no particular context like a user or anything. To get the context of the current user that is very common for AccessGroups, one could simply use SysSingleton.oclSingleton.CurrentUser from the SysUserAuthentication-pattern [[Documentation:Model Examples Old|described here.]]<br />
<br />
==== The InterestGroup - The Clutter Reducer ====<br />
What differentiates an InterestGroup from an AccessGroup: The InterestGroup is a softer sibling to the AccessGroup. The AccessGroup is used to control if Access is granted - and this has precedence. The InterestGroup is evaluated after AccessGroups and controls if we should bother to show the tagged action to the user or not. In a large system, the left-side menu can be filled with [[Class actions|Class-Actions]] and [[ViewModel actions|ViewModel-Actions;]]<nowiki/>a user from the SalesDepartment may not be interested in actions leading to views or having functionality specific to the Supply department - or vice versa.<br />
<br />
To avoid clutter and confusing options, you can tag your users, or have your users tag themselves in a way that you pick up on in an interestgroup. This way, users can reduce clutter of left-side menu themselves. If the action is <code>Visible-true</code> for any InterestGroup that tags the action - the action will be visible - but only if any AccessGroups on the action also agree that it should be visible.<br />
<br />
Since the whole point of an InterestGroup is to reduce clutter, the InterestGroup only offers the VisibleExpression - whether tagged actions should show or not. <br />
<br />
A particular user may be interested in both Sales and Supply and as such, the user should get <code>Visible=true</code> for both these InterestGroups - and will thus show actions tagged with any of these groups.<br />
<br />
A particular action may always be interesting to both Sales and Supply departments and as such, it can be tagged with both InterestGroups - or no InterestGroup. Until you introduce a third group, for example, the Quality-department, that will be the same. Having no groups or all groups tagged on an action will be equivalent if all users get <code>Visible=true</code> from at least one InterestGroup.<br />
<br />
==== The View-Enable expression - Considerable Complexity Reduced to Almost Nothing ====<br />
The View-Enable expression has been available on the AccessGroups for some time - but it was a bit clumsy (up til now) as it knocked out all fields on the tagged View when in evaluated to <code>Enable=false</code>.<br />
<br />
This was good for some use-cases, like protecting strict data entry forms - but bad in many cases since many views are not 100% data capture. Most views make use of some local variables - or maybe they change something transient - like an object of a Class marked <code>Persistent=false</code>. And in these cases, we really want the user to be able to change the local variables - but not allowing them to change anything that will need a save to the database (anything marked being <code>persistent=true</code> as is default on classes, attributes and associations).<br />
<br />
A common example is a Seeker. The Seeker may have a few actions on it to things you want to restrict - maybe a CreateNew-action. At the same time, you have actions that you want to allow - like search. You also want the user to be able to enter a search criteria in the search box - and maybe set some other search filters. The one separating thing between the "Dont allow this" and "Allow this" is (feel free to challenge this bold statement) that the "Allow this" group of actions and edits are all Transient (they do not change persistent data) - and the "Dont allow this" group DOES change persistent data. Please take a moment to think about this axiom. <br />
<br />
* Is this true for most cases? <br />
* Are there cases when it is not true? <br />
* If it is true very often would you benefit from a platform that could figure this out this on its own?<br />
<br />
===== How can we statically figure out if an expression will effect persistent data or not? =====<br />
How can we statically figure out if an expression will effect persistent data or not - this is the question we wanted to find an answer to and that we deemed important enough - because if we could, the View-Enable expression of the AccessGroups would go from - "Yes, it works and locks or opens everything" to "YES, it can read my mind and lets the user do benign things but stops the user from doing anything that risks permanently changing data".<br />
<br />
Long story short: We solved it! For OCL-expressions, we check if the resulting element is part of the model and if it is persistent. If it is, we lock the expression if View-Enable evaluates to false. For Action-Expressions, the story is a bit more complex; we deem Action-Expressions to effect persistent data if:<br />
<br />
# It uses one of these operators: Create, Delete and if so, that Class is Persistent<br />
# It includes a class member that is Persistent on the left side of any of these operators: ":=" , "add" , "insertat" , "clear" , "removeat", "remove"<br />
# It uses a Method that is a Persistent class and is not marked with IsQuery<br />
The above distinction between AffectingPersistentData and not is only possible because we are in a managed environment when it comes to data management. If you would have implemented your system in most other modern object oriented languages, the lack of available meta information on what is truly persistent, the lack of knowledge on what an association is and if it is persistent, the lack of knowing what properties belong to and if they are persistent would make this kind of static analysis very much harder. Luckily, you have chosen wisely and manage you Gist separate from your Modernity :-).<br />
<br />
2024-02-06: A tagged value WillEffectPersistedDataOverrideValue has been introduced to allow override of the logic that deduce if an expression effects persistent data or not. The TaggedValue has effect on Column,ContextAction(ViewModelAction) and ClassAction.<br />
<br />
2024-02-07: A [[per viewmodel ReadOnly mode]]<br />
[[Category:Access groups]]</div>Hans