Full text search in the Attached documents in Kentico CMS 7 - asp.net

Is there any way in which I can search the attachments and show the attached documents in the search result? My search result should show only the attachments in which the search text is contained. Right now I can search the attachments and the page in which the attachment contains search text is shown.
Say, I have a Page Home and attachment myattachment.docx as its attachment. While searching Background as search text in the Site search, which is contained only in the myattachment.docx (not contained in Home page) , the search result show Home page as a search result. What I am intending is to return something like Home/ myattachment.docx as a result instead of Home page. My page may any number of attachments.
Thanks In Advance!

The info on this is a little bit sketch. See below for the documentation I used.
Create a custom global event handler in AppCode (or Old_App_Code), make sure it is a partial class of CMSModule Loader.
Add your custom event handler in the overriden Init() The one you want is DocumentEvents.GetContent.Execute.
The sender object should be the current TreeNode being indexed for search. You can then use that node to access the relevant attachments and modify the event args e.content to add your document text to the search.
[CustomDocumentEvents]
public partial class CMSModuleLoader
{
private class CustomDocumentEventsAttribute : CMSLoaderAttribute
{
public override void Init()
{
// Assigns custom handlers to the appropriate events
DocumentEvents.GetContent.Execute += Document_GetContent;
}
private void Document_GetContent(object sender, DocumentEventArgs e)
{
TreeNode node = sender as TreeNode;
if (node != null)
{
//Note, this is psuedo code, this isnt the way to iterate over TreeNode.Attachments
foreach( attachment in node.Attachments ) {
e.Content += attachment.content;
}
}
}
}
}
More info
See http://devnet.kentico.com/docs/devguide/index.html?event_handlers_overview.htm for implementing custom events in general in version 7.
See this for custom search in version 5
http://devnet.kentico.com/Knowledge-Base/Search/How-to-search-for-documents-using-assigned-categor.aspx
See http://devnet.kentico.com/Knowledge-Base/API-and-Internals/Custom-Handler-Library-compatibility.aspx for the updated event name for version 7 referred to in the version 5 custom search example.

Related

Manually Open custom inspector for serializable object

I have a window editor that holds nodes. I would like to open a custom inspector when one of these nodes is selected. The node class is a custom serializable class. Is this possible?.
It seems that custom inspectors can be created manually through the Editor.CreateEditor method but can't see how to let it appear docked like an usual inspector in the unity inspector window.
I would like to achieve the same behaviour that we have when we select a gameobject in sceneview that inmediately show properties for the object (Components, etc...) in the unity inspector.
Cheers
As I'm not sure what you're asking, here are multiple different solutions;
Selection
If you just want your nodes to become the focus of the hierarchy, then in your custom window's OnGUI method, use the code below;
[CustomEditor(typeof(NodeManager))]
public class NodeManager : EditorWindow
{
private static NodeManager window;
private Node[] m_nodes;
[MenuItem("Custom Windows/Node Inspector")]
public static void Init()
{
if(window == null)
window = GetWindow<NodeManager>("Node Manager", true, typeof(SceneView));
}
public void OnGUI()
{
m_nodes = GameObject.FindObjectsOfType<Node>();
foreach(Node node in m_nodes)
{
GUILayout.BeginHorizontal();
{
GUILayout.Label(node.name);
if (GUILayout.Button("Select"))
Selection.objects = new Object[] { node.gameObject };
}
GUILayout.EndHorizontal();
}
}
}
This adds a Button for each object in your custom window view, that will then select that object in the hierarchy.
Auto-Docking
I originally found the second response to this question, which goes into the details of parameters of the GetWindow method, and with this you can clearly see how to dock the window (I've converted it from JS to C# below).
(I looked fairly extensively in UnityEditor and UnityEditorInternal namespaces but couldn't find the Hierarchy or the Inspector).
GetWindow<T>(string title, bool focus, params System.Type[] desiredDockNextTo)
Which we can write for this example as;
EditorWindow.GetWindow<NodeInspector>("Node Test", true, typeof(SceneView));
This example docks the windows next to the SceneView window. This functionality can be combined with a custom inspector's OnInspectorGUI method, to automatically launch the custom node window, if it's not already open.
[CustomEditor(typeof(Node))]
public class NodeInspector : Editor
{
public override void OnInspectorGUI()
{
base.OnInspectorGUI();
NodeManager.Init();
}
}
Sorry if this isn't what you are looking for, if it's not then please give more details and I will amend my answer to better suit the question.
Hope this helped.
Has a possibility, you can make a custom ScriptableObject and Custom Editor for It, and when open the node inspector, just find a instance of the scriptable object and use Selection.selectedObject = scriptableObject, and in the custom editor, make a static field called 'editor' to draw inside.
It will work.

How can access sublayout control from another sublayout in Sitecore?

I have master layout and three sublayout header, content, footer. In header sublayout I have search textbox and search button, On click of search button result will display on content sublayout, On content sublayout I have repeater but I am not able to access repeater control in header sublayout.
I've dealt with similar problems in Sitecore sites before. There are two ways of doing this that I'd suggest you consider:
1) With some custom code, your Layout can act as an intermediary for the SubLayouts
Define some sort of "receive results" interface that can be implemented by any sublayout that wants to handle search results. For example:
interface IRenderSearchResults
{
void RenderResults(IEnumerable<SearchResultData> resultSet);
}
Make your "content" sublayout implement this interface, so that when something sends it search results, it can handle them:
public class ContentSublayout : Sublayout, IRenderSearchResults
{
public void RenderResults(IEnumerable<SearchResultData> resultSet)
{
repeaterControl.DataSource = resultSet;
// do whatever other data binding tasks are required
}
}
Then extend your Layout to have methods for "register me to receive search results" and "send search results to wherever they're needed":
public class MyLayout : Page
{
private List<IRenderSearchResults> resultControls = new List<IRenderSearchResults>();
public void RegisterSearchResultsControl(IRenderSearchResults control)
{
resultControls.Add(control);
}
public void DispatchSearchResults(IEnumerable<SearchResultData> resultSet)
{
foreach(var control in resultControls)
{
control.RenderResults(resultSet);
}
}
}
Your "content" control should then register itself when it's created, by calling the register method on the Layout. Add a method like:
protected void Page_Init(object sender, EventArgs e)
{
MyLayout layout = (MyLayout)this.Page;
layout.RegisterSearchResultsControl(this);
}
Finally, when your header control has generated the results to display, it should dispatch them with a call to the Layout:
public class MyHeader : SubLayout
{
public void DoTheSearch()
{
// whatever you do to perform a search
MyLayout layout = (MyLayout)this.Page;
layout.DispatchSearchResults(theSearchResults);
}
}
(This code was typed off the top of my head, rather than copy/pasted - so there may be typos - sorry)
That way any set of results receiving controls can exist anywhere on a page (and can be added / removed via Page Editor if you want) but whenever a search is run, if they're on the page they will get the results. This approach can also allow you to make things like the paging UI for the results, or a results summary display into separate controls that editors can move around the page.
Note that if you have multiple Layouts on your site, you probably need to make sure that the dispatching code is in a base class of all your Layouts, to make sure it's always available if an editor changes the layout of a particular page.
2) You could probably use Sitecore's event model
This article gives a fairly helpful description of how the internal framework for events works: https://adeneys.wordpress.com/2012/10/21/decoupling-through-the-sitecore-event-pool/ but there are others if you google.
The best way to do this would be to use the querystring to hold the search parameters, which is the standard tried and tested way to do this.
When the user presses the search button you then need to redirect to the current page with the quersytring parameters added. Then have the sublayout with repeater read the querystring parameters to list the results, rather than pass data from one sublayout to another.
The use of the querystring means that users can bookmark search results and removes the need for a complex event model.
Also by using the browser layout as an intermediary for passing data means you are tightly coupling the sublayout and the browserlayout which may not be good if you need to use the sublayout else where.

How to set conditions in jsf tags

can any one help on the below.
I need to open a new browser tab using command link tag in jsf, only when a condition is true.
How to set conditions in jsf tags?
You can either use the rendered attribute, and completing them with a EL expression. Using the rendered attribute, the link will only be shown if the condition is true.
If you want to open a commandlink in a new browser tab depending on a condition, you can easily achieve it using the rendered attribute:
<h:commandLink action="my_navigation_rule"
rendered="#{!MyBean.booleanValue}" />
<h:commandLink action="my_navigation_rule"
rendered="#{MyBean.booleanValue}" target="_blank"/>
As the rendered conditions are exclusionary, it will only be printed one of the commandLinks.
More info on JSF Expression Language (EL) (Very useful to set conditions in jsf tags) --> here
Note: I know the solution can be improved since you can use an EL expression to determine the target value, but in my opinion this solution is easier.
You can use managed bean actionListener of the commandMenu and then get the condition tested and redirect to the page in new tab if its true...
import javax.faces.event.ActionEvent;
public class PageBean {
String page;
public PageBean() {
}
public void getMenu(ActionEvent actionEvent) {
// Add event code here...
/* add your code to get and test condition and based upon the condition true and false conditon get commandMenu Id of the menu clicked by the following way
be sure to give the id of the command menu the same name you want to open in the next tab upon menu click like in my case i have given the command menu id as page1
String id = actionEvent.getComponent().getId();
System.out.println(" Menu ID : "+id);
now set the fetched menu id to the page variable with the proper page extension and acccess the pariable in the action from the manged bean to redirect to the page in the next tab upon menu click after testing the validation */
page = id;
System.out.println("Value assigned to the page from the menu Id is :"+page);
}
public String getPage() {
return page;
}
public void setPage(String page) {
this.page = page;
}
public void dashContentEntryCheck(){
System.out.println("entered inside dashboard content task flow");
}
}

asp.net mobile/desktop site toggle button, switching masterpage, but styles "stuck"

Summary
I'm having style issues when flipping master pages via a button event in asp.net 4.0. The new master switches, but the css from the old master remains. I don't understand how this could happen as the styles are defined within the head of the old master, and i can clearly see via the markup the new master is being displayed with whats supposed to be a totally different set of styles. Also, viewing source shows all the new css declarations in the head. How can i get this to "refresh" or "reload"?
Some details
I'm implementing a mobile version of my asp.net site. If a mobile device is detected i set a cookie and switch the master page in the preinit to a mobile friendly one. This works fine:
protected virtual void Page_PreInit(Object sender, EventArgs e)
{
if (IsMobile)
this.Page.MasterPageFile = "m-" + this.Page.MasterPageFile;
}
I have a "full site" button at the bottom that allows you to flip back and forth between the mobile and desktop view. When clicking it, i change the value in the cookie. Then when the page redirects to itself, the value is checked, and it gives the respective masterpage. This also "works", i can tell the right masterpage is rendering via markup. Except the styles from the mobile version remain even when the desktop master is being displayed. I did the redirect thinking it would prevent this.
// desktop/mobile site toggle button click event
protected void viewMobileButton_Click(Object sender, EventArgs e)
{
HttpCookie isMobileCookie = Cookies.snatchCookie("isMobile");
if (bool.Parse(isMobileCookie.Value))
Cookies.bakeCookie("isMobile", "false");
else
Cookies.bakeCookie("isMobile", "true");
Response.Redirect(Request.RawUrl);
}
This is the first time I've done anything like this, and not sure if i'm even going about it the right way, or how to debug from here. Thanks in advance for any help.
Edit
Ok, so i figured out it's related to the JQuery Mobile Scripts. JQuery Mobile has this way of tying pages together. I don't fully understand it, i think they use it for page transitions, and it's preventing my new CSS from registering. When i turn it off, my masterpage flips fine with css included. I'm looking into a way to turn off JQuery Mobile before my redirect. Note sure how though yet.
The problem ended up being related to JQuery Mobile AJAX for page-transitions. JQuery Mobile does not load the head of the document on additional page requests after the first.
So when i'd switch the mobile master to the desktop master, the head of the document wouldn't load to bring in my styles. There are a few way's this can be fixed:
This way just turns off AJAX altogether, and fixes the problem, but then you can't benefit from it:
<form data-ajax="false">
This is a way to do it problematically, but remind you, it will not work via an event after initialization of JQuery Mobile, so again you can't benefit from it:
$.mobile.ajaxEnabled = false;
The above two solutions i support could work if you redirected through a page first if you have to use an onclick event and an event handler.
A better solution is to add rel="external" to the link to tell JQM it's and outgoing link.
<a href="myself.com?mobile=true" rel="external" >
But because i couldn't run some code i wanted to in order to change the cookie, i had to pass a query string parameter, check it on the preinit, then set the cookie which my page also looks at on the preinit and flips the master.
Here's my full solution below in case someone is out there doing the exact same thing. Note because my website is using aliasing, i had to read Request.RawUrl and parse it myself since the Request.QueryString object did not contain the values i passed.
// reusable function that parses a string in standard query string format(foo=bar&dave=awesome) into a Dictionary collection of key/value pairs
// return the reference to the object, you have to assign it to a local un-instantiated name
// will accept a full url, or just a query string
protected Dictionary<string, string> parseQueryString(string url)
{
Dictionary<string, string> d = new Dictionary<string, string>();
if (!string.IsNullOrEmpty(url))
{
// if the string is still a full url vs just the query string
if (url.Contains("?"))
{
string[] urlArray = url.Split('?');
url = urlArray[1]; // snip the non query string business away
}
string[] paramArray = url.Split('&');
foreach (string param in paramArray)
{
if (param.Contains("="))
{
int index = param.IndexOf('=');
d.Add(param.Substring(0, index), param.Substring(++index));
}
}
}
return d;
}
Then i just use my dictionary object to evaluate and rebuild my url with the opposite mobile value, dynamically setting the href on the toggle link. Some code is obviosuly left out, but for perspective, base._iPage.QueryStringParams hold my dictionary object that was returned, and base._iPage.IsMobile is just a bool property i also have via the page interface i use, that all my pages, and user controls, ect, can talk to.
// get the left side fo the url, without querystrings
StringBuilder url = new StringBuilder(Request.RawUrl.Split('?')[0]);
// build link to self, preserving query strings, except flipping mobile value
if (base._iPage.QueryStringParams.Count != 0)
{
if (base._iPage.QueryStringParams.ContainsKey("mobile"))
{
// set to opposite of current
base._iPage.QueryStringParams["mobile"] = (!base._iPage.IsMobile).ToString();
}
int count = 0;
url.Append('?');
// loop through query string params, and add them back on
foreach (KeyValuePair<string, string> item in base._iPage.QueryStringParams)
{
count++;
url.Append(item.Key + "=" + item.Value + (count == base._iPage.QueryStringParams.Count ? "" : "&" ));
}
}
// assign rebuild url to href of toggle link
viewMobileButton.HRef = url.ToString();
}
Then on my pageinit this is where i actually check, first the quesry string, then the cookie, if neither of those are present, i run my mobile detection method, and set a cookie, and my interface bool property for easy access to conditionals that depends on it.
QueryStringParams = base.parseQueryString(Request.RawUrl);
if (QueryStringParams.ContainsKey("mobile") ? QueryStringParams["mobile"].ToLower().Equals("true") : false)
{
Cookies.bakeCookie("isMobile", "true"); // create a cookie
IsMobile = true;
}
else if (QueryStringParams.ContainsKey("mobile") ? QueryStringParams["mobile"].ToLower().Equals("false") : false)
{
Cookies.bakeCookie("isMobile", "false"); // create a cookie
IsMobile = false;
}
else
{
IsMobile = base.mobileDetection();
}
if (IsMobile)
this.Page.MasterPageFile = "m-" + this.Page.MasterPageFile;
}

Using SiteMapPath to create a dynamic page title?

I currently use SiteMapPath to generate a breadcrumb for my ASP.net 3.5 (vb.net) pages and it works great.
Now I am trying to figure out how I might be able to use the same control to build a dynamic page title (in the tag). I want a reverse path listed, but the SiteMapPath control includes links and bunch of styling spans. Is there any way to remove all of that, and just get a plain path with separators?
For example, Let's say we are on the "Press Releases" page inside of the "About" section of my site.
The breadcrumb shows up as:
Home > About > Press Releases
I want to have the page title be:
Press Releases - About - Company Name
So I need it to reverse the order, drop all spans, links and styling (since this is inside the tag) and drop the root node "Home" and then add the company name to the end. Since my menu nav and breadrumbs are all driven from the sitemap file, I thought it would make sense to try to make the title do the same.
Any thoughts? Thanks.
The best way to achieve your desired output is to ignore the SitePath control, and instead use the SiteMap's SiteMapNode's collection. The server parses the web.sitemap into a collection of SiteMapNodes and wires up the SiteMap.CurrentNode by finding a node that matches the current page's URL. Each SiteMapNode has a ParentNode property. Here is the reference page on MSDN.
So, all you need to do is check if the CurrentNode has a parent, if it does you add the ParentNode's title to the CurrentNode's title and keep going until you reach the RootNode (where you substitute your company name for the root node's title).
Below is a quick solution; it could go in the MasterPage if you are using one. I'm not sure your language, but this should be easy to rewrite in VB.Net. I gave it a simple test and it seemed to work.
You can customize the characters that separate the page titles.
protected void Page_Load(object sender, EventArgs e)
{
Page.Title = SiteMapTitle(SiteMap.CurrentNode, "", " - ");
}
private string GetNodeTitle(SiteMapNode oNode)
{
if (oNode == SiteMap.RootNode)
return "Company Name";
else
return oNode.Title;
}
private string SiteMapTitle(SiteMapNode oNode, string szTitle, string szItemSeparator)
{
if (szTitle != string.Empty)
szTitle = szTitle + szItemSeparator + GetNodeTitle(oNode);
else
szTitle = GetNodeTitle(oNode);
if (oNode.ParentNode != null)
szTitle = SiteMapTitle(oNode.ParentNode, szTitle, szItemSeparator);
return szTitle;
}
Hope that helps...

Resources