Documentation : Permissions - Windows Phone 7

This example shows how to protect your shared data.

Application consists of only one model, which has two fields. First field - title - has default set of permissions, which means that it can be read, created and updated by any user. However, second field - director - can be read, created and updated only by user, who owns this record. In this example we will see that first field is visible for both users, where second is visible only for creator. Main screen of application contains list of model entities and two buttons. User can add new entity and synchronize with mobeelizer cloud. There are two users in the system and there's a possibility to switch user on application life time.

Design Your App

First of all, create new application in Mobeelizer App Designer called Permissions.

Secondly, create new model called PermissionsEntity.

We will need two fields. Firstly, create text field, name it title and use default credentials. Secondly, create text field called director and set credentials to own for each option.

For now leave conflict resolving property on overwrite. By default, there is one group - users, device category - mobile and role - users-mobile define. Deploy application to test environment. On test environment create two users with passwords: 

  • a - usera
  • b - userb

Finally, download configured template for Windows Phone project. Use default configuration.

Use the Mobeelizer SDK

Open downloaded project in Microsoft Visual Studio (Windows Phone SDK 7.1 must be installed on your computer). There is a few files generated in our project, applicatiom.xml, app.config (read more) and 'Model/File.cs' - our model class.

Create app skeleton

In our example we will use MVVM pattern. To make it, create two folders in solution explorer - 'View' and 'ViewModel'. Next move 'MainPage.xaml' file to 'View' folder and change path to navigation page in 'WMAppManifet.xml' file - DefaultTask node - to 'View/MainPage.xaml'.

You also need to add this files into project:

  • userAIcon.fw.png - User a icon, will be displayed in entities list item. Add it into project root folder and set build action property to Content.
  • userBIcon.fw.png - User b icon, will be displayed in entities list item. Add it into project root folder and set build action property to Content.
  • OwnerNameToIconConverter.cs - Converter which converts owner name into path to user icon. Add it into ViewModel folder. 
  • movies.xml - Generated list of movies, put it in the project and set build action value to 'content'.

Prepare main screen

Now we have already created skeleton of our project, next things to do is creating View and ViewModel. As described before our main screen contains list of model entities and two buttons to add new entity and synchronize data. Lets create it in our MainPage.xaml file. 
First of all we need to create grid with 3 rows, first row will contain control to switch between users, second will contain list of entities and last one will contain 'sync' and 'add' buttons. 
In code below you can see that ListBox items are bind to Entities list, and buttons are bind to commands responsible for synchronization and adding new entities.

View/MainPage.xaml
...
<Grid x:Name="LayoutRoot" Background="Transparent">
    <Grid.RowDefinitions>
        <RowDefinition Height="Auto"/>
        <RowDefinition Height="*"/>
    </Grid.RowDefinitions>
    <!--TitlePanel contains the name of the application and page title-->
    <StackPanel x:Name="TitlePanel" Grid.Row="0" Margin="12,17,0,28">
        <TextBlock x:Name="ApplicationTitle" Text="MOBEELIZER TUTORIAL" Style="{StaticResource PhoneTextNormalStyle}"/>
        <TextBlock x:Name="PageTitle" Text="Permissions" Margin="9,-7,0,0" Style="{StaticResource PhoneTextTitle1Style}"/>
    </StackPanel>
    <!--ContentPanel - place additional content here-->
    <Grid x:Name="ContentPanel" Grid.Row="1" Margin="12,0,12,0">
        <Grid.RowDefinitions>
            <RowDefinition Height="100" />
            <RowDefinition Height="*" />
            <RowDefinition Height="100"/>
        </Grid.RowDefinitions>
        <Grid Grid.Row="0">
            <!-- Place for switch user control -->
        </Grid>
        <ListBox Grid.Row="1" ItemsSource="{Binding Entities}">
           <!-- TODO: Item template -->
        </ListBox>
        <StackPanel Grid.Row="2" Orientation="Horizontal">
            <Button Content="Add" Width="230" Command="{Binding AddEntityCommand}" CommandParameter="{Binding Entities}" />
            <Button Content="Sync" Width="230" Command="{Binding SyncCommand}" CommandParameter="{Binding Entities}"/>
        </StackPanel>
    </Grid>
</Grid>
...

Ok, let's create ViewModel for our xaml code. In ViewModel folder we need to create MainPageViewModel class. This class will contains properties which we already bind to our View.

ViewModel/MainPageViewModel.cs
public class MainPageViewModel
{
    public MainPageViewModel()
    {
        Entities = new ObservableCollection<PermissionsEntity>();
    }
    public ICommand AddEntityCommand { get; set; }
    public ICommand SyncCommand { get; set; }
    public ObservableCollection<PermissionsEntity> Entities { get; set; }
}

To connect our View with just created ViewModel, open MainPage.xaml.cs file and implement MainPage class constructor like this:

View/MainPage.xaml.cs
...
public MainPage()
{
    InitializeComponent();
    this.DataContext = new MainPageViewModel();
}
...

Create switch user control

Before we start implementing add entity command and sync command we have to complete switching users things. We will create separated control for that. (If you already created previous example you can just copy it). In View folder create new Windows Phone User Control and name it SwitchUserControl. This control will contains grid with two columns, first column will present information which user is currently loged in and second will contains button to switch user.

View/SwitchUserControl.xaml
...
	<Grid x:Name="LayoutRoot">
        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="*"/>
            <ColumnDefinition Width="150"/>
        </Grid.ColumnDefinitions>
        <TextBlock Text="{Binding CurrentUser, StringFormat='Current user is: {0}'}"  Style="{StaticResource PhoneTextLargeStyle}" VerticalAlignment="Center"/>
        <Button Grid.Column="1" Content="Switch" Command="{Binding SwitchUser}"/>
    </Grid> 
...

Code below is ViewModel definition for just created control. There are two properties, first one contains information about currently logged in user and second one is a command to switch users. Class implements INotifyPropertyChanged interface to support notification about value changes of properties.

ViewModel/SwitchUserControlViewModel.cs
   	public class SwitchUserControlViewModel : INotifyPropertyChanged
    {
	    public static String CurrentlyLoggendInUser;
        public SwitchUserControlViewModel()
        {
            // TODO 
        }
        public String CurrentUser
        {
            get
            {
                return CurrentlyLoggendInUser;
            }
        }

        public ICommand SwitchUser { get; set; }

        public event PropertyChangedEventHandler PropertyChanged;

        private void RaisePropertyChanged(String propertyName)
        {
            PropertyChangedEventHandler handler = PropertyChanged;
            if (handler != null)
            {
                handler(this, new PropertyChangedEventArgs(propertyName));
            }
        }
    }

And again remember to connect ViewModel and View. You need to also create our new control in MainPage grid row. 

View/SwitchUserControl.xaml.cs
...
        public SwitchUserControl()
        {
            this.DataContext = new SwitchUserControlViewModel();
            InitializeComponent();
        }
...
View/MainPage.xaml
...
<Grid Grid.Row="0">
	<localControls:SwitchUserControl />
</Grid>
...

When View and ViewModel are almost done it is time to write some buisness logic. We will start with SwitchUserCommand, this command will be responsible for switching between users and also have to notify other classes that user was switched. Create new class in Model folder and implement it like in code below. You can see that there are two events UserSwitched and CanExecuteChanged, first one notify about switching user, second will notify View that execution lock may change. There are also four methods: 'CanExecute' which returns execution lock value, 'Execute' which contains logging in code, 'RaiseCanExecuteChanged' to rise 'CanExecuteChanged' event and 'Mobeelizer_SyncStatusChanged' which is triggered when synchronization status change.

Model/SwitchUserCommand.cs
public class SwitchUserCommand : ICommand
{
    private bool canSwitchUser = true;
    private const String USER_A_PASSWORD = "usera";
    private const String USER_B_PASSWORD = "userb";
    private const String USER_A_LOGIN = "a";
    private const String USER_B_LOGIN = "b";

    public event EventHandler CanExecuteChanged;
    public static event EventHandler UserSwitched;

	public SwitchUserCommand()
    {
        Mobeelizer.SyncStatusChanged += new MobeelizerSyncStatusChangedEventHandler(Mobeelizer_SyncStatusChanged);
    }

    public bool CanExecute(object parameter)
    {
        return canSwitchUser;
    }

    public void Execute(object parameter)
    {
       canSwitchUser = false;
       RaiseCanExecuteChanged();
       switch (SwitchUserControlViewModel.CurrentlyLoggendInUser)
       {
          case USER_A_LOGIN:
               Mobeelizer.Login(USER_B_LOGIN, USER_B_PASSWORD, error =>
               {
                    if (error == null)
                    {
                        SwitchUserControlViewModel.CurrentlyLoggendInUser = USER_B_LOGIN;
                    }
                    else
                    {
                        SwitchUserControlViewModel.CurrentlyLoggendInUser = String.Empty;
                    }
                    EventHandler handler = UserSwitched;
                    if (handler != null)
                    {
                        handler(this, EventArgs.Empty);
                    }
                    Deployment.Current.Dispatcher.BeginInvoke(new Action(() =>
                    {
                        canSwitchUser = true;
                        RaiseCanExecuteChanged();
                    }));
                });
                break;
            default:
            case USER_B_LOGIN:
                    Mobeelizer.Login(USER_A_LOGIN, USER_A_PASSWORD, error =>
                    {
                        if (error == null)
                        {
                            SwitchUserControlViewModel.CurrentlyLoggendInUser = USER_A_LOGIN;
                        }
                        else
                        {
                            SwitchUserControlViewModel.CurrentlyLoggendInUser = String.Empty;
                        }
                        EventHandler handler = UserSwitched;
                        if (handler != null)
                        {
                            handler(this, EventArgs.Empty);
                        }
                        Deployment.Current.Dispatcher.BeginInvoke(new Action(() =>
                        {
                            canSwitchUser = true;
                            RaiseCanExecuteChanged();
                        }));
                    });
                break;
        }
     }

    public void RaiseCanExecuteChanged()
    {
       EventHandler handler = CanExecuteChanged;
       if (handler != null)
       {
            handler(this, EventArgs.Empty);
       }
    }

    void Mobeelizer_SyncStatusChanged(MobeelizerSyncStatus status)
    {
        if (status == MobeelizerSyncStatus.FINISHED_WITH_SUCCESS || status == MobeelizerSyncStatus.FINISHED_WITH_FAILURE
           || status == MobeelizerSyncStatus.NONE)
        {
           canSwitchUser = true;
        }
        else
        {
            canSwitchUser = false;
        }
        Deployment.Current.Dispatcher.BeginInvoke(new Action(() =>
        {
            RaiseCanExecuteChanged();
        }));
    }
}

This code is pretty simple, when user click on switch user button, Execute method will be invoked, while method is executing, execution lock is set, when loging in is finished 'UserSwitched' event will be raised. User can't change while synchronization process is in progress, this functionality is implemented in 'Mobeelizer_SyncStatusChanged' method - when synchronization is in progress, lock flag is up. 

Last thing to do in switching user code is implementing SwichUserControlViewModel constructor. 

ViewModel/SwitchUserControlViewModel.cs
...
public SwitchUserControlViewModel()
{
    SwitchUserCommand command = new SwitchUserCommand();
    SwitchUserCommand.UserSwitched += (object sender, EventArgs e) =>
        {
            Deployment.Current.Dispatcher.BeginInvoke(new Action(() =>
            {
                RaisePropertyChanged("CurrentUser");
            }));
        };
    this.SwitchUser = command;
    this.SwitchUser.Execute(null);
}
...

Get current user entities

Nice, we can login to mobeelizer and switch between users. It is time to get current user entities. Go back to MainPageViewModel class, in constructor add new method to UserSwitched event and fill entities list when user will be switched.

ViewModel/MainPageViewModel.cs
public MainPageViewModel()
{
    Entities = new ObservableCollection<PermissionsEntity>();
    SwitchUserCommand.UserSwitched += new System.EventHandler(UserSwitched);
}
void UserSwitched(object sender, System.EventArgs e)
{
    Deployment.Current.Dispatcher.BeginInvoke(new Action(() =>
    {
        GetEntitiesForCurrentUser();
    }));
}
private void GetEntitiesForCurrentUser()
{
    Entities.Clear();
    if (Mobeelizer.IsLoggedIn)
    {
        using (IMobeelizerTransaction transaction = Mobeelizer.GetDatabase().BeginTransaction())
        {
            foreach (PermissionsEntity entity in transaction.GetModelSet<PermissionsEntity>())
            {
                Entities.Add(entity);
            }
        }
    }
} 

Add entity into database

Our entities list is now visible, it is time to implement AddEntityCommand. We will use predefined values to generate new entities. This is an xml file with list of movie titles and directors, download it, put in the project and set build action value to 'content'. Create new class in Model folder, called AddEntityCommand and take a look on code below. There is one thing more, XDocument class is in System.Xml.Linq namespace, remember to add this component to project references. 

 Create new class in Model folder, called AddEntityCommand and take a look on code below. 

Model/AddEntityCommand.cs
public class AddEntityCommand : ICommand
{
    private List<String> movieTitles;
    private List<String> movieDirectors;
    private Random rand = new Random();
    public AddEntityCommand()
    {
        XDocument movies = XDocument.Load("movies.xml");
        this.movieTitles = new List<string>();
        this.movieDirectors = new List<string>();
        foreach (XElement title in movies.Root.Element("movieTitles").Elements("item"))
        {
            movieTitles.Add(title.Value);
        }
        foreach (XElement director in movies.Root.Element("movieDirectors").Elements("item"))
        {
            movieDirectors.Add(director.Value);
        }
    }
    public bool CanExecute(object parameter)
    {
        return Mobeelizer.IsLoggedIn;
    }
    public event EventHandler CanExecuteChanged;
    public void Execute(object parameter)
    {
        ObservableCollection<PermissionsEntity> entities = (ObservableCollection<PermissionsEntity>)parameter;
        using (IMobeelizerTransaction transaction = Mobeelizer.GetDatabase().BeginTransaction())
        {
            PermissionsEntity entity = new PermissionsEntity();
            int index = rand.Next(0, movieTitles.Count - 1);
            entity.Title = movieTitles[index];
            entity.Director = movieDirectors[index];
            transaction.GetModelSet<PermissionsEntity>().InsertOnSubmit(entity);
            transaction.SubmitChanges();
            Deployment.Current.Dispatcher.BeginInvoke(new Action(() =>
            {
                entities.Add(entity);
            }));
        }
    }
    public void RaiseCanExecuteChanged()
    {
        EventHandler handler = CanExecuteChanged;
        if (handler != null)
        {
            handler(this, EventArgs.Empty);
        }
    }
}

It is easy, right? In constructor we are loading movie titles from xml file and storing them in list. When Execute method is triggered we are getting random value from list, creating new entity and saving it in database. Take a look at CanExecute method, it returns true only if user is loged in to Mobeelizer, this is because there is no access to database for not authorized users. 

Synchronize with mobeelizer

It is time for synchronization now, it will be as easy as adding new entity to list. Create new class SyncCommand in Model folder and implement it like this:

Model/SyncCommand.cs
public class SyncCommand : ICommand
{
    private bool canSyncFlag = true;
    public bool CanExecute(object parameter)
    {
        return Mobeelizer.IsLoggedIn && canSyncFlag;
    }
    public event EventHandler CanExecuteChanged;
    public void Execute(object parameter)
    {
        ObservableCollection<PermissionsEntity> entities = (ObservableCollection<PermissionsEntity>)parameter;
        canSyncFlag = false;
        RaiseCanExecuteChanged();
        Mobeelizer.Sync(error =>
        {
            Deployment.Current.Dispatcher.BeginInvoke(new Action(() =>
            {
                if (error == null)
                {
                    entities.Clear();
                    if (Mobeelizer.IsLoggedIn)
                    {
                        using (IMobeelizerTransaction transaction = Mobeelizer.GetDatabase().BeginTransaction())
                        {
                            foreach (PermissionsEntity entity in transaction.GetModelSet<PermissionsEntity>())
                            {
                                entities.Add(entity);
                            }
                        }
                    }
                }
                canSyncFlag = true;
                RaiseCanExecuteChanged();
            }));
        });
    }
    public void RaiseCanExecuteChanged()
    {
        EventHandler handler = CanExecuteChanged;
        if (handler != null)
        {
            handler(this, EventArgs.Empty);
        }
    }
}

You can synchronize your data only if user is logged in and if other sync process is not in progress. This condition is defined in CanExecute method. Let's take a look at Execute method. It is actually just calling Mobeelizer sync method. When synchronization finished without any errors given entities list is refreshed, and that is all. 

And again, create object of our new classes in MainPageViewModel constructor, and raise CanExecuteChange events when user change.

ViewModel/MainPageViewModel.cs
...
public MainPageViewModel()
{
    Entities = new ObservableCollection<PermissionsEntity>();
    AddEntityCommand = new AddEntityCommand();
    SyncCommand = new SyncCommand();
    SwitchUserCommand.UserSwitched += new System.EventHandler(UserSwitched);
}

void UserSwitched(object sender, System.EventArgs e)
{
    Deployment.Current.Dispatcher.BeginInvoke(new Action(() =>
    {
        (AddEntityCommand as AddEntityCommand).RaiseCanExecuteChanged();
        (SyncCommand as SyncCommand).RaiseCanExecuteChanged();
        GetEntitiesForCurrentUser();
    }));
}
...

Display models on the list

Great, application is almost ready, and you can test it now. List of entities is visible but there is only information about entites class name. Let's create some xaml code to show added entities and theirs owners.We will create one more class to convert null value to string with '*' characters. This class is necessary to show that our entity field is not permitted to read by other users.

ViewModel/NullToStringConverter.cs
public class NullToStringConverter : IValueConverter
{
    public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
    {
        if (value == null)
        {
            return "***********";
        }
        return value;
    }
    public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
    {
        throw new NotImplementedException();
    }
}

When converter is ready let's take a look at MainPage.xaml file. First of all we need to add our converter to page resources (Remember to add ViewModel namespace in page root attribute). 

View/MainPage.xaml
<phone:PhoneApplicationPage 
	...
    xmlns:mobeelizerConverters="clr-namespace:MobeelizerConverters"
    xmlns:converters="clr-namespace:...ViewModel" >

    <phone:PhoneApplicationPage.Resources>
        <mobeelizerConverters:OwnerNameToIconConverter x:Key="OwnerConverter"/>
        <converters:NullToStringConverter x:Key="NullToStringConverter"/>
    </phone:PhoneApplicationPage.Resources>
	...
</phone:PhoneApplicationPage>

Secondly, create ListBox items template using our converter. 

View/MainPage.xaml
<ListBox Grid.Row="1" ItemsSource="{Binding Entities}">
    <ListBox.ItemTemplate>
        <DataTemplate>
            <Grid Margin="2" Height="50" Width="430">
                <Grid.ColumnDefinitions>
                    <ColumnDefinition Width="*"/>
                    <ColumnDefinition Width="*"/>
                </Grid.ColumnDefinitions>
                <StackPanel>
                    <TextBlock Text="{Binding Title}" />
                    <TextBlock Margin="30,0,0,0" Text="{Binding Director, Converter={StaticResource NullToStringConverter}}"/>
                </StackPanel>
                <Image Source="{Binding Owner, Converter={StaticResource OwnerConverter}}" Grid.Column="1" VerticalAlignment="Center" HorizontalAlignment="Right" />
            </Grid>
        </DataTemplate>
    </ListBox.ItemTemplate>
</ListBox>

Permisions example is ready, you can test it now on as many devices as you can find. You can also browse your data in App Designer using Data Browser tool. 

Conclusion

To conclude, this example presents permissions settings. We can observe that with Mobeelizer permissions settings, we can easily control visibility and data access. 


Attachments: