Sharing the experience search

Search sharing-the-experience.blogspot.com

Tuesday, March 29, 2011

WSPBuilder: how to add the dll into GAC. DeploymentTarget

Everybody knows a wonderful and free tool -  WSPBuilder . I use it as an extension to Visual Studio:
 Recently I was wondering how to build a wsp with the strong signed dll to add it to the GAC.

There is a page crafted by the author of the tool which has a reference to bunch of post as a documentation "how-to" use the WSPBuilder
   Particularly for my task I was reading the post Change the WSPBuilder DeploymentTarget to WebApplication
I found useful to know that wsp has manifest file which describes what in the package and how sharepoint should handle it.

 
DeploymentTarget attribute defines where the dll should go
 
  The next question was how WSPBuilder decides what value to put into DeploymentTarget?
Possible values: GlobalAssemblyCache , WebApplication
  So, it turned out that WSPBuilder puts:
GlobalAssemblyCache  :
            - by default ;
            -if the name doesn't have a word "resources" (as I discovered);
            -the dll hasn't been put under folder 80\bin\
            - or "-DeploymentTarget" explicitly set to "GlobalAssemblyCache" in postbuild.scripts

The WSPBuilder has a config file where you can tune the params.

In my case my dll had a name "resources" and that was  the reason why WSPBuilder put it with DeploymentTarget=WebApplication. As soon as I renamed it, it wrote in the manifest file DeploymentTarget="GlobalAssemblyCache".
  

Happy WSPBuilding!

Monday, March 28, 2011

SPD Workflow: Custom action won't work

Yeah... The SPD WF is still in use in my project..

There are several benefits of using them:
 - easy to tailor to users' needs on the fly.
 - easy to understand by non-developers
  - can be done by non-developers.

There are (of course , as everything is in life)  minuses:
 - it's not transferable to another environment (but with extra effort-  it's possible  - explanation is here a hard-coded list id replacement )
 - it's not so flexible as a coded workflow
 - there is now way to debug it as easily as a coded workflow.

For some reason we use the SPD Workflow with custom written actions.
 Even  you are done with the step of writing the custom action. The second step - "using of the custom action" can be frustrating.
Based on my experience - the most common issue with the custom action in  the Sharepoint Designer Workflow is - it's appeared in the drop down list "Actions" , when you select  - it doesn't appear in the workflow.

That's my favorite one) - you won't find any trace of the errors in any logs.

I have found the concise and really helpful "how-to" fix the mysterious issues in the SPD WF - Common Pitfalls in SharePoint Designer Custom Actions
  • When you select a custom activity from the Actions drop-down, nothing happens
    • Usual suspect: the class name and assembly reference do not match in ACTIONS file and DLL. Please check that your class reference in the Action tag in the ACTIONS file exactly matches your DLL full name.
    • Not so usual suspect: SPD has a cached reference of your old DLL. Exit SPD, delete ApplicationData\Microsoft\WebSite Cache\ folder and restart SPD.
    • Even less usual suspect: You've correctly placed the ACTIONS file and the activity DLL, but you haven't added an authorizedType element into your web application web.config file. Add the correct entry (you can check the whole process here) and try again.
  • When you bind a property to a value, it doesn't get set
    • Usual suspect: the demoted property name in your DLL and in ACTIONS file do not match. Please check that all your properties in the code exactly match the Parameters section elements in your ACTIONS file.
 P.S. Thank you

Thursday, March 24, 2011

How to call DisableEventFiring not from SpEventReceiver

In one of my previous posts I have used my custom class to operate with permissions in the list. And one line there disabled the event raising even the class itself wasn't inherited from SPItemEventReceiver
                     SharepointHelper.DisableEventFiring();

public class SharepointHelper: SPEventReceiverBase
    {
        public void DisableEventFiring()
        {
            DisableEventFiring();
        }
        public void EnableEventFiring()
        {
            EnableEventFiring();
        }
    }

There are some rumors that is not going to prevent from firing the event if this is not used in the context of SpEventReceiver.... But practice proved - it does work.
  Here is the example of using it in the form page class:
protected override void OnSaved(EBTSimpleSaveButtonEventArgs e)
        {
            base.OnSaved(e);


SPLongOperationExecutionParams spLongOperationExecutionParams = new SPLongOperationExecutionParams() { LeadingHtml = "Updating incident", TrailingHtml = "Please wait while the incident is being updated", RedirectUrl = _redirectUrl };            
SharePointHelper.InvokeInSPLongOperationContext(spLongOperationExecutionParams, delegate()
            {
                SharePointHelper.DisableEventFiring();
                SharePointHelper.InvokeInSPListItemContext(new OperationContext() { SiteUrl = SPContext.Current.Web.Url, UseSystemAccount = true }, SPContext.Current.List.RootFolder.Url, SPContext.Current.ItemId,
                    delegate(SPSite spSite, SPWeb spWeb, SPList spList, SPListItem spListItem)
                    {
                        ItemHelper.ItemHelperMethod(spListItem);                       
                    });
                SharePointHelper.EnableEventFiring();
            });
        }

splistitem.RoleAssignments comparison (SpRoleAssigementComparer)

   I have got a task to implement assignment population over the splistitem in case the current role assignments are not the same as I want to populate. To find out if the collections of SPRoleAssigment are the same , I need to implement custom IEqualityComparer<SPRoleAssignment>
  
 Here is the function to populate assignments if there are new:
  private void ApplyRoleAssigments(SPListItem item)
        {

            var newAssignments = new List<SPRoleAssignment>();

            var current = from rol in item.RoleAssignments.Cast<SPRoleAssignment>()
                          select rol;
            List<SPRoleAssignment> currentAssigments = current.ToList();


            IEnumerable<SPRoleAssignment> differenceNewAssigments = newAssignments.Except(currentAssigments,
                                                                                          new SpRoleAssigementComparer());
            IEnumerable<SPRoleAssignment> differenceCurrentAssigments = currentAssigments.Except(newAssignments,
                                                                                          new SpRoleAssigementComparer());

            if (differenceNewAssigments.Count() > 0 || differenceCurrentAssigments.Count() > 0)
            {
                SPSecurity.RunWithElevatedPrivileges(() =>
                          SharePointHelper.InvokeInSPListItemContext(operationContext, item.ParentList.RootFolder.ToString(), item.ID, (site, web, list, item_) =>
                          {
                              SharePointHelper.DisableEventFiring();
                              SPGroupHelper.ResetRoleAssignments(item_);
                              item_.RoleAssignments.Add(newAssignments);
                              item_.SystemUpdate();
                              SharePointHelper.EnableEventFiring();
                          }));
            }


        }

Here is a custom IEqualityComparer<SPRoleAssignment>

public class SpRoleAssigementComparer : IEqualityComparer<SPRoleAssignment>
    {

        public bool Equals(SPRoleAssignment x, SPRoleAssignment y)
        {
            //Check whether the compared objects reference the same data.
            if (ReferenceEquals(x, y)) return true;

            //Check whether any of the compared objects is null.
            if (ReferenceEquals(x, null) || ReferenceEquals(y, null))
                return false;

            if (x.Member == null || y.Member == null)
            {
                return false;
            }

            if (x.Member.Name == y.Member.Name && x.RoleDefinitionBindings.Xml == y.RoleDefinitionBindings.Xml)
                return true;
            return false;
        }



        public int GetHashCode(SPRoleAssignment obj)
        {

            return obj.ToString().ToLower().GetHashCode();

        }

    }

get SPUser from SPFieldUser

Wondering why seems puzzling to get SPUser from SPFieldUser? Me too)

SPUser creator = ((SPFieldUserValue)(item.Fields["Created By"]).GetFieldValue(item["Created By"].ToString())).User;
      

Asp:Hyperlink : javascript: window.open and Target=_Blank

The forgotten basic of web development: "To open link in the new window do I need to use Target element in asp:hyperlink or should I use JavaScript ?"

Page1.aspx
<asp:HyperLink runat="server" ID="l1" Text="l1" Target="_blank"/>
 <asp:HyperLink runat="server" ID="l2" Text="l1" Target="_blank"/>
The result: Click on the first link will open the new window. Click on the second linwill open the url  in the previous opened window.

Page2.aspx
 <asp:HyperLink runat="server" ID="l1" Text="l1" onclick="javascript:window.open(this.href);return false;"/>
 <asp:HyperLink runat="server" ID="l2" Text="l1" onclick="javascript:window.open(this.href);return false;"/>
The result: Click on the first link will open a new window. Click on the second link will open the second new window. Every click will open a new window.

 P.S.
     One more basic -  Why we need set "return false;? - JavaScript Onclick Return False  - Anywhere you put an onclick event with a javascript call, if you add return false then the default behavior (what would have happened without the onclick event) will be disregarded.
   ...and don't forget to put ; in the end.
    





Wednesday, March 16, 2011

sharepoint database structure

Want to know where you can find what in sharepoint content database?
Here is a crisp article about it - Database Tables
Disclaimer:Never update data in the tables! It may lead to broken site, site collections, web application

Wednesday, March 9, 2011

SPItemEventProperties itemupdated beforeproperties is empty

Can't quite get where to use beforeproperties, afterproperties and properties.ListItem? Sometimes is null sometimes is just not you are expecting.
 Here is a neat table with what you should expect where and when in SPItemEventProperties class

Sharepoint List

Event
BeforeProperties
AfterProperties
ListItem
ItemAdding
Null
New  Value
Null
ItemAdded
Null
New Value
New Value
ItemUpdating
Null
Changed Value
Original Value
ItemUpdated
Null
Changed Value
Changed Value
ItemDeleting
Null
Null
Original Value
ItemDeleted
Null
Null
Null



Sharepoint Document Library


Event
BeforeProperties
AfterProperties
ListItem
ItemAdding
Null
Null
Null
ItemAdded
Null
Null
New Value
ItemUpdating
Original Value
Changed Value
Original Value
ItemUpdated
Original Value
Changed Value
Changed Value
ItemDeleting
Null
Null
Original Value
ItemDeleted
Null
Null
Null

How to auto-increment assembly version for release

Recently we needed to implement the increment versioning for a common dll with strong type reference replacement in the dependent projects.

I found a graceful way to do it. But first, let's check the msdn out regarding auto-increment:

Using Auto-Increment Version Numbers

An auto-increment version number is established by adopting the default "1.0.*" pattern for the AssemblyVersion attribute.

The UI in vs.net doesn't support it, you have to edit the 'My Project>AssemblyInfo.vb' file directly.

Advantages

Using auto-increment version numbers has the following advantages:
  • The build and revision numbers are handled automatically by Visual Studio .NET and do not have to be handled by the build script or build coordinator.
  • You guarantee never to have different builds of an assembly with the same version number.

Disadvantages

Using auto-increment version numbers has the following disadvantages:
  • The internal assembly build number does not match your system build number, which means there is no easy way to correlate a particular assembly with the build that generated it. This may be particularly problematic when you need to support your system in a production environment.
  • Build and revision numbers are not increased by one but are based on the time an assembly is built.
  • A new version of an assembly is generated each time it is built regardless of whether any changes have been made to the assembly. For strongly named assemblies, this means that all clients of that assembly must also be rebuilt to point to the correct version. However, if the build process rebuilds the whole system this should not be an issue.

Using Static Version Numbers

With this approach, you use a static version number, for example "1.0.1001.1", and update the major or minor numbers only when a new version is shipped with your next system release.

Advantages

Using static version numbers has the following advantages:
  • You have complete control over the exact version number.
  • Assembly build numbers can be synchronized with the system build number.

Disadvantages

Using static version numbers has the following disadvantages:
  • The version numbers must be manually updated by the build coordinator or by the build script.
  • If the version is not incremented with every build, you may end up with multiple builds of the same assembly with the same strong name. This is undesirable and can be problematic for assemblies that are installed in the Global Assembly Cache (GAC).
Important   If you do not change the version of a strongly named assembly and attempt to install it into the GAC using the Microsoft Windows® operating system Installer, the latest dynamic-link library (DLL) does not install if a previous version exists in the GAC with the same version number.
If you use Gacutil.exe instead of Windows Installer to install the assembly, the updated DLL is installed even if the assembly version number is the same.
Since we have 2 options with their own minuses and pluses, I have come up with a hybrid approach:
  With this approach the  increment version is generated by a custom tool which runs when the build  is Release. That eases up the life of developers, they use the fix version of common library and they don't need to change the strong name reference as long as they build in Debug mode.
   Once the build is ready for Production, the build server starts the Release build and the custom tool increments the common library version with the automated replacement of the strong type common reference in every project.
  Sounds perfect! The custom tool for assembly incrementation is on codeproject: "Autoincrement Version in Visual Studio". Regarding the autoreplacement of strong type common library  in the dependent project - I can't help you guys. I have only the exe file and the copyright is not mine.
But as yon can figured out from the Autoincrement source code - it's fairly easy to write own tool with a regex inside to find and replace the reference.

 Happy Building!