here is how I have routing setup.
routes.MapHttpRoute(
name: "Authors",
routeTemplate: "api/authors",
defaults: new { controller = "authors" }
);
controller action method
// GET /api/authors/
public string GetAuthors(string author_ids)
{
return data;
}
Url http://site.com/api/authors?author_ids=1 actually calls controller action but when I don't pass querystring parameter, it says no controller action matching found.
How to handle optional querystring parameter when defining route?
// GET /api/authors/
public IEnumerable<string> GetAuthors()
{
return data;
}
You will need to define an action that takes no parameters.
It would be better, however, to add id to your route as optional:
routes.MapHttpRoute(
name: "Authors",
routeTemplate: "api/{controller}/{id}",
defaults: new { id = RouteParameter.Optional }
);
You can be specific like:
routes.MapHttpRoute(
name: "Authors",
routeTemplate: "api/{controller}/{id}",
defaults: new { id = RouteParameter.Optional }
);
and Action can be:
// GET /api/authors/?XXXX
public IEnumerable<string> GetAuthors( [FromUri] String author_ids)
{
return data;
}
This will only match query string . [FromUri] lets the parameter to be taken from query string.
You may specific like:
routes.MapHttpRoute(
name: "Authors",
routeTemplate: "api/{controller}/{author_ids}",
defaults: new { author_ids = RouteParameter.Optional }
);
Related
I am working with Orchard CMS.
I have created work flow which send mail to Super admin and user himself who create block/news as a notification.
In my CMS there more than one admin and moderator. I want to send email to all moderator and admin as soon as new blog /news/article is posted or updated. Right now it is sending to only one admin user not all.
How can I create workflow to send mail to all admin as well as moderator once content (news/blog) is posted or updated?
I am working on orchard version 1.10.0.
There are two possibilities here: A custom workflow task and a simple token.
Let's start with the task. I've created a simple demo; here's the code:
Activities/RoleMailerTask.cs
namespace My.Module
{
using System.Collections.Generic;
using System.Linq;
using Orchard;
using Orchard.ContentManagement;
using Orchard.Data;
using Orchard.Email.Activities;
using Orchard.Email.Services;
using Orchard.Environment.Extensions;
using Orchard.Localization;
using Orchard.Messaging.Services;
using Orchard.Roles.Models;
using Orchard.Roles.Services;
using Orchard.Users.Models;
using Orchard.Workflows.Models;
using Orchard.Workflows.Services;
[OrchardFeature(Statics.FeatureNames.RoleMailerTask)]
public class RoleMailerTask : Task
{
private readonly IOrchardServices services;
public const string TaskName = "RoleMailer";
public RoleMailerTask(IOrchardServices services)
{
this.services = services;
this.T = NullLocalizer.Instance;
}
public Localizer T { get; set; }
public override string Name
{
get { return TaskName; }
}
public override string Form
{
get { return TaskName; }
}
public override LocalizedString Category
{
get { return this.T("Messaging"); }
}
public override LocalizedString Description
{
get { return this.T("Sends mails to all users with this chosen role"); }
}
public override IEnumerable<LocalizedString> GetPossibleOutcomes(WorkflowContext workflowContext, ActivityContext activityContext)
{
yield return this.T("Done");
}
public override IEnumerable<LocalizedString> Execute(WorkflowContext workflowContext, ActivityContext activityContext)
{
var recipientsRole = int.Parse(activityContext.GetState<string>("RecipientsRole"));
var role = this.services.WorkContext.Resolve<IRoleService>().GetRole(recipientsRole);
var userRolesRepo = this.services.WorkContext.Resolve<IRepository<UserRolesPartRecord>>();
var recepientIds = userRolesRepo.Table.Where(x => x.Role.Name == role.Name).Select(x => x.UserId);
var recipients = this.services.ContentManager.GetMany<UserPart>(recepientIds, VersionOptions.Published, QueryHints.Empty)
.Where(x => !string.IsNullOrEmpty(x.Email))
.Select(x => x.Email);
var body = activityContext.GetState<string>("Body");
var subject = activityContext.GetState<string>("Subject");
var replyTo = activityContext.GetState<string>("ReplyTo");
var bcc = activityContext.GetState<string>("Bcc");
var cc = activityContext.GetState<string>("CC");
var parameters = new Dictionary<string, object> {
{"Subject", subject},
{"Body", body},
{"Recipients", string.Join(",", recipients)},
{"ReplyTo", replyTo},
{"Bcc", bcc},
{"CC", cc}
};
var queued = activityContext.GetState<bool>("Queued");
if ( !queued )
{
this.services.WorkContext.Resolve<IMessageService>().Send(SmtpMessageChannel.MessageType, parameters);
}
else
{
var priority = activityContext.GetState<int>("Priority");
this.services.WorkContext.Resolve<IJobsQueueService>().Enqueue("IMessageService.Send", new { type = SmtpMessageChannel.MessageType, parameters = parameters }, priority);
}
yield return T("Done");
}
}
}
You might want to throw the recipients in a loop, and send a new email for each of them. With the version above, everyone will see the mail address of everyone else...
Forms/RoleMailerTaskForm.cs
namespace My.Module
{
using System;
using System.Linq;
using System.Web.Mvc;
using Activities;
using Orchard;
using Orchard.ContentManagement;
using Orchard.DisplayManagement;
using Orchard.Environment.Extensions;
using Orchard.Forms.Services;
using Orchard.Roles.Services;
[OrchardFeature(Statics.FeatureNames.RoleMailerTask)]
public class RoleMailerTaskForm : Component, IFormProvider
{
private readonly IRoleService roleService;
public RoleMailerTaskForm(IShapeFactory shapeFactory, IRoleService roleService)
{
this.roleService = roleService;
this.Shape = shapeFactory;
}
public dynamic Shape { get; set; }
public void Describe(DescribeContext context)
{
Func<IShapeFactory, dynamic> formFactory = shape =>
{
var form = this.Shape.Form(
Id: RoleMailerTask.TaskName,
_Type: this.Shape.FieldSet(
Title: this.T("Send to"),
_RecepientsRole: this.Shape.SelectList(
Id: "recipientsRole",
Name: "RecipientsRole",
Title: this.T("Recepient role"),
Description: this.T( "The role of the users that should be notified" ),
Items: this.roleService.GetRoles().Select(r => new SelectListItem { Value = r.Id.ToString(), Text = r.Name })
),
_Bcc: this.Shape.TextBox(
Id: "bcc",
Name: "Bcc",
Title: this.T("Bcc"),
Description: this.T("Specify a comma-separated list of email addresses for a blind carbon copy"),
Classes: new[] { "large", "text", "tokenized" }),
_CC: this.Shape.TextBox(
Id: "cc",
Name: "CC",
Title: this.T("CC"),
Description: this.T("Specify a comma-separated list of email addresses for a carbon copy"),
Classes: new[] { "large", "text", "tokenized" }),
_ReplyTo: this.Shape.Textbox(
Id: "reply-to",
Name: "ReplyTo",
Title: this.T("Reply To Address"),
Description: this.T("If necessary, specify an email address for replies."),
Classes: new[] { "large", "text", "tokenized" }),
_Subject: this.Shape.Textbox(
Id: "Subject", Name: "Subject",
Title: this.T("Subject"),
Description: this.T("The subject of the email message."),
Classes: new[] { "large", "text", "tokenized" }),
_Message: this.Shape.Textarea(
Id: "Body", Name: "Body",
Title: this.T("Body"),
Description: this.T("The body of the email message."),
Classes: new[] { "tokenized" })
));
return form;
};
context.Form(RoleMailerTask.TaskName, formFactory);
}
}
This is the form you'll need to configure the task. The form is a blatant rip off from Orchard.Email/Forms/EmailForm. The only thing I've changed is the select list on top of the form.
And that's all you need!
For the token approach, you would just need to define a token like
{RoleRecpients:TheRoleYouWant}, parse that role name and get the users like shown in the Task above.
I have successfully created one route in ember-cli-mirage, but am having trouble loading the related data.
The API should be returning JSON API compliant data.
I'm not really sure if there are any good methods or not for debugging mirage's request interception. Here is my config.js
export default function() {
this.urlPrefix = 'https://myserver/';
this.namespace = 'api/v1';
this.get('/machines', function(db, request) {
return {
data: db.machines.map(attrs => (
{
type: 'machines',
id: attrs.id,
attributes: attrs
}
))
};
});
this.get('/machines/:id', function(db, request){
let id = request.params.id;
debugger;
return {
data: {
type: 'machines',
id: id,
attributes: db.machines.find(id),
relationships:{
"service-orders": db["service-orders"].where({machineId: id})
}
}
};
});
this.get('/machines/:machine_id/service-orders', function(db, request){
debugger; // this never gets caught
});
}
Most of this is working fine (I think). I can create machines and service orders in the factory and see the db object being updated. However, where my application would normally make a call to the api for service-orders: //myserver/machines/:machine_id/service-orders, the request is not caught and nothing goes out to the API
EDIT:
This is the route that my Ember app is using for /machines/:machine_id/service-orders:
export default Ember.Route.extend(MachineFunctionalRouteMixin, {
model: function() {
var machine = this.modelFor('machines.show');
var serviceOrders = machine.get('serviceOrders');
return serviceOrders;
},
setupController: function(controller, model) {
this._super(controller, model);
}
});
And the model for machines/show:
export default Ember.Route.extend({
model: function(params) {
var machine = this.store.find('machine', params.machine_id);
return machine;
},
setupController: function(controller, model) {
this._super(controller, model);
var machinesController = this.controllerFor('machines');
machinesController.set('attrs.currentMachine', model);
}
});
Intuitively, I would think that machine.get('serviceOrders'); would make a call to the API that would be intercepted and handled by Mirage. Which does not seem to be the case
Currently this works:
/api/Company/1089?children=branches
Controller:
public IEnumerable<Branch> Get(int id, string children)
I want my url to be this:
/api/Company/1089/branches
I can't figure out how to configure the route.
This doesn't work:
routes.MapRoute(
name: "cb",
url: "{controller}/{action}/{id}/{children}",
defaults: new { controller = "Company", action = "Get",
id = UrlParameter.Optional, children = UrlParameter.Optional }
);
api/{controller}/{id}/{children}
I've defined the following route:
routes.MapRoute(
null,
"foo/{id}/{title}",
new { controller = "Boo", action = "Details" }
);
When I call this method:
Url.Action("Details", "Boo", new { id = article.Id, title = article.Title })
I get the following URL:
http://localhost:57553/foo/1/Some%20text%20Š
I would like to create a new route that will lowercase all characters and replace some of them.
e.g.
http://localhost:57553/foo/1/some-text-s
Rules:
Uppercase -> lowercase
' ' -> '-'
'Š' -> 's'
etc.
Any help would be greatly appreciated!
Seems like a perfect candidate for a custom route:
public class MyRoute : Route
{
public MyRoute(string url, object defaultValues)
: base(url, new RouteValueDictionary(defaultValues), new MvcRouteHandler())
{
}
public override VirtualPathData GetVirtualPath(RequestContext requestContext, RouteValueDictionary values)
{
values = new RouteValueDictionary(values);
var title = values["title"] as string;
if (!string.IsNullOrEmpty(title))
{
values["title"] = SEOify(title);
}
return base.GetVirtualPath(requestContext, values);
}
private string SEOify(string title)
{
throw new NotImplementedException();
}
}
which will be registered like this:
routes.Add(
"myRoute",
new MyRoute(
"foo/{id}/{title}",
new { controller = "Boo", action = "Details" }
)
);
Now all you have to do is to implement your SEO requirements in the SEOify function that I left. By the way you could get some inspiration from the way StackOverflow does it for the question titles.
I'm trying to create a route to a specific controller/action which needs to accept optional querystring parameters.
the urls i'd like to accept are:
/Products/ProductsListJson
/Products/ProductsListJson?productTypeId=1
/Products/ProductsListJson?productTypeId=1&brandId=2
/Products/ProductsListJson?productTypeId=1&brandId=2&year=2010
I have an action like this:
public JsonResult ProductsListJson(int productTypeId, int brandId, int year)
And a route like this:
routes.MapRoute(
null, "Products/ProductsListJson",
new { controller = "Products", action = "ProductsListJson", productTypeId = 0, brandId = 0, year = 0 }
);
I assumed that the action "ProductsListJson" would simply see the querystring urls and map them to the appropriate arguments however this is not happening.
Anyone know how this could be achived?
You don't need to specify their values in the route if those parameters are passed in the query string:
routes.MapRoute(
null, "Products/ProductsListJson",
new { controller = "Products", action = "ProductsListJson" }
);
and your action:
public ActionResult ProductsListJson(int? productTypeId, int? brandId, int? year)
{
...
}
but you probably don't need a specific route for this as the default route will handle it just fine:
routes.MapRoute(
"Default",
"{controller}/{action}/{id}",
new { controller = "Home", action = "Index", id = UrlParameter.Optional }
);