Tuesday, February 3, 2009

OBA Part 1 - Exposing Line-of-Business Data

 

OBA Part 1 - Exposing Line-of-Business Data

Published 03 February 09 02:23 PM

Last post I talked about the high-level architecture of our Office Business Application for the new Northwind Traders. There are a lot of different architecture options to consider when building an OBA. OBA is all about using Microsoft Office with your Line of Business (LOB) data. Whether that involves using SharePoint as well depends on the application. Since we wanted to store the unstructured data (the Northwind customer P.O.)OBAdiagram SharePoint is a good fit here.

There are a lot of options when thinking about how to expose your LOB data. For instance, you may already have a service oriented architecture at the enterprise that exposes data contracts and processes that you can consume from Office clients. Or maybe you have a small business and have decided to expose a simple service that returns and consumes n-tier DataSets directly. Or you already have a custom LOB data entry system using custom business objects and you want to reuse the business layer in the Office client. OBA doesn't dictate how you expose this data. Because you can consume data in Office clients the same way you do in Windows apps the same types of decisions need to be made.

When we sat down to write the new Northwind Traders application we thought about how our data would need to behave and what would be the best way for all the pieces to easily update and query the Northwind database. Because there was only going to be simple validations needed on the data and mostly CRUD operations we opted to expose an Entity Data Model via ADO.NET Data Services like I showed before. This allowed us to get a secure service up and running in minutes.

We did make some minor changes to our old friend, the Northwind database. First, since we wanted to be able to look up order history for a customer when they emailed the sales reps, we needed to add an EmailAddress field to the Customers table (amazing that we didn't have that field before!). We also added it to the Employees table.

ALTER TABLE dbo.Customers ADD
EmailAddress varchar(50) NULL
GO
ALTER TABLE dbo.Employees ADD
EmailAddress varchar(50) NULL
GO



Then we populated the data with some customers and employees that were actually folks on our team because we need real email addresses to work with :-)



Next I created a new ASP.NET Web Application and added an ADO.NET Data Service and an Entity Data Model just like how I showed in this post. (You will need Visual Studio 2008 Service Pack 1 in order to get these new item templates.) For testing we set the service to allow full access to all the entities in the model  -- we'll lock it down later. I also am passing detailed errors which we won't want to do once we're in production:



Public Class Northwind
Inherits DataService(Of NorthwindEntities)

' This method is called only once to initialize service-wide policies.
Public Shared Sub InitializeService(ByVal config As IDataServiceConfiguration)
config.SetEntitySetAccessRule("*", EntitySetRights.All)
config.UseVerboseErrors = True
End Sub
End Class


One thing we did want to do is set up our data model so that it enforced constraints (i.e. there cannot be an Order without a Customer) but since some of our legacy data didn't specify all of these constraints we made the changes to the model instead, so that the integrity on all new data would be enforced through the service. This is often the case in projects, you cannot change the legacy databases but you still need to work with proper data models. So we changed the EDM so that all the entities were singular and not plural (Customer instead of Customers, Order instead of Orders, etc). We also changed the associations so that they were enforced and so that one to many collections were plural and the one-to-one were singular (i.e. Order has Order_Details collection and Order_Detail has Product reference). You can modify these from the Properties window of the Entity Data Model Designer.



OBAedm



Once I have the model and the data service code set up we can hit F5 and navigate our browser to the Northwind.svc and test the call to pull up all the customers (i.e. http://localhost:1234/Northwind.svc/Customers) just like how I showed in this post.



Now that we have our data exposed as a data service we can build the Office clients to interact with it just like I showed before here when we built a simple Excel client. Next post I'll show how we can use WPF controls in an Outlook Add-In in order to display the customer order history by querying the data through the data service.



Until next time...



by Beth Massi




Beth Massi - Sharing the goodness that is VB : OBA Part 1 - Exposing Line-of-Business Data

Where did my disk space go ? [very long directory names, system directories, symbolink and other hard links ...], meet xdir.exe

 

Where did my disk space go ? [very long directory names, system directories, symbolink and other hard links ...], meet xdir.exe

Published 03 February 09 09:00 PM | gbordier

    Hi geeks,

    The first thing I do when I receive a new Microsoft-issued laptop is : buy the largest hard disk I can find to replace the one that shipped with it.

    But time goes ... and soon I wonder where all that disk space went.

    The other day one of my customers called me with that stance : "Guillaume, I've got a LUN on my SAN with 350GB I cannot account for".

    Where can there be content that is hidden from the system administrator himself ?

    1. System Volume Information is a folder with SYSTEM only  permissions (so administrators do not have access) that can contains many things from Single Instance storage data, recovery points, snapshots ...
    2. very long (> 256 chars) directories are not displayed by either explorer  or the command line because one needs to use the Unicode syntax when accessing those and for some reason our developpers did not.

    To make the story short my customer had VSS shadow storage defined on that LUN for snapshot of a different LUN, so using vssadmin delete shadows or vssadmin delete shadowstorage got his content back.

    But this incident convinced me to launch my could old C++ compiler to build a tool that deals with those kind of nightmares: meet XDIR.EXE

    XDIR is a combination of DIRUSE.EXE (the close parent of DU from Windows 2000 resource kit), RD (remove directory from the nt shell), and dir.

    BUT XDIR.EXE handles directories of any length, or permissions , it does try to follow symbolink links and you can use it to:

    • reclaim disk space : calculate the size of that particular directory and any subdirectory :
      try XDIR c:\
    • read a very long (or any) directory :
      try XDIR <your long directory> /DIR
    • delete a very long directory :
      try XDIR <your long directory> /RMDIR

    Options are :

    /MIN:xxx[GB|MB|KB|B]
    Minimum size to report (default is 500 MB)

    /LEV:nn
    Number of directory levels to display, default is 2, 0 will display all directories

    /FORCE
    known to the user of FILEACL, this will let you use your  SeBackupPrivilege and  SeRestorePrivilege to bypass the NTDS permissions (you need to be backup operator obviously)

    There are also a few other options that let you create an ANSI or UNICODE output file that came directly from FILEACL.

    Let’s try this: where is there content that weight more than 500MB on my C drive ?

    image

    What are those 2 GB in my System Volume Information ?

    image

    Well, that seems like a bunch of restore points to me. Wait, I’m running Windows Server 2008 and restore points are not a server feature ! Where are those kids from ? ohh, maybe from my old Windows XP dual boot !

    Let’s see

    image

    Files from last november? sounds like it’s the last time I booted my XP boot to flash my Windows Mobile, ok, let’s get rid of those (I’ll use the supported method of rebooting to XP and ask nicely that it delete those useless recovery points)

    let’s use another example:

    See that dir ?

    imageimage

    Empty folder, big deal ?

    image

    why the heck, does xdir.exe think there is 1.167 GB in that dir ?

    image

    Well somebody twisted (me) has put 1 Gig into this stupidly long directory.

    Side Note : it is just impossible to go there with explorer so what can I do ? :

    • Create a JUNCTION point
      mklink  /J e:\temp\mount "e:\temp\01234567890123456789012345678901234567890123456789\01234567890123456789012345678901234567890123456789\01234567890123456789……

    • Create a DIRECTORY SYMBOLIC LINK
      mklink  /D e:\temp\mount "e:\temp\01234567890123456789012345678901234567890123456789\01234567890123456789012345678901234567890123456789\01234567890123456789……
    • use the good old SUBST from DOS :)

    if you just want to get rid of it use :

    XDIR <your long directory> /RMDIR

    like this:

    image

    add the /FORCE option if you do not have the rights on all folders (like with System Volume Information)

    Guillaume

    Filed under: XDIR long directories folder size

    Attachment(s): xdir.exe

    Guillaume Bordier's blog : Where did my disk space go ? [very long directory names, system directories, symbolink and other hard links ...], meet xdir.exe

    Database Programming: OBJECT_NAME Takes Two Parameters

     

    Database Programming: OBJECT_NAME Takes Two Parameters

    This post is a cousin of sorts to last December’s post regarding the OBJECT_ID function.  I learned this from Dirk Gubbels when he sent me a follow-up email to the seasonal T-SQL he passed along (the posting of which spawned a small flood).  This “trick” is actually a SQL Server 2005 enhancement, so it will also work in SQL Server 2008, but unfortunately not in SQL Server 2000.

    Here’s a small T-SQL script which makes the point succinctly:

    set nocount on
    use msdb
    go
    -- note the feature we "discovered" in the previous post
    select object_id('master.dbo.sysdatabases')
    -- use the functionality of the previous discovery to build
    -- an environment-neutral demonstration of the new "discovery"
    select object_name(object_id('master.dbo.sysdatabases'),db_id('master'))
    -- this instantiation of the new discovery should produce identical
    -- results to the above
    select object_name(-202,6)
    go

    This script will produces the following output on the SQL Server 2005 instance on my laptop:

    -----------
    -202

    -------------------
    sysdatabases

    -------------------
    sysdatabases

    In SQL Server 2000, if you wanted to run the OBJECT_NAME() function outside the current database context, dynamic SQL was the only option (to either build a call to OBJECT_NAME() or the correct copy of the sysobjects table).  This new syntax allows the call to be built in-line, which offers an additional arrow in the quiver of T-SQL coders on the SQL Server 2005 and SQL Server 2008 platforms.

    Thanks for the head-up, Dirk!

    -wp

    Published Tuesday, February 03, 2009 7:45 PM by Ward Pond

    Ward Pond's SQL Server blog : Database Programming: OBJECT_NAME Takes Two Parameters

    Top Tips for InfoPath form development with SharePoint: Part 1

     

    Top Tips for InfoPath form development with SharePoint: Part 1

    Published 03 February 09 11:00 AM

    InfoPath is a great tool that allows you to quickly design and implement browser-based electronic forms which can be delivered as HTML via SharePoint Form Services. Historically there hasn’t been much information available around best practices for development of browser-based InfoPath forms and most organisations have tended to define their own though trial and error.

    The purpose of this article is to share some of the tips and tricks that we’ve gathered from developing InfoPath forms over the past few years….. some of them may be obvious, but  if even one of of these tips is new to you, then this article will have been a worth-while exercise. This is just part 1, we intend to do several more articles covering similar tips and tricks so if you like this, watch out for part 2 soon.

    The article has been co-authored between Martin Kearn (from Microsoft) and Jay Goodison (from WCI Healthcare and Ascribe Consulting). WCI Healthcare (An Ascribe business) are a Microsoft Gold Partner who maintain specialist knowledge and expertise in designing and implementing SharePoint and InfoPath solutions, Jay brings a lot of invaluable experience to this article.

    These tips are mostly in the context of SharePoint based browser enabled forms.

    Tip 1: Using the SharePoint object model in browser-based forms

    You can implement .net managed code with any InfoPath Form using VSTA (Which is an optional install for the InfoPath client). If you design a form where the target client is only a browser, then effectively all business logic and associated code is executed on the SharePoint web servers, this means that you can directly access and use the SharePoint object model from within the InfoPath form’s code.

    Simple instructions to use the SharePoint object model within InfoPath Forms:

    1. You’ll need to be developing this on a SharePoint server to do this (as with most SharePoint development)

    2. In your form code (InfoPath Design Mode > Tools > Programming > Loading Event) add a reference to the relevant SharePoint DLL that you wish to use. These are located in C:\Program Files\Common Files\Microsoft Shared\Web Server Extensions\12\ISAPI\. For the purposes of this example, we’ll be using the very popular Microsoft.SharePoint.dll. You can add a reference by right-clicking on References, choosing Add Reference and then browsing to the DLL you want.

    3. At the top of your code (just below using mshtml;) add the following line. This simply provides a shortcut directly to the objects located in the Microsoft.SharePoint.dll to save you typing the entire namespace each time

    using Microsoft.SharePoint;

    You can now directly access the SharePoint object model. However a word of warning … not all forms are browser-based (this only works in browser-based forms), therefore it is wise to do a check in your code before using and SharePoint API calls. If you wish to design a form which utilises objects from the SharePoint API and can be authored using the InfoPath client you will need to connect to SharePoint web services (because the code is executing on eth client, not the server, therefore the full SharePoint API is not available).

    4. Add the following code to your FormEvents_Loading method. This checks whether the form is browser-based or not:

    if (this.Application.Environment.IsBrowser)
    {
    }

    Now you can add your SharePoint code.

    5. Add your SharePoint code within the if statement you created in step 4. For example this code will get the SPWeb object for the site that the form is being rendered from:

    SPWeb web = SPContext.Current.Web;

    Tip 2: Using Visual Studio 2008 to develop your forms

    In addition to InfoPath 2007, it is now possible to develop develop InfoPath forms using Visual Studio 2008.

    Visual Studio added full support for designing and developing InfoPath forms in the 2008 version. There is no major functionality over and above InfoPath 2007, it is just that your form project gets to be in the same Visual Studio solution as everything else and can be subject to the same source control etc and Visual Studio does also give you a slightly nicer development environment to work in.

    Another benefit is that Visual Studio works directly with the source files of the form rather than the XSN (which is basically a zip file) this means that it is easier to do advance development where you regularly need to access specific files.

    To try this out simply go to Visual Studio 2008 > File > New > Project > Office > InfoPath 2007 Form Template. You will then be presented with the same ‘Design a form template’ wizard that you would see if you had use InfoPath itself to design your form.

    Tip 3: Saving a form where it was created by using the SaveLocation URL Parameter

    One of the biggest issues with using the default ‘Submit …. To a document library on a SharePoint site’ data connection is that you are forced to hard-code a URL which means that forms will always be submitted to that location. This is fine for some scenarios, however I find it causes the following issues:

    • Complicates Dev > Live deployment
    • Forms often need to get submitted to the library where they were first created. This is especially true if you have published the form as a Content type or Administrator-approved form which can be re-used in multiple locations

    Thankfully, using browser-based forms there is a way to get around this. With browser-based forms, there are a range of URL-based Input Parameters that contain parameters including the ‘SaveLocation’ which is the URL to the library where the form was first created. Using this parameter it is possible to intercept the FormEvents_Submit method and actually save the form to the location it was created rather than the hard-coded URL in the data connection.

    To do this, follow these steps:

    1. Setup- a ‘Submit …. To a document library on a SharePoint site’ data connection as usual. Set a dummy URL as this will be over-written Call the data connection ‘SharePoint Library Submit’.
    2. Create a property in your form’s main data source called ‘MySaveLocation’. Please not that you could also use the FormState Dictionary to store this value (as outlined in Tip 5). We are only storing as a form property here to show different ways of doing things.
    3. Add the following code to your FormEvents_Loading method. This captures the Input Parameter and stores it in a form property called MySaveLocation.

    XPathNavigator xn = this.MainDataSource.CreateNavigator();
    XPathNavigator xnSaveLocation = xn.SelectSingleNode("/my:myFields/my:MySaveLocation", this.NamespaceManager);
    if (this.Application.Environment.IsBrowser)
    {
        if (xnSaveLocation.Value == "")
        {
            xnSaveLocation.SetValue(e.InputParameters["SaveLocation"].ToString());

        }
    }

    4.Add the following code to your FormEvents_Submit method. This intercepts the operation of the data connection and replaces the URL with the contents of our MySaveLocation property:

    XPathNavigator xn = this.MainDataSource.CreateNavigator();
    XPathNavigator xnSaveLocation = xn.SelectSingleNode("/my:myFields/my:MySaveLocation", this.NamespaceManager);
    FileSubmitConnection fc = (FileSubmitConnection)this.DataConnections["SharePoint Library Submit"]; //replace with with the name of your data connection
    fc.FolderUrl = xnSaveLocation.Value.ToString();
    fc.Execute();
    e.CancelableArgs.Cancel = false;

    When users submit the form, it will now be saved in the library where it originated, and not the hard-coded URL in the data connection.

    Tip 4: Using the Contact Selector Control

    Have you have ever wondered how to use the SharePoint Content Selector control in your forms? The guys on the InfoPath team blog wrote an article all about it: http://blogs.msdn.com/infopath/archive/2007/02/28/using-the-contact-selector-control.aspx

    My three pieces of advice are as follows:

    1. Watch your case, ALL of this stuff is case sensitive

    2. You do not need the context.xml file for browser-based forms

    3. If you intend to use the control several times in the same form to store different contact details, you will need to create the data structure once and reference it elsewhere in your data structure. To do this right click on the gpContactSelector folder > Reference > Choose the location where you want to create the referenced properties.

    Tip 5: Using the FormState Dictionary to store variables

    As with any development projects there is usually a requirement to create member variables to prevent the creation of multiple objects storing the same data.

    When you first create your code project to support your InfoPath form you will note a commented section which states member variables are not supported. Below is an example on how to use the FormState Dictionary to store objects which is the supported and recommended alternative to member variables.

    In our experience developers do not make best user of the FormState dictionary so here’s a simple example which simple stored the current username as a FormState Dictionary Object:

    1. Create a dictionary object called ‘_currentUserId’. Add this code right beneath you main class declaration

    private object _currentUserId
    {
        get
        {
            return FormState["_currentUserId"];
        }
        set
        {
            FormState["_currentUserId"] = value;
        }
    }

    2. Set the value of the dictionary object. In this example we’ll do this in the FormEvents_Loading method. Add this code to your FormEvents_Loading method.

    _currentUserId = this.Application.User.LoginName.ToString();

    3. Now the dictionary object is set. you can refer to it in your code. This example simply adds the value to an InfoPath property.

    XPathNavigator xn = this.MainDataSource.CreateNavigator();

    XPathNavigator xnCurrentUser = xp_Root.SelectSingleNode("//my:CurrentUser", this.NamespaceManager);          
    xnCurrentUser.SetValue(_currentUserId.ToString());

    This is quite a rudimentary example but hopefully demonstrates a real world example of using the FormState Dictionary object. 

    This article was co-authored by


    Martin Kearn
    Senior Consultant
    Microsoft Consulting Services UK
    Martin.Kearn@Microsoft.com

    Click here for my bio page

    Jay Goodison
    Senior Consultant 
    Ascribe (Consulting Division)
    Jay.Goodison@Ascribe.com

    by ukpoint

    UK SharePoint Team : Top Tips for InfoPath form development with SharePoint: Part 1

    Help! My Application only runs on a Single Processor system!

     

    Help! My Application only runs on a Single Processor system!

    Given that multi-core / multi-processor systems are the way of the future, this might seem like an odd post for us to be writing.  However, we have run into a few instances where 32-bit applications run with no issues on single processor systems, but fail to run, or perform poorly on multiprocessor systems.  I know that seems counterintuitive, but if an application is not multi-processor aware you may experience issues.  In cases such as these, there are a couple of different approaches you might try:

    Method 1:  Setting processor affinity via Task Manager

    If the application is already running, then you can use the “Set Affinity” feature within Task Manager on Windows XP and later operating systems.  To use this feature, highlight the process within Task Manager, right-click and select the “Set Affinity” option as shown below:

    image

    You are then presented with the option to select your processor affinity:

    image

    Remember that this option is only available on Windows XP and later operating systems.

    Method 2: Use IMAGECFG.EXE to permanently set the processor affinity for the executable (Windows 2000 Systems)

    Using this method you can permanently bind an executable to a processor.  IMAGECFG.EXE was available on the Windows 2000 Server Resource Kit (Supplement One).  To set the processor affinity, use the following syntax from a Command Prompt:  imagecfg –a 0xn <full path to the executable>.  In our sample syntax, 0xn is the processor affinity mask.  This mask is what restricts the application to a specific CPU.  To determine the correct mask, use the table below:

    CPU Number Mask
    0 0x1
    1 0x2
    2 0x3
    3 0x4

    You can use this syntax to set the affinity all the way up from processors 0 through 31.  Remember that this would be the preferred method on a Windows 2000 system, where you cannot dynamically change the processor affinity via Task Manager.

    Method 3: Use PsExec to set the Processor Affinity for an executable on Windows XP and later OS versions

    In Method 1 we used Task Manager to set the processor affinity for an application that had already been launched.  But, what happens if you need to launch the application with a specific processor affinity?  You could certainly use Method 2, but you might have to hunt around for a copy of IMAGECFG.EXE.  Instead you can use PsExec to pre-set your processor affinity.  We normally use PsExec to execute programs on remote systems, but we can also use it to set processor affinity.  The syntax is as follows:  PsExec.exe –a 0 <path to executable>.  The –a switch sets the affinity of the process – in this case to Processor 0.  This isn't a permanent method of setting the affinity, although you could create a .cmd or .bat file that launched the executable using these parameters.

    Method 4: Use the /AFFINITY switch (Windows Server 2003 and later)

    You can also use the START command with the /AFFINITY switch to start the program with whichever processor affinity you wish to set.  This is not a persistent method (unlike Method 2) – you would need to use the command each time, or launch the program via a batch file or a similar mechanism.  The syntax for this command is as follows: START /AFFINITY <processor number> <program>.  The processor number is defined by the same mask we used above in Method 2.

    To verify that your process is started with the proper processor affinity, launch Task Manager once the process has launched.  Right click on the process name and click on “Set Affinity” – the processor number that you set should be the one that is checked.  Here’s an example from my Windows Server 2008 system – using Notepad as my test application I want to constrain Notepad to CPU 0.

    image

    Notepad launches successfully – now let’s check Task Manager:

    image

    And as we can see, Notepad was launched and is constrained to CPU 0.

    With that we’ve come to the end of this post.  As always – thanks for stopping by!  Until next time …

    - CC Hameed

    Ask the Performance Team : Help! My Application only runs on a Single Processor system!

    Monday, February 2, 2009

    Building an Office Business Application for TechReady 8

     

    Building an Office Business Application for TechReady 8

    Published 02 February 09 07:09 PM

    The last week I've been working with a couple teammates on an Office Business Application (OBA) demonstration we wanted to put together for TechReady 8. TechReady is an internal conference we deliver to the field employees. It's my first one and so far it's been pretty fun. This morning Scott Hanselman hosted an "UnKeynote" that showcased a lot of the new stuff coming in Visual Studio 2010 that I can't talk about yet but believe me it was cool. ;-)

    The application I helped build with Rachel and Mike on my team was a total blast. We wanted to demonstrate using Office as a client to Line-of-Business data as well as show some of the nifty features of SharePoint workflows. So we decided it was time to bring poor Northwind Traders into the 21st century. Here's what we came up with:

    OBANorthwind

    We needed a way to easily and securely expose our LOB data -- the Northwind database -- to the enterprise. So we created an entity data model against our SQL Server Northwind database and then exposed it using ADO.NET Data Services . The Northwind sales reps do all of their work in Outlook, communicating with our high-volume customers. So we built an Outlook Add-in that they can use to look up the customer order history from the database and easily work with purchase orders. The Add-in displays a form region with the customer order history in an adjoining area below when they open the email.

    The orders are then submitted to a SharePoint document library as Word 2007 documents with XML data specified via content controls. A SharePoint Workflow then shreds this document data (using VB XML literals support and the Open XML SDK) and passes it to the ADO.NET data Service which persists the data in the database. This was written as a SharePoint sequential workflow.

    We also built an Excel client for our shipping department to use that displays the orders that are ready to ship which allows them to update the ship date and save it back to through the data service. It also shows the products and quantities ordered in an Action Pane and does some data analysis using a pivot table and pie chart to show the breakdown of shipping cities and freight cost. We also used WPF user controls in the Outlook and Excel clients so that we could match the look-and-feel of Office.

    Finally we created a web part for the SharePoint site that displays key performance indicators like oversold products and our top customer sales.

    I have to say it was a very fun project to work on and I personally learned a lot about Excel and WPF as well as the OBA architecture. We spend about 3 days building this and I was pleasantly surprised how well the tools worked together. The pieces we built were predominantly in Visual Studio 2008 by Rachel and I and Mike handled the bleeding edge SharePoint parts.

    In the next few blog posts I'll go into the details of building WPF user controls in the Excel and Outlook clients and how we exposed a simple, clean data model against the not-so-clean Northwind database. I'll also show the technique of how we easily stripped the order data out of the Word 2007 documents.

    For now, some good links on OBA and VSTO development:

    Until next time....

    by Beth Massi

    Beth Massi - Sharing the goodness that is VB : Building an Office Business Application for TechReady 8

    Custom Search Engine to Search All Dynamics GP Blogs

     

    Custom Search Engine to Search All Dynamics GP Blogs

    Jivtesh Singh has been busy.  With the explosion of Microsoft Dynamics GP blogs it has become much harder to find the information you want.  There are now over 20 sites in my list of Microsoft Dynamics GP Blogs.

    Jivtesh has taken on this challenge and created a custom search that will search all of the blogs in the list.  As new blogs are added to the list we will make sure that the custom search gets updated to match.

    For more information, including the link to the search, please see the article below:

    Custom Search Engine to Search All Dynamics GP Blogs

    Well done and thanks to Jivtesh.

    David Meego

    David

    Posted: Tuesday, February 03, 2009 9:00 AM by David Musgrave

    Developing for Dynamics GP : Custom Search Engine to Search All Dynamics GP Blogs

    Blog Archive