What would be the best way to change the source of an image according to a theme selected? Ideally you will just have a CSS for each theme and set the image as a backround for example (that is what I currently do).
However what I need to do now is use an actual image that is functional and is part of the presentation, it is not just an image that is part of the design. This image will look slightly different depending on the theme.
I am saving the theme that each user has selected in the database. I want to be able to change the source of the image when a user requests a page according to the theme in the database. I am using dependency injection (StructureMap), MVC 4 and EF 5. I want to somehow assign value to ViewBag.MyImagePath in my _Layout page and then all pages to just have src="#ViewBag.MyImagePath".
You could write a theme aware image helper:
public static class HtmlExtensions
{
public static IHtmlString ThemeAwareImage(
this HtmlHelper htmlHelper,
string image,
string alt = ""
)
{
var context = htmlHelper.ViewContext.HttpContext;
var theme = context.Session["theme"] as string;
if (string.IsNullOrEmpty(theme))
{
// the theme was not found in the session
// => go and fetch it from your dabatase
string currentUser = context.User.Identity.Name;
theme = GetThemeFromSomeDataStore(currentUser);
// cache the theme in the session for subsequent calls
context.Session["theme"] = theme;
}
var urlHelper = new UrlHelper(htmlHelper.ViewContext.RequestContext);
var img = new TagBuilder("img");
img.Attributes["alt"] = alt;
img.Attributes["src"] = urlHelper.Content(
string.Format("~/images/{0}/{1}", theme, image)
);
return new HtmlString(img.ToString(TagRenderMode.SelfClosing));
}
}
which could be used in your views to render those images:
#Html.ThemeAwareImage("foo.jpg", "This is the foo image")
As a better alternative to using the Session to store the current user theme you could cache it in a cookie or even better make it part of your routes in which case your site will be much more SEO friendly.
Related
Somehow I find this hard to describe, but here I go:
I have a div in my SelectClasses Razor view page with an id="id152".
In order for me to show that div on the page at reload, I have to add the suffix #id152 to my page url.
<div id="id152">blabla</div>
...
..
Section 7
Now my question: Is there a way to add/pass this suffix to a 'RedirectToAction()'?
public ActionResult Index()
{
//All we want to do is redirect to the class selection page and add a suffix
return RedirectToAction("SelectClasses", "Registration", new { id = 99 })); //add suffix here somewhere
}
So when my SelectClasses view is shown, the url looks something like this:
'[url]/SelectClasses/99#id152'
The RedirectToActionResult (among the rest of RedirectTo* results) is meant to be used for generation of URLs based on registered routing data.
In your case, you wish to concatenate a hash parameter value (#id152) that is not being sent to the server and only used by the browser. That's why said methods don't bother dealing with it.
I suggest you do this instead:
var redirUrl = Url.Action("SelectClasses", "Registration", new { id = 99 });
redirUrl = String.Concat(redirUrl, "#id152");
return Redirect(redirUrl);
So here is the explanation of the problem I am facing. It might look very similar other already asked questions, but none of them answered my problem.
I want to open an angular template reference in a new browser window (with all the styles) and use that window to print the contents using system print dialog.
By template reference I mean a whole component or may be just a fraction of template of given component.
Please note that I do not want to do it by below methods:
Opening a new route in a new browser window. (Why? this will cause all other things like common toolbar or help open up with the component. Also a new route will be required which is undesired.)
Opening the content in a closable modal.
Ok, This is how I did it using ComponentFactoryResolver and Injector. Just inject these two dependencies in your component which wants to open other component (ReportComponent in this case) in new browser window.
The constructor looks something like this in snippet below.
constructor(
private resolver: ComponentFactoryResolver,
private injector: Injector
) {}
.
.
.
This method opens up the component in new browser window.
public openPrintableReport(): void {
// resolve and instantiate the component which you want to open in new window.
const componentFactory = this.resolver.resolveComponentFactory(ReportComponent);
let component = componentFactory.create(this.injector);
component.changeDetectorRef.detectChanges();
// define external window
this.externalWindow = window.open('', '', 'width=1000,height=1000,left=150,top=200');
// copy all the styles to external window.
document.querySelectorAll('style').forEach(htmlElement => {
this.externalWindow.document.head.appendChild(htmlElement.cloneNode(true));
});
this.externalWindow.document.title = 'Printer Friendly Report';
// attach the component to external window
this.externalWindow.document.body.appendChild(component.location.nativeElement);
}
I'm using bootstrap 3 and i have to set multiple themes ont my website depends on each client we have.
Client 1 : green theme;
Client 2 : purple theme;
Client 3 : grey theme..
And goes on...
Context
The solution has to be dynamic, the website looks like the same, juste color change.
For now i use the theme on url (get theme... i search for a better solution)
I can't make a specific css by client type, duplicated code -> maintain impossible.
My solution for now
I make a file in php called bootstrap-override.css.php
Wich contain on top this code :
<?php
header("Content-Type: text/css");
$defaultWhite = $white = '#FFFFFF';
$defaultGray1 = $gray1 = '#E7E7E7';
$defaultGray2 = $gray2 = '#CDCDCD';
$defaultGray3 = $gray3 = '#F2F2F2';
$defaultGrayBlue1 = $grayBlue1 = '#99AEBD';
$defaultGrayBlue2 = $grayBlue2 = '#495D6C';
$defaultBlue1 = $blue1 = '#0094D7';
$defaultBlue2 = $blue2 = '#004F9F';
if (isset($_GET['theme'])) {
switch ($_GET['theme']) {
case 'CA':
$grayBlue1 = '#008168';
$blue1 = '#009AA5';
$blue2 = '#2A3B48';
break;
case 'CE':
$defaultGrayBlue1 = $grayBlue1 = '#ABABAB';
$defaultGrayBlue2 = '#727274';
$blue1 = '#D70119';
$blue2 = '#D70119';
break;
// More client...
}
}
?>
And my css using those variables, I call it using this code :
$this->headLink()->prependStylesheet($this->basePath() . '/websiteName/css/bootstrap-override.css.php'.((!empty($_GET['theme'])) ? "?theme={$_GET['theme']}" : ''))
(ZF2 application)
But the switch case part is ugly and the get part too.., i can't leave this in that way if i have a new client type i will make this code more complexe... i wanted a more efficient way to do this.
If someone can help to improve this.
Edit : From comments, Store all theme in Database, with a 1/n relationship between client / theme is possible, I had already thought month ago... but my problem for this solution is to design it. If i store this in Database, i have to create a model layer to respect Zend\MVC best practises, and how i design my CSS in this architecture ?
If I understand the issue correctly I would suggest adding CSS files to the HeadLink.
You could have a 'default' CSS file which implements the shared styles for all clients and then a separate, theme specific, CSS file which then specifies the just the styling for CA, CE etc.
To apply this to the HeadLink view helper you can use an event listener, listening 'on render'.
For example
use Zend\ModuleManager\Feature;
use Zend\EventManager\EventInterface;
use Zend\Mvc\MvcEvent;
class Module implements BootstrapListenerInterface
{
public function onBootstrap(EventInterface $event)
{
$eventManager = $event->getApplication()->getEventManager();
$eventManager->attach(MvcEvent::EVENT_RENDER, [$this, 'addClientCss'], 100);
}
public function addClientCss(MvcEvent $event)
{
$serviceManager = $event->getApplication()->getServiceManager();
$config = $serviceManager->get('config');
if (! isset($config['custom_css']) || ! is_array($config['custom_css'])) {
return;
}
$viewPluginManager = $serviceManager->get('ViewPluginManager');
$headLink = $viewPluginManager->get('headlink');
$basePath = $viewPluginManager->get('basepath')();
foreach($config['custom_css'] as $cssFilePath) {
$headLink->appendStylesheet($basePath . $cssFilePath);
}
}
}
Then the configuration in the required module.config.php.
return [
'client_css' => [
'/some/css/path/to/styles.css',
'/another/path/to/css/file.css'.
],
];
This would ideally be located in a module that is specific for the client or project you are working on (and also last in the module list in application.config.php) so that this config is merged last.
I have a requirement where I need to remove or hide the default links displayed in Suite Bar like NewsFeed, SkyDrive, Sites etc. I want to add my own links and use this section as my Menu.
So while adding I want the items to be easily configurable by content editors. They can edit the links that needs to be shown and control the order. No hard coding of links.
If someone can help in this.
Regards,
navish
This can be done by oevrriding Delegate controls that displays these links. The below links will help
http://www.learningsharepoint.com/2013/02/10/addremove-links-in-top-suitebar-skydrivesitesnewsfeed-in-sharepoint-2013/
You should create a custom delegate control that target the SuiteLinksDelegate ControlId.
Add it to a Farm-scoped feature to make the custom delegate control active in the whole farm.
If you do not like hard-coded links you can program against a custom SharePoint list that stores the configurable links.
To Add custom links you can use the approach described here: http://zimmergren.net/technical/sp-2013-some-new-delegatecontrol-additions-to-the-sharepoint-2013-master-pages
If you need to remove some built-in links while keeping others (I had this requirement) you can use code like this:
public partial class SuiteLinksDelegate : MySuiteLinksUserControl
{
protected override void Render(HtmlTextWriter writer)
{
// save for later
var httpwriter = (writer.InnerWriter as HttpWriter);
// hijack the innerwriter
var sb = new StringBuilder();
var sw = new StringWriter(sb);
var tw = new HtmlTextWriter(sw);
writer.InnerWriter = tw;
// call base
base.Render(writer);
// get the html
var currentHtml = sb.ToString();
XElement element = XElement.Parse(currentHtml);
// remove SkyDrive link
var suiteLinkNodes = element.Elements("li").ToArray();
var remainingNodes = suiteLinkNodes.Where(node => !(node.ToString().Contains("ShellDocuments")));
element.ReplaceNodes(remainingNodes);
var modifiedHTML = element.ToString();
// set back the old innerwriter
writer.InnerWriter = httpwriter;
// write delegate control html
httpwriter.Write(modifiedHTML);
}
}
You can use javascript approach to hide this links as described in below link
http://www.tuyrcorp.com/sharepoint-2013-top-links-name-id-and-how-to-hide-them/
you can also add new item in the dropdown using this same javascript as well
Hope this helps
Thanks
I have a simple function that I want to call in the code behind file name Move
and I was trying to see how this can be done and Im not using asp image button because not trying to use asp server side controls since they tend not to work well with ASP.net MVC..the way it is set up now it will look for a javascript function named Move but I want it to call a function named move in code behind of the same view
<img alt='move' id="Move" src="/Content/img/hPrevious.png" onclick="Move()"/>
protected void Move(){
}
//based on Search criteria update a new table
protected void Search(object sender EventArgs e)
{
for (int i = 0; i < data.Count; i++){
HtmlTableRow row = new HtmlTableRow();
HtmlTableCell CheckCell = new HtmlTableCell();
HtmlTableCell firstCell = new HtmlTableCell();
HtmlTableCell SecondCell = new HtmlTableCell();
CheckBox Check = new CheckBox();
Check.ID = data[i].ID;
CheckCell.Controls.Add(Check);
lbl1.Text = data[i].Date;
lbl2.Text = data[i].Name;
row.Cells.Add(CheckCell);
row.Cells.Add(firstCell);
row.Cells.Add(SecondCell);
Table.Rows.Add(row);
}
}
Scott Guthrie has a very good example on how to do this using routing rules.
This would give you the ability to have the user navigate to a URL in the format /Search/[Query]/[PageNumber] like http://site/Search/Hippopotamus/3 and it would show page 3 of the search results for hippopotamus.
Then in your view just make the next button point to "http://site/Search/Hippopotamus/4", no javascript required.
Of course if you wanted to use javascript you could do something like this:
function Move() {
var href = 'http://blah/Search/Hippopotamus/2';
var slashPos = href.lastIndexOf('/');
var page = parseInt(href.substring(slashPos + 1, href.length));
href = href.substring(0, slashPos + 1);
window.location = href + (++page);
}
But that is much more convoluted than just incrementing the page number parameter in the controller and setting the URL of the next button.
You cannot do postbacks or call anything in a view from JavaScript in an ASP.NET MVC application. Anything you want to call from JavaScript must be an action on a controller. It's hard to say more without having more details about what you're trying to do, but if you want to call some method "Move" in your web application from JavaScript, then "Move" must be an action on a controller.
Based on comments, I'm going to update this answer with a more complete description of how you might implement what I understand as the problem described in the question. However, there's quite a bit of information missing from the question so I'm speculating here. Hopefully, the general idea will get through, even if some of the details do not match TStamper's exact code.
Let's start with a Controller action:
public ActionResult ShowMyPage();
{
return View();
}
Now I know that I want to re-display this page, and do so using an argument passed from a JavaScript function in the page. Since I'll be displaying the same page again, I'll just alter the action to take an argument. String arguments are nullable, so I can continue to do the initial display of the page as I always have, without having to worry about specifying some kind of default value for the argument. Here's the new version:
public ActionResult ShowMyPage(string searchQuery);
{
ViewData["SearchQuery"] = searchQuery;
return View();
}
Now I need to call this page again in JavaScript. So I use the same URL I used to display the page initially, but I append a query string parameter with the table name:
http://example.com/MyControllerName/ShowMyPage?searchQuery=tableName
Finally, in my aspx I can call a code behind function, passing the searchQuery from the view data. Once again, I have strong reservations about using code behind in an MVC application, but this will work.
How to call a code-behind function in aspx:
<% Search(ViewData["searchQuery"]); %>
I've changed the arguments. Since you're not handling an event (with a few exceptions, such as Page_Load, there aren't any in MVC), the Search function doesn't need the signature of an event handler. But I did add the "tablename" argument so that you can pass that from the aspx.
Once more, I'll express my reservations about doing this in code behind. It strikes me that you are trying to use standard ASP.NET techniques inside of the MVC framework, when MVC works differently. I'd strongly suggest going through the MVC tutorials to see examples of more standard ways of doing this sort of thing.