European Silverlight 4 & Silverlight 5 Hosting BLOG

BLOG about Silverlight 5 Hosting and Its Techologies - Dedicated to European Windows Hosting Customer

Silverlight 5 Hosting Spain - HostForLIFE.eu :: Overcome Silverlight Caching when using CRM OData Endpoint

clock April 30, 2014 07:42 by author Peter

The Problem

Recently I was creating a Silverlight based app for one of our customers on my Silverlight 5 Hosting Spain. The applications runs on the contact form embedded in an IFrame. It used OData to retrieve a defined set of columns for the contact. All was looking nice, until I noticed, that after changing some of the columns and saving the contact, my Silverlight application still used old data – even after refreshing the page.

Internally it used an HttpWebRequest object to retrieve data from CRM via a URL like /XrmServices/2011/OrganizationData.svc/ContactSet(GUID’{0}’)?$select=FullName. Unfortunately Silverlight aggressively caches Web-Requests. Since the URL for a contact never changes, Silverlight does not even really do a further request and pulls data from the cache instead. The HttpWebRequest class in the Silverlight Framework does not provide means to disable caching. There are a lot of posts and forum topics out there discussing that problem.

The solution
Obviously the problem is, that the URL does not change. To overcome the caching issue, we just have to make sure, the address changes on every request. The solution I came up with was simply adding a made up parameter to the URL. Fortunately the OData Endpoint simply ignores unknown parameters. I called it ignoreCache, but of course any name will do here. The value of that parameter will be set to basically the current time. Now the URL changes on every request and we can work with the actual data:

Dim urlRaw As String = String.Format(“/XrmServices/2011/OrganizationData.svc/ContactSet(GUID’{0}’)?$select=FullName&IgnoreCache={1}”, id, Date.Now.TimeOfDay.Ticks)



FREE Silverlight 5 Spain Hosting - HostForLIFE.eu :: Silverlight: Updating the XAP Configuration Programmatically

clock March 20, 2014 08:16 by author Peter

One of the bigger annoyances dealing with programming in Silverlight 5 is the deployment of XAP files. In order to properly update a XAP file you typically:

1. Rename XAP file to a ZIP file.

2. Extract the ServiceReferences.ClientConfig file.

3. Update the configuration file with the proper IP information.

4. Update the ZIP file and save.

5. Rename ZIP file back to XAP.

So, how do we do that with code so we can skip this frustration? Let’s first look at a few factors:

We are using the .Net 4.0 Framework. Don’t bother using System.IO.Packaging.ZipPackage. It thinks XAP files are always corrupt. It’s annoying. We are just updating the IP information. First, let’s look at how we update the configuration file if it was committed to a MemoryStream. In this snippet we:

1. Grab all the contents from the MemoryStream.

2. Replace the IP information in the content.

3. Clear the MemoryStream.

4. Overwrite the stream contents with the new content.

5. Reset the position in the stream to 0.

/// <summary>
/// Updates the configuration file
/// </summary>
/// <param name="stream">The stream.</param>
/// <param name="replacementIp">The replacement ip.</param>
/// <param name="destinationIp">The destination ip.</param>
/// <returns></returns>
private bool UpdateConfigurationFile(MemoryStream stream,
    string replacementIp, string destinationIp)
{
    bool isSuccessful = false;
    try
    {
        // Read current file
        var reader = new StreamReader(stream);
        stream.Position = 0;
        var contents = reader.ReadToEnd();       
        // Update IP information
        var newContents = contents.Replace(replacementIp, destinationIp);
        // Reset contents of stream
        stream.SetLength(0);
        // Overwrite original configuration file
        var writer = new StreamWriter(stream);
        writer.Write(newContents);
        writer.Flush();
        // Set position in stream to 0.
        // This allows us to start writing from the beginning of the stream.
        stream.Seek(0, SeekOrigin.Begin);
        // Success
        isSuccessful = true;
    }
    catch (Exception)
    {
    }
    // return
    return isSuccessful;
}

Our main method below does this:

- Extract the ServiceReferences.ClientConfig file.
- Call the UpdateConfigurationFile method above to revise the IP information.
-  Update the ZIP file and commit the changes.
/// <summary>
/// Updates the silverlight configuration file.
/// </summary>
/// <param name="configFileName">Name of the config file.</param>
/// <param name="xapFilePath">The xap file path.</param>
/// <param name="replacementIp">The replacement ip.</param>
/// <param name="destinationIp">The destination ip.</param>
/// <returns></returns>
private bool UpdateSilverlightConfigurationFile(string configFileName,
    string xapFilePath, string replacementIp, string destinationIp)
 {
    // Check file path
    if (!File.Exists(xapFilePath)) { return false; }
    // Open XAP and modify configuration
   using (var zip = ZipFile.Read(xapFilePath))
    {
        // Get config file
        var entry = zip[configFileName];
        var stream = new MemoryStream();
        entry.Extract(stream);
        // Manipulate configuration
        var updated =
            UpdateConfigurationFile(stream, replacementIp, destinationIp);
        // Evaluate
        if (updated)
        {
            // Replace existing configuration file in XAP
            zip.UpdateEntry(configFileName, stream);
            zip.Save();
        }
    }
    // return
    return true;
}

So, let’s look at the code in it’s entirety so that we get an implementation example as well as the needed includes:
using System;
using System.IO;
using System.Windows;
using System.Windows.Controls;
using Ionic.Zip;
namespace XAPFileUpdaterTest
{
    /// <summary>
    /// Interaction logic for MainWindow.xaml
    /// </summary>
    public partial class MainPage : UserControl
    {
        public MainPage()
        {
            // Intialize UI
            InitializeComponent();
            // Parameters
            string configFile = "ServiceReferences.ClientConfig";
            string xap = @"MyAwesomeApp.xap";
            string replacementIp = "127.0.0.1";
            string destinationIp = "12.23.45.67";
             // Check
            var isClientConfigUpdated =
                UpdateSilverlightConfigurationFile(
                   configFile, xap, replacementIp, destinationIp); 
            // Setup message
            var message =
                (isClientConfigUpdated) ? "was successful" : "failed"; 
            // Notify user
            MessageBox.Show("The current update " + message);
        }
        /// <summary>
        /// Updates the configuration file.
        /// </summary>
        /// <param name="stream">The stream.</param>
        /// <param name="replacementIp">The replacement ip.</param>
        /// <param name="destinationIp">The destination ip.</param>
        /// <returns></returns>
        private bool UpdateConfigurationFile(MemoryStream stream,
            string replacementIp, string destinationIp)
        {
            bool isSuccessful = false;
           try
            {
                // Read current file
                var reader = new StreamReader(stream);
                stream.Position = 0;
                var contents = reader.ReadToEnd();
                // Update IP information
                var newContents = contents.Replace(replacementIp, destinationIp);
                // Reset contents of stream
                stream.SetLength(0);
                // Overwrite original configuration file
                var writer = new StreamWriter(stream);
                writer.Write(newContents);
                writer.Flush();
                // Set position in stream to 0.
                // This allows us to start writing from the beginning of the stream.
                stream.Seek(0, SeekOrigin.Begin);
                // Success
                isSuccessful = true;
            }
            catch (Exception)
            {
            }
            // return
            return isSuccessful;
        }
        /// <summary>
        /// Updates the silverlight configuration file.
        /// </summary>
        /// <param name="configFileName">Name of the config file.</param>
        /// <param name="xapFilePath">The xap file path.</param>
        /// <param name="replacementIp">The replacement ip.</param>
        /// <param name="destinationIp">The destination ip.</param>
        /// <returns></returns>
        private bool UpdateSilverlightConfigurationFile(string configFileName,
            string xapFilePath, string replacementIp, string destinationIp)
        {
            // Check file path
            if (!File.Exists(xapFilePath)) { return false; } 
            // Open XAP and modify configuration
            using (var zip = ZipFile.Read(xapFilePath))
            {
                // Get config file
                var entry = zip[configFileName];
               var stream = new MemoryStream();
                entry.Extract(stream);
                // Manipulate configuration
                var updated =
                    UpdateConfigurationFile(stream, replacementIp, destinationIp);
                // Evaluate
                if (updated)
                {
                    // Replace existing configuration file in XAP
                    zip.UpdateEntry(configFileName, stream);
                    zip.Save();
                }
            }
           // return
            return true;
        }
    }
}

That’s all for now. I hope I helped you with this annoyance.



Silverlight Italy Hosting - HostForLIFE.eu :: Silverlight Control Size and Browser Resize Problem

clock March 10, 2014 10:21 by author Peter

Sometime if Silverlight control have a bigger size than it create some problems. If browser is smaller than Silverlight control then some part of SL control get clipped. In this scenario sometime you want to get scrollbar some time you want to change SL control height according to browser.

There are some different scenario that how people want to embed there Silverlight control in there page

1. If Silverlight Control is long then it should be in scrollbar.

2. Silverlight control should filled whole page in width and height without caring of height – width ratio.

3. Silverlight control should filled page with maintaining aspect ratio.

Silverlight Control is long then it should be in scrollbar

If your Silverlight control height is fixed assume its 800px and width is 900px then you should define this in your user control in this way :

<UserControl x:Class=”Slider.Page” xmlns=”http://schemas.microsoft.com/winfx/2006/xaml/presentation&#8221;  
 xmlns:x=”http://schemas.microsoft.com/winfx/2006/xaml&#8221;  
 Width=”700″ Height=”800″ xmlns:d=”http://schemas.microsoft.com/expression/blend/2008&#8243; xmlns:mc=”http://schemas.openxmlformats.org/markup-compatibility/2006&#8243; mc:Ignorable=”d”>  
 <Grid x:Name=”LayoutRoot” Background=”White”>  
 </Grid>  
 </UserControl>   

and in aspx page it should be

<div id=”silverlightControlHost” style=”width:700;height:800; “>  

it will give you scrollbar.

Silverlight control should filled whole page in width and height without caring of height – width ratio

In this case you need put your layout in Canvas instead of Grid and add transforms in that way:

<UserControl x:Class=”SlSizeChange.Page” xmlns=”http://schemas.microsoft.com/winfx/2006/xaml/presentation&#8221;  
 xmlns:x=”http://schemas.microsoft.com/winfx/2006/xaml&#8221; Height=”Auto” Width=”Auto” Background=”#FF29D530″ HorizontalAlignment=”Stretch” VerticalAlignment=”Stretch” >  
 <Canvas HorizontalAlignment=”Center” x:Name=”Layout” Width=”930″ Height=”640″ VerticalAlignment=”Center” RenderTransformOrigin=”0.5,0.5″ Background=”#FFB9B839″>  
 <Canvas.RenderTransform>  
    <TransformGroup>  
       <ScaleTransform ScaleX=”1″ ScaleY=”1″ x:Name=”LayoutScale” />  
      <SkewTransform />  
      <RotateTransform />  
      <TranslateTransform />  
   </TransformGroup>  
 </Canvas.RenderTransform>  
 <TextBlock Height=”75″ Width=”276″ Canvas.Left=”71″ Canvas.Top=”18″ Text=”TextBlock” TextWrapping=”Wrap”/>  
 <TextBlock Height=”77″ Width=”140″ Canvas.Left=”248″ Canvas.Top=”341″ Text=”TextBlock” TextWrapping=”Wrap”/>  
 <TextBlock Height=”64″ Width=”69″ Canvas.Left=”365″ Canvas.Top=”509″ Text=”TextBlock” TextWrapping=”Wrap”/>  
 <TextBlock Height=”52″ Width=”125″ Canvas.Left=”652″ Canvas.Top=”525″ Text=”TextBlock” TextWrapping=”Wrap”/>  
 </Canvas>  
 </UserControl>  

Then attach sizeChanged event handler

public partial class Page : UserControl  
  {  
     public Page()  
     {  
      InitializeComponent();  
      this.SizeChanged += new SizeChangedEventHandler(Layout_SizeChanged);  
     }  
     void Layout_SizeChanged(object sender, SizeChangedEventArgs e)  
    {  
      LayoutScale.ScaleX = e.NewSize.Width / (Layout.Width);  
     LayoutScale.ScaleY = e.NewSize.Height / (Layout.Height);  
    }   
 }  

Silverlight control should filled page with maintaining aspect ratio

Now third case where you want to maintain your width – height aspect ratio.To achieve this follow last process in same way with a small difference:

<UserControl x:Class=”SlSizeChange.Page”  
 xmlns=”http://schemas.microsoft.com/winfx/2006/xaml/presentation&#8221;  
 xmlns:x=”http://schemas.microsoft.com/winfx/2006/xaml&#8221; Height=”Auto” Width=”Auto” Background=”#FF29D530″ HorizontalAlignment=”Stretch” VerticalAlignment=”Stretch” >  
 <Canvas HorizontalAlignment=”Center” x:Name=”Layout” Width=”930″ Height=”640″ VerticalAlignment=”Top” RenderTransformOrigin=”0.5,0″ Background=”#FFB9B839″>  
 <Canvas.RenderTransform>  
   <TransformGroup>  
     <ScaleTransform ScaleX=”1″ ScaleY=”1″ x:Name=”LayoutScale” />  
     <SkewTransform />  
     <RotateTransform />  
     <TranslateTransform />  
   </TransformGroup>  
 </Canvas.RenderTransform>  
 <TextBlock Height=”75″ Width=”276″ Canvas.Left=”71″ Canvas.Top=”18″ Text=”TextBlock” TextWrapping=”Wrap”/>  
 <TextBlock Height=”77″ Width=”140″ Canvas.Left=”248″ Canvas.Top=”341″ Text=”TextBlock” TextWrapping=”Wrap”/>  
 <TextBlock Height=”64″ Width=”69″ Canvas.Left=”365″ Canvas.Top=”509″ Text=”TextBlock” TextWrapping=”Wrap”/>  
 <TextBlock Height=”52″ Width=”125″ Canvas.Left=”652″ Canvas.Top=”525″ Text=”TextBlock” TextWrapping=”Wrap”/>  
 </Canvas>  
 </UserControl>  
 And for C#  
 public partial class Page : UserControl  
 {  
    public Page()  
    {     InitializeComponent();  
      this.SizeChanged += new SizeChangedEventHandler(Layout_SizeChanged);  
     }   
    void Layout_SizeChanged(object sender, SizeChangedEventArgs e)  
    {  
     double aspectRatio = e.NewSize.Width / e.NewSize.Height;  
     if (aspectRatio < (Layout.Width / Layout.Height))  
      LayoutScale.ScaleX = LayoutScale.ScaleY = e.NewSize.Width / (Layout.Width);  
     else  
      LayoutScale.ScaleX = LayoutScale.ScaleY = e.NewSize.Height / (Layout.Height);  
    }   
 }  

Now this will always maintain the aspect ratio.



Silvelight Germany Hosting - HostForLIFE.eu :: How To Clear Silverlight Cache

clock March 5, 2014 07:12 by author Peter

I recently had an issue where a Silverlight application was acting up on me. It was an application that I was using, not one I’d developed and I really didn’t have much experience with Silverlight at that point. It was working for other people and other browsers on my computer were experiencing the same problem. I’d already cleared the Internet cache multiple times including cookies and other stuff. I applied all the latest patches, hot fixes and service packs as well as updated my browsers to the latest versions. I finally came to the point where I updated Silverlight thinking it could be related to Silverlight itself. None of this solved the problem.

Finally another developer alerted me to the possibility of clearing the Silverlight cache. Now don’t laugh, but I didn’t know that Silverlight had its own cache. I looked online for quite a while on how to clear the Silverlight cache but came up empty. So, I went looking myself. Here’s how you do it.

Clear Silverlight Cache

1. Open the Microsoft Silverlight shortcut in your start menu. Depending on what version and how you manage your Start Menu, this could be in various places. You could also just click Run or browse to ”C:\Program Files\Microsoft Silverlight\{VERSION}\Silverlight.Configuration.exe” where {VERSION} equals the version you have installed. For me, as of writing this article, it was 5.1.20125.0.

2. You will see the Microsoft Silverlight Configuration application come up.

3. Click on the Application Storage tab and you will see tab contents like this.

4. Click on a web site you want to clear from cache and click the Delete button, or click the Delete all button to clear all web site data from cache.

5. Click OK

Congratulations! You’ve just cleared the Microsoft Silverlight cache on your system.



Silverlight 5 Hosting - Performing Create, Read, Update, Delete using Silverlight 5 with WCF RIA Services

clock February 27, 2014 07:02 by author Peter

In this article let’s play around with one of the most useful concept CRUD using Silverlight 5 with WCF RIA Services. Well, What is CRUD using Silverlight 5 with WCF RIA Services? In Simple terms “Performing CRUD operation using Silverlight 5 with WCF RIA Services”

Step 1: Create a new Silverlight application

Check Enable WCF RIA Services in New Silverlight Application window as shown below:

Step 2: Adding New Entity Data Model Framework

Step 3: Adding New Domain Service

Step 4: The complete code of Main.XAML looks as below

</p>
<UserControl xmlns:sdk="http://schemas.microsoft.com/winfx/2006/xaml/presentation/sdk" x:Class="CRUDviaRIAStoredProc.MainPage"
 xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
 xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
 xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
 xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
 mc:Ignorable="d"
 d:DesignHeight="600" d:DesignWidth="650">
 
<Grid x:Name="LayoutRoot" Background="White">
 <TextBlock Height="23" HorizontalAlignment="Left" Margin="148,42,0,0" Name="textBlock5" Text="EmployeeId" FontFamily="Verdana" FontSize="15" VerticalAlignment="Top"/>
 <TextBox Height="23" HorizontalAlignment="Left" Margin="248,42,0,0" Name="textBox5" VerticalAlignment="Top" Width="120"/>
 <TextBlock Height="23" HorizontalAlignment="Left" Margin="148,82,0,0" Name="textBlock1" Text="FirsName" FontFamily="Verdana" FontSize="15" VerticalAlignment="Top"/>
 <TextBox Height="23" HorizontalAlignment="Left" Margin="248,78,0,0" Name="textBox1" VerticalAlignment="Top" Width="120"/>
 <TextBlock Height="23" HorizontalAlignment="Left" Margin="148,121,0,0" Name="textBlock2" Text="LastName" FontFamily="Verdana" FontSize="15" VerticalAlignment="Top"/>
 <TextBox Height="23" HorizontalAlignment="Left" Margin="248,121,0,0" Name="textBox2" VerticalAlignment="Top" Width="120"/>
 <TextBlock Height="23" HorizontalAlignment="Left" Margin="148,166,0,0" Name="textBlock3" Text="Age" FontFamily="Verdana" FontSize="15" VerticalAlignment="Top"/>
 <TextBox Height="23" HorizontalAlignment="Left" Margin="248,166,0,0" Name="textBox3" VerticalAlignment="Top" Width="120"/>
 <TextBlock Height="23" HorizontalAlignment="Left" Margin="148,206,0,0" Name="textBlock4" Text="Location" FontFamily="Verdana" FontSize="15" VerticalAlignment="Top"/>
 <TextBox Height="23" HorizontalAlignment="Left" Margin="248,206,0,0" Name="textBox4" VerticalAlignment="Top" Width="120"/>
 <Button Content="ReadData" FontFamily="Verdana" FontSize="15" Background="DeepSkyBlue" Height="34" HorizontalAlignment="Left" Margin="148,249,0,0" Name="button1" VerticalAlignment="Top" Width="80" Click="button1_Click"/>
 <Button Content="Insert" FontFamily="Verdana" FontSize="15" Background="DeepSkyBlue" Height="34" HorizontalAlignment="Left" Margin="246,249,0,0" Name="button2" VerticalAlignment="Top" Width="80" Click="button2_Click"/>
 <Button Content="Update" FontFamily="Verdana" FontSize="15" Background="DeepSkyBlue" Height="34" HorizontalAlignment="Left" Margin="346,249,0,0" Name="button3" VerticalAlignment="Top" Width="80" Click="button3_Click"/>
 <Button Content="Delete" FontFamily="Verdana" FontSize="15" Background="DeepSkyBlue" Height="34" HorizontalAlignment="Left" Margin="446,249,0,0" Name="button4" VerticalAlignment="Top" Width="80" Click="button4_Click"/>
 <sdk:DataGrid Height="216" HorizontalAlignment="Left" Margin="146,310,0,0" Name="dataGrid1" VerticalAlignment="Top" Width="380"/>
 </Grid>
</UserControl>
<p style="text-align: justify;">


Step 5: The complete code of Main.XAML.cs looks as below

</p>
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Shapes;
using CRUDviaRIAStoredProc.Web;
 
namespace CRUDviaRIAStoredProc
{
 public partial class MainPage : UserControl
 {
 # region Service Instance
 CompanyDomainContext ctx = new CompanyDomainContext();
 #endregion
 
public MainPage()
 {
 InitializeComponent();
 }
 
private void button1_Click(object sender, RoutedEventArgs e)
 {
 ctx.Load<tblEmployee>(ctx.GetTblEmployeesQuery(), (lo) => { dataGrid1.ItemsSource = lo.Entities; }, false);
 }
 
private void button2_Click(object sender, RoutedEventArgs e)
 {
 ctx.InsertEmp(textBox1.Text, textBox2.Text, Convert.ToInt32(textBox3.Text), Convert.ToInt32(textBox4.Text));
 MessageBox.Show("Data Inserted Succesfully");
 }
 
private void button3_Click(object sender, RoutedEventArgs e)
 {
 ctx.UpdateEmp(Convert.ToInt32(textBox5.Text), textBox1.Text, textBox2.Text, Convert.ToInt32(textBox3.Text));
 MessageBox.Show("Data Updated Succesfully");
 }
 
private void button4_Click(object sender, RoutedEventArgs e)
 {
 ctx.DeleteEmp(Convert.ToInt32(textBox5.Text));
 MessageBox.Show("Employee deleted Succesfully");
 }
 }
}
<p style="text-align: justify;">


Silverlight France Hosting - HostForLIFE.eu :: How to get unrestricted access in Silverlight 5

clock February 10, 2014 07:45 by author Peter

As you all know Silverlight 5 released with unrestricted access in InBrowser mode which gives full control to the Silverlight developers in the client machine. This post is to describe what are the changes you need to do at the server side as well as client side to leverage the trusted application feature.Try HostForLife.eu, only with € 3.00/month, you can get a reasonable price with best service. This topic contains only brief information about why you must use Silverlight 4. So, if you want to be more familiar with Silverlight 5, you should try HostForLife.eu

Server side

- Make the IB elevated from project properties

- Sign the xap using certificate.

Client side

- Install the cert into “trusted publishers”

- Change the reg key “HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Silverlight\AllowElevatedTrustAppsInBrowser” to

-The “Protected mode” of browser should be turned off for the zone.

The above steps are mainly for Silverlight 4 to Silverlight 5 conversion and want to get the full trust support. If you just upgrading your projects there is no need to do above items. Just open in Visual Studio 2010 after installing the SL 5 tools.It will show the conversion wizard and just follows that. One thing you need to make sure is the reference path to Sl 5 assemblies.

Steps to setup Silverlight 5 application to have unrestricted access in IB using elevated trust mode
Ok.Now lets look at details of how we can create new SL project which access a file from c:\ in the In-Browse mode.First as usual create a Silverlight 5 Application. Then place a Button inside and wire the Click event handler.Write the below code in the handler to write a file to c:\

private void btnWriteToCDrive_Click ( object sender, RoutedEventArgs e )

{

    File.WriteAllText(@"c:\FromSL5.txt","This is written from Silverlight 5 web application in InBrowser mode");

}

Giving Elevated trust to SL 5 application

Just to go to the Silverlight 5 project properties and in the Silverlight tab check the “Require elevated trust when running in-browser” check box.See the below screen shot for reference.

Signing the xap
Next step is to sign the xap file with a certificate. Just goto Silverlight project properties and check the “Sign the Xap File” check box.If you don’t have any certificate just create a new one using the “Create Test Certificate” button.

Disable the browser protected mode

The above changes are mainly for the server side. You need to do some at client side as well.The first one is to disable the protected mode in browser.Goto the internet options and disable it.

Change registry entry

As mentioned above just change the registry entry as it is.If you cant find the key just add a DWORD and set to 1. Still I am not sure how this step is carried out in other platforms such as MAC and Linux. That’s it.Now run your application.It will create a file inside c:\ from in browser.Just uploaded the sample by removing the certificate as it give some information about my company laptop.



Tag cloud

Sign in