WebMasterSam

.Net, SEO, Dynamics CRM, AdSense/AdWords, Dating sites, Silverlight, Web hosting and more

About me

I'm an IT consultant working primarily with the .Net Framework as a developper and architect. I also work on my own on my personnal dating websites. I've been developping websites since 2000.

If you like what I do, feel free to support me

PayPal - The safer, easier way to pay online!

Bookmark

Bookmark and Share

Sponsored links

Amazon hot deals

Computer releases

Last comments

Comment RSS

Rent a coder; does this really work ?

If you're a programmer, you certainly already created a little program or website for someone and get paid for it. This is a normal thing; you have a talent that some others don't have and you can help them, charging something for your time and effort.

On the web, there is this kind of thing too. You can pay someone for a specifig job you want to be done. There is websites that exposes this principle like RentACoder and GetACoder. These websites look great, there is a lot of projects awaiting for bids and plenty of coders ready to code ! Fine, this seems to be a great place to search for custom jobs or to post a project you don't the time (or knowledge) to do by yourself.

So, let's take a look to both sides of the medal...

Being a coder, bidding on projects

When you signup on websites like RentACoder, you can search for projects you want to realize. There is plenty of different categories: .Net, Java, SQL Server, MySQL, graphic design, bug fixing, etc... so you can find a perfect project for you and bid on.

Now that you've found the project you start to bid on and you think about a price (between the price range). You than have your price in mind but you find that 50 indians have already bidded on this for 10% of the price you thought of charging... Damn ! This is the reality, in india, being paid 5$/hour is big. Here, if we work as a freelancer we can easily charge 40$-100$ per hour. Every single project you will find will have indian bids on it. Maybe the project manager don't want cheap indian code for a fraction of the price, but wants someone who will charge a lot more but do a great job so with those projects, you may have a chance on winning it.

If you win the project, you will have to realize it so you will need specifications, you will need to talk to the project manager often to really understand his needs. This can be hard. Remember what you do at your day job... you're near from the client, you ask questions anytime, you do meetings, you chat, etc... The communication is easy, but if the client is about 1000km away, will you spend hours on phone ? Paying bills will kill all the profit you can make out of the project itself. Will you chat everyday ? Chatting is good for easy questions, but don't make serious decisions using MSN. Will you have the opportunity to meet up with the project manager ? Maybe not if he lives far from you, and I'm sure no one of you will pay 200$ for a plane ticket on a 400$ project !

There is also another thing that may scares some people... will you really get paid for you work ? When you give your source code, the project manager can stop talking to you, stole your work and don't pay a dime for it... it's a danger. I don't know if this happens often but, it's not risk-free.

Being a project manager, awaiting for bids

Being a project manager is not very different from being a coder because the real problem with coder rental is the communication, and this is a problem for both sides. Even if this problem is the same, there is some other things that are different... Like I said previously, there is a lot of indians that want to work for peanuts so you will be able to buy something that fits your needs for a small amount of money.

So you decide to take someone for your project (whoever he his and where he's from). You send him the specifications of the work to be done and he starts to work on it. You communicate everyday to have a feedback about the way the developpment is going... eveything seems to be fine.

Then, you get your work, you pay for it and do what you have to do with your project (put it on the internet, use it on your intranet, etc...). Everything seems to be fine but soon you discover some bugs and you open the code... What you find is not, and absolutely not following any existing programming best practices. You're not able to follow the code logic (if there is a logic...), you find weird things and... Damn it ! You can maintain this code without rewriting a big part... This is normal, you can't control someone else's code. Personnaly at my day job I check my coworkers code barely every day and find things that I tell them to change or fix it the best they can, but I will never be satisfied until I do it myself.

Conclusion...

Personnaly I think that you can't rent a coder... at least 90% of the time. I think sometimes you can rent someone to create graphical content or database design, but I don't think you can have success with programming. So in general, I don't think it's a good idea being a coder and not a good idea being a project manager (on rent a coder).

Posted: Apr 30 2009, 16:29 by WebMasterSam | Comments (69) RSS comment feed |
  • Currently 0/5 Stars.
  • 1
  • 2
  • 3
  • 4
  • 5
Social Bookmarks: E-mail | Kick it! | DZone it! | del.icio.us

Be paid to fill surveys online; can we really make money with this ?

If you read this article it is because you are thinking about filling surveys online to make an extra cash every week. Like many other people, making money online is a thing we want to be able to do. If you think you can make money online, you're right, there IS a lot of ways to make money online, sitting at home and working only a few hours per day.

To day I will explain what is all about be paid to fill surveys online.

Is it hard to fill surveys and hard to find surveys to fill ?

Non, it's easy to fill, you only give your opinions about some products/services. You have offers everywhere on the internet, so you can find some good surveys only by surfing on your favorite sites. If you don't trust me, just spend about 5 minutes on facebook and you will see ads for "surveys money".

How much money can we make from this ?

It will depend on how much time you want to spend online filling surveys, but for sure, you will not become millionaire with this ! If you spend a few hours a week doing this you can make from 20$ to 100$ per week. You can make more than this but it general it will look like this. It always depends on current offers and the time you want to spend on it. I presonnaly don't know people who do 1000$ a week, even in a month.

Is it right or me ?

If you want something you can do every week (maybe everyday) because you're tired of spending your time on useless websites playing online little flash game or reading jokes or whatever you do to spend your time on your computer, this can be a little sideline. You will spend time, and be paid to spend it.

On the other hand, if for you time is money, don't fill surveys online, you'll lose your time. At the begginning you will say "Wow ! it's very easy to make money with this, I will do this all weekends and every evening when I comeback home from a day of work", but 3 or 4 days after you will say "Ahh damn it, I'm tired of filling these, I don't want this anymore".

Is it all legitimate or I can really lose my time with this ?

Like any other thing on the internet, you can be fooled if you trust anything you see. There is a lot more scams in online paid surveys than legitimate one so don't be surprised if one day you don't receive your paycheck of even worse, if you get frauded.

Conclusion...

Personnaly, I think that online paid surveys are only for a small audience and whoever you are you should think about starting to fill surveys online to make money. You can lose more time than money you can get.

Posted: Apr 30 2009, 15:38 by WebMasterSam | Comments (81) RSS comment feed |
  • Currently 0/5 Stars.
  • 1
  • 2
  • 3
  • 4
  • 5
Filed under: Misc | Make money online
Social Bookmarks: E-mail | Kick it! | DZone it! | del.icio.us

What is a .XAP file (SilverLight) and what does it contain

If you already created a Silverlight application you have certainly noticed that all you do in your Silverlight project will be packaged as a XAP file that you will then put on your website.

What is a XAP file ?

The XAP file is a package that contains everything your Silverlight application needs to run on the client browser.

What does it contain ?

All the assemblies (DLL) you created, all the Framework assemblies you referenced in your application (like System.Xml.Linq, System.Windows.Controls and some others) and a manifest file, named AppManifest.xaml. It also contains culture-specific assemblies (if you translated your application in some other languages).

What does XAP stands for ?

You have to pronounce "ZAP" and it stands for "Silverlight Application Package". I don't know why MS didn't called it SAP instead of XAP but... this is it's meaning !

Can I open a XAP file ?

Oh sure you can, it's only a ZIP file. You can rename it an open it with WinRar or WinZip.

How can I create a XAP file ?

You build your Silverlight project ! Simple, no ?

Why does my Silverlight application is packaged in a XAP file ?

There's 3 main reasons for this :

  1. Because all the content can be compressed within the zip file so you save bandwith and time
  2. Because DLLs are prohibited from downloading (by default)
  3. Because it's easier to download one file than "72" (any number) files
What does the AppManifest.xaml looks like ?

This file is the main entry point in the package for the Silverlight client. When Silverlight extracts the content of the XAP package it reads the AppManifest.xaml to know which assembly it has to load.


<Deployment EntryPointAssembly="SilverlightApplication1">
  <Deployment.Parts>
    <AssemblyPart x:Name="SilverlightApplication1" Source="SilverlightApplication1.dll" />
    <AssemblyPart x:Name="Silverlight.Trace" Source="Silverlight.Trace.dll" />
    <AssemblyPart x:Name="System.Windows.Controls" Source="System.Windows.Controls.dll" />
    <AssemblyPart x:Name="System.Xml.Linq" Source="System.Xml.Linq.dll" />
    <AssemblyPart Source="en/SilverlightApplication1.resources.dll" />
    <AssemblyPart Source="fr/SilverlightApplication1.resources.dll" />
    <AssemblyPart Source="fr/System.Xml.Linq.resources.dll" />
    <AssemblyPart Source="fr/System.Windows.Controls.resources.dll" />
  </Deployment.Parts>
</Deployment>

How do I use a XAP file ?

You ? You don't use this file; Silverlight does. The only thing you have to do with it is copy it to your ASP.Net application.

Where do I deploy my XAP file ?

If you want to do it the common way, deploy it to the ClientBin folder. You can deploy it wherever you want but, I suggest you put it in the ClientBin folder.

Complete .Net COM+ interop class (COMAdmin wrapper)

If you already worked with the Interop.COMAdmin, you certainly said a lot of bad bad words ! Don't worry, you're not alone. Recently a friend of mine has to create an application that can export security from COM+ and then reimport it on another server. Because I love to do very technical code I decided to write a complete COM+ interop class that wraps the Interop.COMAdmin one. By doing this, my friend had a clean interop do deal with and so the tool to export security has been realized very quickly.

I decided to write a really clean interop so I spent about 3 days of work just to write this interop because no one on the internet has took the time to create it ! I want to share it with you because it can make you save a looooooooooot of time and frustration. You can download the code right here : ComPlusInterop.zip (31.45 kb).

The architecture behind that interop is as follow; there is a base class, COMElementBase, that wraps the access to the COMObject and there is derived classes matching the different things in COM+ such a applications, components, roles, methods and interfaces. Here is a list of the different classes the interop has :

  • ComponentServices
    • Represents the COM+ instance (can be from a remote server)
  • COMElementBase
    • The base class for the following classes
  • COMApplication
    • Represents an application
  • COMComponent
    • Represents a component (DLL)
  • COMInterface
    • Represents an interface of a component
  • COMMethod
    • Represents a method of an interface
  • COMRole
    • Represents a role definition
Because everything in COM+ is retrieved by string, I have a lot of constant values in each different class; each constant matches the internal property name. To retrieve a property value or to set one, the derived class (such as COMApplication) calls an internal method of the COMElementBase class. To simplify the process of casting property values, I created several methods in the base class to retrieve property values like GetPropertyValueBoolean, GetPropertyValueInteger and so on...

To show you in code what it looks like, here's the COMComponent class :


using Interop.COMAdmin;

using System.Runtime.InteropServices;

public class COMComponent : COMElementBase, IComparable
{
    #region "Members"

        private ReadOnlyCollection<COMInterface> _lstInterfaces;
        private ReadOnlyCollection<string> _lstRoles;
        private COMApplication _parent = null;

        private COMAdminCatalogCollection _colInterfaces = null;
        private COMAdminCatalogCollection _colRoles = null;

        private bool _dispose = false;

    #endregion

    #region "Constantss"

        internal const string NUMBER_ERROR_VERSION_INVALID = "0x8011042C";

        internal const string KEY_COLLECTION_INTERFACE = "InterfacesForComponent";
        internal const string KEY_COLLECTION_ROLES = "RolesForComponent";

        internal const string PROPERTY_NAME = "ProgID";
        internal const string PROPERTY_GUID = "CLSID";
        internal const string PROPERTY_DESCRIPTION = "Description";
        internal const string PROPERTY_DLL = "DLL";
        internal const string PROPERTY_MAJOR_VERSION = "VersionMajor";
        internal const string PROPERTY_MINOR_VERSION = "VersionMinor";
        internal const string PROPERTY_BUILD_VERSION = "VersionBuild";
        internal const string PROPERTY_SUBBUILD_VERSION = "VersionSubBuild";
        internal const string PROPERTY_TRANSACTION = "Transaction";
        internal const string PROPERTY_COMPONENT_TRANSACTION_TIMEOUT_ENABLED = "ComponentTransactionTimeoutEnabled";
        internal const string PROPERTY_COMPONENT_TRANSACTION_TIMEOUT = "ComponentTransactionTimeout";
        internal const string PROPERTY_ACCESS_CHECKS_ENABLED = "ComponentAccessChecksEnabled";
        internal const string PROPERTY_POOLING_ENABLED = "ObjectPoolingEnabled";
        internal const string PROPERTY_MAX_POOL_SIZE = "MaxPoolSize";
        internal const string PROPERTY_MIN_POOL_SIZE = "MinPoolSize";
        internal const string PROPERTY_CREATION_TIMEOUT = "CreationTimeout";
        internal const string PROPERTY_CONSTRUCTION_ENABLED = "ConstructionEnabled";
        internal const string PROPERTY_CONSTRUCTION_STRING = "ConstructorString";
        internal const string PROPERTY_JIT_ACTIVATION = "JustInTimeActivation";
        internal const string PROPERTY_EVENT_TRACKING_ENABLED = "EventTrackingEnabled";
        internal const string PROPERTY_MUST_RUN_IN_CLIENT_CONTEXT = "MustRunInClientContext";
        internal const string PROPERTY_MUST_RUN_IN_DEFAULT_CONTEXT = "MustRunInDefaultContext";
        internal const string PROPERTY_IS_PRIVATE = "IsPrivateComponent";
        internal const string PROPERTY_SYNCHRONIZATION = "Synchronization";
        internal const string PROPERTY_THREADING_MODEL = "ThreadingModel";
        internal const string PROPERTY_EXCEPTION_CLASS = "ExceptionClass";

    #endregion

    #region "CTOR"

        internal COMComponent(COMAdminCatalogObject COMobject, COMAdminCatalogCollection collection, COMApplication parent)
            : base(COMobject, collection)
        {


            _parent = parent;
        }

        protected override void Dispose(bool disposing)
        {

            base.Dispose(disposing);

            if (!_dispose)
            {

                if (_colInterfaces != null)
                {
                    Marshal.ReleaseComObject(_colInterfaces);
                }

                if (_colRoles != null)
                {
                    Marshal.ReleaseComObject(_colRoles);

                }
            }


            _dispose = true;
        }

    #endregion

    #region "Properties"

        public ReadOnlyCollection<COMInterface> Interfaces
        {
            get
            {

                if (_lstInterfaces == null)
                {
                    RetrieveInterfaces();
                }


                return _lstInterfaces;
            }
        }

        public ReadOnlyCollection<string> RolesCoches
        {
            get
            {

                if (_lstRoles == null)
                {
                    RetrieveRoles();
                }


                return _lstRoles;
            }
        }

        public string Name
        {
            get { return base.GetPropertyValueString(PROPERTY_NAME); }
        }

        public Guid ID
        {
            get { return base.GetPropertyValueGUID(PROPERTY_GUID); }
        }

        public string DLLPath
        {
            get { return base.GetPropertyValueString(PROPERTY_DLL); }
        }

        public string Version
        {
            get { return string.Format("{0}.{1}.{2}.{3}", base.GetPropertyValueInteger(PROPERTY_MAJOR_VERSION), base.GetPropertyValueInteger(PROPERTY_MINOR_VERSION), base.GetPropertyValueInteger(PROPERTY_BUILD_VERSION), base.GetPropertyValueInteger(PROPERTY_SUBBUILD_VERSION)); }
        }

        public string Description
        {
            get { return base.GetPropertyValueString(PROPERTY_DESCRIPTION); }
            set { base.SetPropertyValue(PROPERTY_DESCRIPTION, value); }
        }
        public bool ComponentTransactionTimeoutEnabled
        {
            get { return base.GetPropertyValueBoolean(PROPERTY_COMPONENT_TRANSACTION_TIMEOUT_ENABLED); }
            set { base.SetPropertyValue(PROPERTY_COMPONENT_TRANSACTION_TIMEOUT_ENABLED, value); }
        }

        public int ComponentTransactionTimeout
        {
            get { return base.GetPropertyValueInteger(PROPERTY_COMPONENT_TRANSACTION_TIMEOUT); }
            set
            {
                if (value < 0 || value > 3600)
                {
                    throw new OverflowException("La valeur doit être comprise entre 0 et 3600.");
                }
                else
                {
                    base.SetPropertyValue(PROPERTY_COMPONENT_TRANSACTION_TIMEOUT, value);
                }
            }
        }

        public bool AccessChecksEnabled
        {
            get { return base.GetPropertyValueBoolean(PROPERTY_ACCESS_CHECKS_ENABLED); }
            set { base.SetPropertyValue(PROPERTY_ACCESS_CHECKS_ENABLED, value); }
        }

        public bool PoolingEnabled
        {
            get { return base.GetPropertyValueBoolean(PROPERTY_POOLING_ENABLED); }
            set { base.SetPropertyValue(PROPERTY_POOLING_ENABLED, value); }
        }

        public int MaxPoolSize
        {
            get { return base.GetPropertyValueInteger(PROPERTY_MAX_POOL_SIZE); }
            set
            {
                if (value < 1 || value > 1048576)
                {
                    throw new OverflowException("The value must be between 1 and 1048576.");
                }
                else
                {
                    base.SetPropertyValue(PROPERTY_MAX_POOL_SIZE, value);
                }
            }
        }

        public int MinPoolSize
        {
            get { return base.GetPropertyValueInteger(PROPERTY_MIN_POOL_SIZE); }
            set
            {
                if (value < 0 || value > 1048576)
                {
                    throw new OverflowException("The value must be between 0 and 1048576.");
                }
                else
                {
                    base.SetPropertyValue(PROPERTY_MIN_POOL_SIZE, value);
                }
            }
        }

        public int CreationTimeout
        {
            get { return base.GetPropertyValueInteger(PROPERTY_CREATION_TIMEOUT); }
            set
            {
                if (value < 0 || value > 2147483647)
                {
                    throw new OverflowException("The value must be between 0 and 2147483647.");
                }
                else
                {
                    base.SetPropertyValue(PROPERTY_CREATION_TIMEOUT, value);
                }
            }
        }

        public bool ConstructionEnabled
        {
            get { return base.GetPropertyValueBoolean(PROPERTY_CONSTRUCTION_ENABLED); }
            set { base.SetPropertyValue(PROPERTY_CONSTRUCTION_ENABLED, value); }
        }

        public string ConstructionString
        {
            get { return base.GetPropertyValueString(PROPERTY_CONSTRUCTION_STRING); }
            set { base.SetPropertyValue(PROPERTY_CONSTRUCTION_STRING, value); }
        }

        public bool JITActivation
        {
            get { return base.GetPropertyValueBoolean(PROPERTY_JIT_ACTIVATION); }
            set { base.SetPropertyValue(PROPERTY_JIT_ACTIVATION, value); }
        }

        public bool EventTrackingEnabled
        {
            get { return base.GetPropertyValueBoolean(PROPERTY_EVENT_TRACKING_ENABLED); }
            set { base.SetPropertyValue(PROPERTY_EVENT_TRACKING_ENABLED, value); }
        }

        public bool MustRunInClientContext
        {
            get { return base.GetPropertyValueBoolean(PROPERTY_MUST_RUN_IN_CLIENT_CONTEXT); }
            set { base.SetPropertyValue(PROPERTY_MUST_RUN_IN_CLIENT_CONTEXT, value); }
        }

        public bool MustRunInDefaultContext
        {
            get { return base.GetPropertyValueBoolean(PROPERTY_MUST_RUN_IN_DEFAULT_CONTEXT); }
            set { base.SetPropertyValue(PROPERTY_MUST_RUN_IN_DEFAULT_CONTEXT, value); }
        }

        public bool IsPrivate
        {
            get { return base.GetPropertyValueBoolean(PROPERTY_IS_PRIVATE); }
            set { base.SetPropertyValue(PROPERTY_IS_PRIVATE, value); }
        }

    #endregion

    #region "Public methods"

        public void AssociateRole(string name)
        {

            if (_colRoles == null)
            {
                RetrieveRoles();
            }

            if (!_colRoles.AddEnabled)
            {
                throw new NotSupportedException("Associating role is not supported.");
            }

            if (this.Parent.IsSystem)
            {
                throw new NotSupportedException("Cannot associate role in a system application.");
            }

            if (!this.Parent.Changeable)
            {
                throw new NotSupportedException("The application does not support modifications.");
            }

            if (RoleAssociated(name))
            {
                throw new DuplicateNameException(string.Format("The role '{0}' is already associated with this component.", name));
            }

            COMAdminCatalogObject role = (COMAdminCatalogObject)_colRoles.Add();

            role.Value(COMRole.PROPERTY_NAME) = (object)name;

            _colRoles.SaveChanges();


            RetrieveRoles();
        }

        public void DissociateRole(string name)
        {

            if (_colRoles == null)
            {
                RetrieveRoles();
            }

            if (!_colRoles.AddEnabled)
            {
                throw new NotSupportedException("Dissociating role is not supported.");
            }

            if (this.Parent.IsSystem)
            {
                throw new NotSupportedException("Cannot dissociate role in a system application.");
            }

            if (!this.Parent.Changeable)
            {
                throw new NotSupportedException("The application does not support modifications.");
            }

            if (!RoleAssociated(name))
            {
                throw new KeyNotFoundException(string.Format("The role '{0}' is not associated with this component.", name));
            }

            for (int i = 0; i <= _colRoles.Count - 1; i++)
            {
                if (((string)((COMAdminCatalogObject)_colRoles.Item(i)).Name).ToLower() == name.ToLower())
                {
                    _colRoles.Remove(i);
                    break;
                }
            }

            _colRoles.SaveChanges();

            RetrieveRoles();
        }

        public void DissociateRoles()
        {

            for (int i = this.RolesCoches.Count - 1; i >= 0; i += -1)
            {
                DissociateRole(this.RolesCoches(i));

            }
        }

        public bool RoleAssociated(string name)
        {

            bool found = false;

            for (int i = 0; i <= this.RolesCoches.Count - 1; i++)
            {
                if (this.RolesCoches(i).ToLower() == name.ToLower())
                {
                    found = true;
                    break;
                }
            }


            return found;
        }

        public bool InterfaceExists(string name)
        {

            bool found = false;

            for (int i = 0; i <= this.Interfaces.Count - 1; i++)
            {
                if (this.Interfaces(i).Name.ToLower() == name.ToLower())
                {
                    found = true;
                    break;
                }
            }


            return found;
        }

        public COMInterface RetrieveInterface(string name)
        {

            COMInterface interfaceFound = null;

            for (int i = 0; i <= this.Interfaces.Count - 1; i++)
            {
                if (this.Interfaces(i).Name.ToLower() == name.ToLower())
                {
                    interfaceFound = this.Interfaces(i);
                    break;
                }
            }

            if (interfaceFound == null)
            {
                throw new KeyNotFoundException(string.Format("The interface '{0}' was not found.", name));
            }


            return interfaceFound;
        }

        public void Refresh()
        {

            RetrieveInterfaces();

            RetrieveRoles();
        }

        public int CompareTo(object obj)
    {


        return this.Name.CompareTo(((COMComponent)obj).Name);
    }

    #endregion

    #region "Private methods"

        private void RetrieveInterfaces()
        {

            _lstInterfaces = new ReadOnlyCollection<COMInterface>();

            _colInterfaces = base.GetCollection(KEY_COLLECTION_INTERFACE);

            for (int i = 0; i <= _colInterfaces.Count - 1; i++)
            {
                _lstInterfaces.Add(new COMInterface((COMAdminCatalogObject)_colInterfaces.Item(i), _colInterfaces, this));
            }


            _lstInterfaces.Sort();
        }

        private void RetrieveRoles()
    {

        _lstRoles = new ReadOnlyCollection<string>();

        _colRoles = base.GetCollection(KEY_COLLECTION_ROLES);

        for (int i = 0; i <= _colRoles.Count - 1; i++)
        {
            _lstRoles.Add((string)((COMAdminCatalogObject)_colRoles.Item(i)).Name);
        }


        _lstRoles.Sort();
    }

    #endregion
}

Once again, if you want to download the code, you can get it here : ComPlusInterop.zip (31.45 kb).
If you want a complete COM+ programming reference, you can check this MSDN article. This is where I got all the string needed to retrieve property values from COMAdmin.
Enjoy !

Microsoft Dynamics CRM 4.0 tabSet list

As I explained in another article, when you want to retrieve to name of the entity displayed in a CRM page it's easy to check for the etc parameter and then translate the number to a name by calling the MetaData service. But, as I also said, when you check for the page "areas.aspx", you don't have the entity number of the related entity... instead you get a tabSet parameter. The tabSet contains either the relationship name or a tabSet name...

Because the same related entity can have multiple tabSet names, I decided to list everything I found to simplify the job for you (I all got those using Fiddler and clicking on every link of related entities in CRM...).

Area name Related entity name
areaActivities ActivityPointer
areaActivityHistory ActivityPointer
areaAsyncOperations WFProcess
areaRelationships CustomerRelationship
areaRelationship CustomerRelationship
areaContacts Contact
areaSubConts Contact
areaSubAccts Account
areaAddresses CustomerAddress
areaOpps Opportunity
areaOpportunities Opportunity
areaQuotes Quote
areaOrders SalesOrder
areaInvoices Invoice
areaService Incident
areaCases Incident
areaContract Contract
areaListsInSFA List
areaCampaignsInSFA Campaign
areaProducts Product
areaSubs Product
areaComp Competitor
areaComps Competitor
areaSalesLit SalesLiterature
areaItems ProductPriceLevel
areaContractLines ContractDetail
areaRoles Role
areaTeams Team
areaServices Service
areaResourceGroup ResourceGroup
areaResourceGroups ResourceGroup
areaUsers SystemUser
areaBiz BusinessUnit
areaResource Resource
areaUnits UoM
areaPrices ProductPriceLevel
areaMembers SystemUser
areaExistingProducts QuoteDetail
SalesOrder
Invoice
areaWriteInProducts QuoteDetail
SalesOrder
Invoice

All this may save you some hours of clicking !

Posted: Apr 27 2009, 13:46 by WebMasterSam | Comments (43) RSS comment feed |
  • Currently 0/5 Stars.
  • 1
  • 2
  • 3
  • 4
  • 5
Filed under: .Net | CRM 4.0 | Programming
Social Bookmarks: E-mail | Kick it! | DZone it! | del.icio.us

Retrieving the entity name or number from a CRM page URL

Recently I had to create an HttpModule that catch every call to a CRM page and inject some JavaScript code in it. The first the HttpModule does is to retrieve to entity name (or number) and instanciates a class that contains some information about the page (organization, page url, entity name, entity number, etc...). Normally it is easy, you can get the etc querystring parameter to get the number and then call the MetaData service to get the entity name. Sometimes the querystring parameter gets another name... it's not always etc: it can be "etc", "oType" or "iObjType".

Life will be too easy if everytime when want to get the entity number we just have to check for the querystring parameter... so Microsoft though about a more challenging way to get the entity number. You will face that if you catch the "areas.aspx" page. This page is the one that is called when you click a left navigation bar item (in an entity record).

Let's check some different URLs you can get in CRM :

For the entity form...

...of CRM entities

 - http://crmserver/org/sfa/accts/edit.aspx (Account)
 - http://crmserver/org/sfa/conts/edit.aspx (Contact)
 - http://crmserver/org/activities/email/edit.aspx (Email activity)

...of user-defined entities

 - http://crmserver/org/userdefined/edit.aspx?etc=#entity_number#

These looks very easy to understand and it's easy to retrieve the entity numer or name. You will have to hard-code the entity number or name in your code when you get CRM entities because the URL (page) tells which entity it is. For the user-defined, you simply get the "etc" parameter in the QueryString.

For the entity record list...

...of the activites, campains and cases

 - http://crmserver/org/workplace/home_activities.aspx
 - http://crmserver/org/ma/home_camps.aspx
 - http://crmserver/org/cs/home_cases.aspx

...of anything else

 - http://crmserver/org/_root/homepage.aspx?etc=#entity_number#

These looks also very easy to understand but you will notice that barely everything (including user-defined entities) are shown in the same page (homepage.aspx) so you will not have to hard-code a lot of thing. And now, for the entity relationship view (the one you get on the right side of the CRM entity form when you click on a left navigation bar item of the entity form)...

...of CRM entities

 - http://crmserver/org/sfa/accts/areas.aspx?oId=[GUID]&oType=1&security=[number]&tabSet=areaContacts (Account showing related contacts)

...of user-defined entities (one-to-many relationship)

 - http://crmserver/org/sfa/accts/areas.aspx?oId=[GUID]&oType=1&security=[number]&tabSet=account_new_myentityname (Account showing related new_myentityname)

...of user-defined entities (many-to-many relationship)

 - http://crmserver/org/sfa/accts/areas.aspx?oId=[GUID]&oType=1&security=[number]&tabSet=areaaccount_new_myentityname (Account showing related new_myentityname)

This not sound very cool... the same querystring parameter (tabSet) can contains 3 different types of value... depending on whether the related entity is custom or built-in and if it is a one-to-many or many-to-many relationship... !

More horrible than this, for the same related entity (shown in different parent entity), the tabSet parameter will not be named the same way (but sometimes it is...). For example, if the related entity is a contact, the tabSet can be named "areaContacts" or "areaSubConts". Wow ! I don't have to tell you it has to be a lot hard-coded !

For the user-defined entities it is a lot more simple than this; the value in the tabSet is the relationship name. This is cool because you can query the CRM MetaData service and get both entities of the relationship. There's only a little twist with this... when you have a many-to-many relationship, the tabSet starts with "area"... I don't know why but MS decided to do it this way so we have to deal with it.

Because the tabSet parameter is a pain, I decided to write an article listing all the different tabSet values I've found in hour of clicking in CRM (because I didn't find it on the web).

Posted: Apr 27 2009, 12:55 by WebMasterSam | Comments (9) RSS comment feed |
  • Currently 0/5 Stars.
  • 1
  • 2
  • 3
  • 4
  • 5
Filed under: .Net | CRM 4.0 | Programming
Social Bookmarks: E-mail | Kick it! | DZone it! | del.icio.us

How to pass Culture and UI-Culture to Silverlight

Globalization is a common thing when you create websites that targets many different cultures (country and languages), so localizing your interfaces is a must.

If you already know how to pass parameters to a Silverlight application with the InitParams, you can think of passing your culture via those parameters and then set the culture at runtime. It will work, there's no doubt about it, but when a functionnality is supported by a specific technology, you must it the way it's used to be; not create a parallel functionnality.

So here's what you will have to add to you Silverlight HTML object code :


<param name="culture" value="fr-CA" /><param name="uiculture" value="fr-CA" />

It's easy and clean !

Posted: Apr 26 2009, 18:13 by WebMasterSam | Comments (7) RSS comment feed |
  • Currently 0/5 Stars.
  • 1
  • 2
  • 3
  • 4
  • 5
Filed under: .Net | Programming | Silverlight
Social Bookmarks: E-mail | Kick it! | DZone it! | del.icio.us

Multiline TextBox in Silverlight 2.0

If, like me, you create some Silverlight applications, you probably said the same thing I said today : "Where the hell is the TextMode property of the TextBox ?!". In Silverlight it does not exist... so you can't set it to "Multiline" when you want a "TextArea" so you can type the Enter key so it adds a carriage return to the content of the TextBox.

The property's not completely gone... but the new property you get only gives you the chance to accept EnterKey or not; it's name is AcceptReturn. Use it like this :


<TextBox AcceptsReturn="True" VerticalScrollBarVisibility="Visible" HorizontalScrollBarVisibility="Auto" />

Easily pass parameters to Silverlight (and retrieve it everywhere in your app)

 

When you want to pass values (parameters) to a Silverlight application from within a web page, you can use the "initParams" fixed parameter. By default it does not contain anything. If you want to use it you must declare it this way :

You will notice that all the params must be passed within a single comma-separated string. Silverlight works this way. Because it works this way, you don't have to manually parse that string to get parameters and values, Silverlight parses it for you. If you want to retrieve a value of a parameter you passed, you can check on the dictionnary of parameters. The dictionnary is available on the StartupEventArgs of the Application_Startup event.

Because I wanted my parameters to be available everywhere and aasily parseable to the right type I created this simple helper class that wraps all the logic behind initParams.


    public class StartupParameters
    {
        #region Members

            private static IDictionary<string, string> _initParams = null;

        #endregion

        #region Methods

            public static void Initialize(IDictionary<string, string> initParams)
            {
                _initParams = initParams;
            }

            public static string GetParameter(string name)
            {
                if (ParameterExists(name))
                    return _initParams[name];
                else
                    return string.Empty;
            }

            public static bool GetParameterBoolean(string name)
            {
                bool blnResult = false;

                if (bool.TryParse(GetParameter(name), out blnResult))
                    return blnResult;
                else
                    return false;
            }

            public static int GetParameterInteger(string name)
            {
                int intResult = 0;

                if (int.TryParse(GetParameter(name), out intResult))
                    return intResult;
                else
                    return 0;
            }

            public static long GetParameterLong(string name)
            {
                long lngResult = 0;

                if (long.TryParse(GetParameter(name), out lngResult))
                    return lngResult;
                else
                    return 0;
            }

            public static bool ParameterExists(string nom)
            {
                return _initParams.ContainsKey(nom);
            }

        #endregion
    }

The only thing you have to do to properly use this class is to initialize it using the Initialize method receiving the initParams dictionnary, just like this :


     private void Application_Startup(object sender, StartupEventArgs e)
     {
           StartupParameters.Initialize(e.InitParams);

           this.RootVisual = new SilverlightControl1();
     }

Now you always have your parameters with you wherever you are in your Silverlight application.

Posted: Apr 25 2009, 14:03 by WebMasterSam | Comments (54) RSS comment feed |
  • Currently 0/5 Stars.
  • 1
  • 2
  • 3
  • 4
  • 5
Filed under: .Net | Silverlight
Social Bookmarks: E-mail | Kick it! | DZone it! | del.icio.us

How to start a forum from scratch

Everybody knows what is a forum and some of you already posted on it. When you post on forums you normally ask for a question of answer one, but if you find a forum that is totally empty, would you post a question on it ? Most probably not because you absolutely don't know if your question will be answered. In fact, if you post on an empty forum it is because you already posted on many many forums and you don't got the answer you wanted, so you're desperate.

So if you want people to post on your forum (whatever the subject of the forum is) you have to create some posts. There is 2 possible ways to do this :

1. Create it all by yourself

If you have time and a lot of content you want to post on it, you can create your own posts. If you take that strategy, be sure to create many accounts so you don't answer all the questions with the same user that asked it. I already did that by myself one time, but if your time is money (like mine), you probably don't want to spend a lot of time writing posts. If so, you will love the second way to fill a forum.

2. Pay someone to do it

If you don't want to do it by yourself, just pay someone to do it. It can be as simple as asking one of your friend to post on your forum and tell him that you will buy him something he want or you will pay him for every post he writes on your forum. If you don't want to bother your friends, you can pay an internet-only enterprise (that is specialized in forum posting) to do it. There is more and more people who are willing to pass their days writing posts on different subject and get paid to do it.

I personnaly paid some internet companies to post on my forums and it saved me a lot of time. I was surprised to see how good the posts were; they don't look fake. Normally it costs from 10 to 25 cents per post. The more posts you buy, the cheapest it is by post.

Here are some forum posting companies (the best I know so far) :

http://www.seofocused.com
http://www.contentcurrent.com
http://www.velocityposting.com

For my experience, if you try to start a forum without boosting it, you will only get a few posts and they will take a long time to arrive... so you absolutely need to add content to boost it.

Posted: Apr 23 2009, 13:53 by WebMasterSam | Comments (33) RSS comment feed |
  • Currently 0/5 Stars.
  • 1
  • 2
  • 3
  • 4
  • 5
Filed under: Forums
Tags: ,
Social Bookmarks: E-mail | Kick it! | DZone it! | del.icio.us