replace highlights
This commit is contained in:
@@ -12,9 +12,9 @@ Turns out none of the “regular” methods really work. The Trim function only
|
|||||||
|
|
||||||
You have to use the Chr(13) or Chr(10) functions to actually get the character values for the newline characters. So, my solution looked like this:
|
You have to use the Chr(13) or Chr(10) functions to actually get the character values for the newline characters. So, my solution looked like this:
|
||||||
|
|
||||||
{% highlight csharp %}
|
```csharp
|
||||||
Replace(Replace(stringVariable, Chr(10), “”), Chr(13), “”)
|
Replace(Replace(stringVariable, Chr(10), “”), Chr(13), “”)
|
||||||
{% endhighlight %}
|
```
|
||||||
|
|
||||||
Thanks to this forum post for providing the clues:
|
Thanks to this forum post for providing the clues:
|
||||||
[http://www.daniweb.com/forums/thread54797.html](http://www.daniweb.com/forums/thread54797.html)
|
[http://www.daniweb.com/forums/thread54797.html](http://www.daniweb.com/forums/thread54797.html)
|
||||||
|
|||||||
@@ -10,9 +10,9 @@ I try to avoid setting styles in my code as much as possible. However, I ran acr
|
|||||||
|
|
||||||
The particular border I needed is the ContentStyle.Border border of the ASPxPageControl and I needed to set the BorderColor property which is a System.Drawing.Color object. There isn’t any straightforward way to get a Color object from a hexadecimal color using the Color object itself. You have to use the ColorTranslator (also in the System.Drawing namespace) which gives you a FromHtml method and returns the correct Color object.
|
The particular border I needed is the ContentStyle.Border border of the ASPxPageControl and I needed to set the BorderColor property which is a System.Drawing.Color object. There isn’t any straightforward way to get a Color object from a hexadecimal color using the Color object itself. You have to use the ColorTranslator (also in the System.Drawing namespace) which gives you a FromHtml method and returns the correct Color object.
|
||||||
|
|
||||||
{% highlight csharp %}
|
```csharp
|
||||||
Color hexColor = ColorTranslator.FromHtml(“#666666″);
|
Color hexColor = ColorTranslator.FromHtml(“#666666″);
|
||||||
{% endhighlight %}
|
```
|
||||||
|
|
||||||
Thanks to this guy for the quick answer:
|
Thanks to this guy for the quick answer:
|
||||||
[http://www.akxl.net/labs/articles/converting-a-hexadecimal-color-to-a-system.drawing.color-object/](http://www.akxl.net/labs/articles/converting-a-hexadecimal-color-to-a-system.drawing.color-object/)
|
[http://www.akxl.net/labs/articles/converting-a-hexadecimal-color-to-a-system.drawing.color-object/](http://www.akxl.net/labs/articles/converting-a-hexadecimal-color-to-a-system.drawing.color-object/)
|
||||||
|
|||||||
@@ -12,14 +12,14 @@ One helpful way I’ve been able to use post-build events is to copy the compile
|
|||||||
|
|
||||||
Adding a post-build event couldn’t be much simpler either. In Visual Studio just right-click on the project you want to add the post-build event for in Solution Explorer and select “Properties”. You can either do that or, with the project selected in Solution Explorer, hit Alt-Enter. The project properties window will display. On the left-hand side, click the “Build Events” tab. You’ll see two boxes there. One is for pre-build events and one is for post-build events.
|
Adding a post-build event couldn’t be much simpler either. In Visual Studio just right-click on the project you want to add the post-build event for in Solution Explorer and select “Properties”. You can either do that or, with the project selected in Solution Explorer, hit Alt-Enter. The project properties window will display. On the left-hand side, click the “Build Events” tab. You’ll see two boxes there. One is for pre-build events and one is for post-build events.
|
||||||
|
|
||||||
{% highlight text %}
|
```text
|
||||||
In the post-build events textbox add the following:
|
In the post-build events textbox add the following:
|
||||||
IF NOT ($(ConfigurationName)) == (Debug) GOTO END
|
IF NOT ($(ConfigurationName)) == (Debug) GOTO END
|
||||||
cd $(ProjectDir)
|
cd $(ProjectDir)
|
||||||
copy /y bin\debug\*.dll C:\inetpub\wwwroot\wss\VirtualDirectories\{YOUR SHAREPOINT SITE DIRECTORY}\bin
|
copy /y bin\debug\*.dll C:\inetpub\wwwroot\wss\VirtualDirectories\{YOUR SHAREPOINT SITE DIRECTORY}\bin
|
||||||
copy /y bin\debug\*.pdb C:\inetpub\wwwroot\wss\VirtualDirectories\{YOUR SHAREPOINT SITE DIRECTORY}\bin
|
copy /y bin\debug\*.pdb C:\inetpub\wwwroot\wss\VirtualDirectories\{YOUR SHAREPOINT SITE DIRECTORY}\bin
|
||||||
:END
|
:END
|
||||||
{% endhighlight %}
|
```
|
||||||
|
|
||||||
These are just basic Windows batch commands like you’d find in any Windows batch file. This is a very basic setup, but it should be a good starting point for you to work from. All it does is check whether you are building with your Debug build configuration, if you are, then it moves to your project directory and copies the DLLs in your bin\debug folder to the bin folder of your SharePoint site.
|
These are just basic Windows batch commands like you’d find in any Windows batch file. This is a very basic setup, but it should be a good starting point for you to work from. All it does is check whether you are building with your Debug build configuration, if you are, then it moves to your project directory and copies the DLLs in your bin\debug folder to the bin folder of your SharePoint site.
|
||||||
|
|
||||||
|
|||||||
@@ -12,17 +12,17 @@ I successfully installed SharePoint 2010 and proceeded to run the configuration
|
|||||||
|
|
||||||
Apparently, at some point in the process the configuration wizard looks for the serviceHostingEnvironment element under the `<system.serviceModel>` section in the Central Administration web.config.
|
Apparently, at some point in the process the configuration wizard looks for the serviceHostingEnvironment element under the `<system.serviceModel>` section in the Central Administration web.config.
|
||||||
|
|
||||||
{% highlight xml %}
|
```xml
|
||||||
<serviceHostingEnvironment aspNetCompatibilityEnabled=”true” />
|
<serviceHostingEnvironment aspNetCompatibilityEnabled=”true” />
|
||||||
{% endhighlight %}
|
```
|
||||||
|
|
||||||
If it doesn’t find it adds it itself, but curiously, it wraps it in an incomplete system.serviceModel tag so it looks like this:
|
If it doesn’t find it adds it itself, but curiously, it wraps it in an incomplete system.serviceModel tag so it looks like this:
|
||||||
|
|
||||||
{% highlight xml %}
|
```xml
|
||||||
<system.serviceModel>
|
<system.serviceModel>
|
||||||
<serviceHostingEnvironment aspNetCompatibilityEnabled=”true” />
|
<serviceHostingEnvironment aspNetCompatibilityEnabled=”true” />
|
||||||
</system.serviceModel>
|
</system.serviceModel>
|
||||||
{% endhighlight %}
|
```
|
||||||
|
|
||||||
This then caused the configuration wizard to bomb out.
|
This then caused the configuration wizard to bomb out.
|
||||||
|
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ We wanted an easy solution to open every document in a SharePoint from a documen
|
|||||||
|
|
||||||
I don’t know of any way to do this via a setting in SharePoint, but a little JavaScript employing jQuery does the job just perfectly.
|
I don’t know of any way to do this via a setting in SharePoint, but a little JavaScript employing jQuery does the job just perfectly.
|
||||||
|
|
||||||
{% highlight javascript %}
|
```javascript %}
|
||||||
$(document).ready(
|
$(document).ready(
|
||||||
function () {
|
function () {
|
||||||
// has to be on an interval for grouped doc libraries
|
// has to be on an interval for grouped doc libraries
|
||||||
@@ -45,7 +45,7 @@ $(document).ready(
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
{% endhighlight %}
|
```
|
||||||
|
|
||||||
This JavaScript takes into account that some Document Libraries are grouped and so not all the document links (or icon links) will appear on the page load. This is the reason for the half-second interval. At the most half a second after the group is expanded the newly loaded links will be altered to also open in a new window.
|
This JavaScript takes into account that some Document Libraries are grouped and so not all the document links (or icon links) will appear on the page load. This is the reason for the half-second interval. At the most half a second after the group is expanded the newly loaded links will be altered to also open in a new window.
|
||||||
|
|
||||||
|
|||||||
@@ -8,12 +8,12 @@ tags: [command-line]
|
|||||||
---
|
---
|
||||||
I find myself often having to sanitize file names and paths because I’m creating files from some kind of user input that I’m unsure about. I found this handy way of doing this in one line today:
|
I find myself often having to sanitize file names and paths because I’m creating files from some kind of user input that I’m unsure about. I found this handy way of doing this in one line today:
|
||||||
|
|
||||||
{% highlight text %}
|
```text
|
||||||
fileName = Path.GetInvalidFileNameChars().Aggregate(fileName, (name, c) => name.Replace(c, ‘_’));
|
fileName = Path.GetInvalidFileNameChars().Aggregate(fileName, (name, c) => name.Replace(c, ‘_’));
|
||||||
{% endhighlight %}
|
```
|
||||||
|
|
||||||
Note that the same goes for paths. The only thing that changes is the static method you use from Path:
|
Note that the same goes for paths. The only thing that changes is the static method you use from Path:
|
||||||
|
|
||||||
{% highlight text %}
|
```text
|
||||||
filePath = Path.GetInvalidPathChars().Aggregate(filePath , (name, c) => name.Replace(c, ‘_’));
|
filePath = Path.GetInvalidPathChars().Aggregate(filePath , (name, c) => name.Replace(c, ‘_’));
|
||||||
{% endhighlight %}
|
```
|
||||||
|
|||||||
@@ -20,14 +20,14 @@ After some digging around and playing with Notepad++ (where I could switch the e
|
|||||||
|
|
||||||
So, eventually, I came up with these few lines of code which convert the encoding of my text and fixed all of my problems. Woohoo!
|
So, eventually, I came up with these few lines of code which convert the encoding of my text and fixed all of my problems. Woohoo!
|
||||||
|
|
||||||
{% highlight csharp %}
|
```csharp
|
||||||
Encoding targetEncoding = Encoding.GetEncoding(1252);
|
Encoding targetEncoding = Encoding.GetEncoding(1252);
|
||||||
byte[] utf8Bytes = targetEncoding.GetBytes(text);
|
byte[] utf8Bytes = targetEncoding.GetBytes(text);
|
||||||
byte[] ansiBytes = Encoding.Convert(Encoding.UTF8,
|
byte[] ansiBytes = Encoding.Convert(Encoding.UTF8,
|
||||||
targetEncoding,
|
targetEncoding,
|
||||||
utf8Bytes);
|
utf8Bytes);
|
||||||
return targetEncoding.GetString(ansiBytes);
|
return targetEncoding.GetString(ansiBytes);
|
||||||
{% endhighlight %}
|
```
|
||||||
|
|
||||||
What this simple code does is first get the Encoding for the ANSI character encoding (1252 is ANSI, apparently). It then gets the bytes of the string with the ANSI encoding, converts them from the ANSI encoding to UTF-8 encoding and then gets the string with the ANSI encoding.
|
What this simple code does is first get the Encoding for the ANSI character encoding (1252 is ANSI, apparently). It then gets the bytes of the string with the ANSI encoding, converts them from the ANSI encoding to UTF-8 encoding and then gets the string with the ANSI encoding.
|
||||||
|
|
||||||
|
|||||||
@@ -14,8 +14,8 @@ In any case, the bug showed up when I had an existing overlay showing and wanted
|
|||||||
## Solution
|
## Solution
|
||||||
While it’s somewhat of a hack, the best solution I found was to simply remove the div that Overlay creates with id “exposeMask” before I display the second overlay.
|
While it’s somewhat of a hack, the best solution I found was to simply remove the div that Overlay creates with id “exposeMask” before I display the second overlay.
|
||||||
|
|
||||||
{% highlight text %}
|
```text
|
||||||
$(“#exposeMask”).remove();
|
$(“#exposeMask”).remove();
|
||||||
{% endhighlight %}
|
```
|
||||||
|
|
||||||
I believe this is what is happening: when the first overlay is closed by the library, it doesn’t properly handle the existing exposeMask div and so when the second overlay shows it misunderstands the mask to be displaying properly and doesn’t calculate its own display values properly. Therefore, its z-index is wrong (or non-existing) and the mask remains hidden (because the library hid it when it closed the first overlay).
|
I believe this is what is happening: when the first overlay is closed by the library, it doesn’t properly handle the existing exposeMask div and so when the second overlay shows it misunderstands the mask to be displaying properly and doesn’t calculate its own display values properly. Therefore, its z-index is wrong (or non-existing) and the mask remains hidden (because the library hid it when it closed the first overlay).
|
||||||
|
|||||||
@@ -14,18 +14,18 @@ I knew the real solution had to be some type of command-line utility and a batch
|
|||||||
|
|
||||||
First of all, I found HTML Tidy which I could run from the Windows command line to format a file. Using a configuration file for the tidy.exe (placed in the same directory as tidy.exe and named tidcfg.ini–although neither matters, see below) that looked like this:
|
First of all, I found HTML Tidy which I could run from the Windows command line to format a file. Using a configuration file for the tidy.exe (placed in the same directory as tidy.exe and named tidcfg.ini–although neither matters, see below) that looked like this:
|
||||||
|
|
||||||
{% highlight text %}
|
```text
|
||||||
indent:yes
|
indent:yes
|
||||||
indent-attributes:yes
|
indent-attributes:yes
|
||||||
{% endhighlight %}
|
```
|
||||||
|
|
||||||
I got the formatting I wanted.
|
I got the formatting I wanted.
|
||||||
|
|
||||||
Now, all I had to do was brush up on my Windows batch command skills to run tidy.exe on multiple files. Easy enough! This is what my batch file looked like:
|
Now, all I had to do was brush up on my Windows batch command skills to run tidy.exe on multiple files. Easy enough! This is what my batch file looked like:
|
||||||
|
|
||||||
{% highlight text %}
|
```text
|
||||||
for /d %%X in (C:\<path_to_parent_directory>\*) do (c:\<path_to_tidy.exe>\tidy.exe -m -xml -config c:\<path_to_tidy.ini_file>\tidycfg.ini %%X\<xml_file_name>.xml)
|
for /d %%X in (C:\<path_to_parent_directory>\*) do (c:\<path_to_tidy.exe>\tidy.exe -m -xml -config c:\<path_to_tidy.ini_file>\tidycfg.ini %%X\<xml_file_name>.xml)
|
||||||
{% endhighlight %}
|
```
|
||||||
|
|
||||||
I had a folder structure where there were hundreds of directories inside this one parent directory. Each of the child directories had a single XML file in it. Therefore, I needed the C:\<path_to_parent_directory>\* wildcard.
|
I had a folder structure where there were hundreds of directories inside this one parent directory. Each of the child directories had a single XML file in it. Therefore, I needed the C:\<path_to_parent_directory>\* wildcard.
|
||||||
|
|
||||||
|
|||||||
@@ -28,13 +28,13 @@ This part is easy. Actually, all the parts are easy. You just need to know wha
|
|||||||
1. Select your new template and click on the Edit button
|
1. Select your new template and click on the Edit button
|
||||||
1. In the Layout field (the XML that describes the player), add an Image element inside the Layout element. See the BEML reference documentationfor details. Here is what my Image element looks like:
|
1. In the Layout field (the XML that describes the player), add an Image element inside the Layout element. See the BEML reference documentationfor details. Here is what my Image element looks like:
|
||||||
|
|
||||||
{% highlight xml %}
|
```xml
|
||||||
<Image id="logoOverlay"
|
<Image id="logoOverlay"
|
||||||
width="50"
|
width="50"
|
||||||
height="50"
|
height="50"
|
||||||
scaleMode="exactFit"
|
scaleMode="exactFit"
|
||||||
visible="{!videoPlayer.menu.open}"/>
|
visible="{!videoPlayer.menu.open}"/>
|
||||||
{% endhighlight %}
|
```
|
||||||
|
|
||||||
All of the attributes are optional except the id attribute. It doesn’t have to be named “logoOverlay”, but you have to have something there. Notice the value for my “visible” attribute. This ensures that my logo disappears when the video is done and doesn’t awkwardly overlay any of the video menu UI elements. Remove it if you want your logo to display the whole time. Also note the “x” and “y” values. Change these to your liking. You might have to wait to finish Step Two below when you’ve added a logo and then you can test a video with your player to see where the logo fits best. From there, you can keep adjusting your logo until you’re satisfied.
|
All of the attributes are optional except the id attribute. It doesn’t have to be named “logoOverlay”, but you have to have something there. Notice the value for my “visible” attribute. This ensures that my logo disappears when the video is done and doesn’t awkwardly overlay any of the video menu UI elements. Remove it if you want your logo to display the whole time. Also note the “x” and “y” values. Change these to your liking. You might have to wait to finish Step Two below when you’ve added a logo and then you can test a video with your player to see where the logo fits best. From there, you can keep adjusting your logo until you’re satisfied.
|
||||||
1. Now click on All Players in the left sidebar and then click on the New Player button at the bottom
|
1. Now click on All Players in the left sidebar and then click on the New Player button at the bottom
|
||||||
|
|||||||
@@ -8,18 +8,18 @@ tags: [xslt]
|
|||||||
---
|
---
|
||||||
Let’s say you have a simple XML document that looks something like this:
|
Let’s say you have a simple XML document that looks something like this:
|
||||||
|
|
||||||
{% highlight xml %}
|
```xml
|
||||||
<root>
|
<root>
|
||||||
<Pod id="1"></Pod>
|
<Pod id="1"></Pod>
|
||||||
<Pod id="2"></Pod>
|
<Pod id="2"></Pod>
|
||||||
<Pod id="3"></Pod>
|
<Pod id="3"></Pod>
|
||||||
<Pod id="4"></Pod>
|
<Pod id="4"></Pod>
|
||||||
</root>
|
</root>
|
||||||
{% endhighlight %}
|
```
|
||||||
|
|
||||||
The content or meaning of the pods is irrelevant. The idea is that you have a “list” of elements (pods in this case) in your XML. How, then, do you use XSLT to group these pods into rows of two, three or whatever number of pods across? Something like this:
|
The content or meaning of the pods is irrelevant. The idea is that you have a “list” of elements (pods in this case) in your XML. How, then, do you use XSLT to group these pods into rows of two, three or whatever number of pods across? Something like this:
|
||||||
|
|
||||||
{% highlight xml %}
|
```xml
|
||||||
<div>
|
<div>
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="pod"> pod 1 </div>
|
<div class="pod"> pod 1 </div>
|
||||||
@@ -30,7 +30,7 @@ The content or meaning of the pods is irrelevant. The idea is that you have a
|
|||||||
<div class="pod"> pod 4 </div>
|
<div class="pod"> pod 4 </div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{% endhighlight %}
|
```
|
||||||
|
|
||||||
This example would required three pods per row. The example XSLT below will work for any number of pods per row with a simple edit (explained below).
|
This example would required three pods per row. The example XSLT below will work for any number of pods per row with a simple edit (explained below).
|
||||||
|
|
||||||
@@ -38,7 +38,7 @@ My first thought was from a very normal programmer’s perspective. I’d use so
|
|||||||
|
|
||||||
Turns out, it really isn’t difficult. You just have to think a little differently and start using xsl:template, xsl:call-template and xsl:apply-templates effectively along with their select and match attributes. So, take a look at this XSLT file:
|
Turns out, it really isn’t difficult. You just have to think a little differently and start using xsl:template, xsl:call-template and xsl:apply-templates effectively along with their select and match attributes. So, take a look at this XSLT file:
|
||||||
|
|
||||||
{% highlight xml %}
|
```xml
|
||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<xsl:stylesheet version="1.0"
|
<xsl:stylesheet version="1.0"
|
||||||
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
|
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
|
||||||
@@ -76,7 +76,7 @@ Turns out, it really isn’t difficult. You just have to think a little differen
|
|||||||
<!– whatever content your "pods" contain can be marked up here –>
|
<!– whatever content your "pods" contain can be marked up here –>
|
||||||
</xsl:template>
|
</xsl:template>
|
||||||
</xsl:stylesheet>
|
</xsl:stylesheet>
|
||||||
{% endhighlight %}
|
```
|
||||||
|
|
||||||
Here’s what’s going on: first, we call xsl:apply-templates on the first and then every third Pod using “Pod[position() mod 3 = 1]”. This will select the Pod at the start of every row: Pods 1 and 4. Notice that XSLT indexing starts at 1 and NOT 0. The PodRow template will match these selected Pods.
|
Here’s what’s going on: first, we call xsl:apply-templates on the first and then every third Pod using “Pod[position() mod 3 = 1]”. This will select the Pod at the start of every row: Pods 1 and 4. Notice that XSLT indexing starts at 1 and NOT 0. The PodRow template will match these selected Pods.
|
||||||
|
|
||||||
@@ -89,7 +89,7 @@ I needed another feature from my XSLT though. I needed every last pod in each ro
|
|||||||
|
|
||||||
I needed my HTML to look like this (notice the additional “last” class):
|
I needed my HTML to look like this (notice the additional “last” class):
|
||||||
|
|
||||||
{% highlight xml %}
|
```xml
|
||||||
<div>
|
<div>
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="pod"> pod 1 </div>
|
<div class="pod"> pod 1 </div>
|
||||||
@@ -100,11 +100,11 @@ I needed my HTML to look like this (notice the additional “last” class):
|
|||||||
<div class="pod last"> pod 4 </div>
|
<div class="pod last"> pod 4 </div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{% endhighlight %}
|
```
|
||||||
|
|
||||||
So, this is what I had to do:
|
So, this is what I had to do:
|
||||||
|
|
||||||
{% highlight xml %}
|
```xml
|
||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<xsl:stylesheet version="1.0"
|
<xsl:stylesheet version="1.0"
|
||||||
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
|
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
|
||||||
@@ -157,7 +157,7 @@ So, this is what I had to do:
|
|||||||
<!– whatever content your "pods" contain can be marked up here –>
|
<!– whatever content your "pods" contain can be marked up here –>
|
||||||
</xsl:template>
|
</xsl:template>
|
||||||
</xsl:stylesheet>
|
</xsl:stylesheet>
|
||||||
{% endhighlight %}
|
```
|
||||||
|
|
||||||
This version of the XSLT functions exactly the same as the previous one. The only additions are for identifying the last pod in each row for all cases. So, here is what has changed.
|
This version of the XSLT functions exactly the same as the previous one. The only additions are for identifying the last pod in each row for all cases. So, here is what has changed.
|
||||||
|
|
||||||
|
|||||||
@@ -12,14 +12,14 @@ I got caught today in a stupid problem. But, it wasn’t obvious (at least to m
|
|||||||
## Solution
|
## Solution
|
||||||
The solution was just correctly a stupid oversight on my part. I use ReSharper, so what I would do is just type the control prefix, colon then the name of the control and ReSharper would give me the suggestion to add the right Register tag. Well, ReSharper looks at it like a WebControl, not a UserControl. So, the Register tag is adds looks like this:
|
The solution was just correctly a stupid oversight on my part. I use ReSharper, so what I would do is just type the control prefix, colon then the name of the control and ReSharper would give me the suggestion to add the right Register tag. Well, ReSharper looks at it like a WebControl, not a UserControl. So, the Register tag is adds looks like this:
|
||||||
|
|
||||||
{% highlight csharp %}
|
```csharp
|
||||||
<%@ Register TagPrefix="ogden" Namespace="Ogden.Web.controls" Assembly="Ogden.Web" %>
|
<%@ Register TagPrefix="ogden" Namespace="Ogden.Web.controls" Assembly="Ogden.Web" %>
|
||||||
{% endhighlight %}
|
```
|
||||||
|
|
||||||
It should look like this:
|
It should look like this:
|
||||||
|
|
||||||
{% highlight csharp %}
|
```csharp
|
||||||
<%@ Register TagPrefix="ogden" TagName="BlogComments" src="BlogComments.ascx" %>
|
<%@ Register TagPrefix="ogden" TagName="BlogComments" src="BlogComments.ascx" %>
|
||||||
{% endhighlight %}
|
```
|
||||||
|
|
||||||
So, if you’re dumb like me, make sure you have the right Register tag if you’re nesting a UserControl (or using a UserControl anywhere really).
|
So, if you’re dumb like me, make sure you have the right Register tag if you’re nesting a UserControl (or using a UserControl anywhere really).
|
||||||
|
|||||||
@@ -14,9 +14,9 @@ I found a great, compact solution on Stack Overflow here: [http://stackoverflow.
|
|||||||
|
|
||||||
Here’s the code I used to set a Repeater DataSource:
|
Here’s the code I used to set a Repeater DataSource:
|
||||||
|
|
||||||
{% highlight csharp %}
|
```csharp
|
||||||
PodsRepeater.DataSource = Data.Items
|
PodsRepeater.DataSource = Data.Items
|
||||||
.Select((x, i) => new { Index = i, Value = x })
|
.Select((x, i) => new { Index = i, Value = x })
|
||||||
.GroupBy(obj => obj.Index / 4)
|
.GroupBy(obj => obj.Index / 4)
|
||||||
.Select(obj => obj.Select(v => v.Value).ToList());
|
.Select(obj => obj.Select(v => v.Value).ToList());
|
||||||
{% endhighlight %}
|
```
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ tags: [sitecore]
|
|||||||
## Problem
|
## Problem
|
||||||
I got the error message (in my Firebug console) in the Sitecore Page Editor on a page where I had a nested sublayout. I had a basic two-column layout for the main sublayout of the page. Then, in the right column, I had another sublayout that further divided up the right column as I needed. Here is what the nested sublayout looked like:
|
I got the error message (in my Firebug console) in the Sitecore Page Editor on a page where I had a nested sublayout. I had a basic two-column layout for the main sublayout of the page. Then, in the right column, I had another sublayout that further divided up the right column as I needed. Here is what the nested sublayout looked like:
|
||||||
|
|
||||||
{% highlight html %}
|
```html %}
|
||||||
<sc:Placeholder runat="server" Key="blogRightColumnTop"/>
|
<sc:Placeholder runat="server" Key="blogRightColumnTop"/>
|
||||||
|
|
||||||
<div class="sidebar-blog">
|
<div class="sidebar-blog">
|
||||||
@@ -19,14 +19,14 @@ I got the error message (in my Firebug console) in the Sitecore Page Editor on a
|
|||||||
<div class="sidebar-ads">
|
<div class="sidebar-ads">
|
||||||
<sc:Placeholder runat="server" Key="blogRightColumnBottomRight"/>
|
<sc:Placeholder runat="server" Key="blogRightColumnBottomRight"/>
|
||||||
</div>
|
</div>
|
||||||
{% endhighlight %}
|
```
|
||||||
|
|
||||||
This was a Javascript error that was preventing the Page Editor from fully loading the editor buttons for the edit frames I had on the page.
|
This was a Javascript error that was preventing the Page Editor from fully loading the editor buttons for the edit frames I had on the page.
|
||||||
|
|
||||||
## Solution
|
## Solution
|
||||||
The error message (surprise, surprise) was a little mystifying, but it gave me enough of a clue to think that there was some type of nesting issue. I noticed that I had a sc:Placeholder element at the top of my nested sub-layout with nothing wrapping it. Perhaps this was the issue? I tried this edit to my nested sub-layout:
|
The error message (surprise, surprise) was a little mystifying, but it gave me enough of a clue to think that there was some type of nesting issue. I noticed that I had a sc:Placeholder element at the top of my nested sub-layout with nothing wrapping it. Perhaps this was the issue? I tried this edit to my nested sub-layout:
|
||||||
|
|
||||||
{% highlight html %}
|
```html %}
|
||||||
<div>
|
<div>
|
||||||
<sc:Placeholder runat="server" Key="blogRightColumnTop"/>
|
<sc:Placeholder runat="server" Key="blogRightColumnTop"/>
|
||||||
|
|
||||||
@@ -38,6 +38,6 @@ The error message (surprise, surprise) was a little mystifying, but it gave me e
|
|||||||
<sc:Placeholder runat="server" Key="blogRightColumnBottomRight"/>
|
<sc:Placeholder runat="server" Key="blogRightColumnBottomRight"/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{% endhighlight %}
|
```
|
||||||
|
|
||||||
Notice the single, wrapping div. This worked! So, I’m not sure why, but apparently it solves this nesting problem. Perhaps Sitecore just does not expect a sc:Placeholder to be directly inside of another sc:Placeholder and so wrapping it solves that direct nesting issue.
|
Notice the single, wrapping div. This worked! So, I’m not sure why, but apparently it solves this nesting problem. Perhaps Sitecore just does not expect a sc:Placeholder to be directly inside of another sc:Placeholder and so wrapping it solves that direct nesting issue.
|
||||||
|
|||||||
@@ -14,18 +14,18 @@ Posts like this one [http://sharepoint.stackexchange.com/questions/41358/delete-
|
|||||||
|
|
||||||
Here was my specific case. In my FeatureDeactivating method I was setting all the SPWebs back to the original v4.master like this:
|
Here was my specific case. In my FeatureDeactivating method I was setting all the SPWebs back to the original v4.master like this:
|
||||||
|
|
||||||
{% highlight csharp %}
|
```csharp
|
||||||
foreach (SPWeb site in siteColl.AllWebs)
|
foreach (SPWeb site in siteColl.AllWebs)
|
||||||
{
|
{
|
||||||
site.MasterUrl = masterUrl;
|
site.MasterUrl = masterUrl;
|
||||||
site.CustomMasterUrl = masterUrl;
|
site.CustomMasterUrl = masterUrl;
|
||||||
site.Update();
|
site.Update();
|
||||||
}
|
}
|
||||||
{% endhighlight %}
|
```
|
||||||
|
|
||||||
Notice how I am looping through the SPSite.AllWebs SPWebCollection and updating each SPWeb after I reset the master page URLs. Now, here is what I was doing to try to delete the master page file from the _catalogs/masterpage library:
|
Notice how I am looping through the SPSite.AllWebs SPWebCollection and updating each SPWeb after I reset the master page URLs. Now, here is what I was doing to try to delete the master page file from the _catalogs/masterpage library:
|
||||||
|
|
||||||
{% highlight csharp %}
|
```csharp
|
||||||
string fileUrl = SPUrlUtility.CombineUrl(
|
string fileUrl = SPUrlUtility.CombineUrl(
|
||||||
siteColl.ServerRelativeUrl,
|
siteColl.ServerRelativeUrl,
|
||||||
file.FullRelativeUrl);
|
file.FullRelativeUrl);
|
||||||
@@ -39,21 +39,21 @@ try
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
catch { }
|
catch { }
|
||||||
{% endhighlight %}
|
```
|
||||||
|
|
||||||
When I would get to the spFile.Delete() line, it would catch the exception whose error message is the title of this post. But why?? The master page wasn’t being referenced anywhere anymore! As I would discover, the problem is on this line:
|
When I would get to the spFile.Delete() line, it would catch the exception whose error message is the title of this post. But why?? The master page wasn’t being referenced anywhere anymore! As I would discover, the problem is on this line:
|
||||||
|
|
||||||
{% highlight csharp %}
|
```csharp
|
||||||
SPFile spFile = siteColl.RootWeb.GetFile(fileUrl);
|
SPFile spFile = siteColl.RootWeb.GetFile(fileUrl);
|
||||||
{% endhighlight %}
|
```
|
||||||
|
|
||||||
Do you see it? I didn’t at first either. The RootWeb SPWeb reference isn’t pointing to the same object that the same SPWeb in the SPSite.AllWebs collection is pointing to. So, technically, the RootWeb object still thinks its master page URLs are pointing to my custom master that I’m trying to delete because it hasn’t had Update() called on it.
|
Do you see it? I didn’t at first either. The RootWeb SPWeb reference isn’t pointing to the same object that the same SPWeb in the SPSite.AllWebs collection is pointing to. So, technically, the RootWeb object still thinks its master page URLs are pointing to my custom master that I’m trying to delete because it hasn’t had Update() called on it.
|
||||||
|
|
||||||
To fix it, I had to get my SPFile from one of the SPWebs in SPSite.AllWebs, like this:
|
To fix it, I had to get my SPFile from one of the SPWebs in SPSite.AllWebs, like this:
|
||||||
|
|
||||||
{% highlight csharp %}
|
```csharp
|
||||||
SPFile spFile = siteColl.AllWebs.First().GetFile(fileUrl);
|
SPFile spFile = siteColl.AllWebs.First().GetFile(fileUrl);
|
||||||
{% endhighlight %}
|
```
|
||||||
|
|
||||||
That enables me to delete the file with no errors.
|
That enables me to delete the file with no errors.
|
||||||
|
|
||||||
@@ -67,7 +67,7 @@ When iterating through SPWebs dispose of items with the using statement. This wi
|
|||||||
|
|
||||||
So it might look like this:
|
So it might look like this:
|
||||||
|
|
||||||
{% highlight csharp %}
|
```csharp
|
||||||
using(SPSite siteColl = properties.Feature.Parent as SPSite)
|
using(SPSite siteColl = properties.Feature.Parent as SPSite)
|
||||||
{
|
{
|
||||||
foreach (SPWeb site in siteColl.AllWebs)
|
foreach (SPWeb site in siteColl.AllWebs)
|
||||||
@@ -75,4 +75,4 @@ using(SPSite siteColl = properties.Feature.Parent as SPSite)
|
|||||||
site.MasterUrl = masterUrl; site.CustomMasterUrl = masterUrl; site.Update();
|
site.MasterUrl = masterUrl; site.CustomMasterUrl = masterUrl; site.Update();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
{% endhighlight %}
|
```
|
||||||
|
|||||||
@@ -22,7 +22,7 @@ As I read more, I realized this was a bigger clue than I initially thought. The
|
|||||||
|
|
||||||
Here is my eventual solution. Unless they change the file location or configuration XML structure, it’s pretty safe, albeit a little hacky. I created a simple DynamicModulesHelper class.
|
Here is my eventual solution. Unless they change the file location or configuration XML structure, it’s pretty safe, albeit a little hacky. I created a simple DynamicModulesHelper class.
|
||||||
|
|
||||||
{% highlight csharp %}
|
```csharp
|
||||||
public static class DynamicModuleHelper
|
public static class DynamicModuleHelper
|
||||||
{
|
{
|
||||||
public const string DynamicModulesConfigRelativePath =
|
public const string DynamicModulesConfigRelativePath =
|
||||||
@@ -93,12 +93,12 @@ public static class DynamicModuleHelper
|
|||||||
return choicesNodes;
|
return choicesNodes;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
{% endhighlight %}
|
```
|
||||||
|
|
||||||
To get the Choices field options, you call the GetChoiceFieldOptions method, passing in the dynamic module’s content type string (like this one: “Telerik.Sitefinity.DynamicTypes.Model.TestModule.TestModule”) and the name of the Choices field (like this: “RandomChoices”). So, my particular call for just a goofy test module I created was this:
|
To get the Choices field options, you call the GetChoiceFieldOptions method, passing in the dynamic module’s content type string (like this one: “Telerik.Sitefinity.DynamicTypes.Model.TestModule.TestModule”) and the name of the Choices field (like this: “RandomChoices”). So, my particular call for just a goofy test module I created was this:
|
||||||
|
|
||||||
{% highlight csharp %}
|
```csharp
|
||||||
KeyValuePair<string, string>[] choices = DynamicModuleHelper.GetChoiceFieldOptions(
|
KeyValuePair<string, string>[] choices = DynamicModuleHelper.GetChoiceFieldOptions(
|
||||||
"Telerik.Sitefinity.DynamicTypes.Model.TestModule.TestModule",
|
"Telerik.Sitefinity.DynamicTypes.Model.TestModule.TestModule",
|
||||||
"RandomChoices");
|
"RandomChoices");
|
||||||
{% endhighlight %}
|
```
|
||||||
|
|||||||
@@ -10,14 +10,14 @@ After you create and activate a dynamic module with Module Builder in Sitefinity
|
|||||||
|
|
||||||
## ContentViewConfig.config
|
## ContentViewConfig.config
|
||||||
A config:link element is added under contentViewControls. Example:
|
A config:link element is added under contentViewControls. Example:
|
||||||
{% highlight xml %}
|
```xml
|
||||||
<config:link definitionName="Telerik.Sitefinity.DynamicTypes.Model.Configchangetests.ConfigChangeBackendDefinition" path="dynamicModulesConfig/contentViewControls/Telerik.Sitefinity.DynamicTypes.Model.Configchangetests.ConfigChangeBackendDefinition" module="ModuleBuilder" />
|
<config:link definitionName="Telerik.Sitefinity.DynamicTypes.Model.Configchangetests.ConfigChangeBackendDefinition" path="dynamicModulesConfig/contentViewControls/Telerik.Sitefinity.DynamicTypes.Model.Configchangetests.ConfigChangeBackendDefinition" module="ModuleBuilder" />
|
||||||
{% endhighlight %}
|
```
|
||||||
|
|
||||||
## DynamicModulesConfig.config
|
## DynamicModulesConfig.config
|
||||||
And entire contentViewControl section is added under the contentViewControls element. Example:
|
And entire contentViewControl section is added under the contentViewControls element. Example:
|
||||||
|
|
||||||
{% highlight xml %}
|
```xml
|
||||||
<contentViewControl contentType="Telerik.Sitefinity.DynamicTypes.Model.Configchangetests.ConfigChange" managerType="Telerik.Sitefinity.DynamicModules.DynamicModuleManager, Telerik.Sitefinity" useWorkflow="True" definitionName="Telerik.Sitefinity.DynamicTypes.Model.Configchangetests.ConfigChangeBackendDefinition">
|
<contentViewControl contentType="Telerik.Sitefinity.DynamicTypes.Model.Configchangetests.ConfigChange" managerType="Telerik.Sitefinity.DynamicModules.DynamicModuleManager, Telerik.Sitefinity" useWorkflow="True" definitionName="Telerik.Sitefinity.DynamicTypes.Model.Configchangetests.ConfigChangeBackendDefinition">
|
||||||
<views>
|
<views>
|
||||||
<view gridCssClass="sfPagesTreeview" searchFields="Title" doNotBindOnClientWhenPageIsLoaded="False" allowPaging="True" allowUrlQueries="True" disableSorting="False" itemsPerPage="50" canUsersSetItemsPerPage="False" sortExpression="LastModified DESC" detailsPageId="00000000-0000-0000-0000-000000000000" webServiceBaseUrl="~/Sitefinity/Services/DynamicModules/Data.svc/" templateEvaluationMode="None" itemsParentId="00000000-0000-0000-0000-000000000000" renderLinksInMasterView="True" enableSocialSharing="False" displayMode="Read" useWorkflow="True" title="Config changes" viewType="Telerik.Sitefinity.DynamicModules.Web.UI.Backend.DynamicContentMasterGridView" viewName="Config changeBackendList" type:this="Telerik.Sitefinity.Web.UI.ContentUI.Views.Backend.Master.Config.MasterGridViewElement, Telerik.Sitefinity">
|
<view gridCssClass="sfPagesTreeview" searchFields="Title" doNotBindOnClientWhenPageIsLoaded="False" allowPaging="True" allowUrlQueries="True" disableSorting="False" itemsPerPage="50" canUsersSetItemsPerPage="False" sortExpression="LastModified DESC" detailsPageId="00000000-0000-0000-0000-000000000000" webServiceBaseUrl="~/Sitefinity/Services/DynamicModules/Data.svc/" templateEvaluationMode="None" itemsParentId="00000000-0000-0000-0000-000000000000" renderLinksInMasterView="True" enableSocialSharing="False" displayMode="Read" useWorkflow="True" title="Config changes" viewType="Telerik.Sitefinity.DynamicModules.Web.UI.Backend.DynamicContentMasterGridView" viewName="Config changeBackendList" type:this="Telerik.Sitefinity.Web.UI.ContentUI.Views.Backend.Master.Config.MasterGridViewElement, Telerik.Sitefinity">
|
||||||
@@ -205,11 +205,11 @@ And entire contentViewControl section is added under the contentViewControls ele
|
|||||||
</view>
|
</view>
|
||||||
</views>
|
</views>
|
||||||
</contentViewControl>
|
</contentViewControl>
|
||||||
{% endhighlight %}
|
```
|
||||||
|
|
||||||
## SecurityConfig.config
|
## SecurityConfig.config
|
||||||
A permission element is added underneath the permissions node. Example:
|
A permission element is added underneath the permissions node. Example:
|
||||||
{% highlight xml %}
|
```xml
|
||||||
<permission title="Config changes permissions" description="Represents the most common application security permissions." loginUrl="~/Sitefinity/Login" ajaxLoginUrl="~/Sitefinity/Login/Ajax" name="Configchangetests-ConfigChange">
|
<permission title="Config changes permissions" description="Represents the most common application security permissions." loginUrl="~/Sitefinity/Login" ajaxLoginUrl="~/Sitefinity/Login/Ajax" name="Configchangetests-ConfigChange">
|
||||||
<actions>
|
<actions>
|
||||||
<add title="View Config changes" description="Allows or denies viewing Config changes." type="View" name="View" />
|
<add title="View Config changes" description="Allows or denies viewing Config changes." type="View" name="View" />
|
||||||
@@ -219,19 +219,19 @@ A permission element is added underneath the permissions node. Example:
|
|||||||
<add title="Change a Config changes permissions" description="Allows or denies changing the permissions of Config changes." type="ChangePermissions" name="ChangePermissions" />
|
<add title="Change a Config changes permissions" description="Allows or denies changing the permissions of Config changes." type="ChangePermissions" name="ChangePermissions" />
|
||||||
</actions>
|
</actions>
|
||||||
</permission>
|
</permission>
|
||||||
{% endhighlight %}
|
```
|
||||||
|
|
||||||
## ToolboxesConfig.config
|
## ToolboxesConfig.config
|
||||||
A tool is added in the tools section. Example:
|
A tool is added in the tools section. Example:
|
||||||
{% highlight xml %}
|
```xml
|
||||||
<add enabled="True" type="Telerik.Sitefinity.DynamicModules.Web.UI.Frontend.DynamicContentView, Telerik.Sitefinity" title="Config changes" cssClass="sfNewsViewIcn" moduleName="Config change tests" DynamicContentTypeName="Telerik.Sitefinity.DynamicTypes.Model.Configchangetests.ConfigChange" DefaultMasterTemplateKey="844f4eff-9a33-4799-84e5-2973d7a3db9b" DefaultDetailTemplateKey="7fd83546-0785-4050-998f-06428e7c6fa1" visibilityMode="None" name="Telerik.Sitefinity.DynamicTypes.Model.Configchangetests.ConfigChange" />
|
<add enabled="True" type="Telerik.Sitefinity.DynamicModules.Web.UI.Frontend.DynamicContentView, Telerik.Sitefinity" title="Config changes" cssClass="sfNewsViewIcn" moduleName="Config change tests" DynamicContentTypeName="Telerik.Sitefinity.DynamicTypes.Model.Configchangetests.ConfigChange" DefaultMasterTemplateKey="844f4eff-9a33-4799-84e5-2973d7a3db9b" DefaultDetailTemplateKey="7fd83546-0785-4050-998f-06428e7c6fa1" visibilityMode="None" name="Telerik.Sitefinity.DynamicTypes.Model.Configchangetests.ConfigChange" />
|
||||||
{% endhighlight %}
|
```
|
||||||
|
|
||||||
## WorkflowConfig.config
|
## WorkflowConfig.config
|
||||||
A workflow type is added in the workflowTypes section. Example:
|
A workflow type is added in the workflowTypes section. Example:
|
||||||
{% highlight xml %}
|
```xml
|
||||||
<add title="Config change" moduleName="Config change tests" contentType="Telerik.Sitefinity.DynamicTypes.Model.Configchangetests.ConfigChange" />
|
<add title="Config change" moduleName="Config change tests" contentType="Telerik.Sitefinity.DynamicTypes.Model.Configchangetests.ConfigChange" />
|
||||||
{% endhighlight %}
|
```
|
||||||
|
|
||||||
## Deactivation
|
## Deactivation
|
||||||
No configuration changes are made in the Sitefinity configuration files when you just deactivate a dynamic module with Module Builder.
|
No configuration changes are made in the Sitefinity configuration files when you just deactivate a dynamic module with Module Builder.
|
||||||
|
|||||||
@@ -12,20 +12,20 @@ You have to setup the error pages in two places in the application’s web.confi
|
|||||||
|
|
||||||
## system.web/customErrors
|
## system.web/customErrors
|
||||||
You have to setup your error pages in the system.web/customErrors section of your web.config to cover calls to non-existent *.aspx pages (and probably other ASP.NET-handled pages as well). My customErrors section looks like this. It will be expanded to cover other error codes.
|
You have to setup your error pages in the system.web/customErrors section of your web.config to cover calls to non-existent *.aspx pages (and probably other ASP.NET-handled pages as well). My customErrors section looks like this. It will be expanded to cover other error codes.
|
||||||
{% highlight xml %}
|
```xml
|
||||||
<customErrors mode="On" redirectMode="ResponseRewrite">
|
<customErrors mode="On" redirectMode="ResponseRewrite">
|
||||||
<error statusCode="404" redirect="~/Static/404.htm"/>
|
<error statusCode="404" redirect="~/Static/404.htm"/>
|
||||||
</customErrors>
|
</customErrors>
|
||||||
{% endhighlight %}
|
```
|
||||||
|
|
||||||
## system.webServer/httpErrors
|
## system.webServer/httpErrors
|
||||||
The next section you need is the system.webServer/httpErrors section. I never knew this section exists, but it corresponds to the Errors panel in IIS Manager 7.5 for your website. This section covers request for non-existing folders and static files (like /bogusFolder or /bogusFile.htm). Mine looks like this and it will likewise be expanded for other error codes.
|
The next section you need is the system.webServer/httpErrors section. I never knew this section exists, but it corresponds to the Errors panel in IIS Manager 7.5 for your website. This section covers request for non-existing folders and static files (like /bogusFolder or /bogusFile.htm). Mine looks like this and it will likewise be expanded for other error codes.
|
||||||
{% highlight xml %}
|
```xml
|
||||||
<httpErrors errorMode="Custom" defaultResponseMode="File">
|
<httpErrors errorMode="Custom" defaultResponseMode="File">
|
||||||
<remove statusCode="404"/>
|
<remove statusCode="404"/>
|
||||||
<error statusCode="404" path="Static\404.htm"/>
|
<error statusCode="404" path="Static\404.htm"/>
|
||||||
</httpErrors>
|
</httpErrors>
|
||||||
{% endhighlight %}
|
```
|
||||||
|
|
||||||
Because IIS has default settings for this stuff, you need to use the remove element before adding your own error messages. You can find more specifics on this configuration element here: [http://www.iis.net/configreference/system.webserver/httperrors](http://www.iis.net/configreference/system.webserver/httperrors).
|
Because IIS has default settings for this stuff, you need to use the remove element before adding your own error messages. You can find more specifics on this configuration element here: [http://www.iis.net/configreference/system.webserver/httperrors](http://www.iis.net/configreference/system.webserver/httperrors).
|
||||||
|
|
||||||
|
|||||||
@@ -25,7 +25,7 @@ Turns out, I was right. With some more trial and error from that point on, I cam
|
|||||||
Override ConcatenationTranslator
|
Override ConcatenationTranslator
|
||||||
First of all, you need to create a new class that inherits from ConcatenationTranslator. This is a public class in the Telerik.Sitefinity.Publishing.Translators namespace. I called my class CustomConcatenationTranslator. IN the base class, the Translate method turns an array of object values into a space-separated string. I’ll show the code in a minute, but the first thing you need to do after creating the class is register it with the pipeline. In Global.asax.cs, add these lines (around any other code you have there):
|
First of all, you need to create a new class that inherits from ConcatenationTranslator. This is a public class in the Telerik.Sitefinity.Publishing.Translators namespace. I called my class CustomConcatenationTranslator. IN the base class, the Translate method turns an array of object values into a space-separated string. I’ll show the code in a minute, but the first thing you need to do after creating the class is register it with the pipeline. In Global.asax.cs, add these lines (around any other code you have there):
|
||||||
|
|
||||||
{% highlight csharp %}
|
```csharp
|
||||||
protected void Application_Start(object sender, EventArgs e)
|
protected void Application_Start(object sender, EventArgs e)
|
||||||
{
|
{
|
||||||
Bootstrapper.Initialized += Bootstrapper_Initialized;
|
Bootstrapper.Initialized += Bootstrapper_Initialized;
|
||||||
@@ -38,13 +38,13 @@ private void Bootstrapper_Initialized(object sender, Telerik.Sitefinity.Data.Exe
|
|||||||
PipeTranslatorFactory.RegisterTranslator(new CustomConcatenationTranslator());
|
PipeTranslatorFactory.RegisterTranslator(new CustomConcatenationTranslator());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
{% endhighlight %}
|
```
|
||||||
|
|
||||||
You don’t have to unregistered the existing ConcatenationTranslator. Your registration will replace it as long as you don’t rename the translator by overriding it’s Name property.
|
You don’t have to unregistered the existing ConcatenationTranslator. Your registration will replace it as long as you don’t rename the translator by overriding it’s Name property.
|
||||||
|
|
||||||
What I wanted to do was check if the data being translated was a TrackedList<Guid> type. This is what your taxonomy fields are in code. Here is my code for the CustomConcatenationTranslator class.
|
What I wanted to do was check if the data being translated was a TrackedList<Guid> type. This is what your taxonomy fields are in code. Here is my code for the CustomConcatenationTranslator class.
|
||||||
|
|
||||||
{% highlight csharp %}
|
```csharp
|
||||||
public class CustomConcatenationTranslator : ConcatenationTranslator
|
public class CustomConcatenationTranslator : ConcatenationTranslator
|
||||||
{
|
{
|
||||||
private readonly TaxonomyManager _taxonomyManager;
|
private readonly TaxonomyManager _taxonomyManager;
|
||||||
@@ -105,7 +105,7 @@ public class CustomConcatenationTranslator : ConcatenationTranslator
|
|||||||
return string.Join(" ", taxNames);
|
return string.Join(" ", taxNames);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
{% endhighlight %}
|
```
|
||||||
|
|
||||||
It should be pretty simple to follow. Basically, if I find a data value is a TrackedList<Guid>, I get the Taxon items with that Guid ID, get the name of that Taxon and then concatenate those values, separated by a space. If the data object is anything other than a TrackedList<Guid>, I used the default ConcatenationTranslator behavior.
|
It should be pretty simple to follow. Basically, if I find a data value is a TrackedList<Guid>, I get the Taxon items with that Guid ID, get the name of that Taxon and then concatenate those values, separated by a space. If the data object is anything other than a TrackedList<Guid>, I used the default ConcatenationTranslator behavior.
|
||||||
|
|
||||||
|
|||||||
@@ -19,17 +19,17 @@ I am using a WidgetDesigner in which I have the PagesSelector control. My goal i
|
|||||||
Everything works fine. Except, once I save a selection in the PagesSelector, on subsequent edits, the currently selected items would not show up, as described above.
|
Everything works fine. Except, once I save a selection in the PagesSelector, on subsequent edits, the currently selected items would not show up, as described above.
|
||||||
|
|
||||||
Here is the bug I found in the Sitefinity code. The set_selectedItemIds function is really simple. It looks like this:
|
Here is the bug I found in the Sitefinity code. The set_selectedItemIds function is really simple. It looks like this:
|
||||||
{% highlight javascript %}
|
```javascript %}
|
||||||
set_selectedItemIds: function(ids) {
|
set_selectedItemIds: function(ids) {
|
||||||
this._selectedItemIds = ids;
|
this._selectedItemIds = ids;
|
||||||
this._updateGridSelection();
|
this._updateGridSelection();
|
||||||
this._updateTreeSelection();
|
this._updateTreeSelection();
|
||||||
}
|
}
|
||||||
{% endhighlight %}
|
```
|
||||||
|
|
||||||
It updates a property (_selectedItemIds) and then updates the grid (not sure what that is) and then the tree (of page nodes). The _updateTreeSelection method is what we’re looking for–this is where the bug is. The method looks like this:
|
It updates a property (_selectedItemIds) and then updates the grid (not sure what that is) and then the tree (of page nodes). The _updateTreeSelection method is what we’re looking for–this is where the bug is. The method looks like this:
|
||||||
|
|
||||||
{% highlight javascript %}
|
```javascript %}
|
||||||
_updateTreeSelection: function () {
|
_updateTreeSelection: function () {
|
||||||
if (this._treeIsBound == false) {
|
if (this._treeIsBound == false) {
|
||||||
this._treeMustBeUpdated = true;
|
this._treeMustBeUpdated = true;
|
||||||
@@ -53,13 +53,13 @@ _updateTreeSelection: function () {
|
|||||||
}
|
}
|
||||||
this._raiseSelectionApplied(this, {});
|
this._raiseSelectionApplied(this, {});
|
||||||
}
|
}
|
||||||
{% endhighlight %}
|
```
|
||||||
|
|
||||||
The problem is on line 13. tree.get_selectedItems() returns an empty array when there are no items selected. So, the expression if (selectedItems) { } will always be true–but we want to get to the else statement, as you can see, to set the selection from the this._selectedItemIds array.
|
The problem is on line 13. tree.get_selectedItems() returns an empty array when there are no items selected. So, the expression if (selectedItems) { } will always be true–but we want to get to the else statement, as you can see, to set the selection from the this._selectedItemIds array.
|
||||||
|
|
||||||
The beauty (and danger!) of javascript is that you can easily fix something like this in your own code by simply redefining their method. Here is my code that fixes this bug:
|
The beauty (and danger!) of javascript is that you can easily fix something like this in your own code by simply redefining their method. Here is my code that fixes this bug:
|
||||||
|
|
||||||
{% highlight javascript %}
|
```javascript %}
|
||||||
var wrapper = this.get_pagesSelector();
|
var wrapper = this.get_pagesSelector();
|
||||||
var selector = this.get_pagesSelector().get_pageSelector();
|
var selector = this.get_pagesSelector().get_pageSelector();
|
||||||
var selecting = false;
|
var selecting = false;
|
||||||
@@ -99,7 +99,7 @@ wrapper.add_selectionApplied(function () {
|
|||||||
selector._updateTreeSelection = oldMethod;
|
selector._updateTreeSelection = oldMethod;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
{% endhighlight %}
|
```
|
||||||
|
|
||||||
First, I attach to the selectionApplied method. If you try to do the above code before that is done, your selection will just be overridden (or just not work–not sure which). This event will get called when you call set_selectedItemIds. To avoid an infinite loop, then, I use the selecting flag at the top of the event function I am attaching.
|
First, I attach to the selectionApplied method. If you try to do the above code before that is done, your selection will just be overridden (or just not work–not sure which). This event will get called when you call set_selectedItemIds. To avoid an infinite loop, then, I use the selecting flag at the top of the event function I am attaching.
|
||||||
|
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ I had a case recently where I needed to convert a LINQ expression of the type of
|
|||||||
|
|
||||||
First, the helper method in a static class.
|
First, the helper method in a static class.
|
||||||
|
|
||||||
{% highlight csharp %}
|
```csharp
|
||||||
internal static Expression<Func<TConcrete, bool>> ConvertToConcreteExpression<TConcrete, TInterface>( Expression<Func<TInterface, bool>> interfaceExpression)
|
internal static Expression<Func<TConcrete, bool>> ConvertToConcreteExpression<TConcrete, TInterface>( Expression<Func<TInterface, bool>> interfaceExpression)
|
||||||
{
|
{
|
||||||
if (!typeof(TInterface).IsAssignableFrom(typeof(TConcrete)))
|
if (!typeof(TInterface).IsAssignableFrom(typeof(TConcrete)))
|
||||||
@@ -20,11 +20,11 @@ internal static Expression<Func<TConcrete, bool>> ConvertToConcreteExpression<TC
|
|||||||
|
|
||||||
return TransformVisitor<TConcrete, TInterface>.Transform(interfaceExpression);
|
return TransformVisitor<TConcrete, TInterface>.Transform(interfaceExpression);
|
||||||
}
|
}
|
||||||
{% endhighlight %}
|
```
|
||||||
|
|
||||||
Here is the TransformVisitor class.
|
Here is the TransformVisitor class.
|
||||||
|
|
||||||
{% highlight csharp %}
|
```csharp
|
||||||
internal class TransformVisitor<TConcrete, TInterface> : ExpressionVisitor
|
internal class TransformVisitor<TConcrete, TInterface> : ExpressionVisitor
|
||||||
{
|
{
|
||||||
private readonly ParameterExpression _param = Expression.Parameter(typeof(TConcrete), "param_0");
|
private readonly ParameterExpression _param = Expression.Parameter(typeof(TConcrete), "param_0");
|
||||||
@@ -71,4 +71,4 @@ internal class TransformVisitor<TConcrete, TInterface> : ExpressionVisitor
|
|||||||
return base.VisitParameter(node);
|
return base.VisitParameter(node);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
{% endhighlight %}
|
```
|
||||||
|
|||||||
@@ -16,7 +16,7 @@ DO include a trailing slash
|
|||||||
Example: “folder1/folder2/otherfolder/”
|
Example: “folder1/folder2/otherfolder/”
|
||||||
Given this code:
|
Given this code:
|
||||||
|
|
||||||
{% highlight csharp %}
|
```csharp
|
||||||
FolderManager fm = new FolderManager();
|
FolderManager fm = new FolderManager();
|
||||||
FolderCriteria folderCrit = new FolderCriteria();
|
FolderCriteria folderCrit = new FolderCriteria();
|
||||||
folderCrit.AddFilter(
|
folderCrit.AddFilter(
|
||||||
@@ -25,7 +25,7 @@ CriteriaFilterOperator.EqualTo,
|
|||||||
folderPath);
|
folderPath);
|
||||||
|
|
||||||
FolderData folder = fm.GetList(folderCrit).FirstOrDefault();
|
FolderData folder = fm.GetList(folderCrit).FirstOrDefault();
|
||||||
{% endhighlight %}
|
```
|
||||||
|
|
||||||
Then the “folderPath” variable needs to be in this format: “folderName1/folderName2/”. The same would apply for finding all the content in a folder via the contents’ path.
|
Then the “folderPath” variable needs to be in this format: “folderName1/folderName2/”. The same would apply for finding all the content in a folder via the contents’ path.
|
||||||
|
|
||||||
@@ -37,9 +37,9 @@ Do NOT use a trailing slash
|
|||||||
Example: “\taxonomy1\taxonomy2\othertaxonomy”
|
Example: “\taxonomy1\taxonomy2\othertaxonomy”
|
||||||
So, given this code:
|
So, given this code:
|
||||||
|
|
||||||
{% highlight csharp %}
|
```csharp
|
||||||
ITaxonomyManager _taxManager = ObjectFactory.GetTaxonomyManager();
|
ITaxonomyManager _taxManager = ObjectFactory.GetTaxonomyManager();
|
||||||
TaxonomyData tax = _taxManager.GetItem(taxPath);
|
TaxonomyData tax = _taxManager.GetItem(taxPath);
|
||||||
{% endhighlight %}
|
```
|
||||||
|
|
||||||
The “taxPath” variable needs to be in this format: “\rootTaxonomyName\subTaxonomyName1\subTaxonomyName2″.
|
The “taxPath” variable needs to be in this format: “\rootTaxonomyName\subTaxonomyName1\subTaxonomyName2″.
|
||||||
|
|||||||
@@ -20,23 +20,23 @@ Whatever the case may be, I started snooping around on the net for others having
|
|||||||
|
|
||||||
I started snooping around with decompiling the Ektron.Cms.ObjectFactory DLL code where the ITaxonomyManager interface is defined. Everything looked good–it had the same-named methods, but was using the OperationContract attribute which lets you define the action name. The two GetList actions were defined with different names, so something wasn’t matching up with the error I was seeing in my import project. You can see that the two GetList methods have different Action values, making it OK for WCF.
|
I started snooping around with decompiling the Ektron.Cms.ObjectFactory DLL code where the ITaxonomyManager interface is defined. Everything looked good–it had the same-named methods, but was using the OperationContract attribute which lets you define the action name. The two GetList actions were defined with different names, so something wasn’t matching up with the error I was seeing in my import project. You can see that the two GetList methods have different Action values, making it OK for WCF.
|
||||||
|
|
||||||
{% highlight csharp %}
|
```csharp
|
||||||
[OperationContract(Action="GetList")]
|
[OperationContract(Action="GetList")]
|
||||||
List<TaxonomyData> GetList(TaxonomyCriteria criteria);
|
List<TaxonomyData> GetList(TaxonomyCriteria criteria);
|
||||||
|
|
||||||
[OperationContract(Action="GetListByCustomProperty", Name="GetListByCustomPropertyCriteria")]
|
[OperationContract(Action="GetListByCustomProperty", Name="GetListByCustomPropertyCriteria")]
|
||||||
List<TaxonomyData> GetList(TaxonomyCustomPropertyCriteria criteria);
|
List<TaxonomyData> GetList(TaxonomyCustomPropertyCriteria criteria);
|
||||||
{% endhighlight %}
|
```
|
||||||
|
|
||||||
When I decompiled the Ektron.Cms.ObjectFactory DLL that I was using in my import project, I found the problem. You can see it here:
|
When I decompiled the Ektron.Cms.ObjectFactory DLL that I was using in my import project, I found the problem. You can see it here:
|
||||||
|
|
||||||
{% highlight csharp %}
|
```csharp
|
||||||
[OperationContract(Action="GetList")]
|
[OperationContract(Action="GetList")]
|
||||||
List<TaxonomyData> GetList(TaxonomyCriteria criteria);
|
List<TaxonomyData> GetList(TaxonomyCriteria criteria);
|
||||||
|
|
||||||
[OperationContract(Action="GetList")]
|
[OperationContract(Action="GetList")]
|
||||||
List<TaxonomyData> GetList(TaxonomyCustomPropertyCriteria criteria);
|
List<TaxonomyData> GetList(TaxonomyCustomPropertyCriteria criteria);
|
||||||
{% endhighlight %}
|
```
|
||||||
|
|
||||||
Notice how the Action is defined with the same name. I checked the DLL versions of my DLL and the DLL in the Ektron 8.5 SP3 site I was pushing content too. They were both the same: 8.5.0.356. So, somewhere along the line, Ektron fixed the issue without updating the AssemblyFileVersion to signify a bug fix/updated code. Once I copied the DLL from the SP3 site’s bin folder to my import project, it worked just fine.
|
Notice how the Action is defined with the same name. I checked the DLL versions of my DLL and the DLL in the Ektron 8.5 SP3 site I was pushing content too. They were both the same: 8.5.0.356. So, somewhere along the line, Ektron fixed the issue without updating the AssemblyFileVersion to signify a bug fix/updated code. Once I copied the DLL from the SP3 site’s bin folder to my import project, it worked just fine.
|
||||||
|
|
||||||
|
|||||||
@@ -15,7 +15,7 @@ So, lesson #1 when getting 401 in SharePoint: debug your application.
|
|||||||
|
|
||||||
Lesson #2: apparently accessing the DefaultPage property of a PublishingWeb requires elevated privileges. Here was my code:
|
Lesson #2: apparently accessing the DefaultPage property of a PublishingWeb requires elevated privileges. Here was my code:
|
||||||
|
|
||||||
{% highlight csharp %}
|
```csharp
|
||||||
private string GetDefaultPageUrl()
|
private string GetDefaultPageUrl()
|
||||||
{
|
{
|
||||||
if (!PublishingWeb.IsPublishingWeb(SPContext.Current.Web))
|
if (!PublishingWeb.IsPublishingWeb(SPContext.Current.Web))
|
||||||
@@ -31,11 +31,11 @@ private string GetDefaultPageUrl()
|
|||||||
|
|
||||||
return web.DefaultPage == null ? string.Empty : web.DefaultPage.Url;
|
return web.DefaultPage == null ? string.Empty : web.DefaultPage.Url;
|
||||||
}
|
}
|
||||||
{% endhighlight %}
|
```
|
||||||
|
|
||||||
That last line was throwing the 401. So, the solution was to wrap it in an SPSecurity.RunWithElevatedPrivileges call, like this:
|
That last line was throwing the 401. So, the solution was to wrap it in an SPSecurity.RunWithElevatedPrivileges call, like this:
|
||||||
|
|
||||||
{% highlight csharp %}
|
```csharp
|
||||||
private string GetDefaultPageUrl()
|
private string GetDefaultPageUrl()
|
||||||
{
|
{
|
||||||
string url = string.Empty;
|
string url = string.Empty;
|
||||||
@@ -65,7 +65,7 @@ private string GetDefaultPageUrl()
|
|||||||
});
|
});
|
||||||
return url;
|
return url;
|
||||||
}
|
}
|
||||||
{% endhighlight %}
|
```
|
||||||
|
|
||||||
Thanks to this post for the answer about re-opening the site and web inside the RunWithElevatedPrivileges code:
|
Thanks to this post for the answer about re-opening the site and web inside the RunWithElevatedPrivileges code:
|
||||||
[http://henry-chong.com/2010/11/sharepoint-anonymous-access-and-publishing-web-default-page](http://henry-chong.com/2010/11/sharepoint-anonymous-access-and-publishing-web-default-page).
|
[http://henry-chong.com/2010/11/sharepoint-anonymous-access-and-publishing-web-default-page](http://henry-chong.com/2010/11/sharepoint-anonymous-access-and-publishing-web-default-page).
|
||||||
|
|||||||
@@ -25,18 +25,18 @@ Still, the post mentioned above got me on the right track. By commenting out co
|
|||||||
|
|
||||||
I had the following code in my HttpModule. Here is what Init method looked like:
|
I had the following code in my HttpModule. Here is what Init method looked like:
|
||||||
|
|
||||||
{% highlight csharp %}
|
```csharp
|
||||||
public void Init(HttpApplication context)
|
public void Init(HttpApplication context)
|
||||||
{
|
{
|
||||||
context.BeginRequest += SetupOutputFilter;
|
context.BeginRequest += SetupOutputFilter;
|
||||||
context.PreSendRequestHeaders += WritePdfHeaders;
|
context.PreSendRequestHeaders += WritePdfHeaders;
|
||||||
context.PreSendRequestContent += WritePdfToOutput;
|
context.PreSendRequestContent += WritePdfToOutput;
|
||||||
}
|
}
|
||||||
{% endhighlight %}
|
```
|
||||||
|
|
||||||
Commenting out the tie into the BeginRequest event fixed the issue, so it had to be something there. SetupOutputFilter looked like this:
|
Commenting out the tie into the BeginRequest event fixed the issue, so it had to be something there. SetupOutputFilter looked like this:
|
||||||
|
|
||||||
{% highlight csharp %}
|
```csharp
|
||||||
private void SetupOutputFilter(object sender, EventArgs e)
|
private void SetupOutputFilter(object sender, EventArgs e)
|
||||||
{
|
{
|
||||||
if (!IsPdfRequest)
|
if (!IsPdfRequest)
|
||||||
@@ -48,11 +48,11 @@ private void SetupOutputFilter(object sender, EventArgs e)
|
|||||||
_pdfStream = new PdfMemoryStream(response.Filter);
|
_pdfStream = new PdfMemoryStream(response.Filter);
|
||||||
response.Filter = _pdfStream;
|
response.Filter = _pdfStream;
|
||||||
}
|
}
|
||||||
{% endhighlight %}
|
```
|
||||||
|
|
||||||
Commenting out the if expression also fixed the issue. My IsPdfRequest property looked like this:
|
Commenting out the if expression also fixed the issue. My IsPdfRequest property looked like this:
|
||||||
|
|
||||||
{% highlight csharp %}
|
```csharp
|
||||||
private static bool IsPdfRequest
|
private static bool IsPdfRequest
|
||||||
{
|
{
|
||||||
get
|
get
|
||||||
@@ -61,7 +61,7 @@ private static bool IsPdfRequest
|
|||||||
&& HttpContext.Current.Request["as"] == "pdf";
|
&& HttpContext.Current.Request["as"] == "pdf";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
{% endhighlight %}
|
```
|
||||||
|
|
||||||
And there you have my access to the HttpContext object. I kind of needed this though, so I thought it might be a timing issue. Maybe accessing this on BeginRequest was the problem. I took a look at the ordering of the HttpModule events to find a good replacement. This StackOverflow thread helped me there:
|
And there you have my access to the HttpContext object. I kind of needed this though, so I thought it might be a timing issue. Maybe accessing this on BeginRequest was the problem. I took a look at the ordering of the HttpModule events to find a good replacement. This StackOverflow thread helped me there:
|
||||||
|
|
||||||
|
|||||||
@@ -8,10 +8,10 @@ tags: [sharepoint]
|
|||||||
---
|
---
|
||||||
I ran across an issue today in SharePoint 2010 where the Page Layout button would not open. It would throw the following error in Internet Explorer:
|
I ran across an issue today in SharePoint 2010 where the Page Layout button would not open. It would throw the following error in Internet Explorer:
|
||||||
|
|
||||||
{% highlight javascript %}
|
```javascript %}
|
||||||
SCRIPT5007: Unable to get property ‘nodeName’ of undefined or null reference
|
SCRIPT5007: Unable to get property ‘nodeName’ of undefined or null reference
|
||||||
cui.js, line 2 character 6422
|
cui.js, line 2 character 6422
|
||||||
{% endhighlight %}
|
```
|
||||||
|
|
||||||
The following link gave me the answer.
|
The following link gave me the answer.
|
||||||
|
|
||||||
|
|||||||
@@ -9,6 +9,6 @@ tags: []
|
|||||||
|
|
||||||
Here it is, folks: some of the dumbest code I’ve ever written and I just now noticed it.
|
Here it is, folks: some of the dumbest code I’ve ever written and I just now noticed it.
|
||||||
|
|
||||||
{% highlight csharp %}
|
```csharp
|
||||||
string.Concat("attachment; filename=\"", string.Concat(pagePdf.Name, ".pdf"), "\"");
|
string.Concat("attachment; filename=\"", string.Concat(pagePdf.Name, ".pdf"), "\"");
|
||||||
{% endhighlight %}
|
```
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ I ran into the following error while working with an installation of EPiServer 7
|
|||||||
|
|
||||||
First, here’s the error and stack trace:
|
First, here’s the error and stack trace:
|
||||||
|
|
||||||
{% highlight text %}
|
```text
|
||||||
2014-03-27 12:12:43,396 [6] ERROR EPiServer.Global: 1.2.5 Unhandled exception in ASP.NET
|
2014-03-27 12:12:43,396 [6] ERROR EPiServer.Global: 1.2.5 Unhandled exception in ASP.NET
|
||||||
System.Web.HttpException (0x80004005): The file ‘/link/cc299342a697494c8a4bc47717210bf0.aspx’ does not exist.
|
System.Web.HttpException (0x80004005): The file ‘/link/cc299342a697494c8a4bc47717210bf0.aspx’ does not exist.
|
||||||
at System.Web.Compilation.BuildManager.GetVPathBuildResultInternal(VirtualPath virtualPath, Boolean noBuild, Boolean allowCrossApp, Boolean allowBuildInPrecompile, Boolean throwIfNotFound, Boolean ensureIsUpToDate)
|
at System.Web.Compilation.BuildManager.GetVPathBuildResultInternal(VirtualPath virtualPath, Boolean noBuild, Boolean allowCrossApp, Boolean allowBuildInPrecompile, Boolean throwIfNotFound, Boolean ensureIsUpToDate)
|
||||||
@@ -22,11 +22,11 @@ at System.Web.Routing.PageRouteHandler.GetHttpHandler(RequestContext requestCont
|
|||||||
at System.Web.Routing.UrlRoutingModule.PostResolveRequestCache(HttpContextBase context)
|
at System.Web.Routing.UrlRoutingModule.PostResolveRequestCache(HttpContextBase context)
|
||||||
at System.Web.HttpApplication.SyncEventExecutionStep.System.Web.HttpApplication.IExecutionStep.Execute()
|
at System.Web.HttpApplication.SyncEventExecutionStep.System.Web.HttpApplication.IExecutionStep.Execute()
|
||||||
at System.Web.HttpApplication.ExecuteStep(IExecutionStep step, Boolean& completedSynchronously)
|
at System.Web.HttpApplication.ExecuteStep(IExecutionStep step, Boolean& completedSynchronously)
|
||||||
{% endhighlight %}
|
```
|
||||||
|
|
||||||
As it turns out, I did not have a TemplateDescriptor attribute on the page template that I was trying to access. Adding the following TemplateDescriptor attribute to the code-behind class for my page template saved me.
|
As it turns out, I did not have a TemplateDescriptor attribute on the page template that I was trying to access. Adding the following TemplateDescriptor attribute to the code-behind class for my page template saved me.
|
||||||
|
|
||||||
{% highlight csharp %}
|
```csharp
|
||||||
[TemplateDescriptor(Path = "~/Templates/PageTemplates/Home.aspx")]
|
[TemplateDescriptor(Path = "~/Templates/PageTemplates/Home.aspx")]
|
||||||
public partial class Home : TemplatePage<HomePage>
|
public partial class Home : TemplatePage<HomePage>
|
||||||
{% endhighlight %}
|
```
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ tags: [episerver]
|
|||||||
## Problem
|
## Problem
|
||||||
I’ve been dealing with an issue in EPiServer 7 recently. I got this error when trying to log into the admin back-end of the site.
|
I’ve been dealing with an issue in EPiServer 7 recently. I got this error when trying to log into the admin back-end of the site.
|
||||||
|
|
||||||
{% highlight text %}
|
```text
|
||||||
Cannot decrypt password
|
Cannot decrypt password
|
||||||
|
|
||||||
Description: An unhandled exception occurred during the execution of the current web request. Please review the stack trace for more information about the error and where it originated in the code.
|
Description: An unhandled exception occurred during the execution of the current web request. Please review the stack trace for more information about the error and where it originated in the code.
|
||||||
@@ -28,7 +28,7 @@ EPiServer.Common.Security.HMACPasswordProvider.DecryptPassword(Byte[] ciphertext
|
|||||||
EPiServer.Common.Web.Authorization.Integrator.SynchronizeUser(MembershipUser membershipUser, String password, Boolean enableCreateNew) +1116
|
EPiServer.Common.Web.Authorization.Integrator.SynchronizeUser(MembershipUser membershipUser, String password, Boolean enableCreateNew) +1116
|
||||||
System.Web.SyncEventExecutionStep.System.Web.HttpApplication.IExecutionStep.Execute() +80
|
System.Web.SyncEventExecutionStep.System.Web.HttpApplication.IExecutionStep.Execute() +80
|
||||||
System.Web.HttpApplication.ExecuteStep(IExecutionStep step, Boolean& completedSynchronously) +165
|
System.Web.HttpApplication.ExecuteStep(IExecutionStep step, Boolean& completedSynchronously) +165
|
||||||
{% endhighlight %}
|
```
|
||||||
|
|
||||||
## Scenario
|
## Scenario
|
||||||
We originally installed the Relate+ site for our EPiServer installation. Through various bad decisions, we didn’t keep the custom code and templates we wrote separate from the Relate+ stuff. So, we inevitably created a mess where we did not need a ton of the Relate+ templates, page types, block types, etc but we could not longer easily pull our custom stuff out from the tangled mess.
|
We originally installed the Relate+ site for our EPiServer installation. Through various bad decisions, we didn’t keep the custom code and templates we wrote separate from the Relate+ stuff. So, we inevitably created a mess where we did not need a ton of the Relate+ templates, page types, block types, etc but we could not longer easily pull our custom stuff out from the tangled mess.
|
||||||
|
|||||||
@@ -45,15 +45,15 @@ Using Sitefinity Thunder, create your widget. Don't create the designer at this
|
|||||||
##### Step 1.1: Update controller properties
|
##### Step 1.1: Update controller properties
|
||||||
Remove whatever properties Thunder automatically adds on the MVC controller and add your own List property of type Guid.
|
Remove whatever properties Thunder automatically adds on the MVC controller and add your own List property of type Guid.
|
||||||
|
|
||||||
{% highlight csharp %}
|
```csharp
|
||||||
[Category("Widget Properties")]
|
[Category("Widget Properties")]
|
||||||
public Guid List { get; set; }
|
public Guid List { get; set; }
|
||||||
{% endhighlight %}
|
```
|
||||||
|
|
||||||
##### Step 1.2: Update widget view model
|
##### Step 1.2: Update widget view model
|
||||||
I added a simple POCO to describe each FAQ item (containing a question and answer) and then added a list of these POCOs as the only property on my view model.
|
I added a simple POCO to describe each FAQ item (containing a question and answer) and then added a list of these POCOs as the only property on my view model.
|
||||||
|
|
||||||
{% highlight csharp %}
|
```csharp
|
||||||
public class FAQListModel
|
public class FAQListModel
|
||||||
{
|
{
|
||||||
public List<FAQItem> Items { get; set; }
|
public List<FAQItem> Items { get; set; }
|
||||||
@@ -64,12 +64,12 @@ public class FAQItem
|
|||||||
public string Answer { get; set; }
|
public string Answer { get; set; }
|
||||||
public string Question { get; set; }
|
public string Question { get; set; }
|
||||||
}
|
}
|
||||||
{% endhighlight %}
|
```
|
||||||
|
|
||||||
##### Step 1.3: Update Index action
|
##### Step 1.3: Update Index action
|
||||||
Update the Index action to use the List property to access the content-author-chosen list, pull the items and stick them in the model.
|
Update the Index action to use the List property to access the content-author-chosen list, pull the items and stick them in the model.
|
||||||
|
|
||||||
{% highlight csharp %}
|
```csharp
|
||||||
public ActionResult Index()
|
public ActionResult Index()
|
||||||
{
|
{
|
||||||
var model = new FAQListModel();
|
var model = new FAQListModel();
|
||||||
@@ -91,7 +91,7 @@ public ActionResult Index()
|
|||||||
|
|
||||||
return View("Default", model);
|
return View("Default", model);
|
||||||
}
|
}
|
||||||
{% endhighlight %}
|
```
|
||||||
|
|
||||||
#### Step 2: Create widget designer/public
|
#### Step 2: Create widget designer/public
|
||||||
Once your widget is created, you can create your widget designer. Using Thunder, create a widget designer for an existing widget. Name your widget designer. I use the name of the widget and append "Designer".
|
Once your widget is created, you can create your widget designer. Using Thunder, create a widget designer for an existing widget. Name your widget designer. I use the name of the widget and append "Designer".
|
||||||
|
|||||||
@@ -11,10 +11,10 @@ tags: [sitefinity]
|
|||||||
|
|
||||||
When you use Sitefinity Thunder (a great help, by the way) to generate a Widget Designer for an existing widget that has a boolean property, you get this javascript in the auto-generated javascript file (let’s say my boolean field is named HasSubMenu):
|
When you use Sitefinity Thunder (a great help, by the way) to generate a Widget Designer for an existing widget that has a boolean property, you get this javascript in the auto-generated javascript file (let’s say my boolean field is named HasSubMenu):
|
||||||
|
|
||||||
{% highlight javascript %}
|
```javascript %}
|
||||||
/* RefreshUI HasSubMenu */
|
/* RefreshUI HasSubMenu */
|
||||||
jQuery(this.get_hasSubMenu()).attr("checked", controlData.HasSubMenu);
|
jQuery(this.get_hasSubMenu()).attr("checked", controlData.HasSubMenu);
|
||||||
{% endhighlight %}
|
```
|
||||||
|
|
||||||
This code is in the refreshUI method. It’s supposed to mark a checkbox as checked if HasSubMenu is true and not check it if it’s false. The problem is, for some reason, HasSubMenu is a string in Javascript instead of a real boolean. So, HasSubMenu will be either “true” or “false”–both of which are true values in Javascript. That is, because “true” and “false” are both non-empty strings, they evaluate to true when used as an expression.
|
This code is in the refreshUI method. It’s supposed to mark a checkbox as checked if HasSubMenu is true and not check it if it’s false. The problem is, for some reason, HasSubMenu is a string in Javascript instead of a real boolean. So, HasSubMenu will be either “true” or “false”–both of which are true values in Javascript. That is, because “true” and “false” are both non-empty strings, they evaluate to true when used as an expression.
|
||||||
|
|
||||||
@@ -24,9 +24,9 @@ This means, no matter what you do with the checkbox, the next time you click “
|
|||||||
|
|
||||||
The solution is pretty simple. Instead of using controlData.HasSubMenu for the expression by itself, update your Javascript so that you check if the string is “true” or not: controlData.HasSubMenu === "true". The line in the refreshUI method should now look like this:
|
The solution is pretty simple. Instead of using controlData.HasSubMenu for the expression by itself, update your Javascript so that you check if the string is “true” or not: controlData.HasSubMenu === "true". The line in the refreshUI method should now look like this:
|
||||||
|
|
||||||
{% highlight javascript %}
|
```javascript %}
|
||||||
/* RefreshUI HasSubMenu */
|
/* RefreshUI HasSubMenu */
|
||||||
jQuery(this.get_hasSubMenu()).attr("checked", controlData.HasSubMenu === "true");
|
jQuery(this.get_hasSubMenu()).attr("checked", controlData.HasSubMenu === "true");
|
||||||
{% endhighlight %}
|
```
|
||||||
|
|
||||||
Now your checkbox will keep it’s value as you would expect.
|
Now your checkbox will keep it’s value as you would expect.
|
||||||
|
|||||||
@@ -11,20 +11,20 @@ I noticed a goofy issue today while working with a jQuery countdown plugin. Thi
|
|||||||
|
|
||||||
I was setting the date like this:
|
I was setting the date like this:
|
||||||
|
|
||||||
{% highlight javascript %}
|
```javascript %}
|
||||||
element.countdown({
|
element.countdown({
|
||||||
until: new Date(’10/13/14 00:00:00′)
|
until: new Date(’10/13/14 00:00:00′)
|
||||||
});
|
});
|
||||||
{% endhighlight %}
|
```
|
||||||
|
|
||||||
In Chrome, this worked just fine. The string “10/13/14 00:00:00″ parsed to a Date object for midnight on October 13th, 2014.
|
In Chrome, this worked just fine. The string “10/13/14 00:00:00″ parsed to a Date object for midnight on October 13th, 2014.
|
||||||
|
|
||||||
In IE, however (versions10, 9 and 8 at least) it parsed as October 13th, 1914. Why? Who knows. Updating my string to “10/13/2014 00:00:00″ fixed it. My final javascript looked like this:
|
In IE, however (versions10, 9 and 8 at least) it parsed as October 13th, 1914. Why? Who knows. Updating my string to “10/13/2014 00:00:00″ fixed it. My final javascript looked like this:
|
||||||
|
|
||||||
{% highlight javascript %}
|
```javascript %}
|
||||||
element.countdown({
|
element.countdown({
|
||||||
until: new Date(’10/13/2014 00:00:00′)
|
until: new Date(’10/13/2014 00:00:00′)
|
||||||
});
|
});
|
||||||
{% endhighlight %}
|
```
|
||||||
|
|
||||||
So, be aware of this oddity! It caused my whole countdown widget to screw up because the current time was already past the “until” date.#
|
So, be aware of this oddity! It caused my whole countdown widget to screw up because the current time was already past the “until” date.#
|
||||||
|
|||||||
@@ -22,7 +22,7 @@ class to use as your membership provider. Putting an `[Authorize]` attribute on
|
|||||||
|
|
||||||
Here's my relevant web.config setup:
|
Here's my relevant web.config setup:
|
||||||
|
|
||||||
{% highlight xml %}
|
```xml
|
||||||
<connectionStrings>
|
<connectionStrings>
|
||||||
<add name="ADConnectionString" connectionString="<ldap connection string here>" />
|
<add name="ADConnectionString" connectionString="<ldap connection string here>" />
|
||||||
</connectionStrings>
|
</connectionStrings>
|
||||||
@@ -39,15 +39,15 @@ Here's my relevant web.config setup:
|
|||||||
attributeMapUsername="sAMAccountName"/>
|
attributeMapUsername="sAMAccountName"/>
|
||||||
</providers>
|
</providers>
|
||||||
</membership>
|
</membership>
|
||||||
{% endhighlight %}
|
```
|
||||||
|
|
||||||
The tough part came when I wanted to limit access to users in that AD group. Microsoft doesn't provide a RoleProvider along with its ActiveDirectoryMembershipProvider. So, what to do?
|
The tough part came when I wanted to limit access to users in that AD group. Microsoft doesn't provide a RoleProvider along with its ActiveDirectoryMembershipProvider. So, what to do?
|
||||||
|
|
||||||
I tried several methods I found online. Most of them were based on creating my own custom RoleProvider and querying AD to iterate through the user's groups (treating them like roles) and seeing if one of them matched my AD group I was looking for. However, I could never get it to work. Each code example I found eventually gave me this AD error when I iterated through the current user's AD groups:
|
I tried several methods I found online. Most of them were based on creating my own custom RoleProvider and querying AD to iterate through the user's groups (treating them like roles) and seeing if one of them matched my AD group I was looking for. However, I could never get it to work. Each code example I found eventually gave me this AD error when I iterated through the current user's AD groups:
|
||||||
|
|
||||||
{% highlight text %}
|
```text
|
||||||
The specified directory service attribute or value does not exist.
|
The specified directory service attribute or value does not exist.
|
||||||
{% endhighlight %}
|
```
|
||||||
|
|
||||||
## The Solution
|
## The Solution
|
||||||
|
|
||||||
@@ -55,7 +55,7 @@ Eventually, I found a solution online that worked. Instead of setting up a custo
|
|||||||
|
|
||||||
Here is my custom AuthorizeAttribute:
|
Here is my custom AuthorizeAttribute:
|
||||||
|
|
||||||
{% highlight c# %}
|
```csharp %}
|
||||||
public class AuthorizeADAttribute : AuthorizeAttribute
|
public class AuthorizeADAttribute : AuthorizeAttribute
|
||||||
{
|
{
|
||||||
private bool _authenticated;
|
private bool _authenticated;
|
||||||
@@ -105,13 +105,13 @@ public class AuthorizeADAttribute : AuthorizeAttribute
|
|||||||
return _authorized;
|
return _authorized;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
{% endhighlight %}
|
```
|
||||||
|
|
||||||
Notice that I also included a little code to distinguish between the user not being authenticated (which the call to base.AuthorizeCore takes care of) and not being authorized. Without the code in HandleUnauthorizedRequest, if the user successfully logs in but is not in the AD group, he just sees the log in screen again which doesn't communicate the problem very well.
|
Notice that I also included a little code to distinguish between the user not being authenticated (which the call to base.AuthorizeCore takes care of) and not being authorized. Without the code in HandleUnauthorizedRequest, if the user successfully logs in but is not in the AD group, he just sees the log in screen again which doesn't communicate the problem very well.
|
||||||
|
|
||||||
The this.Log() code uses a Nuget package called this.Log. The LDAPHelper class is something I wrote. The code is below:
|
The this.Log() code uses a Nuget package called this.Log. The LDAPHelper class is something I wrote. The code is below:
|
||||||
|
|
||||||
{% highlight c# %}
|
```csharp %}
|
||||||
public static class LDAPHelper
|
public static class LDAPHelper
|
||||||
{
|
{
|
||||||
public static string GetLDAPContainer()
|
public static string GetLDAPContainer()
|
||||||
@@ -171,18 +171,18 @@ public static class LDAPHelper
|
|||||||
return new PrincipalContext(ContextType.Domain, null, container);
|
return new PrincipalContext(ContextType.Domain, null, container);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
{% endhighlight %}
|
```
|
||||||
|
|
||||||
My code is mostly based on example code I found on a very helpful [StackOverflow post][so-post].
|
My code is mostly based on example code I found on a very helpful [StackOverflow post][so-post].
|
||||||
|
|
||||||
To use this code, all you have to do is use your custom AuthorizeAttribute instead of the built-in one. Something like this:
|
To use this code, all you have to do is use your custom AuthorizeAttribute instead of the built-in one. Something like this:
|
||||||
|
|
||||||
{% highlight c# %}
|
```csharp %}
|
||||||
[AuthorizeAD(Groups="Some AD group name")]
|
[AuthorizeAD(Groups="Some AD group name")]
|
||||||
public class HomeController : Controller
|
public class HomeController : Controller
|
||||||
{
|
{
|
||||||
…
|
…
|
||||||
}
|
}
|
||||||
{% endhighlight %}
|
```
|
||||||
|
|
||||||
[so-post]: http://stackoverflow.com/questions/4342271/asp-net-mvc-forms-authorization-with-active-directory-groups/4383502#4383502
|
[so-post]: http://stackoverflow.com/questions/4342271/asp-net-mvc-forms-authorization-with-active-directory-groups/4383502#4383502
|
||||||
|
|||||||
@@ -13,13 +13,13 @@ the RabbitMQ Windows service and the logs still showed that the configuration fi
|
|||||||
If you're experiencing the same problem, you'll see a similar error message in the RabbitMQ log file after
|
If you're experiencing the same problem, you'll see a similar error message in the RabbitMQ log file after
|
||||||
you restart the Windows service.
|
you restart the Windows service.
|
||||||
|
|
||||||
{% highlight text %}
|
```text
|
||||||
=INFO REPORT==== <date here>
|
=INFO REPORT==== <date here>
|
||||||
node : rabbit@<server>
|
node : rabbit@<server>
|
||||||
home dir : C:\Windows
|
home dir : C:\Windows
|
||||||
config file(s) : c:/path/to/config/rabbitmq.config (not found)
|
config file(s) : c:/path/to/config/rabbitmq.config (not found)
|
||||||
...some other stuff...
|
...some other stuff...
|
||||||
{% endhighlight %}
|
```
|
||||||
|
|
||||||
This will happen if you just install your RabbitMQ server with the normal, default installation process
|
This will happen if you just install your RabbitMQ server with the normal, default installation process
|
||||||
and then try to add a configuration file later. By default, RabbitMQ doesn't install configuration file and just
|
and then try to add a configuration file later. By default, RabbitMQ doesn't install configuration file and just
|
||||||
@@ -33,11 +33,11 @@ or removing a configuration file."
|
|||||||
Oops. Guess I should have read the documentation more closely the first time around! The easiest way to
|
Oops. Guess I should have read the documentation more closely the first time around! The easiest way to
|
||||||
do this is as follows (start a command prompt as administrator):
|
do this is as follows (start a command prompt as administrator):
|
||||||
|
|
||||||
{% highlight text %}
|
```text
|
||||||
> cd "C:\Program Files (x86)\RabbitMQ Server\rabbitmq_server-3.6.0\sbin"
|
> cd "C:\Program Files (x86)\RabbitMQ Server\rabbitmq_server-3.6.0\sbin"
|
||||||
> .\rabbitmq-service.bat remove
|
> .\rabbitmq-service.bat remove
|
||||||
> .\rabbitmq-service.bat install
|
> .\rabbitmq-service.bat install
|
||||||
> .\rabbitmq-service.bat start
|
> .\rabbitmq-service.bat start
|
||||||
{% endhighlight %}
|
```
|
||||||
|
|
||||||
Of course, The path you cd into will depend on the version of RabbitMQ you have installed.
|
Of course, The path you cd into will depend on the version of RabbitMQ you have installed.
|
||||||
|
|||||||
@@ -23,7 +23,7 @@ implemented `IHandleMessages<IEvent>` should be all I should have needed!
|
|||||||
|
|
||||||
I was testing this with only partial success and so I thought it was working.
|
I was testing this with only partial success and so I thought it was working.
|
||||||
|
|
||||||
{% highlight csharp %}
|
```csharp
|
||||||
public class EventHandler : IHandleMessages<IEvent>
|
public class EventHandler : IHandleMessages<IEvent>
|
||||||
{
|
{
|
||||||
public void Handle(IEvent message)
|
public void Handle(IEvent message)
|
||||||
@@ -31,7 +31,7 @@ public class EventHandler : IHandleMessages<IEvent>
|
|||||||
...
|
...
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
{% endhighlight %}
|
```
|
||||||
|
|
||||||
## My Partial Success
|
## My Partial Success
|
||||||
The reason I had partial success was because I was also *explicitly* handling
|
The reason I had partial success was because I was also *explicitly* handling
|
||||||
@@ -63,16 +63,16 @@ system produces.
|
|||||||
|
|
||||||
So, I simply created a marker interface called `ICustomEvent`.
|
So, I simply created a marker interface called `ICustomEvent`.
|
||||||
|
|
||||||
{% highlight csharp %}
|
```csharp
|
||||||
public interface ICustomEvent : IEvent { }
|
public interface ICustomEvent : IEvent { }
|
||||||
{% endhighlight %}
|
```
|
||||||
|
|
||||||
Then, on all of my bus event classes, instead of implementing `IEvent` directly,
|
Then, on all of my bus event classes, instead of implementing `IEvent` directly,
|
||||||
I implemented `ICustomEvent`.
|
I implemented `ICustomEvent`.
|
||||||
|
|
||||||
My handler then looked like this.
|
My handler then looked like this.
|
||||||
|
|
||||||
{% highlight csharp %}
|
```csharp
|
||||||
public class EventHandler : IHandleMessages<ICustomEvent>
|
public class EventHandler : IHandleMessages<ICustomEvent>
|
||||||
{
|
{
|
||||||
public void Handle(ICustomEvent message)
|
public void Handle(ICustomEvent message)
|
||||||
@@ -80,7 +80,7 @@ public class EventHandler : IHandleMessages<ICustomEvent>
|
|||||||
...
|
...
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
{% endhighlight %}
|
```
|
||||||
|
|
||||||
Now, the right subscriptions are all set up and my single handler gets
|
Now, the right subscriptions are all set up and my single handler gets
|
||||||
every single event published by my entire bus.
|
every single event published by my entire bus.
|
||||||
|
|||||||
@@ -18,9 +18,9 @@ far behind!
|
|||||||
So, I updated my NuGet package to the latest version of GitVersionTask. That's
|
So, I updated my NuGet package to the latest version of GitVersionTask. That's
|
||||||
3.4.1 right now. Too bad, so sad! That caused this error:
|
3.4.1 right now. Too bad, so sad! That caused this error:
|
||||||
|
|
||||||
{% highlight xml %}
|
```xml
|
||||||
The "CreatePackages" task was not given a value for the required parameter "Version". 1
|
The "CreatePackages" task was not given a value for the required parameter "Version". 1
|
||||||
{% endhighlight %}
|
```
|
||||||
|
|
||||||
## The Problem
|
## The Problem
|
||||||
The problem turns out that NuGetPackager 0.5.5 is looking for a MSBuild property
|
The problem turns out that NuGetPackager 0.5.5 is looking for a MSBuild property
|
||||||
@@ -37,19 +37,19 @@ CreatePackage task is called in NuGetPackager.
|
|||||||
Open up your .csproj file directly in some text editor like Notepad++. Find
|
Open up your .csproj file directly in some text editor like Notepad++. Find
|
||||||
these two lines at the bottom of the file:
|
these two lines at the bottom of the file:
|
||||||
|
|
||||||
{% highlight xml %}
|
```xml
|
||||||
<Import Project="..\packages\GitVersionTask.3.4.1\Build\dotnet\GitVersionTask.targets"
|
<Import Project="..\packages\GitVersionTask.3.4.1\Build\dotnet\GitVersionTask.targets"
|
||||||
Condition="Exists('..\packages\GitVersionTask.3.4.1\Build\dotnet\GitVersionTask.targets')" />
|
Condition="Exists('..\packages\GitVersionTask.3.4.1\Build\dotnet\GitVersionTask.targets')" />
|
||||||
<Import Project="..\packages\NuGetPackager.0.5.5\build\NuGetPackager.targets"
|
<Import Project="..\packages\NuGetPackager.0.5.5\build\NuGetPackager.targets"
|
||||||
Condition="Exists('..\packages\NuGetPackager.0.5.5\build\NuGetPackager.targets')" />
|
Condition="Exists('..\packages\NuGetPackager.0.5.5\build\NuGetPackager.targets')" />
|
||||||
{% endhighlight %}
|
```
|
||||||
|
|
||||||
Before those two lines, add a target and a property group. The target will
|
Before those two lines, add a target and a property group. The target will
|
||||||
create a task that translates the new property to the new property. The
|
create a task that translates the new property to the new property. The
|
||||||
property group wil define a BuildDependsOn to call that target before the
|
property group wil define a BuildDependsOn to call that target before the
|
||||||
CreatePackages target is called.
|
CreatePackages target is called.
|
||||||
|
|
||||||
{% highlight xml %}
|
```xml
|
||||||
<Target Name="TranslateNugetVersion"
|
<Target Name="TranslateNugetVersion"
|
||||||
Condition="'$(Configuration)' == 'Release'">
|
Condition="'$(Configuration)' == 'Release'">
|
||||||
<CreateProperty
|
<CreateProperty
|
||||||
@@ -69,6 +69,6 @@ CreatePackages target is called.
|
|||||||
Condition="Exists('..\packages\GitVersionTask.3.4.1\Build\dotnet\GitVersionTask.targets')" />
|
Condition="Exists('..\packages\GitVersionTask.3.4.1\Build\dotnet\GitVersionTask.targets')" />
|
||||||
<Import Project="..\packages\NuGetPackager.0.5.5\build\NuGetPackager.targets"
|
<Import Project="..\packages\NuGetPackager.0.5.5\build\NuGetPackager.targets"
|
||||||
Condition="Exists('..\packages\NuGetPackager.0.5.5\build\NuGetPackager.targets')" />
|
Condition="Exists('..\packages\NuGetPackager.0.5.5\build\NuGetPackager.targets')" />
|
||||||
{% endhighlight %}
|
```
|
||||||
|
|
||||||
Now, when you build your project, everything should work great!
|
Now, when you build your project, everything should work great!
|
||||||
|
|||||||
@@ -47,7 +47,7 @@ use to encrypt a `ClaimsIdentity` into a valid bearer token.
|
|||||||
|
|
||||||
Update your server creation code like this:
|
Update your server creation code like this:
|
||||||
|
|
||||||
{% highlight csharp %}
|
```csharp
|
||||||
var dataProtector;
|
var dataProtector;
|
||||||
var server = TestServer.Create(app =>
|
var server = TestServer.Create(app =>
|
||||||
{
|
{
|
||||||
@@ -58,7 +58,7 @@ Update your server creation code like this:
|
|||||||
typeof(OAuthBearerAuthenticationMiddleware).Namespace,
|
typeof(OAuthBearerAuthenticationMiddleware).Namespace,
|
||||||
"Access_Token", "v1");
|
"Access_Token", "v1");
|
||||||
});
|
});
|
||||||
{% endhighlight %}
|
```
|
||||||
|
|
||||||
You have to get the `dataProtector` this way because it's the same way that
|
You have to get the `dataProtector` this way because it's the same way that
|
||||||
the OWIN OAuth libraries get it before trying to decrypt the bearer token.
|
the OWIN OAuth libraries get it before trying to decrypt the bearer token.
|
||||||
@@ -69,7 +69,7 @@ Once you've "captured" the `dataProtector`, in your integration test (or in some
|
|||||||
helper code, probably) you can generate your `ClaimsIdentity` and then your
|
helper code, probably) you can generate your `ClaimsIdentity` and then your
|
||||||
bearer token with the `dataProtector`.
|
bearer token with the `dataProtector`.
|
||||||
|
|
||||||
{% highlight csharp %}
|
```csharp
|
||||||
// inside an integration test
|
// inside an integration test
|
||||||
using (server)
|
using (server)
|
||||||
{
|
{
|
||||||
@@ -89,4 +89,4 @@ bearer token with the `dataProtector`.
|
|||||||
.GetAsync()
|
.GetAsync()
|
||||||
.Result;
|
.Result;
|
||||||
}
|
}
|
||||||
{% endhighlight %}
|
```
|
||||||
|
|||||||
@@ -18,7 +18,7 @@ Here's what the problem looked like.
|
|||||||
### The Error Message
|
### The Error Message
|
||||||
First things first, this is what the symptom looked like:
|
First things first, this is what the symptom looked like:
|
||||||
|
|
||||||
{% highlight text %}
|
```text
|
||||||
[ComponentNotFoundException: No component for supporting the service System.Threading.Tasks.Task was found]
|
[ComponentNotFoundException: No component for supporting the service System.Threading.Tasks.Task was found]
|
||||||
Castle.MicroKernel.DefaultKernel.Castle.MicroKernel.IKernelInternal.Resolve(Type service, IDictionary arguments, IReleasePolicy policy) +120
|
Castle.MicroKernel.DefaultKernel.Castle.MicroKernel.IKernelInternal.Resolve(Type service, IDictionary arguments, IReleasePolicy policy) +120
|
||||||
Castle.Facilities.TypedFactory.Internal.TypedFactoryInterceptor.Resolve(IInvocation invocation) +147
|
Castle.Facilities.TypedFactory.Internal.TypedFactoryInterceptor.Resolve(IInvocation invocation) +147
|
||||||
@@ -27,7 +27,7 @@ First things first, this is what the symptom looked like:
|
|||||||
Castle.Proxies.Func`2Proxy.Invoke(OAuthMatchEndpointContext arg) +168
|
Castle.Proxies.Func`2Proxy.Invoke(OAuthMatchEndpointContext arg) +168
|
||||||
|
|
||||||
...
|
...
|
||||||
{% endhighlight %}
|
```
|
||||||
|
|
||||||
This is actually a pretty standard error when you're using Castle.Windsor. It usually
|
This is actually a pretty standard error when you're using Castle.Windsor. It usually
|
||||||
means Windsor tried to resolve some dependency but discovered that it was missing a
|
means Windsor tried to resolve some dependency but discovered that it was missing a
|
||||||
@@ -46,7 +46,7 @@ was that it had nothing to do with any TypedFactoryFacility.
|
|||||||
|
|
||||||
So, I inspected my registrations a little closer. I basically had a setup like this.
|
So, I inspected my registrations a little closer. I basically had a setup like this.
|
||||||
|
|
||||||
{% highlight csharp %}
|
```csharp
|
||||||
public class ChildClass : ParentClass
|
public class ChildClass : ParentClass
|
||||||
{
|
{
|
||||||
public override Task DoSomething(Options options)
|
public override Task DoSomething(Options options)
|
||||||
@@ -70,7 +70,7 @@ public class ParentClass : IInterface
|
|||||||
return OnDoSomethingElse.Invoke(options);
|
return OnDoSomethingElse.Invoke(options);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
{% endhighlight %}
|
```
|
||||||
|
|
||||||
Notice a couple of things.
|
Notice a couple of things.
|
||||||
1. ParentClass has several virtual methods. I'm only overriding one.
|
1. ParentClass has several virtual methods. I'm only overriding one.
|
||||||
@@ -114,13 +114,13 @@ Now, I hate property injection, so I was fine with just turning it off completel
|
|||||||
the documentation link above also included instructions how to do it. I've included that code
|
the documentation link above also included instructions how to do it. I've included that code
|
||||||
here.
|
here.
|
||||||
|
|
||||||
{% highlight csharp %}
|
```csharp
|
||||||
var propInjector = Kernel.ComponentModelBuilder
|
var propInjector = Kernel.ComponentModelBuilder
|
||||||
.Contributors
|
.Contributors
|
||||||
.OfType<PropertiesDependenciesModelInspector>()
|
.OfType<PropertiesDependenciesModelInspector>()
|
||||||
.Single();
|
.Single();
|
||||||
Kernel.ComponentModelBuilder.RemoveContributor(propInjector);
|
Kernel.ComponentModelBuilder.RemoveContributor(propInjector);
|
||||||
{% endhighlight %}
|
```
|
||||||
|
|
||||||
I added this code into my Windsor container setup and tried again. Everything worked just
|
I added this code into my Windsor container setup and tried again. Everything worked just
|
||||||
as expected.
|
as expected.
|
||||||
|
|||||||
@@ -30,7 +30,7 @@ an `IEnumerable` in C# like an array or a `List<T>`.
|
|||||||
The iterators are implemented as extension methods on the `ICatalogSystem` interface. So, iterating through all
|
The iterators are implemented as extension methods on the `ICatalogSystem` interface. So, iterating through all
|
||||||
of the catalog entry MetaObjects looks like this.
|
of the catalog entry MetaObjects looks like this.
|
||||||
|
|
||||||
{% highlight csharp %}
|
```csharp
|
||||||
using Mediachase.Commerce.Catalog;
|
using Mediachase.Commerce.Catalog;
|
||||||
using Mediachase.MetaDataPlus;
|
using Mediachase.MetaDataPlus;
|
||||||
|
|
||||||
@@ -42,7 +42,7 @@ foreach ((MetaObject metaObject, MetaDataContext metaDataContext) entryMO in cat
|
|||||||
entryMO.metaObject["SomeCustomMetaFieldName"] = "external data source value here";
|
entryMO.metaObject["SomeCustomMetaFieldName"] = "external data source value here";
|
||||||
entryMO.metaObject.AcceptChanges(entryMO.metaDataContext);
|
entryMO.metaObject.AcceptChanges(entryMO.metaDataContext);
|
||||||
}
|
}
|
||||||
{% endhighlight %}
|
```
|
||||||
|
|
||||||
I implemented the separate iterators for NodeContent MetaObjects and the EntryContent
|
I implemented the separate iterators for NodeContent MetaObjects and the EntryContent
|
||||||
MetaObjects. You easily use them in combination to iterate through the MetaObjects of the entire catalog tree.
|
MetaObjects. You easily use them in combination to iterate through the MetaObjects of the entire catalog tree.
|
||||||
@@ -58,7 +58,7 @@ The extension method code for AllCatalogSystemEntryMetaObjects is pretty simple.
|
|||||||
1. All entries
|
1. All entries
|
||||||
1. All meta objects
|
1. All meta objects
|
||||||
|
|
||||||
{% highlight csharp %}
|
```csharp
|
||||||
public static IEnumerable<(MetaObject metaObject, MetaDataContext metaDataContext)> AllCatalogSystemEntryMetaObjects(
|
public static IEnumerable<(MetaObject metaObject, MetaDataContext metaDataContext)> AllCatalogSystemEntryMetaObjects(
|
||||||
this ICatalogSystem catalogSystem,
|
this ICatalogSystem catalogSystem,
|
||||||
MetaDataContext metaDataContext)
|
MetaDataContext metaDataContext)
|
||||||
@@ -77,7 +77,7 @@ public static IEnumerable<(MetaObject metaObject, MetaDataContext metaDataContex
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
{% endhighlight %}
|
```
|
||||||
|
|
||||||
I broke the iteration down into separate iterators in this fashion so that I could easily iterate through all the entries (for example) in a single node, if I knew from the beginning which node I wanted to start at. If that was the case, I could easily write another extension method that took a node ID (or node code) as a parameter, use the ICatalogSystem to find that node and then pass the node ID to the CatalogSystemEntries iterator to iterate just the entries under that node.
|
I broke the iteration down into separate iterators in this fashion so that I could easily iterate through all the entries (for example) in a single node, if I knew from the beginning which node I wanted to start at. If that was the case, I could easily write another extension method that took a node ID (or node code) as a parameter, use the ICatalogSystem to find that node and then pass the node ID to the CatalogSystemEntries iterator to iterate just the entries under that node.
|
||||||
|
|
||||||
@@ -93,7 +93,7 @@ The code is pretty simple. We implement an IEnumerable which uses our implement
|
|||||||
|
|
||||||
The IEnumerator simply gets all the catalogs from the ICatalogSystem and, as you call MoveNext() (usually done for you by the foreach loop), increments an index value to give you the current CatalogDto.CatalogRow to work with.
|
The IEnumerator simply gets all the catalogs from the ICatalogSystem and, as you call MoveNext() (usually done for you by the foreach loop), increments an index value to give you the current CatalogDto.CatalogRow to work with.
|
||||||
|
|
||||||
{% highlight csharp %}
|
```csharp
|
||||||
public class CatalogSystemCatalogs : IEnumerable<CatalogDto.CatalogRow>
|
public class CatalogSystemCatalogs : IEnumerable<CatalogDto.CatalogRow>
|
||||||
{
|
{
|
||||||
private readonly CatalogSystemCatalogEnumerator _enumerator;
|
private readonly CatalogSystemCatalogEnumerator _enumerator;
|
||||||
@@ -159,7 +159,7 @@ public class CatalogSystemCatalogEnumerator : IEnumerator<CatalogDto.CatalogRow>
|
|||||||
private Lst<CatalogDto.CatalogRow> GetCatalogs()
|
private Lst<CatalogDto.CatalogRow> GetCatalogs()
|
||||||
=> _catalogSystem.GetCatalogDto().Catalog.Freeze();
|
=> _catalogSystem.GetCatalogDto().Catalog.Freeze();
|
||||||
}
|
}
|
||||||
{% endhighlight %}
|
```
|
||||||
|
|
||||||
### CatalogSystemNodes Iterator
|
### CatalogSystemNodes Iterator
|
||||||
For the nodes iterator, things get a little more complicated. Nodes can be nested, so we have tree structure we have to navigate. We use a Queue to walk through the nodes in a breadth-first fashion.
|
For the nodes iterator, things get a little more complicated. Nodes can be nested, so we have tree structure we have to navigate. We use a Queue to walk through the nodes in a breadth-first fashion.
|
||||||
@@ -168,7 +168,7 @@ I like to use as many functional programming techniques as I can in C# these day
|
|||||||
|
|
||||||
If you're not familiar with breadth-first tree traversals, it's not too complicated. What we do first is get all the nodes at the top level (in our case, all the nodes that are direct children of the catalog we pass in) and put them in the queue. Then, we iterate through each of the nodes we put in the queue and add their child nodes to the queue. We do this until we run out of nodes to process in the queue which indicates we've gone through all nodes in the tree.
|
If you're not familiar with breadth-first tree traversals, it's not too complicated. What we do first is get all the nodes at the top level (in our case, all the nodes that are direct children of the catalog we pass in) and put them in the queue. Then, we iterate through each of the nodes we put in the queue and add their child nodes to the queue. We do this until we run out of nodes to process in the queue which indicates we've gone through all nodes in the tree.
|
||||||
|
|
||||||
{% highlight csharp %}
|
```csharp
|
||||||
public class CatalogSystemNodes : IEnumerable<CatalogNodeDto.CatalogNodeRow>
|
public class CatalogSystemNodes : IEnumerable<CatalogNodeDto.CatalogNodeRow>
|
||||||
{
|
{
|
||||||
private readonly CatalogSystemNodeEnumerator _enumerator;
|
private readonly CatalogSystemNodeEnumerator _enumerator;
|
||||||
@@ -267,14 +267,14 @@ public class CatalogSystemNodeEnumerator : IEnumerator<CatalogNodeDto.CatalogNod
|
|||||||
_current = Option<CatalogNodeDto.CatalogNodeRow>.None;
|
_current = Option<CatalogNodeDto.CatalogNodeRow>.None;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
{% endhighlight %}
|
```
|
||||||
|
|
||||||
### CatalogSystemEntries Iterator
|
### CatalogSystemEntries Iterator
|
||||||
Since entries cannot be arrange in a nested fashion, this iterator is a little less complicated than the nodes iterator. All we do is take in a catalogId and a catalogNodeId and retrieve all the entries below the given node ID, passing them to you one at a time as you call MoveNext().
|
Since entries cannot be arrange in a nested fashion, this iterator is a little less complicated than the nodes iterator. All we do is take in a catalogId and a catalogNodeId and retrieve all the entries below the given node ID, passing them to you one at a time as you call MoveNext().
|
||||||
|
|
||||||
The only mildly complex thing here is that we use a `Lazy<T>` so that we're not retrieving the entries immediately when you instanstiate the IEnumerator. We only load the entries when you MoveNext() and access Current which indicates you really do want some entries.
|
The only mildly complex thing here is that we use a `Lazy<T>` so that we're not retrieving the entries immediately when you instanstiate the IEnumerator. We only load the entries when you MoveNext() and access Current which indicates you really do want some entries.
|
||||||
|
|
||||||
{% highlight csharp %}
|
```csharp
|
||||||
public class CatalogSystemEntries : IEnumerable<CatalogEntryDto.CatalogEntryRow>
|
public class CatalogSystemEntries : IEnumerable<CatalogEntryDto.CatalogEntryRow>
|
||||||
{
|
{
|
||||||
private readonly CatalogSystemEntryEnumerator _enumerator;
|
private readonly CatalogSystemEntryEnumerator _enumerator;
|
||||||
@@ -355,14 +355,14 @@ public class CatalogSystemEntryEnumerator : IEnumerator<CatalogEntryDto.CatalogE
|
|||||||
private Lst<CatalogEntryDto.CatalogEntryRow> GetEntries()
|
private Lst<CatalogEntryDto.CatalogEntryRow> GetEntries()
|
||||||
=> _catalogSystem.GetCatalogEntriesDto(_catalogId, _catalogNodeId).CatalogEntry.Freeze();
|
=> _catalogSystem.GetCatalogEntriesDto(_catalogId, _catalogNodeId).CatalogEntry.Freeze();
|
||||||
}
|
}
|
||||||
{% endhighlight %}
|
```
|
||||||
|
|
||||||
### CatalogSystemEntryMetaObjects Iterator
|
### CatalogSystemEntryMetaObjects Iterator
|
||||||
The MetaObjects iterator is a different than the other iterators in that it doesn't really iterate through a list or tree of MetaObjects. Instead, it iterates through all fo the MetaObject instances for a particular entry--one instance for reach culture available for the catalog.
|
The MetaObjects iterator is a different than the other iterators in that it doesn't really iterate through a list or tree of MetaObjects. Instead, it iterates through all fo the MetaObject instances for a particular entry--one instance for reach culture available for the catalog.
|
||||||
|
|
||||||
So, as you'll see in the code below, one of the first things we do is grab all of the languages for the catalog so that we know what we need to interate through.
|
So, as you'll see in the code below, one of the first things we do is grab all of the languages for the catalog so that we know what we need to interate through.
|
||||||
|
|
||||||
{% highlight csharp %}
|
```csharp
|
||||||
public class CatalogSystemEntryMetaObjects : IEnumerable<(MetaObject metaObject, MetaDataContext metaDataContext)>
|
public class CatalogSystemEntryMetaObjects : IEnumerable<(MetaObject metaObject, MetaDataContext metaDataContext)>
|
||||||
{
|
{
|
||||||
private readonly CatalogSystemEntryMetaObjectsEnumerator _enumerator;
|
private readonly CatalogSystemEntryMetaObjectsEnumerator _enumerator;
|
||||||
@@ -449,14 +449,14 @@ public class CatalogSystemEntryMetaObjectsEnumerator : IEnumerator<(MetaObject m
|
|||||||
.Map(lr => new CultureInfo(lr.LanguageCode))
|
.Map(lr => new CultureInfo(lr.LanguageCode))
|
||||||
.Freeze();
|
.Freeze();
|
||||||
}
|
}
|
||||||
{% endhighlight %}
|
```
|
||||||
|
|
||||||
## Warning
|
## Warning
|
||||||
Even though the above code will give you the convenience of iterating through your entire EPiServer catalog(s), be warned! You could easily forget what the IEnumerators really do above and do something like this:
|
Even though the above code will give you the convenience of iterating through your entire EPiServer catalog(s), be warned! You could easily forget what the IEnumerators really do above and do something like this:
|
||||||
|
|
||||||
{% highlight csharp %}
|
```csharp
|
||||||
// REALLY BAD CODE, DON'T DO
|
// REALLY BAD CODE, DON'T DO
|
||||||
List<(MetaObject metaObject, MetaDataContext mdc)> allTheMetaObjects = _catalogSystem.AllCatalogSystemEntryMetaObjects(mdc).ToList();
|
List<(MetaObject metaObject, MetaDataContext mdc)> allTheMetaObjects = _catalogSystem.AllCatalogSystemEntryMetaObjects(mdc).ToList();
|
||||||
{% endhighlight %}
|
```
|
||||||
|
|
||||||
I hope you see the danger of this. If you have a large catalog, you're loading the ENTIRE set of MetaObjects into memory in your `allTheMetaObjects` list. This probably won't be good for your application. If you have a small catalog, this might not be a big deal. But, keep this in mind!
|
I hope you see the danger of this. If you have a large catalog, you're loading the ENTIRE set of MetaObjects into memory in your `allTheMetaObjects` list. This probably won't be good for your application. If you have a small catalog, this might not be a big deal. But, keep this in mind!
|
||||||
|
|||||||
Reference in New Issue
Block a user