I am trying to sync Sharepoint task list with Outlook. When the users connect the task list to outlook, the task for all users are visible in outlook. Rather than applying filtering in Outlook, can I provide a filtering at the source itself? There are considerable number of users for my application, it wouldn't be good to ask all users to apply filters on their own.
Any other suggestions?
Thanks.
I`v asked the same question: Sync list with outlook only with items in current view.. In this case it was possible to use stssync protocol to do whatever you want. It takes much effort (luckily someone already wrote an implementation)
But there was another solution i ended up using - implementing a wrapper for Lists.asmx webservice and rewriting outlook requests (by using custom Global.asax file) to use this new webservice instead of Lists.asmx, that only queries specific view in a list.
if (ctx.Request.UserAgent.Contains("Microsoft Office Outlook") && path.ToLower().IndexOf("_vti_bin/lists.asmx") >= 0)
{
ctx.RewritePath("/_layouts/OutlookLists.asmx");
}
I'm not sure you would want a solution like this. If you do, you may ask and i may publish the solution source for the webservice, however i'm not using this webservice myself anymore. And you could use it as a draft, not a production ready code.
The source has been published on CodePlex.
Regarding to the script problem
I don't know why list id isn't being replaced by view id. I tried to run the function within script console (F12 for IE8/9)
>> var menuItems = document.getElementsByTagName('ie:menuitem');
for (var i = 0; i < menuItems.length; i++) {
itm = menuItems(i);
if (itm.id.match('OfflineButton') != null) {
console.log('listName:' + ctx.listName.toLowerCase() + 'viewName:' + ctx.view.toLowerCase());
if (ctx != null && ctx.listName != null && ctx.view != null) {
console.log('Inside if block');
//Replace listId to viewId being used so outlook will query only items in current view.
//Must have custom web service in place to handle that request, because it iwll not work OOTB.
console.log("Before: " + itm.onMenuClick);
itm.onMenuClick = itm.onMenuClick.replace(ctx.listName.toLowerCase(), ctx.view.toLowerCase());
console.log("After: " + itm.onMenuClick);
break;
}
}
}
LOG: listName:{fe89e809-7de4-4f43-9bc2-7e8ce6624ed0}viewName:{7364a843-c7f2-47d8-b4a3-5dc7381b6248}
LOG: Inside if block
LOG: Before: javaScript:ExportHailStorm('tasks','https:\u002f\u002fserver\u002fsapulces\u002fdarbu_parskata','{fe89e809-7de4-4f43-9bc2-7e8ce6624ed0}','Uz\u0146\u0113muma darbu p\u0101rskata sapulce','Uzdevumi','\u002fsapulces\u002fdarbu_parskata\u002fLists\u002fUzdevumi','','\u002fsapulces\u002fdarbu_parskata\u002fLists\u002fUzdevumi');
LOG: After: javaScript:ExportHailStorm('tasks','https:\u002f\u002fserver\u002fsapulces\u002fdarbu_parskata','{7364a843-c7f2-47d8-b4a3-5dc7381b6248}','Uz\u0146\u0113muma darbu p\u0101rskata sapulce','Uzdevumi','\u002fsapulces\u002fdarbu_parskata\u002fLists\u002fUzdevumi','','\u002fsapulces\u002fdarbu_parskata\u002fLists\u002fUzdevumi');
As you can see, the function argument (third one) has been replaced with a view id instead of list id.
Don't forget to remove console.log statements before deploying, because if IE doesn't have web developer tools, javascript will crash there.
Were these tasks created from a workflow? this is a known issue with SharePoint 2007.
http://social.technet.microsoft.com/Forums/en/sharepointadmin/thread/64b3b124-085c-4d8e-8e85-8bd20736e0e7
http://blah.winsmarts.com/2007-4-SharePoint_2007__Fine_grained_permission_control.aspx
You could try setting the read/edit permissions to "only their own", but i think that breaks approval/alerts from working
I believe the problem is fixed in SharePoint 2010, i think tasks get created with fine-grained permissions per task.
Related
I'm trying to manipulate the Audit screen (SM205510) through code, using a graph object. The operation of the screen has processes that seem to work when a screen ID is selected in the header. This is my code to create a new record:
Using PX.Data;
Using PX.Objects.SM;
var am = PXGraph.CreateInstance<AUAuditMaintenance>();
AUAuditSetup auditsetup = new AUAuditSetup();
auditsetup.ScreenID = "GL301000";
auditsetup = am.Audit.Insert(auditsetup);
am.Actions.PressSave();
Now, when I execute the code above, it creates a record in the AUAuditSetup table just fine - but it doesn't automatically create the AUAuditTable records the way they are auto-generated in the screen (I realize that the records aren't in the database yet) - but how can I get the graph object to auto-generate the AUAuditTable records in the cache the way they are in the screen?
I've tried looking at the source code for the Audit screen - but it just shows blank, like there's nothing there. I look in the code repository in Visual Studio and I don't see any file for AUAuditMaintenance either, so I can't see any process that I could run in the graph object that would populate those AUAuditTable records.
Any help would be appreciated.
Thanks...
If I had such a need, to manipulate Audit screen records, I'd rather create my own graph and probably generate DAC class. Also I'd add one more column UsrIsArtificial and set it to false by default. And then manage them as ordinary records, but each time I'll add something, I'd set field UsrIsArtificial to false.
You can hardly find how that records are managed at graph level because that records are created and handled on on Graph level, but on framework level. Also think twice or even more about design, as direct writing into Audit history may cause confusion for users in the system of what was caused by user, and what was caused by your code. From that point of view I would rather add one more additional table, then add confusion to existing one.
Acumatica support provided this solution, which works beautifully (Hat tip!):
var screenID = "GL301000"; //"SO303000";
var g = PXGraph.CreateInstance<AUAuditMaintenance>();
//Set Header Current
g.Audit.Current = g.Audit.Search<AUAuditSetup.screenID>(screenID);
if (g.Audit.Current == null) //If no Current then insert
{
var header = new AUAuditSetup();
header.ScreenID = screenID;
header.Description = "Test Audit";
header = g.Audit.Insert(header);
}
foreach (AUAuditTable table in g.Tables.Select())
{
table.IsActive = true;
//Sets Current for Detail
g.Tables.Current = g.Tables.Update(table);
foreach (AUAuditField field in g.Fields.Select())
{
field.IsActive = false;
g.Fields.Update(field);
}
}
g.Actions.PressSave();
I get the error "Exception occurred calling method NotesDatabase.createDocument() null" for the following:
var db:NotesDatabase = session.getDatabase("", viewScope.targetDb);
if (db != null) {
if(db.isOpen()){
}else{
db.open();
}
} else {
}
var doc:NotesDocument = db.createDocument();
Comments:
The database db is available and "open".
The user has enough rights in the targetDb to create documents.
What is wrong?
I changed db.isOpen to db.isOpen(), according to Paul Stephen Withers tips.
And now db.open() gives the error "Exception occurred calling method NotesDatabase.open() null" although that I can get, in viewScope variables, the server, FilePath, etc.
Change
var db:NotesDatabase = session.getDatabase("", viewScope.targetDb);
to
var db:NotesDatabase = session.getDatabase(currentDatabase.getServer(), viewScope.targetDb);
This works on the web as well as XPinC.
XPages isn't the same as formula, it doesn't like the empty string definition for the server name, which is contrary to the documentation which states (for the server parameter of NotesSession.getDatabase - javascript):]
The name of the server on which the database resides. Use null to
indicate the session's environment, for example, the current computer
Using null or "" gives error 500, it would appear.
The code from the question will work, if :
the if block is removed entirely
the viewScope.targetDb variable has a properly specified notes database file path, which exists on the same server as the current database
the current user has access to the target database ( by the ACL ), with the Create Database right
the target database has a Maximum Internet name and password above Reader as per #Paul
I suspect the cause is you're checking db.isOpen. You should check db.isOpen().
Worth bearing in mind is session.getDatabase(String, String) doesn't return null (unless you're using OpenNTF Domino API). It returns a Database object that is not open. So that if statement is irrelevant. Also best practice is to pass a server name to session.getDatabase() - you'll get a different outcome if the application is ever used in XPiNC with your current code.
Regardless of individuals' user access, "Maximum Internet name and password" on the Advanced tab of the ACL will override that. If maximum internet access is No Access, no one will be able to create documents. But I suspect that's not a factor here.
Pro Tip -- Get the Debug Toolbar and use that to print out messages to the XPage debug toolbar to see what is going on and if your viewScope variable is being set. Also, learn to use the try catch block to catch your exceptions and print out the error message to the debug toolbar. You will find your issue that way.
https://www.openntf.org/main.nsf/project.xsp?r=project/XPage%20Debug%20Toolbar
I'm new to programming in Dynamics CRM Online and I'm having a problem updating a deployed plugin. I'm using Visual Studio 2012 as my IDE. I deployed a plugin that I need to modify and when I re-deploy it thru VS the modified date in CRM is correct but the changes are not there. Here is my code..
if (context.InputParameters.Contains("Target")
&& context.InputParameters["Target"] is Entity)
{
Entity entity = (Entity)context.InputParameters["Target"];
if (entity.LogicalName == "lead")
{
if (entity.Attributes.Contains("companyname") == true)
{
if (entity["firstname"].ToString() != "null")
firstName = entity["firstname"].ToString();
else
firstName = "";
if (entity["lastname"].ToString() != "null")
lastName = entity["lastName"].ToString();
else
lastName = "";
entity["companyName"] = "This is a test";
//entity["companyname"] = firstName + " " + lastName;
}
else
throw new InvalidPluginExecutionException(
"The company name can only be set by the system.");
}
}
When I create a lead the company name is not "This is a test". I'm not sure what I'm doing wrong.
Thanks for the help!
You detect whether the field with company name exists by:
if (entity.Attributes.Contains("companyname") == true)
but you write to an other, namely:
entity["companyName"] = "This is a test";
The value is put in the entity but since it has no counterpart in the metadata, it's not being stored. Set the field name to it's to schema name, i.e. lower cased.
A few other things to consider in case you get additional errors.
You need to call Update method on the service after you've set the field's value.
The field should have some kind of prefix (e.g. new_something, beep_something).
The camel casing is not applicable here (schema name is), so go alltolowercase.
What do you get as the company name? Do you get the exception being thrown?
Also, some pointers on the code quality. I've rebuilt the logic to eliminate unnecessary complexity of scopes. I removed the superfluous else statement and comparisons to true. I'd also recommend that you split the process into different methods but I'm sure you've got that covered already. And you might want to use an auxiliary method to obtain the values from the fields. See in this post at my suggestion.
if (!context.InputParameters.Contains("Target") ||
context.InputParameters["Target"] is Entity)
return;
Entity entity = context.InputParameters["Target"] as Entity;
if (entity.LogicalName != "lead")
return;
if (!entity.Attributes.Contains("companyname"))
throw new InvalidPluginExecutionException(
"The company name can only be set by the system.");
String firstName = String.Empty;
if (entity.Contains("firstname"))
firstName = entity["firstname"] as String;
String lastName = String.Empty;
if (entity.Contains("lastname"))
lastName = entity["lastname"] as String;
entity["companyname"] = "This is a test";
//entity["companyname"] = firstName + " " + lastName;
EDIT:
If you still don't get the requested behavior, try the following. (I'm not sure on what level of expertise you are so olease accept my apology if you feel insulted by me mentioning some very basic stuff you've already tried a gazillion times.)
Technical tricks.
Publish all customizations (I do it very often, just in case).
Hit F5 to reload.
Login/logout.
Reboot IIS (if on-premise).
Unregister the plug in, see if the behavior remains. Then re-register it.
Check for forgotten workflows running.
There might be some delays and lags. Once, I actually had both the old old and the new version of a plugin being fired, depending on whether I created a record from Settings or Workplace. That was weird but resolved itself after a few hours. Seriously. That was weird!
Programmatic tricks.
Check if there are other plugins you might have forgotten to deactivate.
Remove all the leads and make sure that the plugin fires upon the new one being created.
Change the text to e.g. I'm a giant moose, just to make sure that the change goes through.
Slash away all code (or place return in the beginning of Execute. Then, move it down step by step to detect when the weirdness commences.
Of what you've shown, it should work so either you didn't mention something relevant (we do appreciated that you didn't post 100000 lines of code, of course) or it's CRM that's weirding off (which is equally annoying and confusing). So, let's trouble-shoot this thing. What happens when you try the tricks above?
As for the code stub, yeah - I'm not too proud of MS effort there. Try to post that code under the tag C# on Programmers for code review. Be prepared for an angry discussion. :)
Is there any solution to create record in other DBs from the CRM 2011 records? When a record such as "cost" was created in CRM 2011, we want a record would be created in out Oracle DB. Could it be done through a plugin? Or a service should be created for this?
Could you please provide me references or solutions for this.
Any helps would be greatly appreciated.
We had a similar request from a customer a while ago. They claimed that CRM's database wasn't to be trusted and wanted to securely store a copy of the records created in - guess what - SQL Server too. (Yes, we do understand the irony. They didn't.)
The way we've resolved it was to create a plugin. However, bear in mind that simply reacting to the message of Create won't really do. You need to set up a listener for three of the CRUD operations (retrieval doesn't affect the external database so it's rather C_UD operations, then).
Here's the skeleton of the main Execute method.
public void Execute(IServiceProvider serviceProvider)
{
Context = GetContextFromProvider(serviceProvider);
Service = GetServiceFromProvider(serviceProvider);
switch (Context.MessageName)
{
case "Create": ExecuteCreate(); break;
case "Update": ExecuteUpdate(); break;
case "Delete": ExecuteDelete(); break;
}
}
After this dispatcher, you can implement the actual calls to the other database. There are three gotchas I'd like to give you head-up on.
Remember to provide a suitable value to the outer DB when CRM doesn't offer you one.
Register the plugin as asynchronous since you'll be talking to an external resource.
Consider the problem with entity references, whether to store them recursively as well.
Walk-through for plugin construction
Link to CRM SDK if you haven't got that
Information on registering the plugin
And besides that, I've got a walk-through (including code and structure) on the subject in my blog. The URL to it, you'll have to figure out yourself - I'm not going to self-promote but it's got to do with my name and WP. Google is your friend. :)
You could use a plugin to create a record in another system, although you would need to think about syncing and ensure you don't get duplicates, but it certainly can be done.
Tutorial on plugins can be found here.
You need to write a plugin that runs on Create and uses the information on the created Cost entity to create a record in your Oracle DB.
As an example:
public void Execute(IServiceProvider serviceProvider)
{
var context = (IPluginExecutionContext)serviceProvider.GetService(typeof(IPluginExecutionContext));
//get the created entity from CRM
var theCreatedEntity = context.InputParameters["Target"] as Entity;
//build up a stored procedure call
using (OracleConnection objConn = new OracleConnection("connection string"))
{
var cmd = new OracleCommand();
cmd.Connection = objConn;
cmd.CommandText = "stored procedure name";
cmd.CommandType = CommandType.StoredProcedure;
cmd.Parameters.Add("param1", OracleType.Number).Value = theCreatedEntity.GetAttributeValue<int>("Attribute1");
cmd.Parameters.Add("param2", OracleType.Number).Value = theCreatedEntity.GetAttributeValue<int>("Attribute2");
//etc
cmd.ExecuteNonQuery();
}
}
That should give you enough to get going
WSS 3.0 will let me send an email to a group when a new task is added to a task list. What I would like to do is to run a weekly task that sends out reminders of tasks due within certain periods, i.e. 2 days, 7 days, 14 days etc. I thought the simplest way would be to build a little C# app that sits on the WS2K3 box and queries the WSS database. Any ideas on which tables I should be checking? More generally is there an overall schema for the WSS3 database system?
If anyone is aware of an existing solution with code please let me know.
Thx++
Jerry
My suggestions:
don't create a console app, create a class that inherits from SPJobDefinition.
set SPJobLockType.Job to this timer, this will grant that the job is executed only once in the whole farm, even if you are running multiple front-end servers
in the, timer job, open the SPSite, SPWeb objects you need, then find the SPList\
Using SPQuery filter out only the items you need - I believe, you will have to filter out the ones where Status!=Complete
Loop through the results collection (which will be of type SPListItemCollection, apply your rules, checking the DueDate and Datetime.Now, send the e-mails
Since a task is simply a SPListItem, it has a Properties property, which is actually a property bag - you can add whatever properties you need. So, add a property My_LastSentReminderDate. Use this property to check if you are not sending too much of "corporate spam" :-)
To install your SPJobDefinition in a SharePoint farm, you can use a PowerShell script. I can give you examples, if needed.
Don't forget to Threading.Thread.CurrentThread.CurrentCulture = Your_SPWeb_Instance.Locale, otherwise date comparisons may not work if the web has a different locale!
EDIT: This is how a typical reminder looks like in my applications:
Public Class TypicalTimer
Inherits SPJobDefinition
Public Sub New(ByVal spJobName As String, ByVal opApplication As SPWebApplication)
'this way we can explicitly specify we need to lock the JOB
MyBase.New(spJobName, opApplication, Nothing, SPJobLockType.Job)
End Sub
Public Overrides Sub Execute(ByVal opGuid As System.Guid)
'whatever functionality is there in the base class...
MyBase.Execute(Guid.Empty)
Try
Using oSite As SPSite = New SPSite("http://yourserver/sites/yoursite/subsite")
Using oWeb As SPWeb = oSite.OpenWeb()
Threading.Thread.CurrentThread.CurrentCulture = oWeb.Locale
'find the task list and read the "suspects"
Dim oTasks As SPList = oWeb.Lists("YourTaskListTitle")
Dim oQuery As New SPQuery()
oQuery.Query = "<Where><Neq><FieldRef Name='Status'/>" & _
"<Value Type='Choice'>Complete</Value></Neq></Where>"
Dim oUndoneTasks As SPListItemCollection = oTasks.GetItems(oQuery)
'extra filtering of the suspects.
'this can also be done in the query, but I don't know your rules
For Each oUndoneTask As SPListItem In oUndoneTasks
If oUndoneTask(SPBuiltInFieldId.TaskDueDate) IsNot Nothing AndAlso _
CDate(oUndoneTask(SPBuiltInFieldId.TaskDueDate)) < Now().Date Then
' this is where you send the mail
End If
Next
End Using
End Using
Catch ex As Exception
MyErrorHelper.LogMessage(ex)
End Try
End Sub
End Class
To register a timer job, I typically use this kind of a script:
[System.Reflection.Assembly]::LoadWithPartialName("Microsoft.SharePoint")
[System.Reflection.Assembly]::LoadWithPartialName("Microsoft.SharePoint.Administration")
[System.Reflection.Assembly]::LoadWithPartialName("Your.Assembly.Name.Here")
$spsite= [Microsoft.SharePoint.SPSite]("http://yourserver/sites/yoursite/subsite")
$params = [System.String]("This text shows up in your timer job list (in Central Admin)", $spsite.WebApplication
$newTaskLoggerJob = new-object -type Your.Namespace.TypicalTimer -argumentList $params
$schedule = new-object Microsoft.SharePoint.SPDailySchedule
$schedule.BeginHour = 8
$schedule.BeginMinute = 0
$schedule.BeginSecond = 0
$schedule.EndHour = 8
$schedule.EndMinute = 59
$schedule.EndSecond = 59
$newTaskLoggerJob.Schedule = $schedule
$newTaskLoggerJob.Update()
Any time you need something in sharepoint that is executed periodically, 99 times out of a 100 you'll need to build a TimerJob. These are scheduled tasks that run inside SharePoint and you can create your own, then using a feature + featurereceiver to actually "install" the timoerjob (definition) and assign it a schedule.
For more info: see Andrew Connell's article on TimerJobs.
P.S. Never query /update the databases related to SharePoint directly! This will make you "unsupported", i.e. if anything happens microsoft will charge (a lot of) money to come and fix it, instead of being able to ask for regular support. (if you are say an MSDN subscriber you get up to 4 free support calls a year).
Don't bother trying to go directly to the database. You will have a very hard time because it's undocumented, unsupported, and not recommended. SharePoint does in fact have a full featured object model though.
If you reference Microsoft.SharePoint.dll (located in the Global Assembly Cache of a machine with SharePoint installed on it) you can access the data that way. The objects you'll want to start with are SPSite, SPWeb, SPList, SPQuery, and SPListItem. All of which you can find very easily on http://msdn.microsoft.com in a search.
Another less-flexible but code-free possibility you could try is creating several different views that include upcoming tasks then via the GUI set up an alert for when items are added to that view.