pow
pow copied to clipboard
PowInvitation custom redirection
I'm adding users by invitation from a group (Parent organization or team), I have a user overview page that lists all the users for that group. I want an (admin) user to be able to invite people from that page, so I added a link to invitations/new. By default after sending an invitation it returns to the invitations/new page. What would be the advised way to change the routing / redirection so it returns to the user overview page?
There is no easy way to do that right now, since the extensions don't use the Pow.Phoenix.Routes
setup. I've had a PR up for a long time in #141 to allow extensions to work with the pow routes module. In your case then all you've to do is to update it like this:
defmodule MyAppWeb.Pow.Routes do
use Pow.Phoenix.Routes
use Pow.Extension.Phoenix.Routes, extensions: [PowInvitation]
alias MyAppWeb.Router.Helpers, as: Routes
@impl true
def pow_invitation_after_invitation_sent(conn), do: Routes.invitation_path(conn, :index)
end
This would make it super easy. I'll look at the PR again and see if I can get it done ASAP.
The only way to fix it with the current release of Pow is to set up a custom controller action that overrides your PowInvitation controller:
defmodule MyAppWeb.InvitationController do
use MyAppWeb, :controller
alias PowInvitation.{Phoenix.Mailer, Plug}
def create(conn, %{"user" => user_params}) do
case Plug.create_user(conn, user_params) do
{:ok, user, conn} ->
deliver_email(conn, user)
conn
|> put_flash(:info, "Invitation sent")
|> redirect(to: Routes.invitation_path(conn, :index))
{:error, changeset, conn} ->
conn
|> assign(:changeset, changeset)
|> render("new.html")
end
end
defp deliver_email(conn, user) do
token = Plug.sign_invitation_token(conn, user)
url = Routes.pow_invitation_invitation_url(conn, :edit, token)
invited_by = Pow.Plug.current_user(conn)
email = Mailer.invitation(conn, user, invited_by, url)
Pow.Phoenix.Mailer.deliver(conn, email)
end
end
defmodule MyAppWeb.Router do
use Phoenix.Router
# ...
scope "/", MyAppWeb do
post "/invitations", InvitationController, :create
end
scope "/" do
pow_routes()
pow_extension_routes()
end
end
Thanks for the quick response, that indeed would be a great improvement!
Continuing with this, I get a FunctionClauseError at POST /testorganization/invitations: "no function clause matching in Keyword.has_key?/2" for the line url = Routes.pow_invitation_invitation_url(conn, user)
in MyAppWeb.InvitationController.
Should I change the route for the url in deliver_email?
Oh right, the url helper was incorrect, I've update the example. This is how it should look:
url = Routes.pow_invitation_invitation_url(conn, :edit, user.invitation_token)
Right that fixed it. And I think we need to pass in conn in the :error case at create: {:error, changeset, conn} ->
?
Yeah, you're right.
Updating for posterity, here... I believe the PowInvitation.Plug.sign_invitation_token/2
needs to be used in deliver_email/2
for encoding the token for the URL. So the function ends up being:
defp deliver_email(conn, user) do
token = Plug.sign_invitation_token(conn, user)
url = Routes.pow_invitation_invitation_url(conn, :edit, token)
invited_by = Pow.Plug.current_user(conn)
email = Mailer.invitation(conn, user, invited_by, url)
Pow.Phoenix.Mailer.deliver(conn, email)
end
Thanks @cunningryan, I've updated the example 😄
Hi @danschultzer! I just found this issue trying to solve the exact same use-case as @joepstender. I have something similar to Github's "members" page where you can invite new users and see users that are already part of the organization.
If you have the time, I'd like to validate some ideas with you. In my mind I could approach the problem in the following ways:
- Using a callback and redirecting after the invitation, as you suggested (
pow_invitation_after_invitation_sent
).
Based on that, I want to know if there's anything that can be done to help validate/ speed up the approval of https://github.com/danschultzer/pow/pull/141. The way I see, this is super useful for a lot of use-cases.
- Using a scoped custom route + custom controller (
/organizations/:organization_id/members
)
I had a problem with this approach because I couldn't easily separate only PowInvitation's routes from pow_extension_routes()
.
Currently, if you want to opt-out you have to manually define all the other extensions you are using. Do you think having :only
and :except
options in pow_extension_routes()
would be out of scope?
Also, I'm looking into having this custom controller so I can scope the member invitation to a single organization at a time and select the proper layout for each case I'm using it...
Are there any remarks that should be considered or can I just call the common methods from PowInvitation.Phoenix.InvitationController
?
PS.: This approach is not dependent on being able to configure the pow_invitation_after_invitation_sent
path, but it would reduce the code duplication since both invitation_sent_redirect
and deliver_email
are private functions.
- Build custom pages using Javascript calling the invitation's create path through ajax
I'm not totally convinced this is a good alternative but it would prevent me from interfering with Pow's extension flow while not having to follow the redirect afterward. I'd basically be using PowInvitation as a API endpoint only.