Monday, October 31, 2011

Sharepoint 2010 - Migrate List-based Workflows between Sites and Site Collections


Thank you Gavinmckay, you saved me!
From his blog: http://gavinmckay.wordpress.com/2011/08/29/howto-move-or-migrate-sharepoint-2010-list-based-workflows-between-sites-and-site-collections/

I’ve experienced this issue a lot when trying to migrate workflows between test SharePoint 2010 farms and production farms, in particular with workflows attached to lists. When moving a workflow to another site collection or server farm, the association to the list is broken and the workflow cannot be attached to the list. You also cannot use SharePoint designer to fix this via the standard methods as the unattached workflow cannot be reattached to the list.

List-based workflows are tied to three different lists – the “main” source list where the data is held (such as a Forms library or custom list), a task list, and a workflow history list. The latter in particular is tricky, because it is a hidden list and cannot be viewed via the normal interface.

The fix for this is to modify the source workflow files to force the workflow to reattach to the list. The following method assumes that your new site/location that you are moving the workflow too does not yet have any workflows already attached to any lists on the site. When a workflow is created in a site for the first time, a Tasks list and a Workflow History list are automatically created/used by the workflow. These lists must exist before you can force your migrated workflow to attach to the list.

Step 1 – Create a Blank Workflow
Using SharePoint designer, connect to your destination site
Create a new list-based workflow called “test” (or whatever), attaching it to your migrated list
Create one condition (i.e. if 1 = 1)
Create one action (i.e. add comment “hello”)
Save and publish the workflow
After this step has been completed, your site will now have a Tasks list and Workflow History list.

Step 2 – Get the list ids for your new workflow
The list ids are required to configure your migrated workflow.

Using SharePoint Designer, connect to your destination site
in the left-hand side, in Site Objects, select All Files
in the All Files list, select Workflows
Select your new workflow that you just created (in this example, “test”)
You should see (at least) four files:
text.xoml
test.xoml.rules
test.xoml.wfconfig.xml
test.xsn
Right-click on test.xoml.wfconfig.xml and select Open With, Notepad
Look for the <Association…> tag in the xml:
<Association ListID=”{7DC232FD-4D0B-4F7B-AC72-3D4D6399147C}” StartManually=”true” TaskListID=”{CB551A8B-F1E1-49F9-A8F8-A2C8EDC241C7}” HistoryListID=”{04B19F9D-5A47-4E14-B85E-53FFF06CFA63}” StartOnCreate=”true” StartOnChange=”true”/>
Record the Guid entries for ListID, TaskListID, and HistoryListID

Step 3 – Update the migrated workflow
Still using SharePoint Designer, All Files, Workflows, this time select your migrated workflow
You should again see (at least) four files. Right-click on “your-workflow.xoml.wfconfig.xml” and select Open with SharePoint Designer (As XML)
Find the Association tag and very carefully change the Guids so that your migrated List IDs are the same as the “test” workflow List IDs.
Save the file
Close SharePoint Designer
Open SharePoint Designer again and open your site
Instead of All Files, this time click on Workflows
Select your workflow
Save the workflow
Publish the workflow

Your workflow should now be reassociated with your list on your new site.

Yes! It is!

Monday, October 24, 2011

Sharepoint 2010 - Enable/Disable Ribbon Button with EcmaScript


I show you a detailed example of how to enable and disable a ribbon button according to the current user’s group.

The example uses “EnabledScript” attribute of the CommandUIHandler of the ribbon button.

Then the EnabledScript is built with EcmaScript to find groups and user info.

Lets Start with creating a Ribbon button first.
1. Create a empty project.
2. Deploy it as a Farm solution.
3. Right click on the feature and click “Add feature”.
4. Right click on the project and add a new “Empty Element” item.
5. Next add the below code to add a custom Ribbon button to your document library.

<Elements xmlns=”http://schemas.microsoft.com/sharepoint/” >
<CustomAction
Id=”ButtonForGroupUsersOnly”
Location=”CommandUI.Ribbon”
RegistrationId=”101″
RegistrationType=”List”
Title=”Owners Group Button”>
<CommandUIExtension>
<CommandUIDefinitions>
<CommandUIDefinition
Location=”Ribbon.Library.ViewFormat.Controls._children”>
<Button Id=”Ribbon.Library.ViewFormat.UsersBtn”
Command=”usersBtnCommand”
LabelText=”Group Users Button”
Image32by32=”/_layouts/1033/IMAGES/buttonIcon.jpg”
TemplateAlias=”o1″ />
</CommandUIDefinition>
</CommandUIDefinitions>
<CommandUIHandlers>
<CommandUIHandler
Command=”usersBtnCommand”
CommandAction=”javascript:OwnerBtnscript();“/>
EnabledScript=”javascript:EnablerScript('10,14');” -> Enable Ribbon function here
</CommandUIHandlers>
</CommandUIExtension>
</CustomAction>
//Referencing the Script
<CustomAction
Id=”OwnersButton.Script”
Location=”ScriptLink”
ScriptSrc =”/_layouts/RibbonScripts/CheckUserInGroup.js”/>
</Elements>

The Group Users Button created in the above code will be in the disabled mode on page load. The code in EnablerScript('10,14'); will determine if the current  user is added to the specified group and the button needs to be enabled.
The CustomAction ScriptLink refers to the path of the CheckUserInGroup.js file which contains EnablerScript('10,14'); and other JavaScript functions.


6. Next add a Javascript file in your project “CheckUserInGroup.js” and add it under Layouts -> RibbonScripts folder. Create Layouts folder using Add-> “Sharepoint Layouts Mapped Folder” .

7. The following goes in your CheckUserInGroup.js file
// The below is called by EnabledScript in ribbon button

var idGroupToVerify1 = 10;

var isGroupToVerify2 = 14;
var nameGroup1 = "nameGroup1";
var nameGroup2 = "nameGroup2";
var arrIsInThisGroup = new Array(2);
arrIsInThisGroup["nameGroup1"] = false;
arrIsInThisGroup["nameGroup2"] = false;

ExecuteOrDelayUntilScriptLoaded(InitGroups, "sp.js");
//initialize groups ids for the current user
function InitGroups() {
    CheckUser(idGroupToVerify1, nameGroup1);
   //beacuse of some errors if execute together
    setTimeout("CheckUser(" + idGroupToVerify2 + ", '" + nameGroup2 + "')", 500);
}

function EnablerScript(groupID) {
    if (groupID) {
        var isButtonEnabled = false;
    //groupid comes in string comma separated
   // '10' or '10,14'
        var arrGrId = groupID.split(',');
        var i = 0;
        for (i = 0; i < arrGrId.length; i++) {
            var currGroupID = arrGrId[i];
            if (currGroupID == idGroupToVerify1)
                isButtonEnabled = isButtonEnabled || arrIsInThisGroup[nameGroup1];
            if (currGroupID == idGroupToVerify2)
                isButtonEnabled = isButtonEnabled || arrIsInThisGroup[nameGroup2];
        }
        return isButtonEnabled;
    }
    return true;
}

// The below checks if the user exists in the group
function CheckUser(groupID, isInThisGroup) {

    var context = SP.ClientContext.get_current();
   //I go to parent site if I'm in a subsite!
    var siteColl = context.get_site();
    web = siteColl.get_rootWeb();
    var groupCollection = web.get_siteGroups();
    // Get the Our Group's ID
    var _group = groupCollection.getById(groupID); // ID of the Group that we are checking
    var users = _group.get_users(); // Get all Users of the group
    context.load(_group);
    context.load(users);
    this._users = users;
    this._currentUser = web.get_currentUser(); // Get current user
    this._isInThisGroup = isInThisGroup;
    context.load(this._currentUser);
    context.executeQueryAsync(Function.createDelegate(this, this.CheckUserSucceeded), Function.createDelegate(this, this.failed));
}

//The below Checks  if User is the member of the specified group
function CheckUserSucceeded() {

    if (this._users.get_count() > 0) {
        var _usersEnum = this._users.getEnumerator();
        while (_usersEnum.moveNext()) {
            var user = _usersEnum.get_current();
            if (user.get_loginName() == this._currentUser.get_loginName()) {
                //debugger;
                arrIsInThisGroup[this._isInThisGroup] = true;
            }
        }
    }
}

function failed(sender, args){alert("error");}

8. Next Build and deploy.

Tuesday, October 18, 2011

Blogger - Redirect when you change Blog URL


Recently I changed URL of my blog from giorgioguerrieri.blogspot.com to webdevshareetc.blogspot.com.
I know that my posts linked in facebook, google+, twitter have the old URL, so I search for a solution, a way to redirect post giorgioguerrieri.blogspot.com/2010/eccccc to webdevshareetc.blogspot.com/2010/eccccc, without 404 or other annoying message of Page Not Found.
So, I googled but nothing.

I found my solution with this process.
NOTE: it works if you have a personal site and it's used not for personal site. I use my site for development, so when you navigate to www.giorgioguerrieri.it your browser redirect to my blog.

You have change URL of your blog (giorgioguerrieri to webdevshareetc.blogspot.com)

Create a new blog with URL equals to old URL (new blog giorgioguerrieri.blogspot.com)

Go to Settings in new Blog and set Redirect to your public domain (I've www.giorgioguerrieri.it).

Important! You have to insert URL as "www.yourdomain.com?", yes, with "?".

Save settings and test: when you navigate to your new blog (with old URL), you navigate to your site.

Now, go to ftp of your site and modify index.php (I use php, but you can do this with your language).

function curPageURL() {
 $pageURL = 'http';
 if ($_SERVER["HTTPS"] == "on") {$pageURL .= "s";}
 $pageURL .= "://";
 if ($_SERVER["SERVER_PORT"] != "80") {
  $pageURL .= $_SERVER["SERVER_NAME"].":".$_SERVER["SERVER_PORT"].$_SERVER["REQUEST_URI"];
 } else {
  $pageURL .= $_SERVER["SERVER_NAME"].$_SERVER["REQUEST_URI"];
 }
 return $pageURL;
}

  $urlpage = curPageURL();
  $pos = strpos($urlpage, '?/');
  if($pos &gt; 0)
  {
  $urlpage = substr($urlpage, $pos+1);
  $urlpage = "http://webdevshareetc.blogspot.com" . $urlpage;
  }

header("location: " . $urlpage);


If you navigate to "http://blog.blogspot.com/2010/10/post.html", blogger asks you to redirect to "http://www.yourdomain.com?/2010/10/post.html".
Note the "?/...."
Then your index.php takes URL, cut from "?" and compose URL with "http://newurlblo.blogspot.com" + "/2010/10/post.html".
Then redirect (header("location")) to new URL correct page!

You can do other redirect from index.php simply checking if exists "?/" or not in the received URL.
In my script, if there's no "?/", I redirect to home of new Blog.

I hope this help you!





Sharepoint 2010 - Modify Documents Properties with ECMA Script

Finally I found the way to modify document properties with ECMA Script!
It's not an hard work for Document Libraries with no mandatory check out.

Here the script:

function updateDocument(action, docLibId, docId, fieldName, fieldValue) {
    var context = new SP.ClientContext.get_current();  
    var web = context.get_web();  
    var docLibGuid = new SP.Guid(docLibId);  
    var docLib = web.get_lists().getById(docLibGuid);  
    var doc = docLib.getItemById(docId);  
    doc.set_item(fieldName, fieldValue);    
    doc.update();  
    context.executeQueryAsync(Function.createDelegate(this, this.successUpdateDocument), Function.createDelegate(this, this.failed)); 
}
function successUpdateDocument(sender, args) {  
    //reload page if success  
    location.reload(true);
}

But when we set check out mandatory, we have message that we cannot modify document not checked out.
So, I have to check out document in this script.
I change script as shown below:

function updateDocument(action, docLibId, docId, fieldName, fieldValue) {
    var context = new SP.ClientContext.get_current();  
    var web = context.get_web();  
    var docLibGuid = new SP.Guid(docLibId);  
    var docLib = web.get_lists().getById(docLibGuid);  
    var doc = docLib.getItemById(docId);  
 
    //perform check out  
    var file = doc.get_file();  
    context.load(file);  
    file.checkOut();  
 
    //set values  
    doc.set_item(fieldName, fieldValue);    
    doc.update();  
 
    //then check in document    
    file.checkIn();    
    context.executeQueryAsync(Function.createDelegate(this, this.successUpdateDocument), Function.createDelegate(this, this.failed));
}

And it works fine!

Monday, October 17, 2011

SharePoint 2010 WebPart Properties not saving

I lost many time to find the way to save properties of a custom web part in Sharepoint 2010.
All my classes, methods, properties are defined correctly, but when I deploy web part and I try to save props, values were not saved and web part load with his default values.

Then I search for solution and I found that it is due to disposing the current web (SPContext.Current.Web).

I understood that I don't have to dispose SPContext.Current.Web object in my code and neither use using(SPWeb web = SPContext.Current.Web).
The solution is to use directly

SPWeb web = (new SPSite(SPContext.Current.Site.ID)).openweb(SPContext.Current.Web.ID);


to initiate a new web object and then dispose it after using.

The Save button finally saved properties values!