Archive

Archive for the ‘SharePoint – Tips & Tricks’ Category

Como buscar informações de status do Workflow no SharePoint Online (365)

November 28, 2018 Leave a comment

CENÁRIO:

Olá!

Nas versões antigas (on-premises) do SharePoint Server é bem fácil obter o status do workflow bastando apenas adicionar uma coluna na view que contém o nome do Workflow.

Entretanto, no SharePoint Online (365) é um pouco diferente. Ainda temos a coluna do workflow (WorkflowColumnStatus), mas ela fica “em branco”. Como fazer para verificar, programaticamente, o status do workflow que foi desenvolvido dentro de um SharePoint Add-in?

ERRO:

Colunas em branco:

wfkcolumn

SOLUÇÃO:

Utilizando o SharePoint CSOM ou JSOM (Javascript Object Model) é possível instanciar o WorkflowServicesManager e obter as informações do workflow.

wfkmanageinstance

Em texto:

var workflowServicesManager = new WorkflowServicesManager(ctx, ctx.Web);

 

// connect to the instance service

var workflowInstanceService = workflowServicesManager.GetWorkflowInstanceService();

 

// get all instances

var workflowInstances = workflowInstanceService.EnumerateInstancesForListItem(listId, itemId);

ctx.Load(workflowInstances, i => i.Include(f => f.Id, f => f.LastUpdated, f => f.Status));

ctx.ExecuteQuery();

foreach (var instance in workflowInstances)

{

try

{

if (instance.Status.ToString() != “Completed”)

{

Console.WriteLine(“{0} – {1} – {2}”,

instance.Id.ToString(),

instance.LastUpdated,

instance.Status.ToString());

 

}

}

catch (Exception ex)

{

_telemetry.TrackException(ex);

}

}

. J

Abraço!

 

Alto consumo CPU e memória noderunner.exe – SharePoint 2013

February 1, 2018 Leave a comment

CENÁRIO:

Olá!

Apesar de ter montando uma máquina virtual de desenvolvimento com um hardware razoável (gastar menos $$) sofri com consumo excessivo de CPU e memória em um servidor com SharePoint 2013, SQL Server 2008 R2 e Visual Studio 2017 instalado.

ERRO:

Não é um erro, mas é um comportamento pesado para um ambiente de desenvolvimento.

Picture1

SOLUÇÃO:

Analisando um pouco o comportamento do servidor para descobrir o “vilão” da jogada… notei que o processo “noderunner.exe” estava consumindo muita memória e CPU considerável.

Picture2

Noderunner.exe é um componente do serviço de busca do SharePoint 2013.

Para ambientes de Dev, podemos reduzir o consumo desse serviço. Principalmente se não estamos usando a busca do SharePoint 2013 no desenvolvimento. Inclusive, desabilitar o serviço.

No caso, eu precisava de um “pouco” de busca… :/

Então, utilizei os passos a seguir para reduzir o impacto da busca no consumo de recursos do servidor.

  1. Rode o cmdlet Powershell abaixo para reduzir o consumo de CPU

Set-SPEnterpriseSearchService -PerformanceLevel Reduced

  1. Altere o parâmetro “memoryLimitMegabytes” do arquivo “exe.config”. Por padrão, a configuração é “0” – ilimitado.

C:\Program Files\Microsoft Office Servers\15.0\Search\Runtime\1.0\noderunner.exe.config

Picture3

  1. Reinicie o serviço de busca do SharePoint com o cmdlet a seguir

Restart-Service SPSearchHostController

Picture4

Agora o servidor ganhou “algum” fôlego.

Informações adicionais: https://docs.microsoft.com/en-us/powershell/module/sharepoint-server/Set-SPEnterpriseSearchService?view=sharepoint-ps

Reduced: Total number of threads = number of processors, Max Threads/host = number of processors

Partly Reduced: Total number of threads = 4 times the number of processors , Max Threads/host = 16 time the number of processors

Maximum: Total number of threads = number of processors

Abraço!

 

Could not load file or assembly Microsoft.VisualStudio.Shell.15.0

January 11, 2018 Leave a comment

CENÁRIO:

Montando o pipeline de Build e Release automático de um projeto SharePoint Add-in (SharePoint App – SharePoint Online 365) descobri que não tem um template de tarefa de build no Visual Studio Online (VSTS).

Então, parti para construir a tarefa de build vazia (Empty) onde colocamos os comandos para realizar o build.

Eis que ao tentar dar build usando o msbuild.exe me deparo com um erro bem estranho.

ERRO:

“C:\Program Files (x86)\Microsoft Visual Studio\2017\Enterprise\MSBuild\Microsoft\VisualStudio\v15.0\SharePointTools\Microsoft.VisualStudio.SharePoint.targets(395,5): Error : Could not load file or assembly ‘Microsoft.VisualStudio.Shell.15.0, Version=15.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a’ or one of its dependencies. The system cannot find the file specified.”

SOLUÇÃO:

Depois de algumas (muitas) pesquisas descobri que é necessário adicionar referências no projeto para fazer o build com sucesso. Passos para resolver:

  1. Adicionar um arquivo “Microsoft.VisualStudio.SharePoint.AddAssemblySearchPaths.targets” na raiz do seu projeto (conteúdo do arquivo mais abaixo)
  2. Adicionar “<Import Project=”Microsoft.VisualStudio.SharePoint.AddAssemblySearchPaths.targets” />” no arquivo .csproj para importar o arquivo adicionado.
  3. Pronto, agora é build com sucesso! 😉

Conteúdo do arquivo “Microsoft.VisualStudio.SharePoint.AddAssemblySearchPaths.targets”:

<Project xmlns=’http://schemas.microsoft.com/developer/msbuild/2003&#8242; >

  <UsingTask TaskName=”AddAssemblySearchPath” TaskFactory=”CodeTaskFactory” AssemblyFile=”$(MSBuildToolsPath)\Microsoft.Build.Tasks.Core.dll”>

    <ParameterGroup>

      <SearchPath ParameterType=”System.String” Required=”true” />

    </ParameterGroup>

    <Task>

      <Using Namespace=”System” />

      <Using Namespace=”System.IO” />

      <Using Namespace=”System.Reflection” />

      <Code Type=”Fragment” Language=”cs”>

        <![CDATA[

AppDomain.CurrentDomain.AssemblyResolve += (sender, e) =>

{

  string assemblySearchPath = Path.Combine(SearchPath, e.Name.Split(‘,’)[0] + “.dll”);

  if (File.Exists(assemblySearchPath)) return Assembly.LoadFrom(assemblySearchPath);

  return null;

};

]]>

      </Code>

    </Task>

  </UsingTask>

  <Target Name=”addAssemblySearchPaths”>

    <AddAssemblySearchPath SearchPath=”$(MSBuildExtensionsPath)\..\Common7\IDE\PrivateAssemblies\” />

    <AddAssemblySearchPath SearchPath=”$(MSBuildExtensionsPath)\..\Common7\IDE\PublicAssemblies\” />

  </Target>

</Project>

Também adicionei o arquivo para download aqui: https://1drv.ms/u/s!Ass2zQvJNjQNguMdQGn53ziBBweFBg

Para resolver essa zica, contei com a ajuda do super http://diegomarques.azurewebsites.net/.

Abraço!

Setup is unable to proceed to the following error(s)

November 24, 2017 Leave a comment

CENÁRIO:

Hoje precisei instalar um VM SharePoint 2013 para fazer um robô de migração/exportação de dados para uma outra aplicação.

Ao instalar o SharePoint 2013 SP1 no Windows Server 2012 R2, logo de cara, me deparo com erro ao instalar os pré-requisitos solicitando .NET 4.5… que já estava instalado no servidor.

ERRO:

Setup is unable to proceed to the following error(s):

This Product requires Microsoft .Net Framework 4.5

framework

SOLUÇÃO:

Depois de algumas pesquisas vi que a MS já lançou um KB para contornar esse problema, onde você irá baixar uma nova DLL e substituir nos arquivos de instalação.

https://support.microsoft.com/en-us/help/3087184/sharepoint-2013-or-project-server-2013-setup-error-if-the–net-framewo

Links para download da nova DLL do instalador:

Abraço!

The security validation for this page is invalid and might be corrupted. Please use your web browser’s Back button to try your operation again

October 19, 2015 3 comments

Cenário:

Estou simulando um “stress test” no meu ambiente de desenvolvimento para simular o impacto (hardware) de algumas operações do SharePoint 2013.

No meu teste, além de navegar e buscar alguns itens no site, também quero que o “robô” crie itens nas listas do SharePoint através de requests HTTP.

Ao tentar criar itens usando uma chamada HTTP com as APIs do SharePoint temos o erro mencionado a seguir.

ERRO:

The security validation for this page is invalid and might be corrupted. Please use your web browser’s Back button to try your operation again

Ou, PT-BR:

A validação de segurança para esta página não é valida e pode estar corrompida. Use o botão Voltar do navegador Web para tentar realizar a operação novamente.

SOLUÇÃO:

Como não estou usando OAuth para autorizar a requisição, precisamos passar um “digest value” válido no Header da requisição.

Conseguimos pegar esse valor em um campo (__REQUESTDIGEST) escondido da MasterPage padrão do SharePoint:

 

<input type="hidden" name="__REQUESTDIGEST" id="__REQUESTDIGEST" value="0xC756DFF038FC6A0D241D7203952F8545BC495084B8DCD4EB0171E88F15DD633F8590
ADE4C8749EDCDA73A9C48C8C8C8B515929E86CBBA83341187B3791F20F71,19 Oct 2015 18:14:37 -0000
" />

 

Ou fazendo uma chamada POST com o “body” vazio para http://<site url>/_api/contextinfo antes da chamada que irá criar o item. O retorno dessa chamada conseguimos pegar o “digest value”.

No “Header” dessa chamada com “body” vazio, passe o parâmetro “Accept” com valor “application/json;odata=verbose”.

Exemplo da chamada ao contextinfo:

image

Resposta do contextinfo:

image

{"d":{"GetContextWebInformation":{"__metadata":{"type":"SP.ContextWebInformation"},"FormDigestTimeoutSeconds":1800,"FormDigestValue":
"0xC756DFF038FC6A0D241D7203952F8545BC495084B8DCD4EB0171E88F15DD633F8
590ADE4C8749EDCDA73A9C48C8C8C8B515929E86CBBA83341187B3791F20F71,19 Oct 2015 18:14:37 -0000
"
,"LibraryVersion":"15.0.4569.1000","SiteFullUrl":http://vmsp2013,
"SupportedSchemaVersions":{"__metadata":{"type":"Collection(Edm.String)"},"results":["14.0.0.0","15.0.0.0"]},"WebFullUrl":"http://vmsp2013&quot;}}}

O valor em realce é o valor do parâmetro “X-RequestDigest” que precisamos passar no “Header” da requisição que irá criar itens na lista do SharePoint.

Exemplo da requisição que cria itens em uma lista do SharePont 2013:

POST http://vmsp2013/_vti_bin/client.svc/ProcessQuery

POST data:

<Request xmlns="http://schemas.microsoft.com/sharepoint/clientquery/2009&quot; SchemaVersion="15.0.0.0" LibraryVersion="15.0.0.0" ApplicationName="Javascript Library"><Actions>TitleTestefalseIdade22falseContentTypeItemfalseContentTypeId0x01000DC117894911AA4BB7ADE719F7B6C168false</Parameter><Parameter Type="Boolean">false</Parameter><Parameter Type="Null" /></Parameters></Method></ExceptionHandlingScopeSimple></Actions>
<ObjectPaths><StaticProperty Id="0" TypeId="{3747adcd-a3c3-41b9-bfab-4a64dd2f1e0a}" Name="Current" /><Property Id="2" ParentId="0" Name="Web" /><Property Id="4" ParentId="2" Name="Lists" /><Method Id="6" ParentId="4" Name="GetById"><Parameters><Parameter Type="String">84dbf580-a818-4904-a726-25fedc09e490</Parameter></Parameters></Method><Method Id="8" ParentId="6" Name="AddItem"><Parameters><Parameter TypeId="{54cdbee5-0897-44ac-829f-411557fa11be}"><Property Name="FolderUrl" Type="String">/Lists/TesteJMeter</Property><Property Name="LeafName" Type="Null" /><Property Name="UnderlyingObjectType" Type="Number">0</Property></Parameter></Parameters></Method></ObjectPaths>
</Request>

[no cookies]

Request Headers:

Cache-Control: no-cache

X-Requested-With: XMLHttpRequest

Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8

User-Agent: Mozilla/5.0 (Windows NT 6.3; rv:38.0) Gecko/20100101 Firefox/38.0

Referer: http://vmsp2013/Lists/TesteJMeter/NewForm.aspx?Source=http%3A%2F%2Fvmsp2013%2FLists%2FTesteJMeter%2FAllItems%2Easpx&RootFolder=

X-RequestDigest: 0xC756DFF038FC6A0D241D7203952F8545BC495084B8DCD4EB0171E88F15DD633F8590ADE4
C8749EDCDA73A9C48C8C8C8B515929E86CBBA83341187B3791F20F71,19 Oct 2015 18:14:37 -0000

Connection: keep-alive

Accept-Encoding: gzip, deflate

Pragma: no-cache

Accept-Language: en-US,en;q=0.5

Content-Length: 2508

Content-Type: text/xml; charset=UTF-8

 

Referência: https://msdn.microsoft.com/en-us/library/jj164022(office.15).aspx.

Abraço!

WebResource ASP.NET – Gerando URL diferente para cada servidor IIS

August 10, 2015 2 comments

Olá! Alegre

Cenário:

Imagine que você está desenvolvendo sua aplicação e ela requer diversos arquivos para funcionar, como: arquivos javascript, css, html, imagens, etc. Mas você não quer a solução final tenha diversos arquivos… Quer colocar toda a solução dentro de um único arquivo.

O WebResource (e o ScriptResource para versões mais atuais o .NET Framework) nos dão essa possibilidade. Assim, podemos compilar todos os arquivos de “apoio” dentro da DLL. Ou seja, o arquivo final será apenas uma DLL.

E os arquivos serão referenciados usando o WebResource. Já viram algumas aplicações que usam o WebResource.axd ou ScriptResource.axd seguido de uma longa QueryString?

Ex: http://<site>/WebResource.axd?d=7NnjFBcUeHcW9L9Vil_Xv8IVDbfvA_jo4F0ysa_kaopudWFLJ_17_kQ3sh-IDTrgjeEVswxCZAo-cYy0GgcvW-9WmDAYSrgwFIVHe3t4QVZpQk0CDLSpRoha_Cfp0zHEGsNVUg2&t=635745693347262797

O “d” é o parâmetro, criptografado, para o arquivo. O “t” é um timestamp usado para saber se o arquivo teve alteração ou não. São gerados automaticamente pelo .NET Framework.

image

image

image

ERRO:

Não é um erro, mas é um comportamento que precisamos contornar. Por padrão, o IIS vai usar a “MachineKey” autogenerated para montar a URL criptografada.

<machineKey validationKey="AutoGenerate,IsolateApps"

decryptionKey="AutoGenerate,IsolateApps"

validation="SHA1" decryption="Auto" />

Logo, em um ambiente com vários IIS em Load Balance, por exemplo, cada servidor irá gerar uma URL diferente para o “mesmo” recurso. Isso pode trazer problemas para ambientes que dependam do cache, pois como a URL muda, sempre irá fazer download.

Outros cenários podem ser afetados, como “robôs” de testes que são configurados com URLs. Quando o robô esperar uma URL, virá outra. Além de ViewState criptografada, session state e outros.

IIS 1:

clip_image002

IIS 2:

clip_image004

SOLUÇÃO:

Podemos “forçar” para que a URL criptografada seja sempre a mesma em todos os servidores IIS.

Para isso devemos configurar um valor para MachineKey no web.config da aplicação.

Na web existem alguns geradores de MachineKey. Tendo em mãos uma configuração de MachineKey (validationKey;decryptionKey), basta inserir no web.config da aplicação dentro da tag <system.web>.

Web.config:

clip_image006

IIS 1:

clip_image008

IIS 2:

clip_image010

NOTA: Atenção para os cenários de Cache, pois para que os arquivos sejam inseridos nas políticas de cache, o “compilation debug=’false’” não pode ser “true”.

https://support.microsoft.com/en-us/kb/2580348 – Debug Mode in ASP.NET Applications

“Scripts and images downloaded from the WebResource.axd and ScriptResource.axd handlers are not cached.”

“It is recommended that debug mode is always disabled in a production environment.”

Abraço!

Microsoft Office SharePoint Server (MOSS) 2007 e SQL Server 2012 são compatíveis?

Olá! Alegre

Cenário:

A equipe decidiu atualizar os servidores e migrar alguns servidores para as versões mais atuais. O MOSS 2007 iria continuar na versão 2007, mas em outro servidor. E o SQL 2005 iria para o SQL 2012.

ERRO:

Ao tentar configurar um nova farm no SQL 2012 temos a seguinte mensagem:

“sp_dboption is deprecated and no longer exists in the master database”

SOLUÇÃO:

Na verdade não chega a ser uma solução. O SQL Server 2012 não é suportado como opção para Banco de Dados do Microsoft Office SharePoint Server (MOSS) 2007.

Ou seja, precisamos atualizar também a versão do SharePoint 2007 para, pelo menos, SharePoint 2010 SP1, caso a utilização do SQL Server 2012 seja requisito. Caso contrário, temos que utilizar outra versão de SQL Server, por exemplo SQL Server 2008 R2 (que é a última versão suportada).

 

Referência: https://technet.microsoft.com/en-us/library/cc262485(v=office.12).aspx#section3 – Determine hardware and software requirements (Office SharePoint Server)

 

Important:

Office SharePoint Server 2007 does not support SQL Server 2012 or later versions of SQL Server.

 

Caso queira prosseguir, e mesmo sem suporte, utilizar o SQL Server 2012:

http://blog.sharepointrx.com/2013/12/22/installing-microsoft-office-sharepoint-server-2007-on-sql-server-2012/

Abraço!

SharePoint:DateTimeControl não funciona em formulário customizado. “Sorry, something went wrong”

Olá! Alegre

Cenário:

Precisei customizar um formulário no SharePoint 2013 e ao tentar abrir o calendário para selecionar uma data no DateTimeControl era exibido a mensagem de erro abaixo.

ERRO:

“Sorry, something went wrong”. Ou em português, algo similar a “Desculpe, algo deu errado.”

E ao procurar no ULS (LOGS) do SharePoint, a seguinte mensagem:

“Exception trying get context compatibility level: System.IO.FileNotFoundException: The site http://vmsp2013:80/_layouts/15/iframe.aspx?&cal=1&lcid=1046&langid=1046 could not be found in the Web application SPWebApplication Name=SharePoint – 80.”

SOLUÇÃO:

Demorei um tempo para reparar, mas percebi que a url não estava “correta”. Meu site utiliza “managed path”. Ou seja, seria algo como: http://vmsp2013/sites/meusite.

No SharePoint 2013 foi introduzido o “Host-Named SiteCollection” e acredito que por isso o DateTimeControl só está buscando no “root”. Mas, ainda há casos que precisamos manter compatibilidade antes de migrar para as novas tecnologias.

Enfim, o problema é causado por que NÃO existia sitecollection criada no root da WebApplication. Ao criar a sitecollection o problema foi resolvido, mas é necessário que o usuário tenha acesso a este site para que o controle DateTimeControl funcione.

Abraço!

Como esconder o botão “Acompanhar” da Ribbon do SharePoint 2013

Olá,

Fiz recentemente um post de como esconder alguns links da MasterPage do SharePoint:

http://thiagottss.com.br/2014/03/31/como-esconder-skydrive-e-newsfeed-links-via-css/

E para remover alguns botões da Ribbon:

http://thiagottss.com.br/2014/04/09/esconder-botes-da-ribbon-ex-baixar-uma-cpia/

 

Mas ainda, podemos pesquisar e verificar que alguns botões são adicionados/removidos por feature do SharePoint. Assim sendo, podemos desativar um feature para que o botão suma…

Eis aqui um exemplo disso: O botão “Acompanhar” (Follow)

Screen Shot 2014-04-09 at 10.47.55 AM

Para remover esse botão basta desativar a feature (ID= A7A2793E-67CD-4dc1-9FD0-43F61581207A) no site (web) desejado.

Para desativar:

Disable-SPFeature –Identity A7A2793E-67CD-4dc1-9FD0-43F61581207A –Url http://urldomeusiteSharePoint

Esconder botões da Ribbon. Ex: Baixar uma cópia

April 9, 2014 1 comment

Olá,

Seguindo o post de esconder links da MasterPage do SharePoint:

http://thiagottss.com.br/2014/03/31/como-esconder-skydrive-e-newsfeed-links-via-css/

 

Às vezes recebemos solicitações dos usuários pedindo que sejam removidos botões da Ribbon para uma aplicação e/ou site específico. E não queremos remover a Ribbon inteira… apenas alguns botões.

Para isso, primeiro identifique qual botão quer remover. Por exemplo, vou desabilidade o “Baixar uma cópia” (Download a copy) da Ribbon.

Usando o F12 do browser, vi que o id desse botão é “Ribbon.Documents.Copies.Download-Large” e ele é uma tag <a>.

Então, a linha de CSS abaixo irá esconder esse botão:

 

/*download a copy*/
a[id$=’Ribbon.Documents.Copies.Download-Large’] {
display:none;
}

 

Se você quiser esconder uma “seção” inteira da Ribbon, siga os mesmos passos, mas atente-se que a seção é uma tag <li> e não um <a>. Então, nesse outro exemplo escondi o “Marcas e Notas” (Tags and Notes). O id dessa seção inteira é “Ribbon.Documents.TagsAndNotes”.

 

/*Tags and Notes*/
li[id$=’Ribbon.Documents.TagsAndNotes’] {
display:none;
}

Abraço!

Thiago.