I'm writing a plugin for a TFS process.
I need to get the Project Name from the TeamFoundationRequestContext whenever a work item is in the process of saving.
Normally I can get the work item ID because the record has already been saved. However, when the work Item is being saved the first time, I do not have a way to get the work item ID.
My question is how can I get the Project Name from the TeamFoundationRequestContext when the work item saves for the first time.
Here is a class that captures the work item changed event and checks to see if it is a newly created work item.
From within the event if it is a newly created work item you can then do what you need with the Project name that is associated with that work item.
using Microsoft.TeamFoundation.Framework.Server;
using System;
using Microsoft.TeamFoundation.Common;
using Microsoft.TeamFoundation.WorkItemTracking.Server;
namespace TfsProcess.CaptureProjectNameOnNewWorkItem
{
public class CaptureProjectNameOnNewWorkItem : ISubscriber
{
public string Name
{
get
{
return "CaptureProjectNameOnNewWorkItem";
}
}
public SubscriberPriority Priority
{
get
{
return SubscriberPriority.Normal;
}
}
public EventNotificationStatus ProcessEvent(
TeamFoundationRequestContext requestContext,
NotificationType notificationType,
object notificationEventArgs,
out int statusCode,
out string statusMessage,
out ExceptionPropertyCollection properties)
{
statusCode = 0;
properties = null;
statusMessage = String.Empty;
try
{
ProcessNotification(notificationType, notificationEventArgs, requestContext);
}
catch (Exception exception)
{
TeamFoundationApplicationCore.LogException("Error processing event", exception);
}
return EventNotificationStatus.ActionPermitted;
}
private static void ProcessNotification(NotificationType notificationType, object notificationEventArgs, TeamFoundationRequestContext requestContext)
{
if (notificationType == NotificationType.Notification && notificationEventArgs is Microsoft.TeamFoundation.WorkItemTracking.Server.WorkItemChangedEvent)
{
var ev = notificationEventArgs as WorkItemChangedEvent;
if (ev.ChangeType == ChangeTypes.New)
{
//Do somethin with the project name of the newly created work item
// projectName = ev.PortfolioProject;
}
}
}
public Type[] SubscribedTypes()
{
return new Type[1] { typeof(WorkItemChangedEvent) };
}
}
}
Update:
You could create a plugin implementing the ITeamFoundationRequestFilter interface, which gets executed BEFORE or AFTER Team Foundation Server receives and processes requests. This allows you to validate work items and cancel the creation of the work item if it is not valid based on some logic for instance.
Here is a link to blog with a implementation of this that will cancel the creation of the work item if it is being created by a certain user.
Getting information from a TFS Request
How to implement ITeamFoundationRequestFilter
In order to get the Work Item ID you will intercept the xml soap response and parse it for the work item value.
Using the filter plugin you can filter for any of the methods responsible for updating, creating and querying work items.
The blog goes into more explanation and implementation.
The WorkItemChangedEvent event has a PortfolioProject property, so you should be able to get it like this:
var ev = (WorkItemChangedEvent)notificationEventArgs;
var projectName = ev.PortfolioProject
Related
I have created a generic inquiry in Acumatica that returns work orders that are open. We know that there is a print action that can be applied however, we want to print out multiple instances of the work orders. So we do not have to print them individually. Is there currently a solution for mass printing or can someone point me in the right direction of maybe a work around? I know there is a mass action print all function that can be found however, it does not do anything.
Just like in any other action redirecting users to the generated report(s), the Service Orders' Print Service Order button throws a PXReportRequiredException to open generated report in new window:
public class ServiceOrderEntry : PXGraph<ServiceOrderEntry, FSServiceOrder>
{
...
public PXAction<FSServiceOrder> printServiceOrder;
[PXUIField]
[PXButton]
public virtual void PrintServiceOrder()
{
if (this.IsDirty) Actions.PressSave();
if (ServiceOrderRecords.Current == null) return;
Dictionary<string, string> serviceOrderParameters =
GetServiceOrderParameters(ServiceOrderRecords.Current, false);
if (serviceOrderParameters.Count != 0)
{
throw new PXReportRequiredException(serviceOrderParameters,
"SD641000", PXBaseRedirectException.WindowMode.NewWindow, string.Empty);
}
}
...
}
If you add Service Orders' Print Service Order action as a mass action to Generic Inquiry (as shown on the screenshots below), the GI mass action will generate and open Service Order report form only for the first selected Service Order. Processing of any consequent Service Order will not be possible due to the PXReportRequiredException thrown to show the report form generated for the first Service Order.
In order to merge Service Order report forms generated for several Service Orders into a single report, you should create a custom processing screen and use the AddSibling method on a PXReportRequiredException instance. The AddSibling method will append the reports generated for 2nd+ Service Order to the PXReportRequiredException instance initially created for the first processed Service Order. After all selected Service Orders has been processed, a single PXReportRequiredException will be thrown to redirect the user to Report Viewer displaying all generated reports at once.
public class PrintServiceOrderProcess : PXGraph<PrintServiceOrderProcess>
{
public PXCancel<FSServiceOrder> Cancel;
public PXProcessing<FSServiceOrder> ServiceOrderRecords;
public PrintServiceOrderProcess()
{
ServiceOrderRecords.SetProcessDelegate(list => PrintServiceOrders(list));
}
public static void PrintServiceOrders(IEnumerable<FSServiceOrder> list)
{
PXReportRequiredException ex = null;
foreach (var order in list)
{
Dictionary<string, string> parameters = new Dictionary<string, string>();
string srvOrdType = SharedFunctions
.GetFieldName<FSServiceOrder.srvOrdType>(true);
string refNbr = SharedFunctions
.GetFieldName<FSServiceOrder.refNbr>(true);
parameters[srvOrdType] = order.SrvOrdType;
parameters[refNbr] = order.RefNbr;
if (ex == null)
{
ex = new PXReportRequiredException(parameters, "SD641000", "SD641000");
}
else
{
ex.AddSibling("SD641000", parameters, false);
}
}
if (ex != null) throw ex;
}
}
I am in the early stages of developing a new module.
Much of it is laid out in terms of the models etc. Also have the migrations all set up and my database now has the tables for my module.
I am encountering the following error when calling ContentManager.New<myPart> and would like some help please.
Error is this:
An unhandled exception has occurred and the request was terminated. Please refresh the page. If the error persists, go back
Specified cast is not valid.
System.InvalidCastException: Specified cast is not valid.
at Orchard.ContentManagement.ContentCreateExtensions.New[T]
(IContentManager manager, String contentType)
The chunk of code that fires the exception is this:
public static T New<T>(this IContentManager manager, string contentType) where T : class, IContent {
var contentItem = manager.New(contentType);
if (contentItem == null)
return null;
var part = contentItem.Get<T>();
if (part == null)
throw new InvalidCastException();
return part;
}
Here are the various parts to my module that are related to the operation i am struggling with:
ContentPart
public class GoogleMapsSettingsPart : ContentPart<GoogleMapsSettingsPartRecord>
{
public string ApiKey {
get { return Record.ApiKey; }
set { Record.ApiKey = value; }
}
}
ContentPartRecord
public class GoogleMapsSettingsPartRecord : ContentPartRecord
{
public virtual string ApiKey { get; set; }
}
Handler
public GoogleMapsSettingsPartHandler(IRepository<GoogleMapsSettingsPartRecord> repository)
{
Filters.Add(StorageFilter.For(repository));
}
Migration for this table
// Settings Table
SchemaBuilder.CreateTable("GoogleMapsSettingsPartRecord", table => table
.ContentPartRecord()
.Column("ApiKey", DbType.String, c => c.WithLength(60))
);
Some of the code from the controller for this model etc
public AdminController(IContentManager contentManager, IShapeFactory shapeFactory, IServiceLocatorService serviceLocatorService, INotifier notifier)
{
_contentManager = contentManager;
_serviceLocatorService = serviceLocatorService;
_notifier = notifier;
Shape = shapeFactory;
T = NullLocalizer.Instance;
}
/// <summary>
/// Display Settings
/// </summary>
/// <returns></returns>
public ActionResult Settings()
{
var settings = _serviceLocatorService.GoogleMapsSettings;
var editor = CreateSettingsEditor(settings);
var model = _services.ContentManager.BuildEditor(settings);
return View((object)model);
}
Finally - the Services where my call throws this exception
private GoogleMapsSettingsPart _settings;
public GoogleMapsSettingsPart GoogleMapsSettings
{
get {
if (_settings == null)
{
_settings = _contentManager.Query<GoogleMapsSettingsPart, GoogleMapsSettingsPartRecord>().List().FirstOrDefault();
if (_settings == null)
{
_settings = _contentManager.New<GoogleMapsSettingsPart>("GoogleMapsSettings");
}
}
return _settings;
}
}
The actual line where the exception happens is _settings = _contentManager.New<GoogleMapsSettingsPart>("GoogleMapsSettings");
I have tried all sorts of stuff in place of "GoogleMapsSettings" though nothing is working.
I'm pretty sure at this point it's something simple, though it's avoiding me..My limited knowledge of Orchard is stumping me
Any help would be appreciated :)
The exception is thrown because your content type does not have the part you specified to get.
_contentManager.New<GoogleMapsSettingsPart>("GoogleMapsSettings");
This method call creates a new content item of type GoogleMapSettings and gets the content item as a GoogleMapsSettingsPart. However, it seems that GoogleMapSettings content type does not have a GoogleMapsSettingsPart. That's why the exception gets thrown here.
var part = contentItem.Get<T>();
if (part == null)
throw new InvalidCastException();
You must either attach the part dynamically to your content type or do it in a migration (or manually in the admin, but that's not a good idea). Your migration should look like this.
this.ContentDefinitionManager.AlterTypeDefinition("GoogleMapsSettings",
alt => alt
.WithPart("GoogleMapsSettingsPart");
Ok, so I fixed it...
My understanding of how Orchard works is still very much in the learning stages.
for this particular operation I didn't want to have a content type in the admin - though not sure why after adding the ContentType it still didn't work...
anyway, adding the lines below to my handler took care of the rest. I believe it's actually creating a temporary type so one isn't needed in the system.
public GoogleMapsSettingsPartHandler(IRepository<GoogleMapsSettingsPartRecord> repository)
{
Filters.Add(new ActivatingFilter<GoogleMapsSettingsPart>("GoogleMapsSettings"));
Filters.Add(StorageFilter.For(repository));
Filters.Add(new TemplateFilterForRecord<GoogleMapsSettingsPartRecord>("GoogleMapsSettings", "Parts/GoogleMapsSettings"));
}
I'v got the same error, but in my case it was everything ok with migration class.
The reason was unlucky merge, which deleted my driver class of my part.
Just look at this code of Activating method of ContentPartDriverCoordinator class. In my case there was no partInfo for my content part and resulted part became ContentPart, so casting throws an exception
var partInfos = _drivers.SelectMany(cpp => cpp.GetPartInfo()).ToList();
foreach (var typePartDefinition in contentTypeDefinition.Parts) {
var partName = typePartDefinition.PartDefinition.Name;
var partInfo = partInfos.FirstOrDefault(pi => pi.PartName == partName);
var part = partInfo != null
? partInfo.Factory(typePartDefinition)
: new ContentPart { TypePartDefinition = typePartDefinition };
context.Builder.Weld(part);
}
I'm working on TFS API 2010.
I would like to get the available option list of a field to create a Combobox control. such as:
Priority --> [1,2,3,4]
Severity-->['4-low','3-medium','2-height','1-Critical']
You will need to export the WorkItemType Definition from TFS, then find the field in the xml and use the values from there. Below is a snippet of code that I used to get a list of transitions, if you think that the options might be in a Global List then you'll set the flag in the export method to true.
public List<Transition> GetTransistions(WorkItemType workItemType)
{
List<Transition> currentTransistions;
// See if this WorkItemType has already had it's transistions figured out.
this._allTransistions.TryGetValue(workItemType, out currentTransistions);
if (currentTransistions != null)
{
return currentTransistions;
}
// Get this worktype type as xml
XmlDocument workItemTypeXml = workItemType.Export(false);
// Create a dictionary to allow us to look up the "to" state using a "from" state.
var newTransitions = new List<Transition>();
// get the transitions node.
XmlNodeList transitionsList = workItemTypeXml.GetElementsByTagName("TRANSITIONS");
// As there is only one transitions item we can just get the first
XmlNode transitions = transitionsList[0];
// Iterate all the transitions
foreach (XmlNode transition in transitions)
{
// save off the transition
newTransitions.Add(new Transition { From = transition.Attributes["from"].Value, To = transition.Attributes["to"].Value });
}
// Save off this transition so we don't do it again if it is needed.
this._allTransistions.Add(workItemType, newTransitions);
return newTransitions;
}
Transition is a small class I have as below.
public class Transition
{
#region Public Properties
public string From { get; set; }
public string To { get; set; }
#endregion
}
I'm really trying hard to put everything on my project to work with the EF, but it's really getting difficult and sometimes it makes me wonder if it's really the smart move (to rely on EF against coding all the ins and outs of the database).
Well, my problem is still related to 1-N creating/editing/deleting functionality (something that should be simple, right?).
Ok, I'm pasting here some simple equivalent of my code.
For the Entities, I got the main class as:
[Table("OLIM_LOTE")]
public class Lote
{
[Key]
[Column("LOTE_ID_LOTE")]
public int? IDLote { get; set; }
[Column("LOTE_TX_OBS")]
public string Obs {get;set;}
[Column("LOTE_TX_DOCUMENTO_EXTRA")]
public string DocumentoExtra { get; set; }
[NotMapped]
public List<DocumentoLote> Documentos { get; set; }
public void LoadLists()
{
OlimpiqueDBContext myDbContext = new OlimpiqueDBContext();
var docs = (from doc in myDbContext.DocumentosLote
where doc.IDLote == this.IDLote
select doc);
this.Documentos = docs.ToList<DocumentoLote>();
}
}
[Notice that i used the nullable int? for Key - otherwise it throws me validation exception asking for a value on creation]
For the child class, i got this:
[Table("OLIM_DOCUMENTO_LOTE")]
public class DocumentoLote
{
[Key]
[Column("DOLO_ID_DOCUMENTO_LOTE")]
public int? IDDocumentoLote { get; set; }
[Column("DOCU_ID_DOCUMENTO")]
[ForeignKey("Documento")]
public int IDDocumento { get; set; }
public virtual Documento Documento { get; set; }
[Column("LOTE_ID_LOTE")]
[ForeignKey("Lote")]
public int IDLote { get; set; }
public virtual Lote Lote { get; set; }
}
[Notice that the child class has a reference back to the owner class, which are the "IDLote" and "Lote" attributes, and the owner class has a list of child class instances - so I got i bi-directional refernce - I assume that this is somehow related to the problems]
I got a Controller and View generated automatically by VS2012 with Read/Write functionality related to the class Lote.
What I did in the View can be described as: I used a Jquery DataTable to manage the child class data (the user can add "N" instances on the DataTable). I substituted the Post Button with a call to a JS method that simply gets all the data from the Form and from the DataTable and wrap it in a JSon object and send it to the controller via Ajax.
The controller method that receives it can be simplified as below:
[HttpPost]
public JsonResult Edit(Lote lote)
{
try
{
if (ModelState.IsValid) //<< HAVING PROBLEMS HERE... DETAILS BELOW
{
if (lote.IDLote.HasValue)
{
//Separete updates/inserts from deletes
List<int?> dbDocs = db.DocumentosLote
.Where(dt => dt.IDLote == lote.IDLote)
.Select(dt => dt.IDDocumentoLote)
.ToList();
List<int?> postedDocs = lote.Documentos
.Select(pt => pt.IDDocumentoLote)
.ToList();
List<int?> deletedDocs = dbDocs
.Except(postedDocs).ToList();
//Perform deletes
foreach (var delDocId in deletedDocs)
{
if (delDocId.HasValue)
{
DocumentoLote delDoc = db.DocumentosLote
.Where(dt => dt.IDLote == lote.IDLote && dt.IDDocumentoLote == delDocId)
.Single();
db.Entry(delDoc).State = EntityState.Deleted;
}
}
//Perform insert and updates
foreach (var doc in lote.Documentos)
{
if (doc.IDDocumentoLote.HasValue)
{
db.Entry(doc).State = EntityState.Modified;
}
else
{
db.Entry(doc).State = EntityState.Added;
doc.IDLote = (int)lote.IDLote;
}
}
}
else
{
db.Lotes.Add(lote);
}
db.SaveChanges();
// If Sucess== 1 then Save/Update Successfull else there it has Exception
return Json(new { Success = 1, ex = "" });
}
else
{
return Json(new { Success = 0, ex = "Falha ao tentar salvar os dados" });
}
}
catch (Exception ex)
{
// If Sucess== 0 then Unable to perform Save/Update Operation and send Exception to View as JSON
return Json(new { Success = 0, ex = ex.Message.ToString() });
}
}
Problems: Well I really passed through a lot to got to this point and now, I got only 2 problems. The first being that the creation is throwing a Validation Exception sayin that it needs an IDLote (for the child classes - but anyway, how would i have it if the owner class itself still doesn't have an Id at that point in creation?)
Second problem: Deletion dont work at all! Doesn't matter how i code it, it throws the exception "objects cannot be defined because they are attached to different ObjectContext objects". I really feel that this has something to do with the bidirectional reference between owner-children classes, but still, don't have a clue on exactly whats happening and how to solve it
I'm starting to feel really lost here. Any ideas on this would be very appreciated. Thanks
As there are a lot of views on this old question and now I do have some answer, I'm posting them for reference:
Q - Regarding the int? type for the key attributes:
A - It doesn't have to be a nullable int at all. The entity can be declared with a simple int attribute as key and when posting the JSon object from the View, back to some controller method, this attribute (the key) can be filled with the value "0". EF will generate the correct value as soon as it persists the object.
Q - Regarding the navigational attributes and how to implement the relation between the two classes when neither of them have already got a value (non-zero) on theis keys:
A - The JSon object to be sent back can implement the exact navigational relationaship between them. Wehn the controller binds the data posted to the model it should be receiving, it will "understand" their relationship and as soon as the values for the keys are generated, they will correctly reference one another.
Q - Regarding the error described on the delete method attempts:
A - When objects should interact with other objects, and those interactions should be persisted or "understood" by EF in any way, they must have been obtained, generated or attached to a same DBContext. EF rely on the DB context to create a tree of this interactions, thus, rendering impossible to build this tree when objets are not present on the same DB Context.
Due to performance issue , I have added the publishing code in thread.
My code is:
public void functionname()
{
----------------
------------
try
{
HttpontextforMailSending gethttpcontextforpublish2 = new HttpontextforMailSending()
{
HttpContextReference = HttpContext.Current,
courseDocument = shortCourseDocument,
createdUser = new User(0)
};
Thread t2 = new Thread(PublishDocument);
t2.Start(gethttpcontextforpublish2);
}
catch { }
-------------
-----------
}
private void PublishDocument(object input)
{
HttpontextforMailSending httpcontextformail = (HttpontextforMailSending)input;
Document course = httpcontextformail.courseDocument;
User createduser = httpcontextformail.createdUser;
if (course != null && createduser != null)
{
course.Publish(createduser);
umbraco.library.UpdateDocumentCache(course.Id);
}
}
public class HttpontextforMailSending
{
public HttpContext HttpContextReference { get; set; }
public Document courseDocument { get; set; }
public User createdUser { get; set; }
}
But I get Object reference not set to an instance of an object" error on "course.Publish(createduser);
The Umbraco version is 4.8.1.
May be this error is due to course.HttpContext. It has null value.
However when I set it as course.HttpContext = httpcontextformail.HttpContextReference; it shows a warning
"umbraco.cms.businesslogic.web.document.HttpContext is obsolete. Do not use this. GethttpContextvia regular ASP.Net methods instead.
When I debug this code, I get HttpContext on httpcontextformail.HttpContextReference.
But after executing course.HttpContext = httpcontextformail.HttpContextReference;, course.HttpContext still have null value.
Please help me
Thanks
Umbraco expects to be used within a web request and therefore the API will not work if called outside of a web request in another application etc.
If you want to execute some something using the Umbraco API externally then you will need to use Umbraco Base (or similar) so that the API has the needed HttpContext.
Umbraco Base is a RESTlike system for allowing the execution of queries or code via defined URLs within an Umbraco context.