I need to be able to share local data (in a yaml file) from templates with a layout. I want to be able to load relevant YAML data in the template then have that data be accessible from the layout (and associated partials). Is this possible?
I found this blog post which seems to indicate that this is possible through some shennaigans but I can't get it to work and this seems hacky. Is there a built in way to pass data between templates and layouts? And if not, can the hack in this blog post be made to work?
Update: now featuring code!
Here is code from the blog post linked above. I tried it exactly as show, and played with it a little, but no dice.
# in your layout file (inside layout/ folder):
<% myvar = yield_content :myvar %>
<% myvar = eval(myvar.to_str) %>
# in your template file (e.g., mypage.html.erb):
<% content_for :myvar, data.somefile %>
# this examples assumes there's a somefile.yml in your data/ folder:
name: hello world
tag: the quick brown jumps over the lazy dog
The above code returned no implicit conversion of nil into String which sounds like the YAML file either didn't load or was't passed to the layout (since it's loaded in the template.) I confirmed it was loading in the template so maybe it wasn't being passed to the template for some reason? (Aside: as a new programmer I've been taught eval is the devil's work and to "never use it" so this solution seems sketchy to me.)
On my own I tried to do something like the below, and if the YAML is loaded into the template like so:
data = YAML.load_file('some/file/data.yml')
it works fine within the template but the layout throws undefined local variable errors.
The same, but reverse, is true if you load the YAML into the layout, it won't be accessible in the template. Additionally, I don't want the data loaded in the layout because it will be different depending on the template it's coming from.
Ideally I would just pass the loaded YAML data from the template to the layout, but I think I could also achieve my ultimate goal if the layout new for which blog (it's a multi blog site) it was being called on. However after talking with some people on the Middleman team that route does not appear to be an option.
It turns out all you have to do to pass data from a template to a layout is declare an instance variable. So for YAML data like I was doing:
# In your template
<% #my_var = YAML.load_file("path/to/data.yml") %>
# In your layout
<p>My_var contains: <%= #my_var %></p>
I don't know if this is obvious if you're familiar with the tool chain underneath middleman, or if it's in the documentation somewhere and I just couldn't find it, but that's all there is to it.
Thanks so much to #dddd1919 for dropping this knowledge bomb.
Related
I would like to write a generator plugin to add some post-like items to my blog. The items are supposed to appear in the blog index, but they have no page associated to them (you can't click on them).
I know I need something like
class QuoteGenerator < Generator
safe true
def generate(site)
# add a single post
site.posts << QuotePost.new(site, site.source, "Blub")
end
end
But what I don't understand is how to implement my Post subclass. I've found that other plugins (like this one to embed Flickr photos) write the data they want to a markdown+YAML file, and then reference this file:
class QuotePost < Post
def initialize(site, base, title)
# Nooo, I don't want to create a .md file for this
name = "2016-05-13-test.md"
dir = ""
# (write out .md file here)
super(site, base, dir, name)
end
end
But then, I would hardly need a Plugin in the first place. I could just generate the markdown files myself (with an external script).
What I'd like to do is to just set a couple of variables in my Post subclass, and have them available in the template for the blog index. How can I do that?
The case you've described looks to be unrelated to Post, since usual posts are file-based. Collections may be suitable, but again - that's not clear how you getting the content.
I'd suggest two major options:
Use _data/ to set an object list, where each item has the required properties if you can define them via a static JSON/CSV/etc file (or generate it once, to separate external data producer and jekyll visualization).
Use :pre_render hook and a plugin if you have to define the data via code -
your hook will also receive a payload hash as a second parameter which allows you full control over the variables that are available while rendering
Having your data in site.data variable allows you to iterate through items, render something or include a template and so on. Also there are plugins which generate new pages based on site's data.
The right answer depends on how you're getting the content, which markup you need and how those items will be used.
I've been scouring the net in pursuit of this one. Magento Commerce comes us dry for me. grepping core code, reading Alan Storm, perusing Inchoo, and even finding related questions on SO turn up no answers for me.
With that said, my problem is with a transactional email template that works when processed from the backend but not from the frontend. Here's a snippet:
<td width="100%" colspan="2" align="left">
<!-- inject products quote table -->
{{block type="adminhtml/sales_quotation_email" template="sales/quotation/email_quote_items.phtml" inherits=$template quote=$quote salesrep=$salesrep}}
<!-- inject cross-sell products table -->
{{block type="adminhtml/sales_quotation_email" template="sales/quotation/email_quote_cross_sells.phtml" inherits=$template quote=$quote salesrep=$salesrep}}
</td>
In the backend, these blocks are rendered as expected. In the front-end, everything above and below these block directives is rendered, but it appears that the directives die in processing when it comes time to render the template. No errors are thrown.
I followed the advice here, but no luck. Originally I tried to use setDesignConfig on the email template model, but that didn't work. I even tried to set the area as an attribute in the directive, but that also did not work. A colleague suggested that I have two copies of the above templates: one set in design/adminhtml and the other design/frontend. I'd rather not have to worry about extra maintenance. Plus, I fear that I'd encounter the same problem if the block type specified in the directive comes from adminhtml. I don't want that solution.
So what am I doing wrong? What do I not understand?
How does Magento resolve the real path to the template, and is a template forced to reside in the area of its parent block?
Help is needed! Thanks.
SOLVED
I found a related post on Magento Commerce that put me back on track. I started dumping out the design configuration in two controllers: 1 in frontend and one in adminhtml. I noticed immediately that theme information was missing in the frontend request. See some sample output from my frontend controller:
Mage_Core_Model_Design_Package Object (
[_store:protected] =>
[_area:protected] => frontend
[_name:protected] => mypackage
[_theme:protected] => Array
(
[layout] =>
[template] =>
[skin] =>
[locale] => mytheme
)
[_rootDir:protected] =>
[_callbackFileDir:protected] =>
[_config:protected] =>
[_shouldFallback:protected] => 1 )
Notice that layout, template, and skin are empty in the theme property. When I dumped the design configuration from an adminhtml controller, these properties were set.
So going back to my frontend controller, I added the following line before I instantiated my email template model:
Mage::getDesign()->setArea('adminhtml');
Mage::getDesign()->setTheme('mytheme');
And poof! It worked! My blocks directives were processed and the fully rendered content was returned as expected.
So although my thinking was correct to set the area, that alone wasn't enough. I also has to configure the theme.
I'm happy with the solution. I hope it helps others. But to fully answer this question, I'm still curious if anyone knows why package information is missing from the design configuration during a frontend request. Does it have to do with the block type in the directive coming from adminhtml? That would make sense, because adminhtml has no need to worry about theme information. I just don't know where those decisions would be made in core code. See update below.
UPDATE:
Learned even more since the original post. My question gave a code sample that built a block of a type that came from adminhtml. The path to the template, I thought, was resolving to the front-end, and that was why no template could be found. That wasn't actually the case. An adminhtml block, because of its class naming convention, will look in design/adminhtml/package/default/module for your template.
However, in my particular Magento installation, I have a design override in local.xml that changes the admin theme so that it admin requests check design/adminhtml/package/mytheme/module for templates. And that is where my phtml templates are stored. So on a front-end request, the controller has no clue about this override, and is only building up the design configuration based on what is set in the store configuration for the particular package and theme.
In summary, my call to setTheme() must utilize that modified config data, like so:
Mage::getDesign()->setTheme(
(string) Mage::app()
->getConfig()
->getNode('stores/admin/design/theme/default')
);
I guess that goes to say, then, that a simple call to setArea() would be sufficient for most installations.
Finally, you will need to revert the design configuration changes after your work is done, otherwise subsequent actions might produce undesired results.
I have a rails app that has a list of Products, and therefore I have an index action on my ProductsController that allows me to see a list of them all.
I want to have another view of the products that presents them with a lot more information and in a different format -- what's The Rails Way for doing that?
I figure my main options are:
pass a parameter (products/index.html?other_view=true) and then have an if else block in ProductsController#index that renders a different view as required. That feels a bit messy.
pass a parameter (products/index.html?other_view=true) and then have an if else block in my view (index.html.haml) that renders different html as required. (I already know this is not the right choice.)
Implement a new action on my controller (e.g.: ProductsController#detailed_index) that has it's own view (detailed_index.html.haml). Is that no longer RESTful?
Is one of those preferable, or is there another option I haven't considered?
Thanks!
Another way of doing it would be via a custom format. This is commonly done to provide mobile specific versions of pages, but I don't see why the same idea couldn't be applied here.
Register :detailed as an alias of text/html and then have index.detailed.haml (or .erb) with the extra information. If you need to load extra data for the detailed view you can do so within the respond_to block.
Then visitors to /somecollection/index.detailed should see the detailed view. You can link to it with some_collection_path(:format=>'detailed')
I'm not sure whether this is 'bettrr' than the alternatives but there is a certain logic I think to saying that a detailed view is just an alternative representation of the data, which is what formats are for.
After doing some reading, I think that adding a new RESTful action (option #3 in my question) is the way to go. Details are here: http://guides.rubyonrails.org/routing.html#adding-more-restful-actions
I've updated my routes.rb like this:
resources :products do
get 'detailed', :on => :collection
end
And added a corresponding action to my ProductsController:
def detailed
# full_details is a scope that eager-loads all the associations
respond_with Product.full_details
end
And then of course added a detailed.html.haml view that shows the products in a the detailed way I wanted. I can link to this with detailed_products_path which generates the URL /products/detailed.
After implementing this I'm sure this was the right way to go. As the RoR guides say, if I was doing a lot of custom actions it probably means I should have another controller, but just one extra action like this is easy to implement, is DRY and works well. It feels like The Rails Way. :-)
I'm fairly new to Joomla (I've been more of a Wordpress guy) and I have a question about module positions.
Can a module know what position it's in. For instance can I do something like this:
if(modulePosition =='left'){
Do this...
}else{
Do that...
}
It seems easy enough, but I've searched for hours and can't find anything that will help me with that. I know there is a countModules function but from what I can tell, that just checks to see if the module is active or not.
Thanks for your help!
I found the answer! Mostly thanks to #Hanny. His idea of using the modules id got me googling for that and I came across the answer. For anyone else that happens to be looking to do something similar here it is.
You use a global variable $module (who'd a thought, right?)
So my code now looks like this:
$class = '';
if($module->position == 'position1'){
$class = 'class1';
}
and so on...
Pretty simple, huh?
To find out what else you can do with the global variable $module just put this in your code and see what info you can use:
echo(print_r($module));
Thanks for all your help!
The short answer is 'yes', you'll assign a module a position based on your template. When it shows up you can have conditionals like that regarding that position (different templates have different naming conventions for positions, so make sure you know what they are before coding).
For example, some use "Position12", others may use "leftcol", etc. You just have to check in the template files to see (you can check the .xml file in the template directory to see the positions listed in the template, or look in the index.php file for the jdoc includes).
In some of my experience, the only time you'll really ever need code like that is in the core layout files of the template (for example, if you have different widths of columns depending on modules being present or not), otherwise there won't really be a time where you 'may or may not' have a module showing up - because you'll explicitly be telling them where to be and when on the back end.
I tried to comment under john's solution but I don't have a enough rep points-- I wanted to add it doesn't matter what you name the module position in your template case-wise the position name you get back from $module->position is always all lowercase regardless of how you named the position in the template... ie. in your template xml somewhere you might have topBar position will be named 'topbar' not 'topBar' when you try to check it with
if($module->position == 'topBar') //always false... use instead
if($module->position == 'topbar') //what you need to use
I'm going to disagree with Hanny. I think the answer is no, not as you describe.
The template knows when it has reached a module position, and it gets a list of modules assigned to that position, then calls for them to be rendered. But it doesn't pass that information on. It's not stored in JApplication or JDocument etc either (like, nothing stores where in the template the rendering is up to or anything).
There are some hacky ways to almost get what you want though. If you know the template positions you need to search (sadly there's no easy method for getting this from the template - otherwise you could parse your template's .XML file for <position> elements...), then you could do something like:
<?php
$positions = array('left', 'right', 'top', 'bottom')
$found_in = false;
foreach ($positions as $cur_position)
{
$module_positions = JModuleHelper::getModules($cur_position);
foreach ($module_positions as $cur_module_in_pos)
{
if ($cur_module_in_pos->module == 'mod_MYMODULE')
{
$found_in = $cur_position;
}
}
}
if ($found_in)
...
Of course, this doesn't work well if your module is included multiple times on the page, but maybe that's an assumption you can make?
Otherwise it'd be up to hacking the core - you could use a JDispatcher::trigger() call before the template calls a module, set some var etc. Unfortunately there's no such event in core to start (a pre_module_render or something).
A module instance is assigned to a single position and this is stored in the database and normally you would style the position in the template. A module instance can only be assigned to one position. So while it's an interesting question, it's not really a practical one.
The exceptions to this are the following:
loadposition ... you might want to know if a module is being loaded using the plugin because this would put it potentially somewhere besides the styled area for the position. THough i would recommend always making a new instance for this precisely so you have more control.
loadmodule ... module loaded by name using the plugin. In this case again you are probably better off making a new instance of the module and styling it. Also I'd put it in a span or div anyway, depending what it is.
jdocinclude:module ... loading a module directly in a template. Again if you are doing this I would wrap it in a span or div. In this case you are also allowed to include a string of inline styles if you like that kind of thing.
Rendering the module to a string and echoing it, again that is basically a very customized solution and you would want to set the styles and options.
I'm implementing AuthorizeNet into my site and as per [this posting] I am to use something like:
#using (Html.BeginSIMForm("http://...", 1.99M, "...", "...", true))
{
#Html.CheckoutFormInputs(true);
#Html.Hidden("order_id", "1234");
<input type = "submit" value = "Pay" />
}
the trouble is that the BeginSIMForm method outputs HTML which, of course, gets escaped by Razor, so I need to output raw.
I've tried:
#Html.Raw(using(html.BeginSIMForm()) { ... })
but that's no good. I can't quite get the syntax right. is there a way to tell Razor to generate raw output between certain markers?
* update *
I missed something which was in the posting. what I need to do is wrap the call to .CheckoutFormInputs() like this:
#Html.Raw(Html.CheckoutFormInputs(true));
and that produces the right output... however, the call to .BeginSIMForm() produces output (a form) at the very top of my page and breaks everything. grr...
I've had a look at the source code for their helper. what garbage. I recommend anyone wanting to implement this service on Razor not bother downloading it. I will code the forms by hand. may come back with some code.
Based on the thread you referenced, it seems like that component is not implemented correctly with respect to how it writes the output. You should contact the authors of that library and verify that it supports MVC 3 including the Razor view engine.
I have a detailed answer here
https://stackoverflow.com/a/8426501/1087034
I hope this helps.