Opencart: How to render a template only on a specific page? - opencart

In my catalog/controller/mycontroller.php, I have a script like this:
$this->data['settings'] = $this->config->get('my_module'); // retrieves data from "setting" table
foreach ($this->data['settings'] as $data) {
if ($data['pageurl'] == 'http://'.$_SERVER['HTTP_HOST'].$_SERVER['REQUEST_URI']) {
$this->render();
}
}
In Extensions > Modules, I have my extension installed where I can set different page URLs to different Layouts and Positions like this:
Page URL Layout Position
================================================
http://...?product_id=10 Product Content Top
http://...?product_id=20 Product Content Top
http://... Home Content Top
My issue is - I'd like to render a template only ONCE on a specific page that meets the condition in the above script. What's currently happening is $this->render() is showing the template MULTIPLE times based on the Position and Layout in Extensions > Modules. For instance, when I visit http://...?product_id=10 page, it displays the template twice while it's only supposed to display it once because it only meets the condition for product_id=10 in the controller. How can I do this?

First of all, do this:
var_dump('http://'.$_SERVER['HTTP_HOST'].$_SERVER['REQUEST_URI']);
I guess You may be missing also the $_SERVER['QUERY_STRING'] part...
to see, what URL are You building to compare to URLs You are setting in the backend...
Next, please, do not call $this->render() in a loop no matter why the condition succeed more times, instead of this do something like this:
$this->data['settings'] = $this->config->get('my_module'); // retrieves data from "setting" table
$render = false;
foreach ($this->data['settings'] as $data) {
if ($data['pageurl'] == HTTP_SERVER . $_SERVER['REQUEST_URI']) { // use defined constant HTTP_SERVER
$render = true;
}
}
if($render) {
$this->render();
}

Related

Color Status indicator in O365 SharePoint Online

I am working with O365 SharePoint Online platform with SharePoint lists around 300 items in All Items View. For the first 30 items Text to Html Javascript function successfully converts text code to Html and displays status in HTML color format, but when I am trying to select next 31 items and go ahead using the pagination the function does not able to convert Html and display only text codes. I also changed the calculated column value type to "Number" to get the HTML to render in the list view. But not being changed yet. Does anyone please who have the code handy to make this work easy? Below is the Text to HTML code used in O365 platform.
<script type="text/javascript">
function TextToHTML(NodeSet, HTMLregexp) {
var CellContent = "";
var i=0;
while (i < NodeSet.length){
try {
CellContent = NodeSet[i].innerText || NodeSet[i].textContent;
if (HTMLregexp.test(CellContent)) {NodeSet[i].innerHTML = CellContent;}
}
catch(err){}
i=i+1;
}
}
// Calendar views
var regexpA = new RegExp("\\s*<([a-zA-Z]*)(.|\\s)*/\\1?>\\s*");
TextToHTML(document.getElementsByTagName("a"),regexpA);
// List views
var regexpTD = new RegExp("^\\s*<([a-zA-Z]*)(.|\\s)*/\\1?>\\s*$");
TextToHTML(document.getElementsByTagName("TD"),regexpTD);
// This function is call continuesly every 100ms until the length of the main field changes
// after which the convert text to HTML is executed.
//
var postElemLength = 0;
function PostConvertToHtml()
{
if (postElemLength == document.getElementsByTagName("TD").length)
{
setTimeout(PostConvertToHtml,100);
}
else
{
var regexpTD = new RegExp("^\\s*<([a-zA-Z]*)(.|\\s)*/\\1?>\\s*$");
TextToHTML(document.getElementsByTagName("TD"),regexpTD);
}
}
// Grouped list views
ExpGroupRenderData = (function (old) {
return function (htmlToRender, groupName, isLoaded) {
var result = old(htmlToRender, groupName, isLoaded);
var regexpTD = new RegExp("^\\s*<([a-zA-Z]*)(.|\\s)*/\\1?>\\s*$");
TextToHTML(document.getElementsByTagName("TD"),regexpTD);
// start the periodic callback to check when the element has been changed
if(isLoaded == 'false')
{
postElemLength = document.getElementsByTagName("TD").length;
setTimeout(PostConvertToHtml,100);
}
};
})(ExpGroupRenderData);
// Preview pane views
if (typeof(showpreview1)=="function") {
showpreview1 = (function (old) {
return function (o) {
var result = old(o);
var regexpTD = new RegExp("^\\s*<([a-zA-Z]*)(.|\\s)*/\\1?>\\s*$");
TextToHTML(document.getElementsByTagName("TD"),regexpTD);
};
})(showpreview1);
}</script>
Below is the generated text code which needs to be converted to Html used in calculated columns. Thanks.
=IF([Trend]="Cancelled","DarkGray",IF([Trend]="Completed","DodgerBlue",IF([Trend]="Declining","DarkOrange",IF([Trend]="Improving","ForestGreen",IF([Trend]="No Change","ForestGreen",IF([Trend]="Not Started","White",IF([Trend]="On Hold","DarkGray","")))))))
And..
="<div style='position:relative;display:inline-block;width:100%;'>
<div style='width:100%;display:inline-block;text-align:center;border:1px solid "&[VPN provisioning_Clr]&";position:absolute;color:"&[VPN provisioning_Clr]&";'> "&[VPN provisioning]&"
</div>
<div style='display:inline-block;width: 100%;background-color:"&[VPN provisioning_Clr]&";text-align:center;border:1px solid;z-index:-1;filter:alpha(opacity=20);opacity:0.2;'>"&[VPN provisioning]&"
</div>
</div>"
Assuming you are using the Classic UI in SharePoint Online...
Two possible issues:
Check to see if the site has the Minimal Download Strategy enabled.
If so disable it and test your code. MDS often is the cause for JavaScript running only once. (The page is not reloaded, only the data area.)
The loading of the next page of the list is via a Web Service call and that may not be triggering your JavaScript. (Again, the page is not reloaded, only the data area.) You may need to intercept the paging link to insure your code is run. (Also check to see if the "Asynchronous Load" option has been enabled. Edit the page, edit the web part, and expand the "AJAX Options" section.)
You may want to take a look at a workflow plus a Calculated column solution to add the color coding. See: http://techtrainingnotes.blogspot.com/2018/01/adding-html-to-sharepoint-columns-color.html

Set queryable source on Rendering Parameter Template field

I have a Rendering Parameter template applied to a sublayout. It has a single Droptree field on it, and I want to set the Source of that field to a Sitecore query so I can limit the options available for that field.
Source can be:
query:./*
or
query:./ancestor-or-self::*[##templatename='MyTemplate']/
The query just needs to grab items relative to the content item that we're on. This normally works with Droptree fields in the content editor.
However I'm finding that the query isn't working here because we're in the rendering parameters, so it's not using the content item as it's context.
The query fails and I just get the full Sitecore tree.
I found this can be fixed up for the Datasource field with 'Queryable Datasource Locations' at this link:-
http://www.cognifide.com/blogs/sitecore/reduce-multisite-chaos-with-sitecore-queries/
However I don't know where to start to get this working for other rendering parameter fields.
Any ideas? (I'm using Sitecore 6.6 Update 5)
Unfortunately, the pipeline mentioned in Adam Najmanowicz's answer works for some other types, like Droplink and Multilist, but the pipeline isn't run for Droptree fields.
After looking into this deeper I found that the Source of a Droptree field IS using the wrong context item, as Adam mentioned, but the code comes from the Droptree field itself:-
Sitecore.Shell.Applications.ContentEditor.Tree, Sitecore.Kernel
Utilising the query string code from Adam's answer, we can create a 'fixed' Droptree custom field, that is almost the same as the regular Droptree but will use the correct context item instead.
The code will inherit from the normal Tree control, and only change the way that the Source property is set.
public class QueryableTree : Sitecore.Shell.Applications.ContentEditor.Tree
{
// override the Source property from the base class
public new string Source
{
get
{
return StringUtil.GetString(new string[]
{
base.Source // slightly altered from the original
});
}
set
{
Assert.ArgumentNotNull(value, "value");
if (!value.StartsWith("query:", StringComparison.InvariantCulture))
{
base.Source = value; // slightly altered from the original
return;
}
Item item = Client.ContentDatabase.GetItem(this.ItemID);
// Added code that figures out if we're looking at rendering parameters,
// and if so, figures out what the context item actually is.
string url = WebUtil.GetQueryString();
if (!string.IsNullOrWhiteSpace(url) && url.Contains("hdl"))
{
FieldEditorParameters parameters = FieldEditorOptions.Parse(new UrlString(url)).Parameters;
var currentItemId = parameters["contentitem"];
if (!string.IsNullOrEmpty(currentItemId))
{
Sitecore.Data.ItemUri contentItemUri = new Sitecore.Data.ItemUri(currentItemId);
item = Sitecore.Data.Database.GetItem(contentItemUri);
}
}
if (item == null)
{
return;
}
Item item2 = item.Axes.SelectSingleItem(value.Substring("query:".Length));
if (item2 == null)
{
return;
}
base.Source = item2.ID.ToString(); // slightly altered from the original
}
}
The above code is pretty much the same as the Source property on the base Tree field, except that we figure out the proper context item from the URL if we've detected that we're in the rendering parameters dialog.
To create the custom field, you just need to edit the Web.Config file as described here. Then add the custom field to the core database as described here.
This means that parameters can now have queries for their source, allowing us to limit the available items to the content editor. (Useful for multi-site solutions).
The key here would be to set the Field Editor's context to be relative to the item you are editing instead of the Rendering parameters (that I think it has by default).
So you could have processor:
public class ResolveRelativeQuerySource
{
public void Process(GetLookupSourceItemsArgs args)
{
Assert.IsNotNull(args, "args");
if (!args.Source.StartsWith("query:"))
return;
Item contextItem = null;
string url = WebUtil.GetQueryString();
if (!string.IsNullOrWhiteSpace(url) && url.Contains("hdl"))
{
FieldEditorParameters parameters = FieldEditorOptions.Parse(new UrlString(url)).Parameters;
var currentItemId = parameters["contentitem"];
if (!string.IsNullOrEmpty(currentItemId))
{
Sitecore.Data.ItemUri contentItemUri = new Sitecore.Data.ItemUri(currentItemId);
contextItem = Sitecore.Data.Database.GetItem(contentItemUri);
}
}
else
{
contextItem = args.Item;
}
}
}
hooked as:
<sitecore>
<pipelines>
<getLookupSourceItems>
<processor patch:before="*[#type='Sitecore.Pipelines.GetLookupSourceItems.ProcessQuerySource, Sitecore.Kernel']"
type="Cognifide.SiteCore.Logic.Processors.ResolveRelativeQuerySource, Cognifide.SiteCore" />
</getLookupSourceItems>
</pipelines>
</sitecore>
Together with ResolveQueryableDatasources from Przemek's blog this should solve your problem.

How to generate media item link with id instead of path in Sitecore

Anyone knows how to generate links in sitecore with ID instead of item path?
If you use GetMediaUrl method from the API, I can get this URL:
/~/media/Images/Archive/content/News and Events/News_and_Events_Level2/20070419162739/iwhiz3.jpg
The problem with this approach is that if someone changes the media item name, removes it somewhere or deletes it, the above link will break.
I notice if I insert a media link from rich text editor, I get the link as below:
/~/media/14BDED00E4D64DFD8F74019AED4D74EB.ashx
The second link is better because it's using the item id, so if the actual media item is renamed, removed, or deleted, all related links will be updated too. On top of that, when Sitecore renders the page, it will actually convert the above link and display the item path so it's readable.
I'm using Sitecore 6.5 and currently doing content migration so I need to make sure all internal links are updated properly.
May I know if there is a method to generate the second link by using sitecore API?
Thanks!
The GetMediaItemUrl extension method seems to give you what you want.
public static class ItemExtensions
{
public static string GetMediaItemUrl(this Item item)
{
var mediaUrlOptions = new MediaUrlOptions() { UseItemPath = false, AbsolutePath = true };
return Sitecore.Resources.Media.MediaManager.GetMediaUrl(item, mediaUrlOptions);
}
}
[TestFixture]
public class when_using_items_extensions
{
[Test]
public void a_url_based_on_media_item_id_can_be_generated()
{
// Arrange
Database db = global::Sitecore.Configuration.Factory.GetDatabase("master");
Item item = db.GetItem("/sitecore/media library/Images/MyImage");
// Act
var mediaUrl = item.GetMediaItemUrl();
// Assert
Assert.That(mediaUrl, Is.EqualTo("/~/media/17A1341ABEEC46788F2159843DCEAB03.ashx"));
}
}
These are called dynamic links and you can normally generate them using the LinkManager e.g:
Sitecore.Links.LinkManager.GetDynamicUrl(item)
.. but I'm not sure of the method to do this with Media links (there probably is one but I cant seem to find it and its not on MediaManager) but the basic syntax is:
"/~/media/" + item.ID.ToShortID() + ".ashx"
If you always want to use ID's instead of paths, you can change this setting in webconfig to false (like this):
<setting name="Media.UseItemPaths" value="false"/>`
Here is what the webconfig describes about it:
MEDIA - USE ITEM PATHS FOR URLS
This setting controls if item paths are used for constructing media URLs.
If false, short ids will be used.
Default value: true
Then you can use the default implementation (without additional parameters):
Sitecore.Resources.Media.MediaManager.GetMediaUrl(item);
This is what I use:
var imgField = ((Sitecore.Data.Fields.ImageField)currentItem.Fields["Icon"]);
MediaUrlOptions opt = new MediaUrlOptions();
opt.AlwaysIncludeServerUrl = true;
// Absolute Path works as well. So either use AbsolutePath or AlwaysIncludeServerUrl
opt.AbsolutePath = true;
string mediaUrl = MediaManager.GetMediaUrl(imgField.MediaItem, opt);

sitecore query items by client url

I am looking for a quick and dirty way to query the layouts files of a particular page by its friendly url. This is probably easy, but I can't find the solution.
Basically I want to say something like the following. Pseudo-code:
var mainpage = Sitecore.EasyQueryUtility.GetItemByFriendlyUrl(requestedUrl);
or
var mainpage = Sitecore.EasyQueryUtility.GetOppositeOfFriendlyUrl(friendlyurl);
It sounds like you want to do two things here:
Determine an item based on its rendered URL in the address bar (i.e. friendly URL)
Determine the layout being used by the item once you determine the item.
If those are correct, hopefully this can help you out:
Note: untested code I did on-the-fly
// if you have the full URL with protocol and host
public static Item GetItemFromUrl(string url)
{
string path = new Uri(url).PathAndQuery;
return GetItemFromPath(path);
}
// if you have just the path after the hostname
public static Item GetItemFromPath(string path)
{
// remove query string
if(path.Contains("?"))
path = path.split('?')[0];
path = path.Replace(".aspx", "");
return Sitecore.Context.Database.GetItem(path);
}
Once you have the item you can get the layout's name like so:
item.Visualization.GetLayout(Sitecore.Context.Device).Name;
Or the layout's physical file path to the ASPX:
item.Visualization.GetLayout(Sitecore.Context.Device).FilePath;
If you want to get the path of the aspx file which is used for the layout of your page, you can use:
Sitecore.Context.Item.Visualization.Layout.FilePath
I may have misunderstood you but if you want to control the format of friendly URLs you can set several attributes via the Sitecore.Links.UrlOptions class and pass an instance of this in to the link manager. See here for more details. (Note - the LinkManager class is only available from SiteCore 6 I beleive).
The code you would end up with looks like this:
Sitecore.Links.UrlOptions urlOptions = (Sitecore.Links.UrlOptions)Sitecore.Links.UrlOptions.DefaultOptions.Clone();
urlOptions.SiteResolving = Sitecore.Configuration.Settings.Rendering.SiteResolving;
string url = Sitecore.Links.LinkManager.GetItemUrl(item, urlOptions);
You can then set fields like AddAspxExtension on the urlOptions you pass in.
As you can see, the process is reliant on you passing in an item - whether it be obtained via the current context or retrieved from the URL you start off with.
If you were asking about obtaining the layout definition item, take a look at this which shows you how.

Adding a search box to filter a list of results in Symfony?

I need to put a search box within a list of objects as a result of a typical indexSuccess action in Symfony. The goal is simple: filter the list according to a criteria.
I've been reading the Zend Lucene approach in Jobeet tutorial, but it seems like using a sledge-hammer to crack a nut (at least for my requirements).
I'm more interested in the auto-generated admin filter forms but I don't know how to implement it in a frontend.
I could simply pass the search box content to the action and build a custom query, but is there any better way to do this?
EDIT
I forgot to mention that I would like to have a single generic input field instead of an input field for each model attribute.
Thanks!
I'm using this solution, instead of integrating Zend Lucene I manage to use the autogenerated Symonfy's filters. This is the way i'm doing it:
//module/actions.class.php
public function executeIndex(sfWebRequest $request)
{
//set the form filter
$this->searchForm = new EmployeeFormFilter();
//bind it empty to fetch all data
$this->searchForm->bind(array());
//fetch all
$this->employees = $this->searchForm->getQuery()->execute();
...
}
I made a search action which does the search
public function executeSearch(sfWebRequest $request)
{
//create filter
$this->searchForm = new EmployeeFormFilter();
//bind parameter
$fields = $request->getParameter($this->searchForm->getName());
//bind
$this->searchForm->bind($fields);
//set paginator
$this->employees = $this->searchForm->getQuery()->execute();
...
//template
$this->setTemplate("index");
}
It's important that the search form goes to mymodule/search action.
Actually, i'm also using the sfDoctrinePager for paginate setting directly the query that the form generate to get results properly paginated.
If you want to add more fields to the search form check this :)
I finally made a custom form using the default MyModuleForm generated by Symfony
public function executeIndex {
...
// Add a form to filter results
$this->form = new MyModuleForm();
}
but displaying only a custom field:
<div id="search_box">
<input type="text" name="criteria" id="search_box_criteria" value="Search..." />
<?php echo link_to('Search', '#my_module_search?criteria=') ?>
</div>
Then I created a route named #my_module_search linked to the index action:
my_module_search:
url: my_module/search/:criteria
param: { module: my_module, action: index }
requirements: { criteria: .* } # Terms are optional, show all by default
With Javascript (jQuery in this case) I append the text entered to the criteria parameter in the href attribute of the link:
$('#search_box a').click(function(){
$(this).attr('href', $(this).attr('href') + $(this).prev().val());
});
And finally, back to the executeIndex action, I detect if text was entered and add custom filters to the DoctrineQuery object:
public function executeIndex {
...
// Deal with search criteria
if ( $text = $request->getParameter('criteria') ) {
$query = $this->pager->getQuery()
->where("MyTable.name LIKE ?", "%$text%")
->orWhere("MyTable.remarks LIKE ?", "%$text%")
...;
}
$this->pager->setQuery($query);
...
// Add a form to filter results
$this->form = new MyModuleForm();
}
Actually, the code is more complex, because I wrote some partials and some methods in parent classes to reuse code. But this is the best I can came up with.