ASP.NET MVC 4 Mobile Display Modes with Exact View Name - mobile

I have set up the Display Mode in Application Start event as
DisplayModeProvider.Instance.Modes.Insert( 0, new DefaultDisplayMode( "iPhone" ){
ContextCondition = ( context =>
context.GetOverriddenUserAgent( ).IndexOf(
"iPhone",
StringComparison.OrdinalIgnoreCase ) >= 0 ) } );
Then in the controller I have return View where I specify the view name:
return View( "~/Views/Common/User/Login.cshtml", viewModel );
And if I visit the page from the iPhone it will go directly to Login View
If I do not specify the view name:
return View( viewModel );
In this case from the iPhone I see the Login.iPhone.cshtml
Question: Is it possible to specify the name of the view but some how the DisplayModeProvider will select general or iPhone version of the cshtml file?

I don't normally like to resurrect old questions but as this one was never answered and this is one I had particular trouble finding an answer to myself, it may be worth having an answer for anyone else who comes across the same problem.
You could add your additional locations to the ViewLocationFormats and PartialViewLocationFormats collections for the ViewEngines you are using. This way you could just specify the view name as tvanfosson suggests and MVC would find the file correctly, which should allow the mobile overriding to work it's magic.
Here is some code I use to override the PartialViewLocationFormats, you could also do the same using ViewLocationFormats. This is added in global.asax as part of application_start
ViewEngines.Engines.Clear();
var razorViewEngine = new RazorViewEngine
{
PartialViewLocationFormats = new[]
{
"~/Views/{1}/{0}.cshtml",
"~/Views/Shared/{0}.cshtml",
"~/Views/{1}/EditorTemplates/{0}.cshtml",
"~/Views/{1}/DisplayTemplates/{0}.cshtml",
"~/Views/Shared/DisplayTemplates/{0}.cshtml"
}
};
Because this method involves clearing the viewengines collection, you will need to add in all locationFormats, even the standard ones, for all the view engines you are using.

Related

How to access Another Module's Content and Presentation Items after 2sxc v10.20+

Here is code that worked up through 2sxc 10.9.1. Though I am able to get the CmsBlock for the TabID, ModuleID and get that to .Render(), I need more. Here is the old code. Not sure it makes any difference, but this View is using the normal Link content-type and is running in an older version of the Content App (appx 3.03=ish). 2sxc has been upgraded and is now 11.22.0 LTS.
I have removed unnecessary stuff, so I doubt this runs as is...
#using ToSic.Razor.Blade
#using ToSic.SexyContent.Environment.Dnn7
#{
var Helpers = CreateInstance("_Helpers.cshtml");
// Display the items from the Manage Links module, we go in 'sideways'
// this gives us just the Content items with their Presentations settings, etc.
var sxci = Factory.SxcInstanceForModule(3360, 606); // ModuleID of Manage Links
var dyn = Factory.CodingHelpers(sxci);
var allLinks = dyn.AsDynamic(dyn.Data["Default"]);
}
#* other stuff *#
<div class="row co-documents justify-content-center align-items-center">
#foreach (var linkItem in allLinks) {
var linkInfo = Helpers.LinkInfos(linkItem.Link, linkItem.Window, linkItem.Icon);
string iconStyle = linkItem.IconStyle ?? "fas";
int linkColumns = (int)linkItem.Presentation.Columns;
string linkIconAlign = linkItem.Presentation.IconAlign;
string linkIconBGColor = linkItem.Presentation.IconBGColor;
#* other stuff *#
}
</div>
So the easy thing to figure out was how to get the module as a CmsBlock which I can Render() as is (below), but what I need to do instead is get proper access to the List of Content Items AND their Presentation data (like above, allLinks).
ToSic.Sxc.Dnn.Factory.CmsBlock(606, 3360).Render();
What am I missing? How can I get access to the other module's data like I was doing before? In this case, I do this in 3 different places on the website. So to outline this in English, I have a module that the client manages a few special links that get displayed in MegaMenus, other special nav, and directly on a couple of pages. In each place they render differently. In their "home" module, where they get edited, they just look boring like this:
I realize its something like this:
var allLinks = something1.AsList(something2.Data["Default"]);
I understand that something2 is an app instance, but how do I create it in the context of the other module?
And what is something1 nowadays? And how do instantiate it? Looks like its a new ToSic.Sxc.Code.DynamicCode() but I can't figure out how to construct that in a way that I can use or doesn't just throw errors.
Thanks in advance for any insight!!
Okay, it took a little testing, trial and error. And also I missed that DynamicCode() was a Method of the Factory class. In retrospect it does seem easy now.
So first you get the BlockBuilder
var block = Factory.CmsBlock(606, 3360);
Then you get the DynamicCode instance (Code.DnnDynamicCodeRoot) from that
var dc = Factory.DynamicCode(block);
And then things are normal
var allLinks = AsList(dc.Data["Default"]);
The rest of the code works like it did before; I can foreach through the links with Header (renamed from ListContent) and Presentation (now Content.Presentation) working just as expected.
The above answer works fine if you are inside the C# Razor template of the 2sxc View. But what if you are outside, for example in a Razor template for a DDR Menu?
Same two steps as above (get the block and the dc), but then you do NOT have access to AsList() or the App. Thankfully, you already have DynamicCode, so you could just get all the records in the Bibliography content-type like this:
<ul>
var items = dc.AsList(dc.App.Data["Bibliography"]);
foreach (var item in items)
{
<li>#item.EntityTitle</li>
}
</ul>
So once you've got your dc you've got access to all the usual 2sxc toys.

Extjs 6.5 - finding an association type (hasOne, hasMany, belongsTo, etc)

I upgraded my application from Sencha Touch 2.4 to ExtJs 6.5.3.
On Sencha Touch 2.4, there is a function for the Association (Ext.data.association.Association) by the name of getType() that returns the association-type as a string, for example: "hasOne".
as seen for example, in here (the Model source code of Sencha Touch 2.4):
for (i = 0; i < associationCount; i++) {
association = associations[i];
associationName = association.getName();
type = association.getType();
allow = true;
if (associationType) {
allow = type == associationType;
}
if (allow && type.toLowerCase() == 'hasmany') {
In my project, by understanding if the association type is hasmany, hasone or belongsto I can "choose" what type of SQL query to create (not originally written by me, but that's a large project and I can't contact the original developer), so that's a must for me.
I try to look into the Extjs 6 / 6.5 documentation and couldn't find anything.
Seems like it was deprecated a long time ago.
I was thinking about inserting the "type" inside the model as an object of models and types, for example:
{
'MyProject.model.Receipt': 'hasone',
'MyProject.model.Order': 'hasmany',
'MyProject.model.invoice': 'belongsto',
...
}
and then try to access it from the model itself and find the type by the association "parent" model.
It feels like a risk and an overkill for such a (suppose-to-be) easy task.
I tried to also find online for solutions but it looks like no one ever tried to find a solution for something like that.
Thank you
The associations property on a record is a bit tricky and a little obscured from us as developers. If you use the inverse property in your associations, they'll sometimes appear in the associations property, which is problematic. I've raised an issue with Sencha Support asking for a reliable method on returning the actual associations on a record, but I've also come up with an interim solution to determine if they're truly an association. I've extended this idea to maybe something that might help you with the types? But you'll have to test this out to determine if it actually works for you... I only use hasOne and hasMany in my code, so I don't know if this is right for belongsTo.
Here's the Fiddle, and here's the code:
Ext.override(Ext.data.Model, {
isActualAssociation: function (role) {
var isThing = !role.instanceName || role.fromSingle;
if (!isThing) {
console.log('found weirdo', role)
}
return isThing;
},
getAssociationType: function (association) {
if (association.fromSingle) {
return 'hasOne';
}
else if (association.storeName) {
return 'hasMany';
}
return 'belongsTo';
}
});

Difference in accessing variables in views

I've two controllers one is "Upload" which deals with images uploads and other is "Page" whid deals with the creation of pages of CMS now if in my "Upload" controller I load both the models i.e 'image_m' which deals with image upload and "page_m" which deals with the pages creation I've highlighted the relevant code my problem is if I access the variables in the view
$this->data['images'] = $this->image_m->get(); sent by this I can access in foreach loop as "$images->image_title, $images->image_path" etc
But the variable sent by this line ***$this->data['get_with_images'] = $this->page_m->get_no_parents();*** as $get_with_images->page_name, $get_with_images->page_id etc produces given error
A PHP Error was encountered
Severity: Notice
Message: Trying to get property of non-object
Filename: upload/index.php
Line Number: 20
what is the difference between these two access levels one for $image & other for $get_with_images because I can only access its values as $get_with_images
class Upload extends Admin_Controller {
public function __construct() {
parent::__construct();
***$this->load->model('image_m');
$this->load->model('page_m');***
}
public function index($id = NULL) {
//var_dump($this->data['images'] = $this->image_m->get_with_images());
//$this->data['images'] = $this->image_m->get_with_images();
***$this->data['images'] = $this->image_m->get();***
$this->data['subview'] = 'admin/upload/index';
if ($id) {
$this->data['image'] = $this->image_m->get($id);
count($this->data['image']) || $this->data['errors'][] = 'Page Could not be found';
}
$id == NULL || $this->data['image'] = $this->image_m->get($id);
/*this calls the page_m model function to load all the pages from pages table*/
***$this->data['get_with_images'] = $this->page_m->get_no_parents();***
You are not posting all your code so its hard to tell but is it because you used $this-> in the controller, but you haven't done the same thing in the view?
In this case i would recommend not using $this-> because its not necessary. Also its much better to check for errors etc when you call the model so do something like
if ( ! $data['images'] = $this->image_m->get($id) ) {
// Failure -- show an appropriate view for not getting any images
// am showing $data in case you have other values that are getting passed
$this->load->view( 'sadview', $data ); }
else {
// Success -- show a view to display images
$this->load->view( 'awesomeview', $data ); }
so we are saying if nothing came back - the ! is a negative - then show the failure view. Else $data['images'] came back, and it will be passed to the view. note i have not had to use $this-> for anything and it won't be needed in the view.
Would also suggest using separate methods - have one method to show all images and a separate method like returnimage($id) to show an image based on a specific validated $id.
====== Edit
You can access as many models as you want and pass that data to the View. You have a different issue - the problem is that you are waiting until the View to find out - and then it makes it more difficult to figure out what is wrong.
Look at this page and make sure you understand the differences between query results
http://ellislab.com/codeigniter/user-guide/database/results.html
When you have problems like this the first thing to do is make a simple view, and echo out directly from the model method that is giving you problems. Its probably something very simple but you are having to look through so much code that its difficult to discover.
The next thing is that for every method you write, you need to ask yourself 'what if it doesn't return anything?' and then deal with those conditions as part of your code. Always validate any input coming in to your methods (even links) and always have fallbacks for any method connecting to a database.
On your view do a var_dump($get_with_images) The error being given is that you are trying to use/access $get_with_images as an object but it is not an object.
or better yet on your controller do a
echo '<pre>';
var_dump($this->page_m->get_no_parents());
exit();
maybe your model is not returning anything or is returning something but the data is not an object , maybe an array of object that you still need to loop through in some cases.

CakePHP: Scaffolding after having written edit/view/add

I have an application in which we give a very friendly interface for managing data. This is done through many controllers' add/edit/view functions. But now the requirement has come that we should have "super admins" able to edit anything, and scaffolding will give them a quick and dirty manner of changing data. Since scaffolding uses add/edit/view by default, I've unintentionally overwritten the ability to scaffold.
I can't just go and change all my calls to edit/add for our "user friendly" data managing. So I want to essentially ignore the add/edit/view when, for example, a user has a flag of "yes, please let me scaffold". I imagined it would be something like:
public function edit($id) {
if (admin_user) {
$scaffold;
} else {
[user-friendly version code]
}
}
But no dice. How can I achieve what I want?
suppose you already have admin users and you want to scaffold only super-user:
Also suppose you store the information about beeing a super-user or not in a column named super in the users table
in your core.php
Configure::write('Routing.prefixes', array('admin', 'super));
in your appController
public $scaffold = 'super';
beforFilter() {
if($this->Auth->user('super') && !isset($this->params['super'])
$this->redirect(array('super' => true));
}
Now I can't try this code but the idea should work.
edit: we need to check if we are already in a super_action to avoid infinite redirect

Unable to find view for viewmodel?

I'm trying to make a composition UI for a small website.
My building tree looks like this:
Shell (Conductor.Collection.AllActive)
Contains multiple IPod's (you can view them as small widgets)
1 Pod is a PagePod.
This last one is of sort IPodConductor, so a combination of a Screen ( the pagepod ) containing IPage (like MainPage, ContactPage .. )
My whole construction can find all my viewmodels and views following Caliburns convention, but not my MainPage.
The error is as following:
"Cannot find view for Gymsport.Client.Pages.Main.MainPageViewModel"
My structure to the view is as following:
Gymsport.Client.Pages.Main.MainPageView
Following the convention, caliburn should be able to locate my view... but it doens't.
Anybody any tips on to figure out or pointers for debugging this error.
Thanks in advance.
In C.M, there is additional logic for finding Views concerning words like Page, etc. (see here).
So you can either change your views to match the rules in C.M, remove word Page from your view models, or you can enforce custom simple view location with something like that:
ViewLocator.LocateTypeForModelType = (modelType, displayLocation, context) =>
{
var viewTypeName = modelType.FullName.Substring(
0,
modelType.FullName.IndexOf("`") < 0
? modelType.FullName.Length
: modelType.FullName.IndexOf("`")
);
viewTypeName = viewTypeName.Replace("Model", string.Empty);
if (context != null)
{
viewTypeName = Regex.Replace(viewTypeName, "View$", string.Empty);
viewTypeName += "." + context;
}
var viewType = (from assembly in AssemblySource.Instance
from type in assembly.GetExportedTypes()
where type.FullName == viewTypeName
select type).FirstOrDefault();
return viewType;
};

Resources