Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

PowInvitation custom redirection #203

Open
joepstender opened this issue May 28, 2019 · 9 comments
Open

PowInvitation custom redirection #203

joepstender opened this issue May 28, 2019 · 9 comments
Labels
enhancement New feature or request

Comments

@joepstender
Copy link
Contributor

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?

@danschultzer
Copy link
Collaborator

danschultzer commented May 28, 2019

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

@danschultzer danschultzer added the enhancement New feature or request label May 28, 2019
@joepstender
Copy link
Contributor Author

Thanks for the quick response, that indeed would be a great improvement!

@joepstender
Copy link
Contributor Author

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?

@danschultzer
Copy link
Collaborator

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)

@joepstender
Copy link
Contributor Author

Right that fixed it. And I think we need to pass in conn in the :error case at create: {:error, changeset, conn} ->?

@danschultzer
Copy link
Collaborator

Yeah, you're right.

@cunningryan
Copy link

cunningryan commented May 8, 2020

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

@danschultzer
Copy link
Collaborator

Thanks @cunningryan, I've updated the example 😄

@thiagomajesk
Copy link

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 #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.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement New feature or request
Projects
None yet
Development

No branches or pull requests

4 participants