Compartilhar via


Este artigo foi traduzido por máquina.

Team System

Personalizando itens de trabalho

Brian A. Randell

Baixar o código de exemplo

Item de trabalho ’s Team Foundation Server sistema de controle fornece uma série de opções avançadas de personalização. Se você ’re novo para personalização do item e o modelo de objeto de item de trabalho, você poderá revisar meus artigos anteriores (consulte de msdn.microsoft.com/magazine/cc301186.aspx), bem como a documentação. A área que ’Ll falarei neste artigo é o suporte de controle personalizado. Para iniciar, por que você desejaria usar um controle personalizado em seus itens de trabalho? Bem, talvez seja conveniente uma melhor experiência de usuário, talvez queira vincular dados de outro sistema externo (como um aplicativo de suporte técnico Ajuda) ou deseja apresentar dados de item de trabalho existente no formato de gráfico. Seja qual for o motivo, controles personalizados podem ajudar.

Controles personalizados

Na versão 2005 SP1 do Team System, a Microsoft adicionou suporte para controles personalizados quando você usa o processador de item de trabalho padrão no Visual Studio ou seus próprios aplicativos do Windows personalizados. Na versão 2008, a Microsoft adicionou suporte para controles personalizados com base na Web para Team System Web Access. Você pode escrever um controle personalizado como uma combinação de interface do usuário e lógica que interage com o item de trabalho API. Controles personalizados hospedados no processador de item de trabalho padrão são classes que herdam de System.Windows.Forms.Control e implementam a interface IWorkItemControl. Para o Team System Web Access, você cria uma classe que herda System.Web.UI.Control e implementa IWorkItemWebControl.

Para começar, você precisa Visual Studio 2008 Professional ou superior, o Visual Studio Team System 2008 Team Explorer e acessar um Team Foundation Server. Como mencionei nas colunas anteriores, eu recomendo que você usar um ambiente de teste como uma das imagens a máquina virtual fornecidas pela Microsoft. Dentro do Visual Studio, crie um projeto de biblioteca de classe e, em seguida, renomeie a classe padrão adicionada pelo modelo para classificação. Esse controle personalizado fornecerá uma interface de caixa de combinação para o campo classificado interno usado no tipo de item de trabalho de tarefas interno. Os estados de orientação de processo fornecido pela Microsoft: “ Necessário. O campo de classificação é uma classificação de importância subjetiva. Se o campo de classificação estiver definido como 1, a tarefa é uma tarefa deve ter e deve ser concluída assim que possível. Se a classificação estiver definida como 2, a tarefa é uma tarefa deve ter, para ser concluída após todas as tarefas classificadas 1. Se a classificação estiver definida como 3, ele ’s uma tarefa pode ter, para ser concluída após tarefas classificados 1 e 2. ” No entanto, o campo classificado quando processado usa um campo de texto aberto que permite que o usuário insira dados aleatórios. Usando uma caixa de combinação permite que você restringir os valores inseridos pelo usuário.

Você precisará adicionar referências a um número de módulos (assemblies) antes de poder escrever qualquer código. Primeiro, você precisa de uma referência para System.Windows.Forms. Em segundo lugar, você precisa referenciar alguns assemblies do Team System. Para iniciar, adicione uma referência para Microsoft.TeamFoundation.WorkItemTracking.Controls.dll, localizado em % Program Files%\Microsoft Visual Studio 9.0\Common7\IDE\PrivateAssemblies.

Na parte superior do arquivo de classe, adicione as seguintes instruções Imports:

Imports System.Windows.Forms
Imports Microsoft.TeamFoundation.WorkItemTracking.Controls

Como você para substituir o controle de caixa de texto existente uma caixa de combinação, você deseja que a classe classificada para herdar da caixa de combinação. Em seguida, em ordem para o processador de item de trabalho hospedar seu controle, você precisará implementar a interface IWorkItemControl. A interface, mostrada na Figura 1, consiste em dois eventos, quatro métodos e quatro propriedades. Figura 2 fornece uma divisão de cada membro e sua finalidade.

A Figura 1 A interface IWorkItemControl

Public Interface IWorkItemControl
    Event AfterUpdateDatasource As EventHandler
    Event BeforeUpdateDatasource As EventHandler
    Sub Clear()
    Sub FlushToDatasource()
    Sub InvalidateDatasource()
    Sub SetSite(ByVal serviceProvider As IServiceProvider)
    Property Properties As StringDictionary
    Property [ReadOnly] As Boolean
    Property WorkItemDatasource As Object
    Property WorkItemFieldName As String
End Interface
Membro Descrição
AfterUpdateDatasource Dispara esse evento depois de você feito alterações ao valor do item de trabalho.
BeforeUpdateDatasource Dispara esse evento antes de fazer quaisquer alterações ao valor do item de trabalho.
Limpar Limpa os elementos de exibição dos dados.
FlushToDatasource
Salva os dados exibidos no controle para o item de trabalho;geralmente ocorre como parte de um item de trabalho operação de salvamento.
InvalidateDatasource Recarrega e exibe dados atuais do item de trabalho.
SetSite Fornece uma referência para o IServiceProvider se você precisar acessar os serviços dentro do Visual Studio;seu valor pode ser nulo.
Properties Um conjunto de propriedades contendo atributos relevantes para o campo específico da definição do XML do tipo de item de trabalho hospedagem.
ReadOnly Indica que o controle deve ser exibido no modo somente leitura.
WorkItemDatasource Uma referência a instância de item de trabalho ativos;Você precisa lançá-lo para uma instância de WorkItem.
WorkItemFieldName O nome do campo ao qual o controle está vinculado.

Figura 2 membros IWorkItemControl

Para cada uma das propriedades de interface, você precisará criar um campo correspondente do backup. Para armazenar o WorkItemDatasource como uma referência com rigidez de tipos, você precisará adicionar uma referência ao assembly Microsoft.TeamFoundation.WorkItemTracking.Client.dll armazenado em % Program Files%\Microsoft Visual Studio 9.0\Common7\IDE\PrivateAssemblies. Depois de adicionado essa referência, você deve importar o namespace Microsoft.TeamFoundation.WorkItemTracking.Client também. Após você vinculado as propriedades com seus campos de backup, você pode implementar os métodos de interface.

No entanto, antes de fazer, você precisará definir os elementos de dados para a caixa de combinação e o endereço a pergunta se você deseja ajustar os valores usados facilmente. Em caso afirmativo, você deve implementar um mecanismo como um serviço da Web que permite que o controle recuperar os valores mais atuais. Além disso, se você for para exibir valores de seqüência de caracteres, você deve considerar problemas de localização. Neste exemplo, no entanto, nós basta adicionar um construtor padrão que hardcodes valores, mostrado a seguir. Observe que o runtime do item de trabalho oferece suporte somente controles que têm um padrão, o construtor sem argumentos.

Public Sub New()
  MyBase.New()

  Me.Items.Add("1 - Must Have")
  Me.Items.Add("2 - Should Have")
  Me.Items.Add("3 - Could Have")
End Sub

Você também deve fornecer uma implementação para o método Clear. Por padrão, quando o runtime do item de trabalho carrega o controle, o valor exibido é em branco. Para retornar a esse estado, você precisará definir a propriedade SelectedIndex como -1 no método Clear. Os dois últimos métodos que você precisa implementar se relacionam ao carregar o valor do campo trabalho acoplado item e para a instância de item de trabalho e persistência, em seguida, o valor se o usuário salva o item de trabalho. Você usa o método InvalidateDataSource para atualizar exibir dados ’s seu controle de item de trabalho atual ou limpar dados previamente carregados. O runtime do item de trabalho chama FlushToDataSource quando ele salvar o item de trabalho ativa. Figura 3 fornece uma implementação para ambos os métodos para o controle personalizado classificado.

Figura 3 implementação de FlushToDatasource e InvalidateDataSource

Public Sub FlushToDatasource() _
  Implements IWorkItemControl.FlushToDatasource
 
  If SelectedIndex > -1 And workItemDS IsNot Nothing Then
    RaiseEvent BeforeUpdateDatasource(Me, EventArgs.Empty)
    workItemDS.Fields(fieldName).Value = CStr(SelectedIndex + 1)
    RaiseEvent AfterUpdateDatasource(Me, EventArgs.Empty)
  End If
End Sub

Public Sub InvalidateDatasource() _
  Implements IWorkItemControl.InvalidateDatasource
  If workItemDS IsNot Nothing AndAlso _
    Not String.IsNullOrEmpty(workItemDS.Fields(fieldName).Value) Then

    Dim current As Integer = CInt(workItemDS.Fields(fieldName).Value)
    ' Make sure the value returned does not exceed our valid range
    ' If it does, set it to our most important value
    If current > Me.Items.Count Then
      current = 1
    End If
    SelectedIndex = (current - 1)
  Else
    Clear()
  End If
End Sub

FlushToDataSource verifica se o usuário tiver selecionado um valor e que a referência para o item de trabalho ativos ainda é válida. Em caso afirmativo, ele aciona o evento BeforeUpdateDataSource, grava o valor atual para o item de trabalho e segue esse por uma chamada para AfterUpdateDataSource. InvalidateDataSource verifica se ’s uma referência válida para um item de trabalho ativa. Se assim, ele verifica se ’s um valor atualmente armazenado no campo acoplado, recupera o valor se houver e converte-lo como um inteiro porque armazenada como uma seqüência de caracteres. Por fim, ele faz uma verificação de intervalo para garantir que o valor doesn’t exceder o intervalo suportados do controle — neste exemplo, 1 a 3. Aqui, o código simplesmente define o valor como 1 se ele ’s fora do intervalo com suporte. Como alternativa, você pode fornecer alguns interface do usuário para permitir que o usuário escolher um valor suportado ou alguma outra experiência. Finalmente, se existe nenhum item de trabalho ativa ou se o usuário não tiver feito uma opção, o código chama o método Clear para redefinir o controle. Figura 4 fornece a implementação completa do controle classificado.

Figura 4 O controle classificado

Imports System.Windows.Forms
Imports Microsoft.TeamFoundation.WorkItemTracking.Controls
Imports Microsoft.TeamFoundation.WorkItemTracking.Client
Imports System.Collections.Specialized

Public Class Rank
  Inherits ComboBox
  Implements IWorkItemControl
  ' Backing fields
  Private fieldName As String
  Private fReadOnly As Boolean
  Private serviceProvider As IServiceProvider
  Private workItemDS As WorkItem
  Private workItemProperties As StringDictionary

  Public Event AfterUpdateDatasource( _
    ByVal sender As Object, ByVal e As System.EventArgs) _
    Implements IWorkItemControl.AfterUpdateDatasource

  Public Event BeforeUpdateDatasource( _
    ByVal sender As Object, ByVal e As System.EventArgs) _
    Implements IWorkItemControl.BeforeUpdateDatasource

  Public Sub New()
    MyBase.New()
 
    Me.Items.Add("1 - Must Have")
    Me.Items.Add("2 - Should Have")
    Me.Items.Add("3 - Could Have")
  End Sub

  Public Sub Clear() Implements IWorkItemControl.Clear
    SelectedIndex = -1
  End Sub

  Public Sub FlushToDatasource() _
    Implements IWorkItemControl.FlushToDatasource

    If SelectedIndex > -1 And workItemDS IsNot Nothing Then
      RaiseEvent BeforeUpdateDatasource(Me, EventArgs.Empty)
      workItemDS.Fields(fieldName).Value = CStr(SelectedIndex + 1)
      RaiseEvent AfterUpdateDatasource(Me, EventArgs.Empty)
    End If
  End Sub

  Public Sub InvalidateDatasource() _
    Implements IWorkItemControl.InvalidateDatasource
    If workItemDS IsNot Nothing AndAlso _
      Not String.IsNullOrEmpty(workItemDS.Fields(fieldName).Value) Then
 
      Dim current As Integer = CInt(workItemDS.Fields(fieldName).Value)
      ' Make sure the value returned does not exceed our valid range
      ' If it does, set it to our most important value
      If current > Me.Items.Count Then
        current = 1
      End If
      SelectedIndex = (current - 1)
    Else
      Clear()
    End If
  End Sub

  Public Property Properties() As StringDictionary _
    Implements IWorkItemControl.Properties
    Get
      Return workItemProperties
    End Get
    Set(ByVal value As System.Collections.Specialized.StringDictionary)
      workItemProperties = value
    End Set
  End Property

  Public Property IsReadOnly() As Boolean _
    Implements IWorkItemControl.ReadOnly
    Get
      Return fReadOnly
    End Get
    Set(ByVal value As Boolean)
      fReadOnly = value
      Enabled = (Not fReadOnly)
    End Set
  End Property

  Public Sub SetSite(ByVal serviceProvider As IServiceProvider) _
    Implements IWorkItemControl.SetSite
    Me.serviceProvider = serviceProvider
  End Sub

  Public Property WorkItemDatasource() As Object _
    Implements IWorkItemControl.WorkItemDatasource
    Get
      Return workItemDS
    End Get
    Set(ByVal value As Object)
      workItemDS = TryCast(value, WorkItem)
    End Set
  End Property

  Public Property WorkItemFieldName() As String _
    Implements IWorkItemControl.WorkItemFieldName
    Get
      Return fieldName
    End Get
    Set(ByVal value As String)
      fieldName = value
    End Set
  End Property
End Class

Antes de você pode experimentar o controle, você precisa executar três etapas adicionais. Primeiro, você precisará fornecer um arquivo de configuração XML simples que define o controle para o runtime do item de trabalho. Em segundo lugar, você precisará implantar o assembly ’s controle e configuração arquivo (bem como o PDB) se você deseja depurar em um dos locais válidos oferece suporte para o tempo de execução. Finalmente, você precisará atualizar a definição de tipo para um item de trabalho que usa o campo classificado e adicionar as informações corretas para carregar o controle.

Implantar e depurar

Cada controle personalizado que você criar precisa ter um arquivo WICC. Você precisará criar um arquivo XML simples, como o mostrado aqui, que define o assembly do host e o nome de classe de seu controle.

<?xml version="1.0" encoding="utf-8" ?>
<CustomControl xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
 xmlns:xsd="http://www.w3.org/2001/XMLSchema">
  <Assembly>BrianRandell.MSDNMag.WICC.Winforms.dll</Assembly>
  <FullClassName>BrianRandell.MSDNMag.WICC.Winforms.Rank</FullClassName>
</CustomControl>

Depois de criado esse arquivo, você deve copiar o arquivo WICC, sua DLL e seu PDB para um dos dois locais. Se desejar que o controle seja visível somente para sua conta, copie-o para % local aplicativo Data%\Microsoft\Team Foundation\Work Item Tracking\Custom Controls\9.0. Para tornar o controle visível para todos os usuários em uma determinada estação de trabalho, copie os arquivos para % Common Application Data%\Microsoft\Team Foundation\Work Item Tracking\Custom Controls\9.0. Você provavelmente necessário criar a estrutura de pasta sob a pasta Microsoft. Você pode usar System.Environment.GetFolderPath com o valor de Environment.SpecialFolder.LocalApplicationData ou Environment.SpecialFolder.CommonApplicationData para descobrir os caminhos na estação de trabalho local. Depois que você copiado os arquivos para um desses diretórios, você precisará modificar a definição de tipo para um item de trabalho — neste exemplo, a tarefa item de trabalho do modelo MSF Agile — para usar o controle. (Consulte meu artigo em msdn.microsoft.com/magazine/dd221363.aspx de para obter mais informações.)

Você pode modificar as informações de layout do formulário para o campo classificado como mostra o código a seguir. Em tempo de execução, o direcionamento de item de trabalho passa todos os atributos, listados como parte da definição de layout ’s o campo, para o controle por meio da propriedade IWorkItemControl.Properties. A única alteração que você precisa fazer é alterar o atributo Type do padrão FieldControl para classificação, o nome do controle recém-criado. Depois de fez a alteração e importados a nova definição de tipo de item de trabalho um Team Project, você pode experimentar coisas.

<Control Type="Rank" FieldName="Microsoft.VSTS.Common.Rank" 
Label="Ran&amp;k:" LabelPosition="Left" 
NumberFormat="WholeNumbers" MaxLength="10"/>

Para depurar, você precisa ter copiados os bits mais atuais para a pasta de implantação correto e importado a definição de tipo modificado para um projeto de equipe. Em seguida, você deve modificar as configurações Debug para seu projeto para usar a opção “ Start external program ” e aponte-o para devenv.exe (consulte do Figura 5). Agora você pode definir alguns pontos de interrupção e iniciar a depuração.


Figura 5 escolha a opção de programas externos conjunto.

Implantação de produção

Após você resolvidas qualquer kinks no seu controle, você precisará tê-lo implantado em estações de trabalho para qualquer pessoa que usará o item de trabalho afetado. Você pode fazer isso de diversas maneiras. Você pode criar um programa de instalação usando o modelo interno do Visual Studio, ou você pode criar um script simples que é executado como parte do script de logon e xcopy ’s um usuário os arquivos no lugar. Finalmente, se você já implantou o lançamento de outubro de 2008 do Team Foundation Server Power Tools, você pode usar o recurso de implantação que do Power Tools fornecer. Para usar este recurso, você precisa fazer check-in a assembly e WICC o arquivo para uma pasta % Team Project Name%/TeamProjectConfig/CustomControls. Em seguida, basta acessar a opção configurações pessoais no nó integrantes da equipe e selecione a opção “ instalar download de componentes personalizados ”. Observe que você ’re incentivados para nomear fortemente seus assemblies se você usar esse recurso. A Microsoft fornece mais informações sobre esse recurso nos arquivos de Ajuda do Power Tools.

Limitações

Neste ponto, você viu as vantagens de controles personalizados. Agora algumas advertências. O primeiro problema grande é suporte de host. Criamos um controle personalizado que é executado somente no Visual Studio 2008 ou um aplicativo que hosts da Microsoft forneceu controle personalizado. Se você quiser dar suporte Visual Studio 2005, você precisará criar uma versão com a versão 2005 do Team Explorer e implantar o assembly e WICC arquivos separadamente. E sobre o Team System Web Access? Em teoria, se você usar somente o Internet Explorer como navegador da Web, isso funcionaria, mas ele doesn’t. O que você vê é semelhante a Figura 6.


A Figura 6 Suporte para Visual Studio 2005 requer etapas adicionais

A boa notícia é que o lançamento de 2008 do Team System Web Access oferece suporte a seu próprio modelo de controle personalizado. Você encontrar documentação com exemplos em % Program Files%\Microsoft Visual Studio 2008 Team System Web Access\Sdk na máquina em que você instalou o Team System Web Access. No entanto, isso funciona somente se você executando a versão 2008;a versão 2005 doesn’t oferece suporte a controles personalizados. Além disso, um produto de terceiros como Teamprise doesn’t um. A melhor solução é definir vários elementos LAYOUT na definição de tipo ’s seu item de trabalho. Em seguida, você precisa duplicar a definição de cada host sem suporte ou diferente. Em seguida, você deseja decorar o elemento LAYOUT com um atributo de destino. Para Windows, use WinForms para o valor. Para o Team System Web Access, use a Web. Para Teamprise, use Teamprise. Consulte de msdn.microsoft.com/library/aa337635(VS.80).aspx para obter mais informações.

Usar layouts personalizados resolve o problema de processamento, mas ele também brilha uma luz sobre um problema mais sutil. Hosts como o Microsoft Excel e Microsoft Project don’t oferecem suporte ao controles personalizados em todos os. Eles não são afetados por ajustes feitos ao elemento de LAYOUT de um tipo de item de trabalho. Portanto, qualquer host que doesn’t oferecem suporte a seu controle personalizado expõe os dados não processados por meio de sua interface padrão. No caso do campo classificado, isso significa que um campo de edição padrão. Cada host aplicará as regras definidas para o item de trabalho. No entanto, a lógica personalizada que você implementar no seu controle personalizado será não. Portanto, se você ficarem inativos fora da escritório do uso de controles personalizados, você precisará instruir os usuários na entrada de dados apropriados ao executar fora hosts com suporte e certificar-se que o controle executa validação de dados apropriados.

Team System 2010

A próxima versão 2010 do Team System promete ser cheia de qualidades de cliente e servidor. Em maio de 2009, a Microsoft lançou Beta 1 para revisão. À medida que escreve esta coluna, o Microsoft tem prometidos um segundo beta um dia no outono de 2009. Embora ainda há algumas alterações sejam feitas, o recurso de rastreamento de item de trabalho é muito maduro. Comprovadamente, o recurso mais solicitado fornecido pela Microsoft é a itens de trabalho hierárquicos. Dito isso, Estou usando a versão Beta 1, portanto, qualquer coisa e tudo o que abordarei aqui é sujeitas a alterações entre agora e a versão final.

Versões anteriores do Team Foundation Server oferece suporte a noção de vincular itens de trabalho. Quando você vincula um item de trabalho a outro item de trabalho, você criar uma relação bidirecional. Qualquer item de trabalho pode ser vinculado a qualquer outro item de trabalho. Embora útil, esse recurso doesn’t fornecer pai-filho relações. Além disso, talvez também queira ser capaz de definir precedência, algo muito comuns ao usar o Microsoft Project para gerenciar tarefas. Felizmente, ambas essas áreas são abordadas na versão 2010. Para oferecer suporte a semântica de link novo, a Microsoft criou dois tipos de consulta de item de trabalho novo: Itens de trabalho e Direct Links (vínculos consulta) e árvore de itens de trabalho (árvore de consulta). Quando você cria uma nova consulta de item de trabalho, você pode escolher um desses novos tipos ou escolher plano de trabalho itens da lista, que representa o tipo padrão de consulta disponível antes para a versão 2010.

Para começar, dê uma olhada novos tipos de item de trabalho internos e consultas de itens de trabalho. Como ele tem com versões anteriores, a Microsoft está atualmente fornecendo dois modelos de processo. Neste estágio no ciclo de desenvolvimento, a maioria das seu trabalho está visível no MSFT para Agile Software Development v5.0 modelo. Microsoft promete que a versão baseada em CMMI mostrará atualizações significativas na versão Beta 2. Assim, eu estar usando o modelo Agile como meu ponto de referência. Depois de você criado um novo projeto de equipe e expandiu o nó de itens de trabalho, você localizar os nós de minhas consultas e consultas de equipe familiarizados. Você observar dois aprimoramentos sutis na janela Team Explorer. Primeiro, o Microsoft alterado a ordem de classificação: Classifica minha consultas antes de consultas de equipe. Em segundo lugar, o Microsoft personalizado os ícones usados para cada nó. Se você abrir o nó de consultas de equipe, você encontrar a lista de consultas predefinidas mudou significativamente (consulte do Figura 7). Além disso, você observar que a Microsoft adicionou uma nova pasta, consultas de pasta de trabalho. Esta pasta contém consultas que oferece suporte ao novo recurso de pastas de trabalho do Excel. ’Re livre para executar as consultas, mas você não deve modificá-las;Caso contrário, você pode quebrar uma ou mais das pastas de trabalho que dependem dessas consultas específicas.

TFS 2008 TFS 2010 Beta 1
Bugs ativos Bugs ativos
Todas as questões Meus Bugs
Todos os qualidade de requisitos de serviço Minhas Tarefas
Todos os cenários Minhas ocorrências de teste
Todas as tarefas Meus Itens de trabalho
Todos os itens de trabalho Questões abertas
Meus Itens de trabalho para todos os projetos de equipe Tarefas abertas
Lista de verificação do projeto Abrir casos de teste
Bugs resolvidos Artigos de usuário aberta
Erros untriaged Itens de trabalho aberta
  P1 e P2 erros Active
  Bugs resolvidos
  Artigos de usuário sem casos de teste

Figura 7 predefinido consultas no Team System 2010

Você ver dos novos nomes que Microsoft fez alterações profundidade para o modelo inteiro. Há novos tipos de item de trabalho, como texto de usuário (consulte do Figura 8) e meu caso de teste. A Microsoft tem ouvidos para a comunidade e aplicadas toneladas de comentários (como usando mais comumente adotada termos), feitas simplificações e relatórios aprimorados. Pastas de novas com base em Excel trabalho, mencionamos para fornecer controle de projeto rich anteriormente, sem precisar de SQL Server Reporting Services, com melhor estimativa e controle de recurso e novos gráficos como gráficos de gravação suspensa. À medida que Microsoft mais próximo é movido liberar, eu se aprofundar ainda mais a versão 2010, incluindo como atualizar seus modelos personalizados e seu código personalizado.


Figura 8 A história de usuário predefinidas consulta

Resumo

Item de trabalho controles personalizados no Team Foundation Server fornecem um mecanismo bom para aprimorar a usabilidade de itens de trabalho em suas equipes. Embora esse recurso não seja perfeito, a Microsoft continua para torná-lo melhor. Na verdade, como espero para a versão 2010 do Team System, consulte um futuro muito brilhante para Team System como um todo e trabalho especialmente acompanhamento do item.