Meet SourceLevel, an All-in-one Data & Analytics for Engineering Teams

SourceLevel provides metrics and insights by collecting data from many sources such as GitHub and GitLab. Our product brings visibility over every corner of the delivery pipeline in a Data & Analytics Solution for Engineering Teams.

Get started for free
Go to line 1
defmodule HexpmWeb.Dashboard.OrganizationController do
Go to line 2
  use HexpmWeb, :controller
Go to line 3
  alias HexpmWeb.Dashboard.KeyController
Go to line 5
  plug :requires_login
Go to line 7
  def redirect_repo(conn, params) do
Go to line 8
    glob = params["glob"] || []
Go to line 9
    path = Routes.organization_path(conn, :new) <> "/" <> Enum.join(glob, "/")
Go to line 12
    |> put_status(301)
Go to line 13
    |> redirect(to: path)
Go to line 16
  def show(conn, %{"dashboard_org" => organization}) do
Go to line 17
    access_organization(conn, organization, "read", fn organization ->
Go to line 18
      render_index(conn, organization)
Go to line 22
  def update(conn, %{
Go to line 23
        "dashboard_org" => organization,
Go to line 24
        "action" => "add_member",
Go to line 25
        "organization_user" => params
Go to line 27
    username = params["username"]
Go to line 29
    access_organization(conn, organization, "admin", fn organization ->
Go to line 30
      user_count = Organizations.user_count(organization)
Go to line 31
      customer = Hexpm.Billing.get(organization.name)
Go to line 33
      if !customer["subscription"] || customer["quantity"] > user_count do
Go to line 34
        if user = Users.public_get(username, [:emails]) do
Go to line 35
          case Organizations.add_member(organization, user, params, audit: audit_data(conn)) do
Go to line 36
            {:ok, _} ->
Go to line 38
              |> put_flash(:info, "User #{username} has been added to the organization.")
Go to line 39
              |> redirect(to: Routes.organization_path(conn, :show, organization))
Go to line 41
            {:error, changeset} ->
Go to line 43
              |> put_status(400)
Go to line 44
              |> render_index(organization, add_member: changeset)
Go to line 48
          |> put_status(400)
Go to line 49
          |> put_flash(:error, "Unknown user #{username}.")
Go to line 50
          |> render_index(organization)
Go to line 54
        |> put_status(400)
Go to line 55
        |> put_flash(:error, "Not enough seats in organization to add member.")
Go to line 56
        |> render_index(organization)
Go to line 61
  def update(conn, %{
Go to line 62
        "dashboard_org" => organization,
Go to line 63
        "action" => "remove_member",
Go to line 64
        "organization_user" => params
Go to line 66
    # TODO: Also remove all package ownerships on organization for removed member
Go to line 67
    username = params["username"]
Go to line 69
    access_organization(conn, organization, "admin", fn organization ->
Go to line 70
      user = Users.public_get(username)
Go to line 72
      case Organizations.remove_member(organization, user, audit: audit_data(conn)) do
Go to line 75
          |> put_flash(:info, "User #{username} has been removed from the organization.")
Go to line 76
          |> redirect(to: Routes.organization_path(conn, :show, organization))
Go to line 78
        {:error, :last_member} ->
Go to line 80
          |> put_status(400)
Go to line 81
          |> put_flash(:error, "Cannot remove last member from organization.")
Go to line 82
          |> render_index(organization)
Go to line 87
  def update(conn, %{
Go to line 88
        "dashboard_org" => organization,
Go to line 89
        "action" => "change_role",
Go to line 90
        "organization_user" => params
Go to line 92
    username = params["username"]
Go to line 94
    access_organization(conn, organization, "admin", fn organization ->
Go to line 95
      if user = Users.public_get(username) do
Go to line 96
        case Organizations.change_role(organization, user, params, audit: audit_data(conn)) do
Go to line 97
          {:ok, _} ->
Go to line 99
            |> put_flash(:info, "User #{username}'s role has been changed to #{params["role"]}.")
Go to line 100
            |> redirect(to: Routes.organization_path(conn, :show, organization))
Go to line 102
          {:error, :last_admin} ->
Go to line 104
            |> put_status(400)
Go to line 105
            |> put_flash(:error, "Cannot demote last admin member.")
Go to line 106
            |> render_index(organization)
Go to line 108
          {:error, changeset} ->
Go to line 110
            |> put_status(400)
Go to line 111
            |> render_index(organization, change_role: changeset)
Go to line 115
        |> put_status(400)
Go to line 116
        |> put_flash(:error, "Unknown user #{username}.")
Go to line 117
        |> render_index(organization)
Go to line 122
  def leave(conn, %{
Go to line 123
        "dashboard_org" => organization,
Go to line 124
        "organization_name" => organization_name
Go to line 126
    access_organization(conn, organization, "read", fn organization ->
Go to line 127
      if organization.name == organization_name do
Go to line 128
        current_user = conn.assigns.current_user
Go to line 130
        case Organizations.remove_member(organization, current_user, audit: audit_data(conn)) do
Go to line 133
            |> put_flash(:info, "You just left the the organization #{organization.name}.")
Go to line 134
            |> redirect(to: Routes.profile_path(conn, :index))
Go to line 136
          {:error, :last_member} ->
Go to line 138
            |> put_status(400)
Go to line 139
            |> put_flash(:error, "The last member of an organization cannot leave.")
Go to line 140
            |> render_index(organization)
Go to line 144
        |> put_status(400)
Go to line 145
        |> put_flash(:error, "Invalid organization name.")
Go to line 146
        |> render_index(organization)
Go to line 151
  def billing_token(conn, %{"dashboard_org" => organization, "token" => token}) do
Go to line 152
    access_organization(conn, organization, "admin", fn organization ->
Go to line 153
      audit = %{audit_data: audit_data(conn), organization: organization}
Go to line 155
      case Hexpm.Billing.checkout(organization.name, %{payment_source: token}, audit: audit) do
Go to line 156
        {:ok, _} ->
Go to line 158
          |> put_resp_header("content-type", "application/json")
Go to line 159
          |> send_resp(200, Jason.encode!(%{}))
Go to line 161
        {:error, reason} ->
Go to line 163
          |> put_resp_header("content-type", "application/json")
Go to line 164
          |> send_resp(422, Jason.encode!(reason))
Go to line 169
  def cancel_billing(conn, %{"dashboard_org" => organization}) do
Go to line 170
    access_organization(conn, organization, "admin", fn organization ->
Go to line 171
      audit = %{audit_data: audit_data(conn), organization: organization}
Go to line 172
      customer = Hexpm.Billing.cancel(organization.name, audit: audit)
Go to line 174
      message = cancel_message(customer["subscription"]["current_period_end"])
Go to line 177
      |> put_flash(:info, message)
Go to line 178
      |> redirect(to: Routes.organization_path(conn, :show, organization))
Go to line 182
  def show_invoice(conn, %{"dashboard_org" => organization, "id" => id}) do
Go to line 183
    access_organization(conn, organization, "admin", fn organization ->
Go to line 184
      id = String.to_integer(id)
Go to line 185
      customer = Hexpm.Billing.get(organization.name)
Go to line 186
      invoice_ids = Enum.map(customer["invoices"], & &1["id"])
Go to line 188
      if id in invoice_ids do
Go to line 189
        invoice = Hexpm.Billing.invoice(id)
Go to line 192
        |> put_resp_header("content-type", "text/html")
Go to line 193
        |> send_resp(200, invoice)
Go to line 195
        not_found(conn)
Go to line 200
  def pay_invoice(conn, %{"dashboard_org" => organization, "id" => id}) do
Go to line 201
    access_organization(conn, organization, "admin", fn organization ->
Go to line 202
      id = String.to_integer(id)
Go to line 203
      customer = Hexpm.Billing.get(organization.name)
Go to line 204
      invoice_ids = Enum.map(customer["invoices"], & &1["id"])
Go to line 206
      audit = %{audit_data: audit_data(conn), organization: organization}
Go to line 208
      if id in invoice_ids do
Go to line 209
        case Hexpm.Billing.pay_invoice(id, audit: audit) do
Go to line 212
            |> put_flash(:info, "Invoice paid.")
Go to line 213
            |> redirect(to: Routes.organization_path(conn, :show, organization))
Go to line 215
          {:error, reason} ->
Go to line 217
            |> put_status(400)
Go to line 218
            |> put_flash(:error, "Failed to pay invoice: #{reason["errors"]}.")
Go to line 219
            |> render_index(organization)
Go to line 222
        not_found(conn)
Go to line 227
  def update_billing(conn, %{"dashboard_org" => organization} = params) do
Go to line 228
    access_organization(conn, organization, "admin", fn organization ->
Go to line 229
      audit = %{audit_data: audit_data(conn), organization: organization}
Go to line 231
      update_billing(
Go to line 233
        organization,
Go to line 235
        &Hexpm.Billing.update(organization.name, &1, audit: audit)
Go to line 240
  def create_billing(conn, %{"dashboard_org" => organization} = params) do
Go to line 241
    access_organization(conn, organization, "admin", fn organization ->
Go to line 242
      user_count = Organizations.user_count(organization)
Go to line 246
        |> Map.put("token", organization.name)
Go to line 247
        |> Map.put("quantity", user_count)
Go to line 249
      audit = %{audit_data: audit_data(conn), organization: organization}
Go to line 251
      update_billing(conn, organization, params, &Hexpm.Billing.create(&1, audit: audit))
Go to line 255
  @not_enough_seats "The number of open seats cannot be less than the number of organization members."
Go to line 257
  def add_seats(conn, %{"dashboard_org" => organization} = params) do
Go to line 258
    access_organization(conn, organization, "admin", fn organization ->
Go to line 259
      user_count = Organizations.user_count(organization)
Go to line 260
      current_seats = String.to_integer(params["current-seats"])
Go to line 261
      add_seats = String.to_integer(params["add-seats"])
Go to line 262
      seats = current_seats + add_seats
Go to line 264
      if seats >= user_count do
Go to line 265
        audit = %{audit_data: audit_data(conn), organization: organization}
Go to line 267
        {:ok, _customer} =
Go to line 268
          Hexpm.Billing.update(organization.name, %{"quantity" => seats}, audit: audit)
Go to line 271
        |> put_flash(:info, "The number of open seats have been increased.")
Go to line 272
        |> redirect(to: Routes.organization_path(conn, :show, organization))
Go to line 275
        |> put_status(400)
Go to line 276
        |> put_flash(:error, @not_enough_seats)
Go to line 277
        |> render_index(organization)
Go to line 282
  def remove_seats(conn, %{"dashboard_org" => organization} = params) do
Go to line 283
    access_organization(conn, organization, "admin", fn organization ->
Go to line 284
      user_count = Organizations.user_count(organization)
Go to line 285
      seats = String.to_integer(params["seats"])
Go to line 287
      if seats >= user_count do
Go to line 288
        audit = %{audit_data: audit_data(conn), organization: organization}
Go to line 290
        {:ok, _customer} =
Go to line 291
          Hexpm.Billing.update(organization.name, %{"quantity" => seats}, audit: audit)
Go to line 294
        |> put_flash(:info, "The number of open seats have been reduced.")
Go to line 295
        |> redirect(to: Routes.organization_path(conn, :show, organization))
Go to line 298
        |> put_status(400)
Go to line 299
        |> put_flash(:error, @not_enough_seats)
Go to line 300
        |> render_index(organization)
Go to line 305
  def change_plan(conn, %{"dashboard_org" => organization} = params) do
Go to line 306
    access_organization(conn, organization, "admin", fn organization ->
Go to line 307
      audit = %{audit_data: audit_data(conn), organization: organization}
Go to line 309
      Hexpm.Billing.change_plan(organization.name, %{"plan_id" => params["plan_id"]}, audit: audit)
Go to line 312
      |> put_flash(:info, "You have switched to the #{plan_name(params["plan_id"])} plan.")
Go to line 313
      |> redirect(to: Routes.organization_path(conn, :show, organization))
Go to line 317
  defp plan_name("organization-monthly"), do: "monthly organization"
Go to line 318
  defp plan_name("organization-annually"), do: "annual organization"
Go to line 320
  def new(conn, _params) do
Go to line 321
    render_new(conn)
Go to line 324
  def create(conn, params) do
Go to line 325
    user = conn.assigns.current_user
Go to line 327
    case Organizations.create(user, params["organization"], audit: audit_data(conn)) do
Go to line 328
      {:ok, organization} ->
Go to line 330
        |> put_flash(:info, "Organization created with one month free trial period active.")
Go to line 331
        |> redirect(to: Routes.organization_path(conn, :show, organization))
Go to line 333
      {:error, changeset} ->
Go to line 335
        |> put_status(400)
Go to line 336
        |> render_new(changeset: changeset, params: params)
Go to line 340
  defp update_billing(conn, organization, params, fun) do
Go to line 341
    customer_params =
Go to line 343
      |> Map.take(["email", "person", "company", "token", "quantity"])
Go to line 344
      |> Map.put_new("person", nil)
Go to line 345
      |> Map.put_new("company", nil)
Go to line 347
    case fun.(customer_params) do
Go to line 348
      {:ok, _} ->
Go to line 350
        |> put_flash(:info, "Updated your billing information.")
Go to line 351
        |> redirect(to: Routes.organization_path(conn, :show, organization))
Go to line 353
      {:error, reason} ->
Go to line 355
        |> put_status(400)
Go to line 356
        |> put_flash(:error, "Failed to update billing information.")
Go to line 357
        |> render_index(organization, params: params, errors: reason["errors"])
Go to line 361
  def create_key(conn, %{"dashboard_org" => organization} = params) do
Go to line 362
    access_organization(conn, organization, "write", fn organization ->
Go to line 363
      key_params = KeyController.munge_permissions(params["key"])
Go to line 365
      case Keys.create(organization, key_params, audit: audit_data(conn)) do
Go to line 366
        {:ok, %{key: key}} ->
Go to line 368
            "The key #{key.name} was successfully generated, " <>
Go to line 369
              "copy the secret \"#{key.user_secret}\", you won't be able to see it again."
Go to line 372
          |> put_flash(:info, flash)
Go to line 373
          |> redirect(to: Routes.organization_path(conn, :show, organization))
Go to line 375
        {:error, :key, changeset, _} ->
Go to line 377
          |> put_status(400)
Go to line 378
          |> render_index(organization, key_changeset: changeset)
Go to line 383
  def delete_key(conn, %{"dashboard_org" => organization, "name" => name}) do
Go to line 384
    access_organization(conn, organization, "write", fn organization ->
Go to line 385
      case Keys.revoke(organization, name, audit: audit_data(conn)) do
Go to line 386
        {:ok, _struct} ->
Go to line 388
          |> put_flash(:info, "The key #{name} was revoked successfully.")
Go to line 389
          |> redirect(to: Routes.organization_path(conn, :show, organization))
Go to line 391
        {:error, _} ->
Go to line 393
          |> put_status(400)
Go to line 394
          |> put_flash(:error, "The key #{name} was not found.")
Go to line 395
          |> render_index(organization)
Go to line 400
  def update_profile(conn, %{"dashboard_org" => organization, "profile" => profile_params}) do
Go to line 401
    access_organization(conn, organization, "admin", fn organization ->
Go to line 402
      case Users.update_profile(organization.user, profile_params, audit: audit_data(conn)) do
Go to line 403
        {:ok, _updated_user} ->
Go to line 405
          |> put_flash(:info, "Profile updated successfully.")
Go to line 406
          |> redirect(to: Routes.organization_path(conn, :show, organization))
Go to line 408
        {:error, _} ->
Go to line 410
          |> put_status(400)
Go to line 411
          |> put_flash(:error, "Oops, something went wrong!")
Go to line 412
          |> render_index(organization)
Go to line 417
  defp render_new(conn, opts \\ []) do
Go to line 420
      "new.html",
Go to line 421
      title: "Dashboard - Organization sign up",
Go to line 422
      container: "container page dashboard",
Go to line 423
      billing_email: nil,
Go to line 424
      person: nil,
Go to line 425
      company: nil,
Go to line 426
      params: opts[:params],
Go to line 427
      errors: opts[:errors],
Go to line 428
      changeset: opts[:changeset] || create_changeset()
Go to line 432
  defp render_index(conn, organization, opts \\ []) do
Go to line 433
    user = organization.user
Go to line 434
    public_email = user && Enum.find(user.emails, & &1.public)
Go to line 435
    gravatar_email = user && Enum.find(user.emails, & &1.gravatar)
Go to line 436
    customer = Hexpm.Billing.get(organization.name)
Go to line 437
    keys = Keys.all(organization)
Go to line 438
    delete_key_path = Routes.organization_path(Endpoint, :delete_key, organization)
Go to line 439
    create_key_path = Routes.organization_path(Endpoint, :create_key, organization)
Go to line 441
    assigns = [
Go to line 442
      title: "Dashboard - Organization",
Go to line 443
      container: "container page dashboard",
Go to line 444
      changeset: user && User.update_profile(user, %{}),
Go to line 445
      public_email: public_email && public_email.email,
Go to line 446
      gravatar_email: gravatar_email && gravatar_email.email,
Go to line 447
      organization: organization,
Go to line 448
      repository: organization.repository,
Go to line 449
      keys: keys,
Go to line 450
      params: opts[:params],
Go to line 451
      errors: opts[:errors],
Go to line 452
      delete_key_path: delete_key_path,
Go to line 453
      create_key_path: create_key_path,
Go to line 454
      key_changeset: opts[:key_changeset] || key_changeset(),
Go to line 455
      add_member_changeset: opts[:add_member_changeset] || add_member_changeset()
Go to line 458
    assigns = Keyword.merge(assigns, customer_assigns(customer, organization))
Go to line 459
    render(conn, "index.html", assigns)
Go to line 462
  defp customer_assigns(nil, _organization) do
Go to line 464
      billing_started?: false,
Go to line 465
      billing_active?: false,
Go to line 466
      checkout_html: nil,
Go to line 467
      billing_email: nil,
Go to line 468
      plan_id: "organization-monthly",
Go to line 469
      subscription: nil,
Go to line 470
      monthly_cost: nil,
Go to line 471
      amount_with_tax: nil,
Go to line 472
      quantity: nil,
Go to line 473
      max_period_quantity: nil,
Go to line 474
      card: nil,
Go to line 475
      invoices: nil,
Go to line 476
      person: nil,
Go to line 477
      company: nil,
Go to line 478
      post_action: nil,
Go to line 479
      csrf_token: nil
Go to line 483
  defp customer_assigns(customer, organization) do
Go to line 484
    post_action = Routes.organization_path(Endpoint, :billing_token, organization)
Go to line 487
      billing_started?: true,
Go to line 488
      billing_active?: !!customer["subscription"],
Go to line 489
      checkout_html: customer["checkout_html"],
Go to line 490
      billing_email: customer["email"],
Go to line 491
      plan_id: customer["plan_id"],
Go to line 492
      proration_amount: customer["proration_amount"],
Go to line 493
      proration_days: customer["proration_days"],
Go to line 494
      subscription: customer["subscription"],
Go to line 495
      monthly_cost: customer["monthly_cost"],
Go to line 496
      amount_with_tax: customer["amount_with_tax"],
Go to line 497
      quantity: customer["quantity"],
Go to line 498
      max_period_quantity: customer["max_period_quantity"],
Go to line 499
      tax_rate: customer["tax_rate"],
Go to line 500
      discount: customer["discount"],
Go to line 501
      card: customer["card"],
Go to line 502
      invoices: customer["invoices"],
Go to line 503
      person: customer["person"],
Go to line 504
      company: customer["company"],
Go to line 505
      post_action: post_action,
Go to line 506
      csrf_token: get_csrf_token()
Go to line 510
  defp access_organization(conn, organization, role, fun) do
Go to line 511
    user = conn.assigns.current_user
Go to line 513
    organization =
Go to line 514
      Organizations.get(organization, [
Go to line 516
        :organization_users,
Go to line 517
        user: :emails,
Go to line 518
        users: :emails,
Go to line 519
        repository: :packages
Go to line 522
    if organization do
Go to line 523
      if repo_user = Enum.find(organization.organization_users, &(&1.user_id == user.id)) do
Go to line 524
        if repo_user.role in Organization.role_or_higher(role) do
Go to line 525
          fun.(organization)
Go to line 528
          |> put_status(400)
Go to line 529
          |> put_flash(:error, "You do not have permission for this action.")
Go to line 530
          |> render_index(organization)
Go to line 533
        not_found(conn)
Go to line 536
      not_found(conn)
Go to line 540
  defp add_member_changeset() do
Go to line 541
    Organization.add_member(%OrganizationUser{}, %{})
Go to line 544
  defp create_changeset() do
Go to line 545
    Organization.changeset(%Organization{}, %{})
Go to line 548
  defp key_changeset() do
Go to line 549
    Key.changeset(%Key{}, %{}, %{})
Go to line 552
  defp cancel_message(nil = _cancel_date) do
Go to line 553
    "Your subscription is cancelled"
Go to line 556
  defp cancel_message(cancel_date) do
Go to line 557
    date = HexpmWeb.Dashboard.OrganizationView.payment_date(cancel_date)
Go to line 559
    "Your subscription is cancelled, you will have access to the organization until " <>
Go to line 560
      "the end of your billing period at #{date}"