Monday, December 30, 2013

Project Server - check if project is approved

This is my new post in my Project Server series. In this post, I've written short c# code (using PSI) on how to check if project in Project Server is already approved.

This short code comes in handy when you need to do some action only after the project is approved.

Other posts regarding Project Server PSI functions: how to use PSI in Project Server in simple way, how to get values from Custom Fields from code with PSI, how to restart workflow from code using PSI, get members of Project Server group using PSI, change EPT using PSI and how to update Custom Field using PSI.


This is the C# code:



public static bool IsProjectApproved(Guid projectUid)
{   
    var logService = new LogService();
    var sessionService = new PSSessionService()

    {
        HostName = "serverName", // your PWA host name
        SiteName = "pwa" // your PWA site name
    };

    // we need this user for LoginContext (it can be read from app settings)
    string _user = "Username";
    string _userPwd = "Pass";
    string _userDomain = "Domain";

    using (var _loginCtx = new LoginContext(_userDomain + "\\" + _user, _userPwd, sessionService))
    {
         try
         {
              PsiContextService psiContextService = new PsiContextService();
              PSISvcsFactory psiSvcsFactory = new PSISvcsFactory(sessionService, psiContextService);

              Workflow _wfSvc = psiSvcsFactory.CreateSvcClient<Workflow>();
 
              WorkflowDataSet dtWf = _wfSvc.ReadWorkflowStatus(projectUid, true);

              // _max is the last ORDER of the Status in DB
              int _max = -1;
              bool _status = false;

              foreach (var item in dtWf.WorkflowStatus)
              {
                   if (item.STAGE_ORDER > _max)
                   {
                        _max = item.STAGE_ORDER;
                        if (item.STAGE_INFO.ToLower().Contains("project approved"))
                             _status = true;
                        else
                             _status = false;
                   }
              }
              return _status;
         }
         catch (Exception)
         {
              return false;
         }
               
    }
       
}

Monday, December 23, 2013

Project Server - check if project is checked-out to current user

In my previous posts on Project Server, I wrote on how to use PSI in Project Server in simple way, how to get values from Custom Fields from code with PSI, how to restart workflow from code using PSI, get members of Project Server group using PSI, change EPT using PSI and how to update Custom Field using PSI.
Today, I will show how to check (with PSI) if currently logged-in user is equal to the user to which project is checked-out. This can be useful many times, for example, if something is to be done with the project data from code, but someone else has checked-out the project, and, in that case the current user must not be able to edit project data.

For this small piece of code, I am using FluentPS library, like it is mentioned in post that I linked at the top of this post. 

This is the code in c#:



public static bool IsProjectCheckedOutToCurrentUser(Guid projectUid)
{
     var logService = new LogService();
     var sessionService = new PSSessionService()
     {
         HostName = "serverName", // your PWA host name
         SiteName = "pwa" // your PWA site name
     };

           
     // we need this user for LoginContext (it can be read from app settings)
     string _user = "dev";
     string _userPwd = "pass";
     string _userDomain = "domain";

     using (var _loginCtx = new LoginContext(_userDomain + "\\" + _user, _userPwd, sessionService))
     {

         sessionService.SetLoginContext(_loginCtx);

         FluentPS.Services.Impl.PsiContextService psiContextService = new PsiContextService();
         FluentPS.Services.Impl.PSISvcsFactory psiSvcsFactory = new PSISvcsFactory(sessionService, psiContextService);

         FluentPS.WebSvcLookupTable.LookupTable svcLookupTable = psiSvcsFactory.CreateSvcClient<FluentPS.WebSvcLookupTable.LookupTable>();
         FluentPS.WebSvcCustomFields.CustomFields svcCustomFields = psiSvcsFactory.CreateSvcClient<FluentPS.WebSvcCustomFields.CustomFields>();

         FluentPS.WebSvcProject.Project svcProject = psiSvcsFactory.CreateSvcClient<FluentPS.WebSvcProject.Project>();
         FluentPS.WebSvcResource.Resource svcResource = psiSvcsFactory.CreateSvcClient<FluentPS.WebSvcResource.Resource>();


         FluentPS.WebSvcProject.ProjectDataSet _projEntities = svcProject.ReadProjectEntities(projectUid, 32, FluentPS.WebSvcProject.DataStoreEnum.WorkingStore);
         FluentPS.WebSvcProject.ProjectDataSet _project = svcProject.ReadProject(projectUid, FluentPS.WebSvcProject.DataStoreEnum.WorkingStore);

         try
         {
             Guid _checkOutUser = Guid.Empty;
             Guid _currentUser = Guid.Empty;
             using (var dsProject = svcProject.ReadProject(projectUid, DataStoreEnum.WorkingStore))
             {
                 var projectRow = dsProject.Project.FindByPROJ_UID(projectUid);
                 if (projectRow == null)
                     return false;

                 if(!projectRow.IsPROJ_CHECKOUTBYNull())
                     _checkOutUser = projectRow.PROJ_CHECKOUTBY;
             }


             using (FluentPS.WebSvcResource.ResourceDataSet resourceDs = svcResource.ReadUserList(FluentPS.WebSvcResource.ResourceActiveFilter.All))
             {
                 foreach (var item in resourceDs.Resources)
                 {
                     if (item.WRES_ACCOUNT.Contains(HttpContext.Current.User.Identity.Name))
                     {
                         _currentUser = item.RES_UID;
                     }
                 }
             }

             if (_checkOutUser == Guid.Empty || _currentUser == Guid.Empty)
                 return false;

             if (_checkOutUser == _currentUser)
                 return true;
             else
                 return false;
                
         }
         catch (Exception ex)
         {
             return false;
         }
     }
 }

What is the GAC folder

When developing solutions for SharePoint platform, you will often deploy your solutions to GAC folder and use libraries that are already deployed to GAC, maybe even without knowing it.

What is GAC folder?

Global Assembly Cache (or simply, GAC) is a special folder in Windows designed for storing .NET global DLLs. When you deploy your SharePoint solution, you are actually deploying that DLL to GAC folder.

Location of GAC is C:\Windows\assembly or, in newer versions of .NET Framework (4.0): C:\Windows\Microsoft.NET\assembly

When your try to access those paths, you can see that this isn't the ordinary folder, you can't simply copy/paste files to that folder or delete files from it. Of course, there is a way of opening GAC as ordinary folder, but it is not recommended for you to do it because you can accidentally erase DLL that some other application is using. 

But, if you want to open GAC as "ordinary" folder, do the following steps:
1. Go to Start --> Run
2. Write c:\windows\assembly\gac_msil

And now you can add or delete files form GAC as in any other folder.


Better way of registering/removing solutions to/from GAC, is with use of command prompt, i.e. command line utility gacutil.exe.

If you want to register an assembly in GAC, use command:

gacutil.exe /i <entirePath\assemblyName>
 
If you want to remove an assembly from GAC, use command:

gacutil.exe /u <assemblyName>


When we are talking about SharePoint Farm solutions, every solution is automatically registered in GAC as soon as you hit "Deploy" in Visual Studio or if you are deploying from command prompt. 

If you want to deploy another library along with your SharePoint solution, the only thing you need is to add that library to the package in your SharePoint solution. 
1. Open the package in Solution Explorer in Visual Studio
2. The package opens in Design view. Click on Advanced tab on the bottom of the package screen.
3. And now, you can add the desired DLL to that package and every time you deploy that SharePoint solution, newly added DLL will also be deployed to GAC.


Wednesday, December 4, 2013

SharePoint 2013 - Get list size (part 2)

In one of my previous posts, I wrote a small SQL query which returns the size of SharePoint lists directly from SharePoint's database. That query works with SharePoint 2007 and 2010, but not with 2013 version because the database structure is slightly changed in 2013 version.

Instead of building another SQL query  that works with 2013 version, I've decided to use SharePoint's inbuilt procedure that returns the size of all lists of one Site Collection. This same procedure can be used on the older versions of SharePoint also.

The name of the procedure is dbo.proc_GetListSizes. It can be found in SQL Server Management Studio by expanding your database name (WSS_Content) and then by expanding "Programability" and then "Stored Procedures" like shown in image below. 



SQL Server Management Studio - position of proceudre in tree list


This is command for execution of procedure in SQL Server Management Studio. Parameter @SiteId is ID of your Site Collection.


Execution of proc_GelListSizes procedure


And this is the result. Along with the size of all List on that Site Collection, there are some additional informations:

Results of proc_GetListSize procedure execution




If you want to see the entire size of your database and size of table space, you can use command sp_spaceused like this:


Results of sp_spaceused procedure execution


Tuesday, October 8, 2013

SharePoint - Logging is not working - logs are empty

Almost every action that occurs in SharePoint is logged. SharePoint stores tons of data into its log file. This log files are very important because every time you get an error on your screen in SharePoint, you get a "Correlation id" on your screen. With this "Correlation id", you can search for detail error description in your logs and this is extremely helpful for resolving that error. SharePoint log files can be found in this path:

C:\Program Files\Common Files\Microsoft Shared\Web Server Extensions\15\LOGS

NOTE: Number "15" in path depends on the version of the SharePoint. If you have SharePoint 2007, then the number is 12. If you have SharePoint 2010, then the number is 14. If you have SharePoint 2013, then the number is 15.


But, sometimes you may ran into situation that logs are created in this folder, but they all have size of 0 byte (which means that nothing is being logged). Something made your logging service to stop.

 
Empty logs created in LOGS folder



There are three thing that you need to check:

1. Check the user permssions for your "SharePoint Tracing Service".

Go to: Start - Administrative Tools - Services - locate "SharePoint Tracing Service".
Make sure that user running this service is a member of Local Administrators group.

2. Restart service from step 1.

3. Free up some disk space.

Now, right  after the restart you may see that there is one log file created (small size, for example 1 KB) and others again have size of 0 byte. Now, open this new small sized log file and you will see the following error in it:

"Not enough free disk space available even if all inactive log files are deleted. The tracing service has temporarily stopped outputting trace messages to the log file. Tracing will resume when more than 1124 MB of disk space becomes available."


It is clear now that you don't have enough free disk space and this is the reason why your logging stopped working in the first place. Make more than 1 GB of free space and your logger will start logging immediately.

Tuesday, October 1, 2013

SharePoint - Create Top Navigation menu programatically

In my previous post, I wrote about creating Quick Launch navigation programmatically . In this post, I will show how to create Top Navigation programmatically. It is easy since the code is almost the same as for Quick Launch.

Top Navigation is navigation located on the top of the screen and it is important part of almost every SharePoint site (marked in red square on the image). 

SharePoint Top Navigation


The code is very simple, it consists of reading the Top Navigation object and deleting the existing links in it. And then, after the existing Top Navigation is cleared, putting the new links which we have read from some text file.

C# CODE:



SPSecurity.RunWithElevatedPrivileges(delegate()
{
     using (SPSite oSiteCollection = new SPSite("http://mySiteUrl"))
     {
           using (SPWeb oWeb = oSiteCollection.OpenWeb())
           {
                 // Create the node.
                 SPNavigationNodeCollection _topNav = oWeb.Navigation.TopNavigationBar;
                 // Delete all existing items in Top Navigation
                 int _counter = 0;
                 while (_topNav.Count != _counter && _topNav.Count > 0)
                 {
                       _counter++;
                       SPNavigationNode _item = _topNav[0];
                       _item.Delete();
                 }

                 // Here we read links that will go in Top Navigation from text file
                 // Let's say that in "abc.txt" file we have links written as (title|link):
                 //
                 // My project |/sites/MyTestSite1
                 // Project documents|/sites/MyDocLib2
                 // Reports|/sites/MyCustomReports
                 //...
   
                 string _line;

                 System.IO.StreamReader _file = new System.IO.StreamReader("abc.txt");
                 while ((_line = _file.ReadLine()) != null)
                 {
                       string[] _splitStr = _line.Split('|'); 
                       string _title = _splitStr[0];
                       string _url = _splitStr[1];

                       SPNavigationNode _SPNode = new SPNavigationNode(_title, _url, true);
 
                       _topNav.AddAsLast(_SPNode);
                 }
                 _file.Close();
           }
     }
});