I am writing custom activity for sharepoint workflow and I don't know how I can use current workflow item, SPWeb or SPSite.
I see http://blogs.microsoft.co.il/blogs/davidbi/archive/2008/07/21/How-to-get-the-context-item-in-workflow-activity-sharepoint.aspx but xml routines of this solution is too bad for me.
Perhaps there is another code-only solution to get context item in Workflow activity?
The answer to this is a couple steps:
Add the properties to your Custom Activity .cs
Link the properties in your .actions file (so SPD knows how to map to your properties)
Use the properties in your code
STEP 1: Here is the code for the properties (my class is named GetEmails which you will need to rename to be your class):
public static DependencyProperty __ContextProperty = System.Workflow.ComponentModel.DependencyProperty.Register("__Context", typeof(WorkflowContext), typeof(GetEmails));
[Description("The site context")]
[Category("User")]
[Browsable(true)]
[DesignerSerializationVisibility(DesignerSerializationVisibility.Visible)]
public WorkflowContext __Context
{
get
{
return ((WorkflowContext)(base.GetValue(GetEmails.__ContextProperty)));
}
set
{
base.SetValue(GetEmails.__ContextProperty, value);
}
}
public static DependencyProperty __ListIdProperty = System.Workflow.ComponentModel.DependencyProperty.Register("__ListId", typeof(string), typeof(GetEmails));
[ValidationOption(ValidationOption.Required)]
public string __ListId
{
get
{
return ((string)(base.GetValue(GetEmails.__ListIdProperty)));
}
set
{
base.SetValue(GetEmails.__ListIdProperty, value);
}
}
public static DependencyProperty __ListItemProperty = System.Workflow.ComponentModel.DependencyProperty.Register("__ListItem", typeof(int), typeof(GetEmails));
[ValidationOption(ValidationOption.Required)]
public int __ListItem
{
get
{
return ((int)(base.GetValue(GetEmails.__ListItemProperty)));
}
set
{
base.SetValue(GetEmails.__ListItemProperty, value);
}
}
public static DependencyProperty __ActivationPropertiesProperty = DependencyProperty.Register("__ActivationProperties", typeof(Microsoft.SharePoint.Workflow.SPWorkflowActivationProperties), typeof(GetEmails));
[ValidationOption(ValidationOption.Required)]
public Microsoft.SharePoint.Workflow.SPWorkflowActivationProperties __ActivationProperties
{
get
{
return (Microsoft.SharePoint.Workflow.SPWorkflowActivationProperties)base.GetValue(GetEmails.__ActivationPropertiesProperty);
}
set
{
base.SetValue(GetEmails.__ActivationPropertiesProperty, value);
}
}
STEP 2: Then in your .actions file add to your block the mappings for those properties (note the entries for __ListID, __ListItem, __Context, and __ActivationProperties):
<Action Name="[DESCRIPTION OF YOUR ACTION]"
ClassName="[Your.Namespace.Goes.Here].GetEmails"
Assembly="[yourDLLName], Version=1.0.0.0, Culture=neutral, PublicKeyToken=0bfc6fa4c4aa913b"
AppliesTo="all"
Category="[Your Category Goes Here]">
<RuleDesigner Sentence="[blah blah blah]">
<FieldBind Field="PeopleFieldName" Text="people field" Id="1"/>
<FieldBind Field="Output" Text="emailAddress" Id="2" DesignerType="parameterNames" />
</RuleDesigner>
<Parameters>
<Parameter Name="__Context" Type="Microsoft.SharePoint.WorkflowActions.WorkflowContext" Direction="In" />
<Parameter Name="__ListId" Type="System.String, mscorlib" Direction="In" />
<Parameter Name="__ListItem" Type="System.Int32, mscorlib" Direction="In" />
<Parameter Name="PeopleFieldName" Type="System.String, mscorlib" Direction="In" />
<Parameter Name="Output" Type="System.String, mscorlib" Direction="Out" />
<Parameter Name="__ActivationProperties" Type="Microsoft.SharePoint.Workflow.SPWorkflowActivationProperties, Microsoft.SharePoint" Direction="Out" />
</Parameters>
</Action>
STEP 3:
Here is an example execute function:
protected override ActivityExecutionStatus Execute(ActivityExecutionContext provider)
{
Output = string.Empty;
try
{
SPWeb web = __Context.Web;
// get all of the information we currently have about the item
// that this workflow is running on
Guid listGuid = new Guid(__ListId);
SPList myList = web.Lists[listGuid];
SPListItem myItem = myList.GetItemById(__ListItem);
//...
}
catch (Exception e)
{
//...
}
return ActivityExecutionStatus.Closed;
}
I'm not sure if this is a change in the 2010 API but the __Context property provides all the necessary pieces, including the list and item. The example below includes #davek's suggestion for discarding the security context:
var contextWeb = __Context.Web;
var site = new SPSite(contextWeb.Url);
var web = site.OpenWeb();
var list = web.Lists[new Guid(__Context.ListId)];
var item = list.GetItemById( __Context.ItemId);
Kit Menke's answer is very comprehensive and covers just about all you need: I would only add the following...
If you do this:
SPWeb tmpweb = __Context.Web;
SPSite site = new SPSite(tmpweb.Url);
SPWeb web = site.OpenWeb();
instead of this:
SPWeb web = __Context.Web;
...
then you are free of the security context passed through to the workflow by the person who triggered it.
Take a look at the SPWorkflowActivationProperties.Item Property
Gets the list item on which the workflow instance is running.
I try this code and runs as well bat the contex objet always is null. some one know why?
protected override ActivityExecutionStatus Execute(ActivityExecutionContext executionContext)
{
//return base.Execute(executionContext);
int IdRetorno = -1;
try {
SPSecurity.RunWithElevatedPrivileges(delegate
{
LogExceptionToWorkflowHistory("Inicio:", executionContext, WorkflowInstanceId);
using (SPSite sitio = new SPSite(this.__Context.Site.ID))
{
using (SPWeb web = sitio.AllWebs[this.__Context.Site.ID])
{
SPList sourceList = web.Lists[new Guid(ListId)];
LogExceptionToWorkflowHistory(ListId, executionContext, WorkflowInstanceId);
SPList destinoList = web.Lists[new Guid(SourceListId)];
LogExceptionToWorkflowHistory(SourceListId, executionContext, WorkflowInstanceId);
SPListItem sourceItem = sourceList.Items.GetItemById(ListItem);
LogExceptionToWorkflowHistory(ListItem.ToString(), executionContext, WorkflowInstanceId);
SPListItem destinoItem = destinoList.Items.Add();
CopyFieldValues(sourceItem, destinoItem);
destinoItem.SystemUpdate();
sourceItem.Delete();
IdRetorno = destinoItem.ID;
}
}
});
}
catch (Exception ex) {
if (!System.Diagnostics.EventLog.SourceExists("MyApp1"))
System.Diagnostics.EventLog.CreateEventSource(
"MyApp1", "Application");
EventLog EventLog1 = new EventLog();
EventLog1.Source = "MyApp1";
EventLog1.WriteEntry(ex.Message,EventLogEntryType.FailureAudit);
LogExceptionToWorkflowHistory(ex.Message, executionContext, WorkflowInstanceId);
}
OutListItemID = IdRetorno;
return base.Execute(executionContext);
}
thanks
I don't know if this is too easy, but I used:
objCurrentItem = workflowProperties.Item
to get the item within the workflow (a list) and then to alter the items in the list
Related
Once again I have a question about Eclipselink/MOXy with external metadata mapping file.
I have a reference xml which applies to a class. This xml contains data that applies to some but not always all the properties that the class can contain.
I also have a custom datetime adapter set for the date fields.
My problem is that the xml I'm unmarshalling does not contain any data for the endDate property, yet when I do this simple test :
Unmarshall reference xml to the class
Marshall that class to a new xml file
Compare the two xml files
That property endDate (which should not be marshalled since it has not been set) is marshalled as 09/01/2012 17:05:28 (it's always marshalled as a new Date() set to the current time).
Here is a sample XML Metadata file :
<?xml version="1.0"?>
<xml-bindings xmlns="http://www.eclipse.org/eclipselink/xsds/persistence/oxm"
version="2.1">
<java-types>
<java-type name="sample.clazz.Task" xml-accessor-type="NONE">
<xml-root-element name="Task" />
<xml-type prop-order="startDate endDate id ci ch cr" />
<java-attributes>
<xml-element java-attribute="startDate" xml-path="StartDate/text()">
<xml-java-type-adapter value="utils.JaxBDateTimeAdapter" type="java.util.Date"/>
</xml-element>
<xml-element java-attribute="endDate" required="false" xml-path="EndDate/text()">
<xml-java-type-adapter value="utils.JaxBDateTimeAdapter" type="java.util.Date"/>
</xml-element>
<xml-element java-attribute="id" xml-path="TaskId/text()" />
<xml-element java-attribute="ci" xml-path="CIPR/text()" />
<xml-element java-attribute="ch" xml-path="CHPR/text()" />
<xml-element java-attribute="cr" xml-path="CRPR/text()" />
</java-attributes>
</java-type>
</java-types>
</xml-bindings>
Here is the class :
package sample.clazz;
public class Task{
private int id;
private Date startDate;
private Date endDate;
private String ci;
private String ch;
private String cr;
public Task(){
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public Date getStartDate() {
return startDate;
}
public void setStartDate(Date startDate) {
this.startDate = startDate;
}
public Date getEndDate() {
return endDate;
}
public void setEndDate(Date endDate) {
this.endDate = endDate;
}
public String getCi() {
return ci;
}
public void setCi(String ci) {
this.ci = ci;
}
public String getCh() {
return ch;
}
public void setCh(String ch) {
this.ch = ch;
}
public String getCr() {
return cr;
}
public void setCr(String cr) {
this.cr = cr;
}
}
Here is my custom DateTimeAdapter :
package utils;
import java.util.Date;
import javax.xml.bind.annotation.adapters.XmlAdapter;
public class JaxBDateTimeAdapter extends XmlAdapter<String, Date> {
#Override
public String marshal(Date d) throws Exception {
if(d != null){
return DateUtil.getFormatedDateTimeString(d);
}
else{
return null;
}
}
#Override
public Date unmarshal(String d) throws Exception {
if(d != null && !"".equals(d)){
return DateUtil.getDateFromString(d);
}
else{
return null;
}
}
}
Here is my reference XML
<?xml version="1.0" encoding="UTF-8"?>
<Task>
<TaskId>147</TaskId>
<CRPR>0087</CRPR>
<CIPR>A683557</CIPR>
<CHPR>BV</CHPR>
<StartDate>22/01/2009 20:56:29</StartDate>
</Task>
and Here is the XML I'm getting when re-marshalling the object :
<?xml version="1.0" encoding="UTF-8"?>
<Task>
<TaskId>147</TaskId>
<CRPR>0087</CRPR>
<CIPR>A683557</CIPR>
<CHPR>BV</CHPR>
<StartDate>01/01/2012 20:56:29</StartDate>
<EndDate>09/01/2012 17:05:28</EndDate> <!-- That element should not exist ! -->
</Task>
It seems like Jaxb generates a new date for the empty field, how can I tell him via the external metadata mapping file not to generate nodes for empty or null values ? I tried to set required=false on the metadata file, and I tried testing with my custom DateTimeAdapter if the values were null, but it seems Jaxb creates a new Date object and passes it to the marshal method of the Adapter. I cant think of any way of preventing him to do this.
As for my previous questions, I have no control over the incoming XML's or the model classes.
Please note : this data is a sample I wrote, it may not be accurate since I cannot expose real data or names, there might be some typing errors.
Thanks for your help.
I'm the EclipseLink JAXB (MOXy) lead and I have not been able to reproduce your issue. It may be possible that there is a problem in your DateUtil class. The following is what I have tried:
oxm.xml
I made a small change to your metadatafile. Basically I changed it to specify the package name on the xml-bindings element rather than the individual java-type elements:
<?xml version="1.0"?>
<xml-bindings
xmlns="http://www.eclipse.org/eclipselink/xsds/persistence/oxm"
version="2.3"
package-name="sample.clazz">
<java-types>
<java-type name="Task" xml-accessor-type="NONE">
<xml-root-element name="Task" />
<xml-type prop-order="startDate endDate id ci ch cr" />
<java-attributes>
<xml-element java-attribute="startDate" xml-path="StartDate/text()">
<xml-java-type-adapter value="forum8791782.JaxBDateTimeAdapter" type="java.util.Date"/>
</xml-element>
<xml-element java-attribute="endDate" required="false" xml-path="EndDate/text()">
<xml-java-type-adapter value="forum8791782.JaxBDateTimeAdapter" type="java.util.Date"/>
</xml-element>
<xml-element java-attribute="id" xml-path="TaskId/text()" />
<xml-element java-attribute="ci" xml-path="CIPR/text()" />
<xml-element java-attribute="ch" xml-path="CHPR/text()" />
<xml-element java-attribute="cr" xml-path="CRPR/text()" />
</java-attributes>
</java-type>
</java-types>
</xml-bindings>
DateUtil
You did not provide an implementation of DateUtil in your question, so I used the following. My guess is there is code in your implementation of DateUtil that is causing the output that you are seeing:
package forum8791782;
import java.text.SimpleDateFormat;
import java.util.Date;
public class DateUtil {
private static SimpleDateFormat formatter = new SimpleDateFormat("dd/MM/yyyy HH:mm:ss");
public static String getFormatedDateTimeString(Date d) {
return formatter.format(d);
}
public static Date getDateFromString(String d) {
try {
return formatter.parse(d);
} catch(Exception e) {
throw new RuntimeException(e);
}
}
}
Demo
Below is the code I used to run this example. input.xml is the reference XML you cite in your question:
package forum8791782;
import java.io.File;
import java.util.*;
import javax.xml.bind.*;
import org.eclipse.persistence.Version;
import org.eclipse.persistence.jaxb.JAXBContextFactory;
import sample.clazz.Task;
public class Demo {
public static void main(String[] args) throws Exception {
System.out.println(Version.getVersionString());
Map<String, Object> properties = new HashMap<String, Object>(1);
properties.put(JAXBContextFactory.ECLIPSELINK_OXM_XML_KEY, "forum8791782/oxm.xml");
JAXBContext jc = JAXBContext.newInstance(new Class[] {Task.class}, properties);
File xml = new File("src/forum8791782/input.xml");
Unmarshaller u = jc.createUnmarshaller();
Task task = (Task) u.unmarshal(xml);
Marshaller m = jc.createMarshaller();
m.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
m.marshal(task, System.out);
}
}
Output
The following is the output I get from running the sample code. I do not see the EndDate element written out.
2.3.2.v20111125-r10461
<?xml version="1.0" encoding="UTF-8"?>
<Task>
<StartDate>22/01/2009 20:56:29</StartDate>
<TaskId>147</TaskId>
<CIPR>A683557</CIPR>
<CHPR>BV</CHPR>
<CRPR>0087</CRPR>
</Task>
I am trying to add a new IFrame portlet in liferay on click of a link. I have achieved it with some issues.
First I created a portlet using liferay sdk and customized the default view.jsp:
<portlet:renderURL var="pageDisp">
<portlet:param name="jspPage" value="/view.jsp" />
</portlet:renderURL>
Google <br />
Yahoo <br />
Daily Tech <br />
Wired <br />
Changed portlet.xml from
<portlet-class>com.liferay.util.bridges.mvc.MVCPortlet</portlet-class>
to
<portlet-class>test.LinksPortlet</portlet-class>
I have implemented the following code in LinksPortlet:
public void render(RenderRequest request, RenderResponse response) throws PortletException, java.io.IOException {
log("LinksPortlet:render");
try {
ThemeDisplay themeDisplay = (ThemeDisplay)request.getAttribute(com.liferay.portal.kernel.util.WebKeys.THEME_DISPLAY);
LayoutTypePortlet layoutTypePortlet = themeDisplay.getLayoutTypePortlet();
List<Portlet> portletList = layoutTypePortlet.getPortlets();
for(Portlet p : portletList) {
log(p.getPortletId());
log(p);
log(p.getDisplayName());
log(p.getInitParams());
}
String c = request.getParameter("clink");
log(c);
if(c != null && (c.equals("g") || c.equals("y") || c.equals("d") || c.equals("w"))) {
String portletId = layoutTypePortlet.addPortletId(Long.parseLong(request.getRemoteUser()), PortletKeys.IFRAME, "column-2", -1);
log("portletId: "+portletId);
long companyId = themeDisplay.getCompanyId();
long ownerId = PortletKeys.PREFS_OWNER_ID_DEFAULT;
int ownerType = PortletKeys.PREFS_OWNER_TYPE_LAYOUT;
Layout layout = themeDisplay.getLayout();
PortletPreferences prefs = PortletPreferencesLocalServiceUtil.getPreferences(companyId, ownerId, ownerType, layout.getPlid(), portletId);
Locale locale = new Locale("en", "US");
prefs.setValue("portlet-setup-use-custom-title", "true");
if(c.equals("g")) {
prefs.setValue("src","http://www.google.com");
prefs.setValue("portlet-setup-title-" + LocaleUtil.toLanguageId(locale), "Google");
}
else if(c.equals("y")) {
prefs.setValue("src","http://www.yahoo.com");
prefs.setValue("portlet-setup-title-" + LocaleUtil.toLanguageId(locale), "Yahoo");
}
else if(c.equals("d")) {
prefs.setValue("src", "http://www.dailytech.com");
prefs.setValue("portlet-setup-title-" + LocaleUtil.toLanguageId(locale), "Daily Tech");
}
else if(c.equals("w")) {
prefs.setValue("src", "http://www.wired.com");
prefs.setValue("portlet-setup-title-" + LocaleUtil.toLanguageId(locale), "Wired");
}
PortletPreferencesLocalServiceUtil.updatePreferences(ownerId, ownerType, layout.getPlid(), portletId, prefs);
portletList = layoutTypePortlet.getPortlets();
for(Portlet p : portletList) {
if(p.getPortletId().equals(portletId)) {
break;
}
}
LayoutLocalServiceUtil.updateLayout(layout.getGroupId(), layout.isPrivateLayout(), layout.getLayoutId(), layout.getTypeSettings());
}
}
catch(Exception e) {
log("LinksPortlet:doView:Exception");
e.printStackTrace();
}
super.render(request, response);
}
The issue with the above implementation is that the new portlet appears on the screen after the page reload:
http://www.go-ahead.in/books/1.JPG
http://www.go-ahead.in/books/2.JPG
http://www.go-ahead.in/books/3.JPG
The reason for the issue is that the new portlet is added after the layout render starts. Is it possible to customize or add a hook for performing the action I am performing during page / layout load. I came across the source LayoutAction.java of liferay where portlets are added. Is there any way to add a post hook to add the portlet without modifying the liferay's source code?
I am using Liferay 6.0.5.
Thanks in advance.
Found the solution to the problem.
I have moved the code from render method to processAction and creating the link using <portlet:actionURL> tag.
I am a newbie at WCF. I created a WCF restful service in VS2010 (WCF service appl). It was targeted for Framework 4.0. I hosted this service on local IIS with appl pool set for framework 4.0. When I call the appl methods from browser or fiddler, they work fine. Now, I created a client console based. When I call any method from client I get the following Communication Exception:
** remote server returned an unexpected response: (405) Method Not Allowed.**
Service Interface file:
namespace MyService
{
[ServiceContract]
public interface ITestService
{
[WebGet(UriTemplate = "GetDateTime")]
[OperationContract]
string GetDateTime();
[WebGet(UriTemplate = "GetName")]
[OperationContract]
string GetName();
}
}
Class that implements the above interface:
namespace MyService
{
public class TestService : ITestService
{
public string GetDateTime()
{
return DateTime.Now.ToString();
}
public string GetName()
{
return "MY name is KingKong";
}
}
}
Web.config file:
<system.serviceModel>
<services>
<service behaviorConfiguration="ServiceBehavior" name="MyService.TestService">
<endpoint address="" behaviorConfiguration="web" binding="webHttpBinding" bindingConfiguration="" contract="MyService.ITestService">
</endpoint>
</service>
</services>
<behaviors>
<endpointBehaviors>
<behavior name="web">
<webHttp />
</behavior>
</endpointBehaviors>
<serviceBehaviors>
<behavior name="ServiceBehavior">
<!-- To avoid disclosing metadata information, set the value below to false and remove the metadata endpoint above before deployment -->
<serviceMetadata httpGetEnabled="true" />
<!-- To receive exception details in faults for debugging purposes, set the value below to true. Set to false before deployment to avoid disclosing exception information -->
<serviceDebug includeExceptionDetailInFaults="false" />
</behavior>
</serviceBehaviors>
</behaviors>
<serviceHostingEnvironment multipleSiteBindingsEnabled="true" />
TestService.svc
<%# ServiceHost Language="C#" Debug="true" Service="MyService.TestService" CodeBehind="TestService.svc.cs" %>
Client app.config:
<system.serviceModel>
<behaviors>
<endpointBehaviors>
<behavior name="">
<webHttp />
</behavior>
</endpointBehaviors>
</behaviors>
<client>
<endpoint address="http://localhost/BestService/TestService.svc"
binding="webHttpBinding" bindingConfiguration="" contract="MyService.ITestService"
name="MyClientConfig" kind="" endpointConfiguration="" />
</client>
</system.serviceModel>
Client program calling proxy class
static void Main(string[] args)
{
TestServiceClient proxy = null;
try
{
proxy = new TestServiceClient();
Console.WriteLine("Test 1: List all products");
string sdatetime = proxy.GetName();
Console.WriteLine("Datetime: {0}", sdatetime);
Console.WriteLine();
// Disconnect from the service
proxy.Close();
}
catch (CommunicationException cex)
{
if (cex.InnerException != null)
{
Console.WriteLine("{0}", cex.InnerException.Message);
}
else
{
Console.WriteLine("General exception: {0}", cex.Message);
}
}
catch (Exception e)
{
if (e.InnerException != null)
{
Console.WriteLine("{0}", e.InnerException.Message);
}
else
{
Console.WriteLine("General exception: {0}", e.Message);
}
}
Console.WriteLine("Press ENTER to finish");
Console.ReadLine();
}
I added a service reference to the application and the Reference.cs file has following partial code:
namespace MyClient.MyService {
[System.CodeDom.Compiler.GeneratedCodeAttribute("System.ServiceModel", "4.0.0.0")]
[System.ServiceModel.ServiceContractAttribute(ConfigurationName="MyService.ITestService")]
public interface ITestService {
[System.ServiceModel.OperationContractAttribute(Action="http://tempuri.org/ITestService/GetDateTime", ReplyAction="http://tempuri.org/ITestService/GetDateTimeResponse")]
string GetDateTime();
[System.ServiceModel.OperationContractAttribute(Action="http://tempuri.org/ITestService/GetName", ReplyAction="http://tempuri.org/ITestService/GetNameResponse")]
string GetName();
}
[System.CodeDom.Compiler.GeneratedCodeAttribute("System.ServiceModel", "4.0.0.0")]
public interface ITestServiceChannel : MyClient.MyService.ITestService, System.ServiceModel.IClientChannel {
}
[System.Diagnostics.DebuggerStepThroughAttribute()]
[System.CodeDom.Compiler.GeneratedCodeAttribute("System.ServiceModel", "4.0.0.0")]
public partial class TestServiceClient : System.ServiceModel.ClientBase<MyClient.MyService.ITestService>, MyClient.MyService.ITestService {
public TestServiceClient() {
}
public TestServiceClient(string endpointConfigurationName) :
base(endpointConfigurationName) {
}
public TestServiceClient(string endpointConfigurationName, string remoteAddress) :
base(endpointConfigurationName, remoteAddress) {
}
public TestServiceClient(string endpointConfigurationName, System.ServiceModel.EndpointAddress remoteAddress) :
base(endpointConfigurationName, remoteAddress) {
}
public TestServiceClient(System.ServiceModel.Channels.Binding binding, System.ServiceModel.EndpointAddress remoteAddress) :
base(binding, remoteAddress) {
}
public string GetDateTime() {
return base.Channel.GetDateTime();
}
public string GetName() {
return base.Channel.GetName();
}
}
}
Please help me as I have spent nearly 2 days trying to figure out the problem in the client
Thanks
VS2010 doesnt have the ability to generate a proxy for a RESTFUL service as it does for a SOAP based service. So the solution is write your own proxy class which inherits from the ClientBase class and use the Channel to call the web methods of the service.
There is a problem in SharePoint (both WSS3 and WSS2) that item registration and edit forms do not have the "autocomplete" feature in Internet Explorer.
That is, if you need the same value in some text field frequently, you have to type it manually. Internet Explorer does not provide you with drop-down list of values you have previously entered. In FireFox this thing works, however.
As I found out from this response to a similar question, it is because Internet Explorer disables auto-complete in pages which have "no-cache" or "expires" headers. SharePoint really does send non-cacheable pages to the client. This SO response also says that one should add autocomplete="on" to the form tag, it overrides the cache headers.
I edited the FORM element in "default master" page on my server to always include the autocomplete="on" and - yes, autocomplete feature works!
However, Microsoft warns us NOT to edit the "default.master" as it will be overwritten by the next service pack or patch.
So, the question is - what are my options to correctly solve this situation? I want to have autocomplete to be enabled in whole server farm.
However, Microsoft warns us NOT to
edit the "default.master" as it will
be overwritten by the next service
pack or patch.
Copy & Paste new masterpage with different name and use it as the default one. Use either SharePoint designer or programmatically set SPWeb.MasterUrl and/or SPWeb.CustomMasterPage.
For this, i have 2 features
One to set custom master page on current website
Other one to activate previous feature on all webs + staple feature to newly created web's
Project http://img251.imageshack.us/img251/7351/ss20100312093605.png
(MWSBalticovo is for meeting workspace - they have a different masterpage)
Web scoped feature for a single web site to use custom masterpage.
I have a feature with my custom master page packaged:
<?xml version="1.0" encoding="utf-8" ?>
<Elements xmlns="http://schemas.microsoft.com/sharepoint/">
<Module Name="BalticovoMasterPages" List="116" Url="_catalogs/masterpage" RootWebOnly="TRUE" Path="MasterPages">
<File Url="Balticovo.master" Type="GhostableInLibrary" IgnoreIfAlreadyExists="TRUE">
<Property Name="ContentType" Value="$Resources:core,MasterPage;"/>
<Property Name="MasterPageDescription" Value="$Resources:Balticovo,BalticovoMasterPageDescription;"/>
</File>
<File Url="MWSBalticovo.master" Type="GhostableInLibrary" IgnoreIfAlreadyExists="TRUE">
<Property Name="ContentType" Value="$Resources:core,MasterPage;"/>
<Property Name="MasterPageDescription" Value="$Resources:Balticovo,MWSBalticovoMasterPageDescription;"/>
</File>
</Module>
</Elements>
And FeatureReceiver:
public override void FeatureActivated(SPFeatureReceiverProperties properties)
{
SPWeb web = properties.Feature.Parent as SPWeb;
string masterUrl = "/_catalogs/masterpage/Balticovo.master";
string mwsMasterUrl = "/_catalogs/masterpage/MWSBalticovo.master";
if (web.CustomMasterUrl.ToLower().Contains("/mws")) //meeting workspace
web.CustomMasterUrl = mwsMasterUrl;
else
web.CustomMasterUrl = masterUrl;
web.MasterUrl = masterUrl;
web.Update();
}
public override void FeatureDeactivating(SPFeatureReceiverProperties properties)
{
SPWeb web = properties.Feature.Parent as SPWeb;
web.MasterUrl = "/_catalogs/masterpage/default.master";
if (web.CustomMasterUrl.ToLower().Contains("/mws")) //meetng workspace
web.CustomMasterUrl = "/_catalogs/masterpage/MWSdefault.master";
else
web.CustomMasterUrl = "/_catalogs/masterpage/default.master";
web.Update();
}
2nd Site scoped feature to enable previous features
elements.xml (activate 1st feature on newly created web's, but will not activate on existing):
<?xml version="1.0" encoding="utf-8" ?>
<Elements xmlns="http://schemas.microsoft.com/sharepoint/">
<FeatureSiteTemplateAssociation
TemplateName="GLOBAL"
Id="{227c6aed-f66b-482d-aea8-a2af3ca203b7}" />
</Elements>
FeatureReceiver (activate 1st feature on existing webs):
public override void FeatureActivated(SPFeatureReceiverProperties properties)
{
Guid masterPageFeatureId = new Guid("{227c6aed-f66b-482d-aea8-a2af3ca203b7}");
SPSite site = properties.Feature.Parent as SPSite;
SPWebCollection webs = site.AllWebs;
foreach (SPWeb web in webs)
{
try
{
if (web.Features[masterPageFeatureId] == null)
web.Features.Add(masterPageFeatureId);
}
catch (InvalidOperationException) //target feature not yet installed
{ throw; }
catch (SPException) { } //If feature could not be activated.
finally
{
if (web != null)
web.Dispose();
}
}
}
public override void FeatureDeactivating(SPFeatureReceiverProperties properties)
{
Guid masterPageFeatureId = new Guid("{227c6aed-f66b-482d-aea8-a2af3ca203b7}");
SPSite site = properties.Feature.Parent as SPSite;
SPWebCollection webs = site.AllWebs;
foreach (SPWeb web in webs)
{
try
{
if (web.Features[masterPageFeatureId] == null)
web.Features.Remove(masterPageFeatureId);
}
catch (InvalidOperationException) { }
catch (SPException) { }
finally
{
if (web != null)
web.Dispose();
}
}
}
Has anybody ever written code to return the GUID of a folder in SharePoint Workflow Activity so I can then pass it into a Workflow Variable?
Would be really keen to see a code sample if you have one!
Thanks
I've done something similar, and I can offer a few tips. I didn't find code samples, so I copied code from SharePoint's DLLs using Reflector.
File: C:\Program Files\Common Files\Microsoft Shared\web server extensions\12\ISAPI\microsoft.sharepoint.WorkflowActions.dll
Class (for example): Microsoft.SharePoint.WorkflowActions.WaitForActivity
You'll find three properties and DependencyPropertys:
[Browsable(true), ValidationOption(ValidationOption.Required), DesignerSerializationVisibility(DesignerSerializationVisibility.Visible)]
public WorkflowContext __Context
{
get { return (WorkflowContext)base.GetValue(__ContextProperty); }
set { base.SetValue(__ContextProperty, value); }
}
[DesignerSerializationVisibility(DesignerSerializationVisibility.Visible), Browsable(true), ValidationOption(ValidationOption.Required)]
public string __ListId
{
get { return (string)base.GetValue(__ListIdProperty); }
set { base.SetValue(__ListIdProperty, value); }
}
[Browsable(true), DesignerSerializationVisibility(DesignerSerializationVisibility.Visible), ValidationOption(ValidationOption.Required)]
public int __ListItem
{
get { return (int)base.GetValue(__ListItemProperty); }
set { base.SetValue(__ListItemProperty, value); }
}
public static DependencyProperty __ContextProperty;
public static DependencyProperty __ListIdProperty;
public static DependencyProperty __ListItemProperty;
And this in a static constructor:
static MyActivity()
{
__ContextProperty = DependencyProperty.Register("__Context", typeof(WorkflowContext), typeof(MyActivity));
__ListIdProperty = DependencyProperty.Register("__ListId", typeof(string), typeof(MyActivity));
__ListItemProperty = DependencyProperty.Register("__ListItem", typeof(int), typeof(MyActivity));
}
Bind them on your actions file:
<Parameters>
<Parameter Name="__Context" Type="Microsoft.SharePoint.WorkflowActions.WorkflowContext, Microsoft.SharePoint.WorkflowActions" Direction="In"/>
<Parameter Name="__ListId" Type="System.String, mscorlib, mscorlib" Direction="In" />
<Parameter Name="__ListItem" Type="System.Int32, mscorlib, mscorlib" Direction="In" />
</Parameters>
This can be copied from the file
C:\Program Files\Common Files\Microsoft Shared\web server extensions\12\TEMPLATE\1033\Workflow\WSS.ACTIONS
Then, it sould be relatively easy to get the GUID, and return it using a property and out parameter binding.