Dailycode.info

Short solution for short problems

Looking for nice Style for your web part buttons for WSS?

I made some webparts that are using htmlbuttons to perform some actions. At first I kept the ugly default button style. But when I found some spare time I start looking for some nice CSS classes to put on my buttons. I chose to use style of SharePoint itself, making sure the buttons will change their looks when I change the theme.

Here is the result of which I'm very pleased.

Before:

After:

The Yes button is the hoover style, the No button is the normal style. The colors used are the same as the Tab on top. Here's the code from my webpart:

_myNoReview = new HtmlButton();
_myNoReview.InnerText = "Yes";
_myNoReview.Attributes.Add("class", "ms-SPButton ms-WPAddButton");
_myNoReview.Attributes.Add("onmouseover", "this.className='ms-SPButton 
ms-WPAddButton ms-WPAddButtonHover';");
_myNoReview.Attributes.Add("onmouseout", "this.className='ms-SPButton ms-WPAddButton';");
 
_myNoReview.ServerClick += new EventHandler(_mybutton_click);
Controls.Add(_myNoReview);

 


Rights Management in combination with SharePoint

If you are looking for a secure document management system, SharePoint can do a lot for you. Still it remains a web based application and when a document is downloaded it can have its own live. Users can do local with the document as they please.

Not anymore, windows came up with: Rights Management. Making it possible to extend your security to where many thought it was not possible. You can secure a document to: read only, but also not making local copies, printable or not etc. I included some screens to show the possibilities of RMS.

This little icon will be visible in your office application:

RMS icon

When you click this icon, this screen will appear:

You can set and change rights. Click on More Options to get more secure:

Together with WSS 3.0 I feel I have a secure and stable environment.


WSS workflow access denied

I was finished developping and testing my workflows.

The result was very pleasing and with great spirit I deploy everything to the live environment. Again all tests work.

So we started to use the workflows. I went to the secretary who had to start a workflow manually. When she clicked on the workflow of a document, she got the error Access denied!!! What could this be? I try upgrading the rights ending up giving her full acces. Still access denied???

Then I started to google because a trigger in my mind jumped: Then I thought it was a SharePoint problem. And the triggerd fired correclty. It seems that when you create a workflow with  SharePoint designer, the workflow will work for all users who have access to the list. But users who are added after you created the workflow will not have access to start the workflow manually. There was only one working solution I found on http://www.sharepointu.com/forums/t/2088.aspx

But I was not in the mood to use the proposed workaround (backup and restore entire site). After looking some more I realized that it could only be solved with a work around, Microsoft has no solutions. So I found an easier workaround:

Copy the workflow. Rename it as described here earlier on this blog. Open the workflow and connect it to the list. Now the new users have access. When you add users after the work around, they again will not have access and you will need to perform this workaround again.This is not solved by any workaround!

Now the secretary can use the workflow. Hope this helps.

 

 


How to check if the current user is in a certain SP group

I made a webpart that enables the user to perform actions on documents. For every type of document, different user groups are used. So for example when I upload a document and set the type to Analysis, only Analysis users and reviewers may perform actions.

In order to check if the logged in user is in a certain user group, I created a function. Its very handy and may help some people to implement security.

This is the call to the function:

if (!UserInGroup(doc["SOP type"].ToString()+"RR"))

{
 _mybutton.Visible = false;
 _mylabel.Text = "The RR needs to review this document." + doc["SOP type"].ToString() + "RR";
}
 

I check if the user is in the correct group, if he's not, I set the button to invisible and show a message. Here is the function that does the work:

 
private bool UserInGroup(string group)
{
SPGroupCollection grps = CurrentUser().Groups; 
(OwnedGroups to check of which groups the user is owner of)
 
      foreach (SPGroup grp in grps)
      {
            if (grp.Name == group)
            {
 return true; 
}
      }
      return false;
}
 

private SPUser CurrentUser()
{
return SPContext.Current.Site.AllWebs[_siteName].CurrentUser;
}

 

Have a nice day or night!


Control.Invoke must be used to interact with controls created on a separate thread

I've migrated a windows CE 4.20 application to windows CE 5.0. This is not without effort. Some things have to be rewritten. I will explain the steps I took in order to get rid of the error: "Control.Invoke must be used to interact with controls created on a separate thread.".

The application worked fine in windows CE 4.2. What is does is when a transponder is scanned it send an event to the forms. In this event I wanted to set properties of my controls. The code looked like this:

public void HandleISOTagScannedEvent(string isoTag)
            {
                  bool Found = false;
 
                  try
                  {
                        int Index = 0;
                        IEnumerator Enumer = cboThings.Items.GetEnumerator();
                        while(Enumer.MoveNext())
                        {
                             Thing ThisThing = new Thing(((DataRowView)Enumer.Current).Row);
                             if (ThisThing._rfId == isoTag)
                             {
                                   cboThings.SelectedIndex = Index;
                                   this.SetThing(ThisThing._guid);
                                   Found=true;
                                   break;
                             }
                             Index++;
                        }
 
                        if (!Found)
                        {     
                             FormsManager.ShowMessage("Thing {0} does not exist!", isoTag);
                        }
                  }
                  catch (Exception ex)
                  {
                        TraceExceptionForm.Display("TubeSetupForm", "HandleISOTagScannedEvent", ex);
                  }
            }
 

When I migrated the application to .Net 2.0 and CE 5 and deployed it to the mobile device, everything seem to work. Then I started testing and when I scanned a transponder, the error first occurred. Control.Invoke must be used to interact with controls created on a separate thread. I started to Google around and found the solution on this website by David Kline: http://blogs.msdn.com/davidklinems/archive/2006/03/09/548235.aspx. I had to make a delegate on the form and use this to change the properties. Now the code looks like this:

 

private delegate void UpdateStatusDelegate(int index, Guid guid);
 
private void ChangeThing(int index, Guid guid)
        {
            Cursor.Current = Cursors.WaitCursor;
            if (this.InvokeRequired)
            {
                // we were called on a worker thread
                // marshal the call to the user interface thread
                this.Invoke(new UpdateStatusDelegate(ChangeThing),
                            new object[] { index,guid });
                return;
            }
            
            cboThings.SelectedIndex = index;
            this.SetThing(guid);
            Cursor.Current = Cursors.Default;
        } 
 
 
            public void HandleISOTagScannedEvent(string isoTag)
            {
                  bool Found = false;
 
                  try
                  {
                        int Index = 0;
                        IEnumerator Enumer = cboThings.Items.GetEnumerator();
                        while(Enumer.MoveNext())
                        {
                             Thing ThisThing = new Thing(((DataRowView)Enumer.Current).Row);
                             if (ThisThing._rfId == isoTag)
                             {
                        ChangeThing(Index, ThisThing._guid);
                                   Found=true;
                                   break;
                             }
                             Index++;
                        }
 
                        if (!Found)
                        {     
                             FormsManager.ShowMessage("Thing {0} does not exist!", isoTag);
                        }
                  }
                  catch (Exception ex)
                  {
                        TraceExceptionForm.Display("SetupForm", "HandleISOTagScannedEvent", ex);
                  }
            }
 

Problem solved. I hope this can help people with simular problems.

 Maybe someone can explain why on wondows CE 4.2 this was not giving any problem and in windows CE 5.0 it is? Or could it have something todo with the compact framework?  


How to open a windows CE database on your desktop?

I'm developing a windows CE application with a SQL CE databse. (.sdf). Something was going wrong while synchronising. I couldn't figure out what it was, so I decided to go and take a look in the database. The dev tools are not installed on the CE device, so I started to look for a program on my desktop to open the database.

Not SQL management studio as some post declared, but VS 2005 can open the SDF databse.

Copy the sdf file to your desktop and open VS2005. Choose open -> file and browse to the sdf file.

Now you can query as much as you like.


Decimal to binairy

Converting a binairy to decimal is easy:

long l = Convert.ToInt64(txtMBBS.Text, 2);

int i = (int)l;

converting a decimal to a binairy can be a litlle bit harder:

public static string ToBinary(Int64 Decimal)

        {

            // Declare a few variables we're going to need

            Int64 BinaryHolder;

            char[] BinaryArray;

            string BinaryResult = "";

            while (Decimal > 0)

            {

                BinaryHolder = Decimal % 2;

                BinaryResult += BinaryHolder;

                Decimal = Decimal / 2;

            }

             // The algoritm gives us the binary number in reverse order (mirrored)

            // We store it in an array so that we can reverse it back to normal

            BinaryArray = BinaryResult.ToCharArray();

            Array.Reverse(BinaryArray);

            BinaryResult = new string(BinaryArray);

            return BinaryResult;

        }

Found this function on this site: http://kyrathaba.dcmembers.com/errata/binary_decimal.htm


Unexpected error on server associating the workflow

Ever had this nice error:

I first got this error when I was migrating the workflow from development to live. I just copied the workflow and linked the list and items correctly. When I press finish it saves the changes but cannot associate the workflow with the list. The problem was because it was the first workflow on the site, there was no folder created for the workflows. A copy paste, copied the workflow in the root directory. Look for the difference in the pictures below!

This doesn't work:

This works:

In both pictures you will find a workflow folder, this was because I had taken the screen after the folder was created. But I dragged the workflow to the root and the same problem arrose. So the solution is to put the workflow in the correct workflow folder. If it does not exists yet:create a dummy workflow and the system will create the workflow folder for you. Then drag your workflow in this folder and now it can associate the workflow with the list. Remove the dummy workflow.


SPListItem.Copy()

This function can copy a listitem from one list to another. After a little debugging and googling I noticed that the url field has to be the complete field. The target list can contain less or more columns, it just copies the they share. Very simple implementation:

SPListItem.Copy(SPContext.Current.Web.Url + "/" + doc.Url, _publicLibUrl +doc.Name);

I made the destinationurl of the library a public property of my webpart. So I can use the webpart on any site. I'm still trying to find out if there is a possibility to overwrite documents. At first site it looks impossible. Any help on this is welcome.(workaround below) It is no problem to copy the item from one site to another on the same server.

I do this at the end of my approval workflow. When the document is approved and published, I copy it to document library on an other site, where all users have read acces. Togehter with RMS we can lock the files completely. When a reviewer changes things in the document, users won't notice it. Only after final approval, the document will be copied to the public library. This creates a working version and a published version.

Added on 8/7/2008 

So I found a workaround for the SPListItem.Copy overwrite problem. First I tried the SPFile.CoptTo. This can overwrite the file but cannot copy to another site. Then I tried the SPFileCollection, but there the overwrite problem arrose again. So now I created a simple workaround, because I don't need versions I look for and delete the file before I copy it. The versions are kept in the original library.

SPSite site = SPContext.Current.Site;

SPFile file = site.AllWebs[_sopSiteName].GetFile(_publicLibUrl + doc.Name);

if (file != null)

{   

file.Delete();

}

SPListItem.Copy(SPContext.Current.Web.Url + "/" + doc.Url, _publicLibUrl +doc.Name);