I have this requirment to only index that data, for searching, which is being used in some page. For example if I upload a document it shouldn't be available for search until I use it in some page. I found out this code online
ContentIndexer.Instance.Conventions.ForInstancesOf().ShouldIndex(x =>
{
var contentRepository =
EPiServer.ServiceLocation.ServiceLocator.Current.GetInstance();
var contentSoftLinkRepository =
EPiServer.ServiceLocation.ServiceLocator.Current.GetInstance();
var softLinks = contentSoftLinkRepository.Load(x.ContentLink, true);
try
{
foreach (var softLink in softLinks)
{
if (softLink.SoftLinkType == ReferenceType.ExternalReference ||
softLink.SoftLinkType == ReferenceType.ImageReference)
{
var content =
contentRepository.Get(softLink.OwnerContentLink);
if (!ContentIndexer.Instance.Conventions.ShouldIndexConvention.ShouldIndex(content).Value) // don't index referenced file if content is marked as not indexed
{
continue;
}
// only index if content is published
var publicationStatus =
content.PublishedInLanguage()[softLink.OwnerLanguage.Name];
if (publicationStatus != null &&
(publicationStatus.StartPublish == null ||
publicationStatus.StartPublish < DateTime.Now) &&
(publicationStatus.StopPublish == null ||
DateTime.Now < publicationStatus.StopPublish))
{
return true;
}
}
}
}
catch
{
// ooops something went wrong. Better not index this one ;-)
}
return false;
});
This works when I attach softlinks. But lets say a page has a property called Content type and when I add something in there, lets say a block which has softlinks to that document, it doesn't work. I am stuck in there. Any hints?
Do you have a wildcard * host configured on any of your sites?
Check Admin > Manage websites and go through all sites to see if there is a * configured.
Having a wildcard tells Episerver Find to index standalone content (i.e. globalassets) using the site primary URL that contains the wildcard, regardless of it being referenced. If you don't have a wildcard configured, Episerver Find can't index content that are NOT referenced (i.e. globalassets) because it cannot generate a public URL for it. Global assets that are referenced will be indexed using the domain of the site that references it. Hope this helps.
Related
I'm writing a visual studio extension based on the Concord Samples Hello World project. The goal is to let the user filter out stack frames by setting a list of search strings. If any of the search strings are in a stack frame, it is omitted.
I've got the filter working for a hardcoded list. That needs to be in a non-package-based dll project in order for the debugger to pick it up. And I have a vsix project that references that dll with an OptionPageGrid to accept the list of strings. But I can't for the life of me find a way to connect them.
On the debugger side, my code looks something like this:
DkmStackWalkFrame[] IDkmCallStackFilter.FilterNextFrame(DkmStackContext stackContext, DkmStackWalkFrame input)
{
if (input == null) // null input frame indicates the end of the call stack. This sample does nothing on end-of-stack.
return null;
if (input.InstructionAddress == null) // error case
return new[] { input };
DkmWorkList workList = DkmWorkList.Create(null);
DkmLanguage language = input.Process.EngineSettings.GetLanguage(new DkmCompilerId());
DkmInspectionContext inspection = DkmInspectionContext.Create(stackContext.InspectionSession, input.RuntimeInstance, input.Thread, 1000,
DkmEvaluationFlags.None, DkmFuncEvalFlags.None, 10, language, null);
string frameName = "";
inspection.GetFrameName(workList, input, DkmVariableInfoFlags.None, result => GotFrameName(result, out frameName));
workList.Execute();
CallstackCollapserDataItem dataItem = CallstackCollapserDataItem.GetInstance(stackContext);
bool omitFrame = false;
foreach (string filterString in dataItem.FilterStrings)
{
if (frameName.Contains(filterString))
{
omitFrame = true;
}
}
The CallstackCollapserDataItem is where I theoretically need to retrieve the strings from user settings. But I don't have access to any services/packages in order to e.g. ask for WritableSettingsStore, like in You've Been Haacked's Example. Nor can I get my OptionPageGrid, like in the MSDN Options Example.
The other thing I tried was based on this StackOverflow question. I overrode the LoadSettingsFromStorage function of my OptionPageGrid and attempted to set a static variable on a public class in the dll project. But if that code existed in the LoadSettingsFromStorage function at all, the settings failed to load without even entering the function. Which felt like voodoo to me. Comment out the line that sets the variable, the breakpoint hits normally, the settings load normally. Restore it, and the function isn't even entered.
I'm at a loss. I really just want to pass a string into my Concord extension, and I really don't care how.
Ok, apparently all I needed to do was post the question here for me to figure out the last little pieces. In my CallstackCollapserDataItem : DkmDataItem class, I added the following code:
private CallstackCollapserDataItem()
{
string registryRoot = DkmGlobalSettings.RegistryRoot;
string propertyPath = "vsix\\CallstackCollapserOptionPageGrid";
string fullKey = "HKEY_CURRENT_USER\\" + registryRoot + "\\ApplicationPrivateSettings\\" + propertyPath;
string savedStringSetting = (string)Registry.GetValue(fullKey, "SearchStrings", "");
string semicolonSeparatedStrings = "";
// The setting resembles "1*System String*Foo;Bar"
if (savedStringSetting != null && savedStringSetting.Length > 0 && savedStringSetting.Split('*').Length == 3)
{
semicolonSeparatedStrings = savedStringSetting.Split('*')[2];
}
}
vsix is the assembly in which CallstackCollapserOptionPageGrid is a DialogPage, and SearchStrings is its public property that's saved out of the options menu.
I am using Google Navigation charts in a project.
Everything works fine when I run the javascript code in the client side (.aspx page), but when I put it in the code behind and echo/write it out (via Response.Write()) it throws an error, specifically at the point where the javascript code trys to call the indexOf() method on an array.
I have tried to examine the cause of the error, but the only info I get is that this is a problem in IE8 and earlier with the indexOf() method- this cannot be my problem, because as I said it works fine when I call it directly from the client - it is only giving a problem form the code-behind.
This is the specific error I receive:
0x800a01b6 - Microsoft JScript runtime error: Object doesn't support property or method 'indexOf'
This will work fine (in client):
for (var i = 0; i < data.getNumberOfColumns() ; i++) {
if (i == 0 || defaultSeries.indexOf(i) > -1) {
// if the column is the domain column or in the default list, display the series
columns.push(i);
}
....
but this will throw an error (in code-behind):
htmlJS += "for (var i = 0; i < data.getNumberOfColumns() ; i++) {";
htmlJS += "if (i == 0 || defaultSeries.indexOf(i) > -1) {";
// if the column is the domain column or in the default li";st, display the series
htmlJS += "columns.push(i);";
htmlJS += "}";
....
Response.Write(htmlJS);
Does anyone know why this error only occurs from the code-behind?
Assuming defaultSeries is an array, you will need to polyfill Array.prototype.indexOf for IE<9, which only supports indexOf on strings.
Here's a polyfill from MDN:
if (!Array.prototype.indexOf) {
Array.prototype.indexOf = function (searchElement, fromIndex) {
if ( this === undefined || this === null ) {
throw new TypeError( '"this" is null or not defined' );
}
var length = this.length >>> 0; // Hack to convert object.length to a UInt32
fromIndex = +fromIndex || 0;
if (Math.abs(fromIndex) === Infinity) {
fromIndex = 0;
}
if (fromIndex < 0) {
fromIndex += length;
if (fromIndex < 0) {
fromIndex = 0;
}
}
for (;fromIndex < length; fromIndex++) {
if (this[fromIndex] === searchElement) {
return fromIndex;
}
}
return -1;
};
}
Two things:
Based on your code, you might have a white-space issue with the javascript - you are concatenating the string, and so, for example, you will have a section that looks like "{if" - however, this is not likely causing your issue.
What IS likely causing your issue is the timing of the javascript hitting the page. Does the object EXIST when the response.write gets flushed to the client? In order to make sure that all the required bits of the page exist when you need them, you normally will want to use the scripting object methods to add the script, and then CALL the code on once the page is loaded. Check out this page on adding script dynamically to a page: http://msdn.microsoft.com/en-us/library/ms178207(v=vs.100).aspx
Thank you everyone - I have found a soltuion that works:
If I create the same string which contains the javascript indexOf() method and then either assign it as output to a literal element on the aspx page, or if I "echo" it out via the <% %> special tags then the javascript code will run fine.
So the following runs:
Code-Behind:
public string jsHtml ="";
jsHtml +="<script type='text/javascript'>";
jsHtml+="var defaultSeries = [1,2,3];";
jsHtml+="alert(defaultSeries.indexOf(2));";
jsHtml+="</script>";
txtValueA.Text = jsHtml;
Client/aspx page:
<asp:Literal ID="txtValueA" runat="server></asp:Literal>
//OR
<%=jsHtml %>
Strange but True..... thanks for the input
I am trying to bind a field to a termset, and if the termset does not exist I want to create it by code. However, even when the code is running with elevated privileges I get the following exception.
The current user has insufficient permissions to perform this operation.
public static void BindTaxonomyField(string taxonomyFieldId, string taxonomyNoteFieldId, string termSetName, string termGroup, SPWeb web)
{
try
{
if (web != null)
{
// get the taxonomyfield from the sitecollection
var field = web.Fields[new Guid(taxonomyFieldId)] as TaxonomyField;
if (field != null)
{
// attach the note field
field.TextField = new Guid(taxonomyNoteFieldId);
// set up the field for my termstore
var session = new TaxonomySession(web.Site);
if (session.TermStores.Count > 0)
{
// get termstore values
TermStore ts = session.TermStores[0];
Group group = GetGroup(termGroup, ts);
if (group == null)
{
ts.CreateGroup(termGroup);
//throw new Exception("Group was not found in the termstore");
}
// ReSharper disable PossibleNullReferenceException
TermSet termSet = group.TermSets.Any(s => s.Name == termSetName) ? group.TermSets[termSetName] : group.CreateTermSet(termSetName);
// ReSharper restore PossibleNullReferenceException
//TermSet termSet = group.TermSets[termSetName];
// actually setup the field for using the TermStore
field.SspId = ts.Id;
field.TermSetId = termSet.Id;
}
field.Update();
}
}
}
catch (Exception ex)
{
}
}
private void BindColumnsToTermStore(string url)
{
try
{
SPSecurity.RunWithElevatedPrivileges(delegate
{
using (var site = new SPSite(url))
{
using (SPWeb web = site.OpenWeb())
{
if (!web.AllowUnsafeUpdates)
web.AllowUnsafeUpdates = true;
BindTaxonomyField("EF810CD2-F2D2-4BD2-9ABF-C19815F13568",
"67E6E777-0D1E-4840-B858-17400CFABD14",
"Business Audience", "IctDocumentation",
web);
web.AllowUnsafeUpdates = false;
}
}
});
}
If you go in to central administration and navigate to your term store(this is in the left hand nav) in the main container of the page there is a box with a few usernames. Is the account you are running the code in listed? If not stick them in there.
i think the path is something like Central admin -> Manage service application -> Managed meta data service - and the are on the page is call Term store Administrators
There is also one more place you must check but check this first and them run again.
The next place to check is to highlight your Manage metadata service which is located
Central admin -> Manage service application
and click on permissions on the ribbon and make sure the users your running the code with has the correct access.
I always start by making sure i know who i am running the code as first of all then do the checks
Using C# with ASP.NET on an Intranet website. I've got multiple "Admin" pages where I'm checking fields to make sure a date is present, checking fields to make sure the Username is present and filling in those fields if they were left blank. On all of these pages, there exists fields with identical names. The block of code I'm using in the code-behind is below:
//If the User fields are empty, set them to the current user
string ActiveUser = System.Web.HttpContext.Current.User.Identity.Name;
string LoginID = ActiveUser.Right(6);
var txtLoadedBy_chk = string.IsNullOrEmpty(str5);
if ((txtLoadedBy_chk == true))
{
str5 = LoginID;
}
var txtUpdatedBy_chk = string.IsNullOrEmpty(str7);
if ((txtUpdatedBy_chk == true))
{
str7 = LoginID;
}
var txtFlgUpdatedBy_chk = string.IsNullOrEmpty(str9);
if ((txtFlgUpdatedBy_chk == true))
{
str9 = LoginID;
}
// If the date fields are NULL, set them to today's date
var txtLoadedOn_chk2 = string.IsNullOrEmpty(str6);
if ((txtLoadedOn_chk2 == true))
{
str6 = DateTime.Today.ToString();
}
var txtUpdatedOn_chk2 = string.IsNullOrEmpty(str8);
if ((txtUpdatedOn_chk2 == true))
{
str8 = DateTime.Today.ToString();
}
var txtFlgUpdatedOn_chk2 = string.IsNullOrEmpty(str10);
if ((txtFlgUpdatedOn_chk2 == true))
{
str10 = DateTime.Today.ToString();
}
// Check to make sure the dates entered are valid. If not, let the user know and
// then exit out of the code so the record is not saved
var txtLoadedOn_chk = DateTimeHelpers.IsValidSqlDateTimeNative(str6);
var txtUpdatedOn_chk = DateTimeHelpers.IsValidSqlDateTimeNative(str8);
var txtFlgUpdatedOn_chk = DateTimeHelpers.IsValidSqlDateTimeNative(str10);
if ((txtLoadedOn_chk == false) || (txtUpdatedOn_chk == false) || (txtFlgUpdatedOn_chk == false))
{
Page.ClientScript.RegisterStartupScript(this.GetType(), "ch", "<script>alert('WARNING !! One of your date fields is invalid')</script>");
return;
}
I'm thinking that since I do the EXACT same checks on all of these forms, I should be able to put this block of code somewhere and just reference it, instead of putting it in every form. How would I go about doing that? If I put it in a separate page or CS file, how will it know which form is calling it so it reads the proper fields? If you can provide some sample code, that would be a huge help. TIA!
Oh, and DateTimeHelpers is a class I created, in case you're wondering.
EDIT: I just want to be clear on something; this code is in the code-behind and is called when the user presses the "Save" button. It's just verifying the data before it tries to write it to the SQL Server table.
What you want to do here is create a user control. You can add one of those to your project with the "New File" stuff Visual Studio provides. I will call that user control Fields.ascx in this answer.
The code in your answer goes in the code-behind for that user control (Fields.ascx.cs). The form elements that you have on every page go in Fields.ascx.
Once you do that, you reference the user control at the top of your page like this:
<%# Register TagPrefix="user2174085" TagName="MyFields" Src="~/Path/To/Fields.ascx" %>
And then you add the fields to your page in the right place with the following code:
<user2174085:MyFields runat="server" />
In the <%# Register %> portion, you can make the TagPrefix and TagName pretty much anything you want, just make sure they match when you use the control.
You could always write a public method in a cs file and pass your controls as reference variables. Here is a quick example of what that could look like:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.UI.WebControls;
namespace mol_intranet
{
public class ClassTesting
{
//pass the fields to be checked/updated here as ref variables
public void checkFields(ref TextBox loadedby, ref TextBox updatedby)
{
string ActiveUser = System.Web.HttpContext.Current.User.Identity.Name;
string LoginID = ActiveUser.Right(6);
var txtLoadedBy_chk = string.IsNullOrEmpty(loadedby.Text);
if ((txtLoadedBy_chk == true))
{
loadedby.Text = LoginID;
}
var txtUpdatedBy_chk = string.IsNullOrEmpty(updatedby.Text);
if ((txtUpdatedBy_chk == true))
{
updatedby.Text = LoginID;
}
}
}
}
Then just call the method in your code and pass your controls:
ClassTesting t = new ClassTesting();
t.checkFields(txtLoadedBy, txtUpdatedBy);
Hope this helps.
Asked this on the forums as well, but no luck as of yet. What I need to do is set the HTML content of each content block on a given page. It seems that I can set the html value okay, but saving it does not update the actual page.
I'm wondering if it's because there needs to be some sort of save call on the control. There doesn't seem to be any methods available for such an action.
foreach (var c in duplicated.Page.Controls)
{
// go through the properties, se the ID to grab the right text
foreach (var p in c.Properties)
{
if (p.Name == "ID")
{
var content = pageContent.Where(content_pair => content_pair.Key == p.Value).SingleOrDefault();
var control = pageManager.LoadControl(c);
if (control is ContentBlock)
{
var contentBlock = pageManager.LoadControl(c) as ContentBlock;
contentBlock.Html = content.Value;
}
}
}
}
pageManager.SaveChanges(); */
WorkflowManager.MessageWorkflow(duplicated.Id, typeof(PageNode), null, "Publish", false, bag);
The following code may help you achieve what you need.
It will first get the page by its title (I am looking for a page by the title "duplicated" as it's implied by your code).
It generates a new draft of the current page, then go over its controls.
Controls which are detected as content blocks are then iterated in a foreach loop.
As written in the comment inside the foreach loop, you may detect controls by their explicit ID (by the property named "ID") or by their related shared content block (by the property named "SharedContentID") or any other condition (or ignore this condition altogether, which would result in updating all the controls n the page.
Once we have a control to update at hand, you can set its new value depending on the localization settings of your project.
After that the draft is saved and published and optionally a new version is created for it.
PageManager pageManager = PageManager.GetManager();
VersionManager vmanager = VersionManager.GetManager();
PageNode duplicated = pageManager.GetPageNodes().FirstOrDefault(p => p.Title == "duplicate");
if (duplicated != null)
{
var draft = pageManager.EditPage(duplicated.Page.Id, true);
string contentBlockTypeName = typeof(ContentBlock).FullName;
PageDraftControl[] contentBlocks = draft.Controls.Where(contentBlock => contentBlock.ObjectType == contentBlockTypeName).ToArray();
foreach (PageDraftControl contentBlock in contentBlocks)
{
Guid contentBlockId = contentBlock.Id;
//User "SharedContentID" if you are looking up controls which are linked to a shared content block of a specific ID.
//If you you are trying to locate a specific control by its own ID, use the explicit "ID" property instead of "SharedCotentID"
if (contentBlock.Properties.Where(prop => prop.Name == "SharedContentID" && prop.Value.ToString() == contentItemIdstr).FirstOrDefault() != null)
{
ControlProperty htmlProperty = contentBlock.Properties.Where(prop => prop.Control.Id == contentBlockId && prop.Name == "Html").FirstOrDefault();
if (htmlProperty != null)
{
if (AppSettings.CurrentSettings.Multilingual)
{
htmlProperty.GetString("MultilingualValue").SetString(CultureInfo.CurrentUICulture, "New Value");
}
else
{
htmlProperty.Value = "New Value";
}
}
}
}
draft = pageManager.SavePageDraft(draft);
draft.ParentPage.LockedBy = Guid.Empty;
pageManager.PublishPageDraft(draft);
pageManager.DeletePageTempDrafts(draft.ParentPage);
//Use the 2 next lines to create a new version of your page, if you wish.
//Otherwise the content will be updated on the current page version.
vmanager.CreateVersion(draft, draft.ParentPage.Id, true);
vmanager.SaveChanges();
pageManager.SaveChanges();
}
I hope this code helps.
Alon.