improve localization

improve localization

<% import ExAdmin.Gettext %>

    <%= ExAdmin.LayoutView.logo_mini %>

    <%= ExAdmin.LayoutView.logo_full %>

      Toggle navigation

      <%= if use_authentication?(@conn) do %>

            <%= current_user_name(@conn) %>

              <%= Phoenix.HTML.Link.link (gettext "Profile"), to: ExAdmin.Utils.admin_resource_path(current_user(@conn)) %>

              <%= Phoenix.HTML.Link.link (gettext "Logout"), to: session_path(@conn, :destroy), "data-method": "delete", "data-csrf": Plug.CSRFProtection.get_csrf_token, rel: "nofollow" %>

      <% end %>
      <%= if ExAdmin.Theme.AdminLte2.Layout.theme_selector? do %>

            Themes

            <%= ExAdmin.Theme.AdminLte2.Layout.theme_selector %>

      <% end %>
      <%= ExAdmin.Theme.AdminLte2.Layout.switch_user(@conn) %>

<%= gettext "Resources" %>

      <%= if Application.get_env(:ex_admin, :nest_resources, false) do %>

           <%= gettext "Resources" %>

            <%= ExAdmin.Navigation.nav_view(@conn) %>

      <% else %>
        <%= ExAdmin.Navigation.nav_view(@conn) %>
      <% end %>

      <% actions = ExAdmin.get_title_actions(@conn) %>
      <%= if any_actions?(actions) do %>

<%= gettext "Actions" %>

        <%= for {action, opts} <- actions do %>

            <% opts = build_menu_icon(action, opts) %>
            <%= build_link(action, opts) %>

        <% end %>
      <% end %>
      <%= if @scope_counts && Enum.count(@scope_counts) > 0 do %>

<%= gettext "Scopes" %>

      <% end %>
      <%= build_scopes @conn, @scope_counts %>

<% import ExAdmin.Gettext %>

    <%= ExAdmin.LayoutView.logo_mini %>

    <%= ExAdmin.LayoutView.logo_full %>

      Toggle navigation

      <%= if use_authentication?(@conn) do %>

            <%= current_user_name(@conn) %>

              <%= Phoenix.HTML.Link.link (gettext "Profile"), to: ExAdmin.Utils.admin_resource_path(current_user(@conn)) %>

              <%= Phoenix.HTML.Link.link (gettext "Logout"), to: session_path(@conn, :destroy), "data-method": "delete", "data-csrf": Plug.CSRFProtection.get_csrf_token, rel: "nofollow" %>

      <% end %>
      <%= if ExAdmin.Theme.AdminLte2.Layout.theme_selector? do %>

            Themes

            <%= ExAdmin.Theme.AdminLte2.Layout.theme_selector %>

      <% end %>
      <%= ExAdmin.Theme.AdminLte2.Layout.switch_user(@conn) %>

<%= gettext "Resources" %>

      <%#
 <%= gettext "Dashboard" %>
 %>
      <%= if Application.get_env(:ex_admin, :nest_resources, false) do %>

           <%= gettext "Resources" %>

            <%= ExAdmin.Navigation.nav_view(@conn) %>

      <% else %>
        <%= ExAdmin.Navigation.nav_view(@conn) %>
      <% end %>

      <% actions = ExAdmin.get_title_actions(@conn) %>
      <%= if any_actions?(actions) do %>

<%= gettext "Actions" %>

        <%= for {action, opts} <- actions do %>

            <% opts = build_menu_icon(action, opts) %>
            <%= build_link(action, opts) %>

        <% end %>
      <% end %>
      <%= if @scope_counts && Enum.count(@scope_counts) > 0 do %>

<%= gettext "Scopes" %>

      <% end %>
      <%= build_scopes @conn, @scope_counts %>


make chat message form permanent

make chat message form permanent

<%= page_reload_listener([:event, @event.id, :chat_message, :create]) %>
<%= page_reload_listener([:event, @event.id, :chat_message, :delete]) %>
<%= page_reload_listener([:event, @event.id, :chat_message, :update]) %>

  <%= if length(@chat_messages) == 0 do %>

There are no chat messages, yet.

Be the first and send a message to all attendees.

  <% else %>

      <%= for chat_message <- @chat_messages do %>
        <%= render "_chat_message.html", conn: @conn, chat_message: chat_message, current_memberships: @current_memberships, event: @event, trainer_memberships: @trainer_memberships %>
      <% end %>

  <% end %>

  <%= render "_form.html", conn: @conn, changeset: @changeset, event: @event,
      action: classroom_chat_message_path(@conn, :create, @event) %>


Add news item about new reporting check

Add news item about new reporting check

<%= gettext "News" %>

This page lists user-visible changes to Webbkoll. For the time before 2018-11-30 (when this page was added), only a few notable events are listed. For the full history, see the commits on GitHub.

2020-06-26

Webbkoll now checks if reports are sent to third-parties with Content Security Policy's report-uri / report-to, Expect-CT's report-uri, and NEL's report-to. Thanks InfoSec Handbook for the suggestion and comments.

2019-05-24

Thanks to Tom Fredrik Blenning - Elektronisk Forpost Norge, Webbkoll is <%= link("now available in Norwegian", to: "/no/") %>!

2019-01-15

We could not find any website that 1) had the consolidated version (i.e., with corrections incorporated) of the GDPR including the recitals, 2) provided it in all 24 EU languages, and 3) made it easy to link to individual articles/recitals. So we hacked together one ourselves: https://gdpr.dataskydd.net/. It's now used for the GDPR references on the Results page.

EUR-Lex does provide a consolidated GDPR, but it doesn't include the recitals (which also received corrections!). Additionally they don't make it easy to link to individual articles, unless you dig for IDs in the HTML source. So we took the XML versions of the consolidated GDPR, added the recitals, and manually applied the corrections to the recitals. All XML files and everything used to generate our GDPR site is available on GitHub (beware: lots of ugly XSLT). Please report any mistakes.

2019-01-11

Thanks to <%= link("Tomas Jakobs", to: "https://jakobssystems.net") %>, Webbkoll is <%= link("now available in German", to: "/de/") %>!

2018-11-30

New version of Webbkoll launched, thanks to a small grant from Digital Rights Fund. Changes/additions:

Redesigned a bit to hopefully make things a bit more clear and navigable. Better separation between results and general information/advice. Separate boxes for "what is this" and "how do you do this". Hide some things between expandable text boxes. Now works better on mobile.

Added Content Security Policy (CSP) analysis, Subresource Integrity (SRI) analysis, and improved X-Content-Type-Options/X-Frame-Options/X-XSS-Protection/HSTS checks. This code was ported from Mozilla's Observatory.

More cooke information shown (HttpOnly, Secure, SameSite)

localStorage usage shown

IP address and country (with flags!) shown for every third-party host

Chromium's security state information shown

Information/technical advice rewritten (sometimes quoted from MDN)

Many references to relevant articles and recitals in the GDPR added

Lots of work to make things a bit cleaner internally

You can now help translate Webbkoll! We hope to add a few more languages, such as French and German, soon.

2018-07-08

Switch from PhantomJS to Chromium in the backend, fixing many issues

2016-06-25

Code published to GitHub

2016-05-10

Beta version made public

<%= gettext "News" %>

This page lists user-visible changes to Webbkoll. For the time before 2018-11-30 (when this page was added), only a few notable events are listed. For the full history, see the commits on GitHub.

2020-05-01

Webbkoll now checks whether Content Security Policy violation reports (report-uri and report-to) are sent to third-parties. Thanks InfoSec Handbook for the suggestion and comments.

2019-05-24

Thanks to Tom Fredrik Blenning - Elektronisk Forpost Norge, Webbkoll is <%= link("now available in Norwegian", to: "/no/") %>!

2019-01-15

We could not find any website that 1) had the consolidated version (i.e., with corrections incorporated) of the GDPR including the recitals, 2) provided it in all 24 EU languages, and 3) made it easy to link to individual articles/recitals. So we hacked together one ourselves: https://gdpr.dataskydd.net/. It's now used for the GDPR references on the Results page.

EUR-Lex does provide a consolidated GDPR, but it doesn't include the recitals (which also received corrections!). Additionally they don't make it easy to link to individual articles, unless you dig for IDs in the HTML source. So we took the XML versions of the consolidated GDPR, added the recitals, and manually applied the corrections to the recitals. All XML files and everything used to generate our GDPR site is available on GitHub (beware: lots of ugly XSLT). Please report any mistakes.

2019-01-11

Thanks to <%= link("Tomas Jakobs", to: "https://jakobssystems.net") %>, Webbkoll is <%= link("now available in German", to: "/de/") %>!

2018-11-30

New version of Webbkoll launched, thanks to a small grant from Digital Rights Fund. Changes/additions:

Redesigned a bit to hopefully make things a bit more clear and navigable. Better separation between results and general information/advice. Separate boxes for "what is this" and "how do you do this". Hide some things between expandable text boxes. Now works better on mobile.

Added Content Security Policy (CSP) analysis, Subresource Integrity (SRI) analysis, and improved X-Content-Type-Options/X-Frame-Options/X-XSS-Protection/HSTS checks. This code was ported from Mozilla's Observatory.

More cooke information shown (HttpOnly, Secure, SameSite)

localStorage usage shown

IP address and country (with flags!) shown for every third-party host

Chromium's security state information shown

Information/technical advice rewritten (sometimes quoted from MDN)

Many references to relevant articles and recitals in the GDPR added

Lots of work to make things a bit cleaner internally

You can now help translate Webbkoll! We hope to add a few more languages, such as French and German, soon.

2018-07-08

Switch from PhantomJS to Chromium in the backend, fixing many issues

2016-06-25

Code published to GitHub

2016-05-10

Beta version made public


mixed recommendations and messages

mixed recommendations and messages

Account

  <%= link [fa_icon("download"), " Download"],
           to: user_json_download_path(@conn, :download),
           class: "btn btn-primary" %>
   all my data as JSON-File

  Manage
  <%= link [fa_icon("user-circle"), " My Profile"],
           to: user_frontend_path(@conn, :my_profile),
           class: "btn btn-default",
           target: "_blank" %>

Name

<%= @user.name %>

Useranme

<%= @user.username %>

Email

<%= @user.email %>

Email adress confirmed?

<%= @user.email_confirmed %>

Is podcaster?

<%= @user.podcaster %>

Shares subscriptions?

<%= @user.share_subscriptions %>

Shares follows?

<%= @user.share_follows %>

Pro account until

<%= Timex.format!(@user.pro_until, "{ISOdate} {h24}:{m}") %>

Billing address

<%= @user.billing_address %>

Payment reference

<%= @user.payment_reference %>

Wants a paper bill?

<%= @user.paper_bill %>

OPML Files

  Manage
  <%= link [fa_icon("file-audio-o"), " My OPMLs"],
           to: opml_frontend_path(@conn, :index),
           class: "btn btn-default",
           target: "_blank" %>

  <%= for opml <- @user.opmls do %>

<%= opml.filename %>

  <% end %>

Personas

You can even delete all manifestations as personas, you created so far, at once, by clicking
<%= link [fa_icon("warning"), " Delete all subscriptions"],
         to: manifestation_frontend_path(@conn, :delete_all),
         method: :delete,
         data: [confirm: "Are you sure? All manifestations will be deleted!"],
         class: "btn btn-danger" %>

<%= for persona <- @user.personas do %>

    Persona <%= persona.name %>
    <%= link "Delete Manifestation", to: manifestation_frontend_path(@conn, :delete, persona.id),
                                     method: :delete,
                                     data: [confirm: "Are you sure?"],
                                     class: "btn btn-danger btn-xs" %>

Pid

<%= persona.pid %>

Name

<%= persona.name %>

Uri

<%= persona.uri %>

Email

<%= persona.email %>

Description

<%= persona.description %>

Url

<%= persona.image_url %>

Title

<%= persona.image_title %>

<% end %>

<%= for persona <- @user.user_personas do %>

    Persona <%= persona.name %>
    <%= link "Disconnect Persona", to: persona_frontend_path(@conn, :disconnect, persona.id),
                                   method: :post,
                                   data: [confirm: "Are you sure?"],
                                   class: "btn btn-danger btn-xs" %>

Pid

<%= persona.pid %>

Name

<%= persona.name %>

Uri

<%= persona.uri %>

Email

<%= persona.email %>

Description

<%= persona.description %>

Url

<%= persona.image_url %>

Title

<%= persona.image_title %>

<% end %>

Invoices

Invoices cannot be deleted, we have to store them due to fiscal regulations.

<%= for invoice <- @user.invoices do %>

Date

<%= Timex.format!(invoice.inserted_at, "{ISOdate} {h24}:{m}") %>

File

<%= link invoice.filename, to: invoice_frontend_path(@conn, :download, invoice) %>

<% end %>

Subscribed Podcasts

Click on the podcast link to open the podcast show page in a new tab
to un/like, un/subscribe, un/follow.

You can even delete all the subscriptions, you created so far, at once, by clicking
<%= link [fa_icon("warning"), " Delete all subscriptions"],
         to: subscription_frontend_path(@conn, :delete_all),
         method: :delete,
         data: [confirm: "Are you sure? All messages will be deleted!"],
         class: "btn btn-danger btn-sm" %>

  <%= for podcast <- @user.podcasts_i_subscribed do %>

<%= link podcast.title, to: podcast_frontend_path(@conn, :show, podcast), target: "_blank" %>

  <% end %>

Followed Podcasts

Click on the podcast link to open the podcast show page in a new tab
to un/like, un/subscribe, un/follow.

You can even unfollow all the podcasts, you followed so far at once, by clicking
<%= link [fa_icon("warning"), " Unfollow all podcasts"],
         to: follow_frontend_path(@conn, :unfollow_all_podcasts),
         method: :delete,
         data: [confirm: "Are you sure? All podcasts will be unfollowed!"],
         class: "btn btn-danger btn-sm" %>

  <%= for podcast <- @user.podcasts_i_follow do %>

<%= link podcast.title, to: podcast_frontend_path(@conn, :show, podcast), target: "_blank" %>

  <% end %>

Liked Podcasts

Click on the podcast link to open the podcast show page in a new tab
to un/like, un/subscribe, un/follow.

You can even unlike all the podcasts, you followed so far at once, by clicking
<%= link [fa_icon("warning"), " Unlike all podcasts"],
         to: like_frontend_path(@conn, :unlike_all_podcasts),
         method: :delete,
         data: [confirm: "Are you sure? All podcasts will be unfollowed!"],
         class: "btn btn-danger btn-sm" %>

  <%= for podcast <- @user.podcasts_i_like do %>

<%= link podcast.title, to: podcast_frontend_path(@conn, :show, podcast), target: "_blank" %>

  <% end %>

Liked Episodes

Click on the episode link to open the episode show page in a new tab
to un/like, un/subscribe, un/follow.

You can even unlike all the episodes, you followed so far at once, by clicking
<%= link [fa_icon("warning"), " Unlike all episodes"],
         to: like_frontend_path(@conn, :unlike_all_episodes),
         method: :delete,
         data: [confirm: "Are you sure? All episodes will be unfollowed!"],
         class: "btn btn-danger btn-sm" %>

  <%= for episode <- @user.episodes_i_like do %>

<%= link episode.title, to: episode_frontend_path(@conn, :show, episode), target: "_blank" %>

  <% end %>

Liked Chapters

Click on the chaptere link to open the episode show page in a new tab
to un/like, un/subscribe, un/follow.

You can even unlike all the chapters, you followed so far at once, by clicking
<%= link [fa_icon("warning"), " Unlike all chapters"],
         to: like_frontend_path(@conn, :unlike_all_chapters),
         method: :delete,
         data: [confirm: "Are you sure? All chapters will be unfollowed!"],
         class: "btn btn-danger btn-sm" %>

  <%= for chapter <- @user.chapters_i_like do %>

<%= link chapter.title, to: episode_frontend_path(@conn, :show, chapter.episode), target: "_blank" %>

  <% end %>

Liked Users

Click on the user link to open the user show page in a new tab
to un/like, un/follow.

You can even unlike all the users, you followed so far at once, by clicking
<%= link [fa_icon("warning"), " Unlike all users"],
         to: like_frontend_path(@conn, :unlike_all_users),
         method: :delete,
         data: [confirm: "Are you sure? All users will be unfollowed!"],
         class: "btn btn-danger btn-sm" %>

  <%= for user <- @user.users_i_like do %>

<%= link user.name, to: user_frontend_path(@conn, :show, user), target: "_blank" %>

  <% end %>

Followed Users

Click on the user link to open the user show page in a new tab
to un/like, un/follow.

You can even unfollow all the users, you followed so far at once, by clicking
<%= link [fa_icon("warning"), " Unfollow all users"],
         to: follow_frontend_path(@conn, :unfollow_all_users),
         method: :delete,
         data: [confirm: "Are you sure? All users will be unfollowed!"],
         class: "btn btn-danger btn-sm" %>

  <%= for user <- @user.users_i_follow do %>

<%= link user.name, to: user_frontend_path(@conn, :show, user), target: "_blank" %>

  <% end %>

Followed Personas

Click on the persona link to open the persona show page in a new tab
to un/like, un/follow.

You can even unfollow all the personas, you followed so far at once, by clicking
<%= link [fa_icon("warning"), " Unfollow all personas"],
         to: follow_frontend_path(@conn, :unfollow_all_personas),
         method: :delete,
         data: [confirm: "Are you sure? All personas will be unfollowed!"],
         class: "btn btn-danger btn-sm" %>

  <%= for persona <- @user.personas_i_follow do %>

<%= link persona.name, to: persona_frontend_path(@conn, :show, persona), target: "_blank" %>

  <% end %>

Liked Personas

Click on the persona link to open the persona show page in a new tab
to un/like, un/follow.

You can even unlike all the personas, you followed so far at once, by clicking
<%= link [fa_icon("warning"), " Unlike all personas"],
         to: like_frontend_path(@conn, :unlike_all_personas),
         method: :delete,
         data: [confirm: "Are you sure? All personas will be unfollowed!"],
         class: "btn btn-danger btn-sm" %>

  <%= for persona <- @user.personas_i_like do %>

<%= link persona.name, to: persona_frontend_path(@conn, :show, persona), target: "_blank" %>

  <% end %>

Followed Categories

Click on the category link to open the category show page in a new tab
to un/like, un/follow.

You can even unfollow all the categories, you followed so far at once, by clicking
<%= link [fa_icon("warning"), " Unfollow all categories"],
         to: follow_frontend_path(@conn, :unfollow_all_categories),
         method: :delete,
         data: [confirm: "Are you sure? All categories will be unfollowed!"],
         class: "btn btn-danger btn-sm" %>

  <%= for category <- @user.categories_i_follow do %>

<%= link category.title, to: category_frontend_path(@conn, :show, category), target: "_blank" %>

  <% end %>

Liked Categories

Click on the category link to open the category show page in a new tab
to un/like, un/follow.

You can even unlike all the categories, you followed so far at once, by clicking
<%= link [fa_icon("warning"), " Unlike all categories"],
         to: like_frontend_path(@conn, :unlike_all_categories),
         method: :delete,
         data: [confirm: "Are you sure? All categories will be unfollowed!"],
         class: "btn btn-danger btn-sm" %>

  <%= for category <- @user.categories_i_like do %>

<%= link category.title, to: category_frontend_path(@conn, :show, category), target: "_blank" %>

  <% end %>

Messages

You can delete a message by clicking the Delete button next to it.

You can even safely delete all the messages, you created so far, at once, by clicking
<%= link [fa_icon("warning"), " Delete all messages"],
         to: message_frontend_path(@conn, :delete_all),
         method: :delete,
         data: [confirm: "Are you sure? All messages will be deleted!"],
         class: "btn btn-danger btn-sm" %>

    Content
    Type
    Topic
    Subtopic
    Event
    Date
    Actions

    <%= for message <- @user.messages_created do %>

        <%= message.content %>
        <%= message.type %>
        <%= message.topic %>
        <%= message.subtopic %>
        <%= message.event %>
        <nobr><%= Timex.format!(message.inserted_at, "{ISOdate} {h24}:{m}:{s}") %></nobr>

          <%= link "Delete", to: message_frontend_path(@conn, :delete, message),
                             method: :delete,
                             data: [confirm: "Are you sure?"],
                             class: "btn btn-danger btn-xs" %>

    <% end %>

Recommendations

You can delete a recommendation by clicking the Delete button next to it.

You can double check the context by clicking the podcast or episode link, that will open
podcast or episode in a new tab, respectively.

You can even delete all the recommendations, you created so far, at once, by clicking
<%= link [fa_icon("warning"), " Delete all recommendations"],
         to: recommendation_frontend_path(@conn, :delete_all),
         method: :delete,
         data: [confirm: "Are you sure? All recommendations will be deleted!"],
         class: "btn btn-danger btn-sm" %>

      Podcast
      Episode
      Chapter
      Date

      Comment
      Actions

    <%= for recommendation <- @user.recommendations do %>

        <%= recommendation.podcast &&
                link recommendation.podcast.title, to: podcast_frontend_path(@conn, :show, recommendation.podcast),
                                                   target: "_blank" %>
        <%= recommendation.episode &&
                link recommendation.episode.title, to: episode_frontend_path(@conn, :show, recommendation.episode),
                                                   target: "_blank" %>
        <%= recommendation.chapter &&
                link recommendation.chapter.title, to: episode_frontend_path(@conn, :show, recommendation.chapter.episode),
                                                   target: "_blank" %>
        <nobr><%= Timex.format!(recommendation.inserted_at, "{ISOdate} {h24}:{m}:{s}") %></nobr>

        <%= recommendation.comment %>

          <%= link "Delete", to: recommendation_frontend_path(@conn, :delete, recommendation),
                             method: :delete,
                             data: [confirm: "Are you sure?"],
                             class: "btn btn-danger btn-xs" %>

    <% end %>

Danger Zone

  You can actually delete your account.

  We would be really sad to see you leave.
  But you can always sign up for a new account.

  <%= link [fa_icon("warning"), " Delete my account"],
           to: user_frontend_path(@conn, :delete_my_account),
           method: :delete,
           data: [confirm: "Are you sure? Your account would be deleted!"],
           class: "btn btn-danger" %>

Account

  <%= link [fa_icon("download"), " Download"],
           to: user_json_download_path(@conn, :download),
           class: "btn btn-primary" %>
   all my data as JSON-File

  Manage
  <%= link [fa_icon("user-circle"), " My Profile"],
           to: user_frontend_path(@conn, :my_profile),
           class: "btn btn-default",
           target: "_blank" %>

Name

<%= @user.name %>

Useranme

<%= @user.username %>

Email

<%= @user.email %>

Email adress confirmed?

<%= @user.email_confirmed %>

Is podcaster?

<%= @user.podcaster %>

Shares subscriptions?

<%= @user.share_subscriptions %>

Shares follows?

<%= @user.share_follows %>

Pro account until

<%= Timex.format!(@user.pro_until, "{ISOdate} {h24}:{m}") %>

Billing address

<%= @user.billing_address %>

Payment reference

<%= @user.payment_reference %>

Wants a paper bill?

<%= @user.paper_bill %>

OPML Files

  Manage
  <%= link [fa_icon("file-audio-o"), " My OPMLs"],
           to: opml_frontend_path(@conn, :index),
           class: "btn btn-default",
           target: "_blank" %>

  <%= for opml <- @user.opmls do %>

<%= opml.filename %>

  <% end %>

Personas

You can even delete all manifestations as personas, you created so far, at once, by clicking
<%= link [fa_icon("warning"), " Delete all subscriptions"],
         to: manifestation_frontend_path(@conn, :delete_all),
         method: :delete,
         data: [confirm: "Are you sure? All manifestations will be deleted!"],
         class: "btn btn-danger" %>

<%= for persona <- @user.personas do %>

    Persona <%= persona.name %>
    <%= link "Delete Manifestation", to: manifestation_frontend_path(@conn, :delete, persona.id),
                                     method: :delete,
                                     data: [confirm: "Are you sure?"],
                                     class: "btn btn-danger btn-xs" %>

Pid

<%= persona.pid %>

Name

<%= persona.name %>

Uri

<%= persona.uri %>

Email

<%= persona.email %>

Description

<%= persona.description %>

Url

<%= persona.image_url %>

Title

<%= persona.image_title %>

<% end %>

<%= for persona <- @user.user_personas do %>

    Persona <%= persona.name %>
    <%= link "Disconnect Persona", to: persona_frontend_path(@conn, :disconnect, persona.id),
                                   method: :post,
                                   data: [confirm: "Are you sure?"],
                                   class: "btn btn-danger btn-xs" %>

Pid

<%= persona.pid %>

Name

<%= persona.name %>

Uri

<%= persona.uri %>

Email

<%= persona.email %>

Description

<%= persona.description %>

Url

<%= persona.image_url %>

Title

<%= persona.image_title %>

<% end %>

Invoices

Invoices cannot be deleted, we have to store them due to fiscal regulations.

<%= for invoice <- @user.invoices do %>

Date

<%= Timex.format!(invoice.inserted_at, "{ISOdate} {h24}:{m}") %>

File

<%= link invoice.filename, to: invoice_frontend_path(@conn, :download, invoice) %>

<% end %>

Subscribed Podcasts

Click on the podcast link to open the podcast show page in a new tab
to un/like, un/subscribe, un/follow.

You can even delete all the subscriptions, you created so far, at once, by clicking
<%= link [fa_icon("warning"), " Delete all subscriptions"],
         to: subscription_frontend_path(@conn, :delete_all),
         method: :delete,
         data: [confirm: "Are you sure? All messages will be deleted!"],
         class: "btn btn-danger btn-sm" %>

  <%= for podcast <- @user.podcasts_i_subscribed do %>

<%= link podcast.title, to: podcast_frontend_path(@conn, :show, podcast), target: "_blank" %>

  <% end %>

Followed Podcasts

Click on the podcast link to open the podcast show page in a new tab
to un/like, un/subscribe, un/follow.

You can even unfollow all the podcasts, you followed so far at once, by clicking
<%= link [fa_icon("warning"), " Unfollow all podcasts"],
         to: follow_frontend_path(@conn, :unfollow_all_podcasts),
         method: :delete,
         data: [confirm: "Are you sure? All podcasts will be unfollowed!"],
         class: "btn btn-danger btn-sm" %>

  <%= for podcast <- @user.podcasts_i_follow do %>

<%= link podcast.title, to: podcast_frontend_path(@conn, :show, podcast), target: "_blank" %>

  <% end %>

Liked Podcasts

Click on the podcast link to open the podcast show page in a new tab
to un/like, un/subscribe, un/follow.

You can even unlike all the podcasts, you followed so far at once, by clicking
<%= link [fa_icon("warning"), " Unlike all podcasts"],
         to: like_frontend_path(@conn, :unlike_all_podcasts),
         method: :delete,
         data: [confirm: "Are you sure? All podcasts will be unfollowed!"],
         class: "btn btn-danger btn-sm" %>

  <%= for podcast <- @user.podcasts_i_like do %>

<%= link podcast.title, to: podcast_frontend_path(@conn, :show, podcast), target: "_blank" %>

  <% end %>

Liked Episodes

Click on the episode link to open the episode show page in a new tab
to un/like, un/subscribe, un/follow.

You can even unlike all the episodes, you followed so far at once, by clicking
<%= link [fa_icon("warning"), " Unlike all episodes"],
         to: like_frontend_path(@conn, :unlike_all_episodes),
         method: :delete,
         data: [confirm: "Are you sure? All episodes will be unfollowed!"],
         class: "btn btn-danger btn-sm" %>

  <%= for episode <- @user.episodes_i_like do %>

<%= link episode.title, to: episode_frontend_path(@conn, :show, episode), target: "_blank" %>

  <% end %>

Liked Chapters

Click on the chaptere link to open the episode show page in a new tab
to un/like, un/subscribe, un/follow.

You can even unlike all the chapters, you followed so far at once, by clicking
<%= link [fa_icon("warning"), " Unlike all chapters"],
         to: like_frontend_path(@conn, :unlike_all_chapters),
         method: :delete,
         data: [confirm: "Are you sure? All chapters will be unfollowed!"],
         class: "btn btn-danger btn-sm" %>

  <%= for chapter <- @user.chapters_i_like do %>

<%= link chapter.title, to: episode_frontend_path(@conn, :show, chapter.episode), target: "_blank" %>

  <% end %>

Liked Users

Click on the user link to open the user show page in a new tab
to un/like, un/follow.

You can even unlike all the users, you followed so far at once, by clicking
<%= link [fa_icon("warning"), " Unlike all users"],
         to: like_frontend_path(@conn, :unlike_all_users),
         method: :delete,
         data: [confirm: "Are you sure? All users will be unfollowed!"],
         class: "btn btn-danger btn-sm" %>

  <%= for user <- @user.users_i_like do %>

<%= link user.name, to: user_frontend_path(@conn, :show, user), target: "_blank" %>

  <% end %>

Followed Users

Click on the user link to open the user show page in a new tab
to un/like, un/follow.

You can even unfollow all the users, you followed so far at once, by clicking
<%= link [fa_icon("warning"), " Unfollow all users"],
         to: follow_frontend_path(@conn, :unfollow_all_users),
         method: :delete,
         data: [confirm: "Are you sure? All users will be unfollowed!"],
         class: "btn btn-danger btn-sm" %>

  <%= for user <- @user.users_i_follow do %>

<%= link user.name, to: user_frontend_path(@conn, :show, user), target: "_blank" %>

  <% end %>

Followed Personas

Click on the persona link to open the persona show page in a new tab
to un/like, un/follow.

You can even unfollow all the personas, you followed so far at once, by clicking
<%= link [fa_icon("warning"), " Unfollow all personas"],
         to: follow_frontend_path(@conn, :unfollow_all_personas),
         method: :delete,
         data: [confirm: "Are you sure? All personas will be unfollowed!"],
         class: "btn btn-danger btn-sm" %>

  <%= for persona <- @user.personas_i_follow do %>

<%= link persona.name, to: persona_frontend_path(@conn, :show, persona), target: "_blank" %>

  <% end %>

Liked Personas

Click on the persona link to open the persona show page in a new tab
to un/like, un/follow.

You can even unlike all the personas, you followed so far at once, by clicking
<%= link [fa_icon("warning"), " Unlike all personas"],
         to: like_frontend_path(@conn, :unlike_all_personas),
         method: :delete,
         data: [confirm: "Are you sure? All personas will be unfollowed!"],
         class: "btn btn-danger btn-sm" %>

  <%= for persona <- @user.personas_i_like do %>

<%= link persona.name, to: persona_frontend_path(@conn, :show, persona), target: "_blank" %>

  <% end %>

Followed Categories

Click on the category link to open the category show page in a new tab
to un/like, un/follow.

You can even unfollow all the categories, you followed so far at once, by clicking
<%= link [fa_icon("warning"), " Unfollow all categories"],
         to: follow_frontend_path(@conn, :unfollow_all_categories),
         method: :delete,
         data: [confirm: "Are you sure? All categories will be unfollowed!"],
         class: "btn btn-danger btn-sm" %>

  <%= for category <- @user.categories_i_follow do %>

<%= link category.title, to: category_frontend_path(@conn, :show, category), target: "_blank" %>

  <% end %>

Liked Categories

Click on the category link to open the category show page in a new tab
to un/like, un/follow.

You can even unlike all the categories, you followed so far at once, by clicking
<%= link [fa_icon("warning"), " Unlike all categories"],
         to: like_frontend_path(@conn, :unlike_all_categories),
         method: :delete,
         data: [confirm: "Are you sure? All categories will be unfollowed!"],
         class: "btn btn-danger btn-sm" %>

  <%= for category <- @user.categories_i_like do %>

<%= link category.title, to: category_frontend_path(@conn, :show, category), target: "_blank" %>

  <% end %>

Messages

You can delete a recommendation by clicking the Delete button next to it.

You can even delete all the messages, you created so far, at once, by clicking
<%= link [fa_icon("warning"), " Delete all messages"],
         to: message_frontend_path(@conn, :delete_all),
         method: :delete,
         data: [confirm: "Are you sure? All messages will be deleted!"],
         class: "btn btn-danger btn-sm" %>

    Content
    Type
    Topic
    Subtopic
    Event
    Date
    Actions

    <%= for message <- @user.messages_created do %>

        <%= message.content %>
        <%= message.type %>
        <%= message.topic %>
        <%= message.subtopic %>
        <%= message.event %>
        <nobr><%= Timex.format!(message.inserted_at, "{ISOdate} {h24}:{m}:{s}") %></nobr>

          <%= link "Delete", to: message_frontend_path(@conn, :delete, message),
                             method: :delete,
                             data: [confirm: "Are you sure?"],
                             class: "btn btn-danger btn-xs" %>

    <% end %>

Recommendations

You can delete a recommendation by clicking the Delete button next to it.

You can double check the context by clicking the podcast or episode link, that will open
podcast or episode in a new tab, respectively.

You can even delete all the recommendations, you created so far, at once, by clicking
<%= link [fa_icon("warning"), " Delete all recommendations"],
         to: recommendation_frontend_path(@conn, :delete_all),
         method: :delete,
         data: [confirm: "Are you sure? All recommendations will be deleted!"],
         class: "btn btn-danger btn-sm" %>

      Podcast
      Episode
      Chapter
      Date

      Comment
      Actions

    <%= for recommendation <- @user.recommendations do %>

        <%= recommendation.podcast &&
                link recommendation.podcast.title, to: podcast_frontend_path(@conn, :show, recommendation.podcast),
                                                   target: "_blank" %>
        <%= recommendation.episode &&
                link recommendation.episode.title, to: episode_frontend_path(@conn, :show, recommendation.episode),
                                                   target: "_blank" %>
        <%= recommendation.chapter &&
                link recommendation.chapter.title, to: episode_frontend_path(@conn, :show, recommendation.chapter.episode),
                                                   target: "_blank" %>
        <nobr><%= Timex.format!(recommendation.inserted_at, "{ISOdate} {h24}:{m}:{s}") %></nobr>

        <%= recommendation.comment %>

          <%= link "Delete", to: recommendation_frontend_path(@conn, :delete, recommendation),
                             method: :delete,
                             data: [confirm: "Are you sure?"],
                             class: "btn btn-danger btn-xs" %>

    <% end %>

Danger Zone

  You can actually delete your account.

  We would be really sad to see you leave.
  But you can always sign up for a new account.

  <%= link [fa_icon("warning"), " Delete my account"],
           to: user_frontend_path(@conn, :delete_my_account),
           method: :delete,
           data: [confirm: "Are you sure? Your account would be deleted!"],
           class: "btn btn-danger" %>


Added classroom to the list of classes

Added classroom to the list of classes

      Classes

      <%= link "New class", to: class_path(@conn, :new), class: "mdl-button mdl-js-button mdl-button--raised" %>

        Term
        Course
        Classroom
        Class date
        Starting at
        Finishes at

      <%= for class <- @classes do %>

            <%= class.offered_course.term.name %>

            <%= class.offered_course.course.name %>

            <%= class.classroom %>

            <%= class.date %>

            <%= String.slice Ecto.Time.to_iso8601(class.starting_at), 0, 5 %>

            <%= String.slice Ecto.Time.to_iso8601(class.finishes_at), 0, 5 %>

            <button id="tr_menu_<%= class.id %>"
                    class="mdl-button mdl-js-button mdl-button--icon"
            >
              more_vert
            </button>
            <ul class="mdl-menu mdl-menu--bottom-right mdl-js-menu"
                for="tr_menu_<%= class.id %>"
            >

                <%= link "Show", to: class_path(@conn, :show, class) %>

                <%= link "Edit", to: class_path(@conn, :edit, class) %>

                <%= link "Delete", to: class_path(@conn, :delete, class), method: :delete, data: [confirm: "Are you sure?"] %>

      <% end %>

      Classes

      <%= link "New class", to: class_path(@conn, :new), class: "mdl-button mdl-js-button mdl-button--raised" %>

        Term
        Course
        Class date
        Starting at
        Finishes at

      <%= for class <- @classes do %>

            <%= class.offered_course.term.name %>

            <%= class.offered_course.course.name %>

            <%= class.date %>

            <%= String.slice Ecto.Time.to_iso8601(class.starting_at), 0, 5 %>

            <%= String.slice Ecto.Time.to_iso8601(class.finishes_at), 0, 5 %>

            <button id="tr_menu_<%= class.id %>"
                    class="mdl-button mdl-js-button mdl-button--icon"
            >
              more_vert
            </button>
            <ul class="mdl-menu mdl-menu--bottom-right mdl-js-menu"
                for="tr_menu_<%= class.id %>"
            >

                <%= link "Show", to: class_path(@conn, :show, class) %>

                <%= link "Edit", to: class_path(@conn, :edit, class) %>

                <%= link "Delete", to: class_path(@conn, :delete, class), method: :delete, data: [confirm: "Are you sure?"] %>

      <% end %>


Perf: ensure 'trusted-by' images on homepage are served via CDN

    Remote

    Retro by Stride Consulting

    Free. World-class. Agile retrospectives.

RemoteRetro helps teams turn painpoints into progress.

  <%= render("sign_in_with_google_to_get_started.html") %>

            Free

Save money by not spending any.

            Collaborative

Interact with teammates in real time.

            Focused

Save time with automated setup and teardown.

          <img src="<%= Routes.static_url(@conn, "/images/trusted_by/spotify.svg") %>" alt="Spotify">

          <img src="<%= Routes.static_url(@conn, "/images/trusted_by/squarespace.svg") %>" alt="Squarespace logo">

          <img src="<%= Routes.static_url(@conn, "/images/trusted_by/weather_channel.svg") %>" alt="The Weather Channel">

          <img src="<%= Routes.static_url(@conn, "/images/trusted_by/flatiron_school.png") %>" alt="Flatiron School">

          <img src="<%= Routes.static_url(@conn, "/images/trusted_by/warby_parker.svg") %>" alt="Warby Parker">

          <img src="<%= Routes.static_url(@conn, "/images/trusted_by/casper.svg") %>" alt="Casper">

          <img height="80" width="80" src="<%= Routes.static_url(@conn, "/images/stride_logo.png") %>" alt="Stride Logo">

        An Open Source Project by Stride Consulting

          RemoteRetro provides a framework for the entire retrospective process, from idea generation to discussion to identification of action items, and it does so no matter what the limitations of the physical space may be. It's a great equalizer, and a major step forward for retro organizers and participants.’

          David A. Black

          author, The Well-Grounded Rubyist

      <%= render("sign_in_with_google_to_get_started.html") %>

        F.A.Q.

<link rel="prefetch" href="<%= Routes.static_url(@conn, "/js/jquery.min.js") %>">
<link rel="prefetch" href="<%= Routes.static_url(@conn, "/js/semantic.min.js") %>">

    Remote

    Retro by Stride Consulting

    Free. World-class. Agile retrospectives.

RemoteRetro helps teams turn painpoints into progress.

  <%= render("sign_in_with_google_to_get_started.html") %>

            Free

Save money by not spending any.

            Collaborative

Interact with teammates in real time.

            Focused

Save time with automated setup and teardown.

          <img height="80" width="80" src="<%= Routes.static_url(@conn, "/images/stride_logo.png") %>" alt="Stride Logo">

        An Open Source Project by Stride Consulting

          RemoteRetro provides a framework for the entire retrospective process, from idea generation to discussion to identification of action items, and it does so no matter what the limitations of the physical space may be. It's a great equalizer, and a major step forward for retro organizers and participants.’

          David A. Black

          author, The Well-Grounded Rubyist

      <%= render("sign_in_with_google_to_get_started.html") %>

        F.A.Q.

<link rel="prefetch" href="<%= Routes.static_url(@conn, "/js/jquery.min.js") %>">
<link rel="prefetch" href="<%= Routes.static_url(@conn, "/js/semantic.min.js") %>">


Make call-to-action buttons more blue-ish

Record and share your terminal sessions, the right way.

          Forget screen recording apps and blurry video.
          Enjoy a lightweight, purely text-based approach to terminal recording.

<button class="btn btn-info" data-content="
Install asciinema with:

brew install asciinema

See other installation options.

Start recording with:

asciinema rec
" data-toggle="popover" type="button" data-original-title="" title="">Start Recording</button>

Supports Linux, macOS and *BSD

          <%= if @asciicast do %>
            <%= player @asciicast, speed: 2 %>
          <% end %>

asciinema [as-kee-nuh-muh] is a free and open source solution for recording
          terminal sessions and sharing them on the web. Read about how it works.

Simple recording

Record right where you work - in a terminal.
          To start just run asciinema rec, to finish hit Ctrl-D or type exit.

Get started »

Copy & paste

Any time you see a command you'd like to try in your own terminal just
    pause the player and copy-paste the content you want.
          It's just a text after all!

Embedding

Easily embed an asciicast player in your blog post, project documentation page or in your conference talk slides.

See the embedding docs »

Example sessions recorded with asciinema

          <%= for asciicasts <- Enum.chunk(@asciicasts, 3, 3, []) do %>

              <%= for asciicast <- asciicasts do %>

                  <%= render AsciinemaWeb.AsciicastView, "_card.html", conn: @conn, asciicast: asciicast, no_created_at: true %>

              <% end %>

          <% end %>

Explore more

Record and share your terminal sessions, the right way.

          Forget screen recording apps and blurry video.
          Enjoy a lightweight, purely text-based approach to terminal recording.

<button class="btn btn-success" data-content="
Install asciinema with:

brew install asciinema

See other installation options.

Start recording with:

asciinema rec
" data-toggle="popover" type="button" data-original-title="" title="">Start Recording</button>

Supports Linux, macOS and *BSD

          <%= if @asciicast do %>
            <%= player @asciicast, speed: 2 %>
          <% end %>

asciinema [as-kee-nuh-muh] is a free and open source solution for recording
          terminal sessions and sharing them on the web. Read about how it works.

Simple recording

Record right where you work - in a terminal.
          To start just run asciinema rec, to finish hit Ctrl-D or type exit.

Get started »

Copy & paste

Any time you see a command you'd like to try in your own terminal just
    pause the player and copy-paste the content you want.
          It's just a text after all!

Embedding

Easily embed an asciicast player in your blog post, project documentation page or in your conference talk slides.

See the embedding docs »

Example sessions recorded with asciinema

          <%= for asciicasts <- Enum.chunk(@asciicasts, 3, 3, []) do %>

              <%= for asciicast <- asciicasts do %>

                  <%= render AsciinemaWeb.AsciicastView, "_card.html", conn: @conn, asciicast: asciicast, no_created_at: true %>

              <% end %>

          <% end %>

Explore more


Make execution of hooks to work on busybox

#!/bin/sh

set -e

# Set VERBOSE to enable verbose logging of the boot script
#VERBOSE=true
if [ ! -z $DEBUG_BOOT ]; then
    set -x
fi;

my_readlink_f() {
    TARGET_FILE="$1"
    cd $(dirname "$TARGET_FILE")
    TARGET_FILE=$(basename "$TARGET_FILE")

    # Iterate down a (possible) chain of symlinks
    while [ -L "$TARGET_FILE" ]
    do
        TARGET_FILE=$(readlink "$TARGET_FILE")
        cd $(dirname "$TARGET_FILE")
        TARGET_FILE=$(basename "$TARGET_FILE")
    done
    # Compute the canonicalized name by finding the physical path
    # for the directory we're in and appending the target file.
    PHYS_DIR=$(pwd -P)
    RESULT="$PHYS_DIR/$TARGET_FILE"
    echo "$RESULT"
}

# Path to this script
if uname | grep -q 'Darwin'; then
  # on OSX, best to install coreutils from homebrew or similar
  # to get greadlink
  if command -v greadlink >/dev/null 2>&1; then
    SCRIPT=$(greadlink -f "$0")
  else
    SCRIPT=$(my_readlink_f "$0" )
  fi
else
  SCRIPT=$(readlink -f "$0" )
fi

# Parent directory of this script
SCRIPT_DIR=$(dirname "${SCRIPT}")
# Root directory of all releases
RELEASE_ROOT_DIR=$(dirname $(dirname ${SCRIPT_DIR}))

# Disable flow control for run_erl
# Flow control can cause several problems. On Linux, if you
# accidentally hit Ctrl-S (instead of Ctrl-D to detach) and
# then some other key, the entire beam process will hang when
# attempting to write to stdout. On Solaris, the beam process
# will hang on writing if ScrollLock is on.
RUN_ERL_DISABLE_FLOWCNTRL="${RUN_ERL_DISABLE_FLOWCNTRL:-true}"
# Name of the release
REL_NAME="<%= release_name %>"
# Current version of the release
REL_VSN="<%= release_version %>"
# Current version of ERTS being used
# If this is not present/unset, it will be detected
<%= unless is_nil(erts_vsn) do %>ERTS_VSN="<%= erts_vsn %>"<% end %>
# VM code loading mode, embedded by default, can also be interactive
# See http://erlang.org/doc/man/code.html
CODE_LOADING_MODE="${CODE_LOADING_MODE:-embedded}"
# Path to start_erl.data
START_ERL_DATA="$RELEASE_ROOT_DIR/releases/start_erl.data"
# Directory containing the current version of this release
REL_DIR="$RELEASE_ROOT_DIR/releases/$REL_VSN"
# The lib directory for this release
REL_LIB_DIR="$RELEASE_ROOT_DIR/lib"
# Options passed to erl
ERL_OPTS="<%= release.profile.erl_opts %>"
# When stdout is piped to a file, this is the directory those files will
# be stored in. defaults to /log in the release root directory

# RELEASE_MUTABLE_DIR
RELEASE_MUTABLE_DIR="${RELEASE_MUTABLE_DIR:-$RELEASE_ROOT_DIR/var}"

RUNNER_LOG_DIR="${RUNNER_LOG_DIR:-$RELEASE_MUTABLE_DIR/log}"
# A string of extra options to pass to erl, here for plugins
EXTRA_OPTS=""

# Pre-start/stop event hook paths
PRE_START_HOOKS="$REL_DIR/hooks/pre_start.d"
POST_START_HOOKS="$REL_DIR/hooks/post_start.d"
PRE_STOP_HOOKS="$REL_DIR/hooks/pre_stop.d"
POST_STOP_HOOKS="$REL_DIR/hooks/post_stop.d"

# Get node pid
get_pid() {
    if output="$(nodetool rpcterms os getpid)"
    then
        echo "$output" | sed -e 's/"//g'
        return 0
    else
        echo "$output"
        return 1
    fi
}

# Generate a unique nodename
gen_nodename() {
    id="longname$(gen_id)-${NAME}"
    "$BINDIR/erl" -boot start_clean -eval '[Host] = tl(string:tokens(atom_to_list(node()),"@")), io:format("~s~n", [Host]), halt()' -noshell ${NAME_TYPE} $id
}

# Generate a random id
gen_id() {
    od -t x -N 4 /dev/urandom | head -n1 | awk '{print $2}'
}

# Control a node
# Use like `nodetool "ping"`
nodetool() {
    command="$1"; shift

    "$ERTS_DIR/bin/escript" "$ROOTDIR/bin/nodetool" "$NAME_TYPE" "$NAME" \
                            -setcookie "$COOKIE" "$command" $@
}

# Run an escript in the node's environment
# Use like `escript "path/to/escript"`
escript() {
    shift; scriptpath="$1"; shift
    export RELEASE_ROOT_DIR

    "$ERTS_DIR/bin/escript" "$ROOTDIR/$scriptpath" $@
}

# Private. Load target cookie, either from vm.args or $HOME/.cookie
_load_cookie() {
    COOKIE_ARG="$(grep '^-setcookie' "$VMARGS_PATH" || true)"
    DEFAULT_COOKIE_FILE="$HOME/.erlang.cookie"
    if [ -z "$COOKIE_ARG" ]; then
        if [ -f "$DEFAULT_COOKIE_FILE" ]; then
            COOKIE="$(cat "$DEFAULT_COOKIE_FILE")"
        fi
    else
        # Extract cookie name from COOKIE_ARG
        COOKIE="$(echo "$COOKIE_ARG" | awk '{print $2}')"
    fi
}

# Private. Ensure that cookie is set.
_check_cookie() {
    # Attempt reloading cookie in case it has been set in a hook
    if [ -z "$COOKIE" ]; then
        _load_cookie
    fi
    # Die if cookie is still not set, as connecting via distribution will fail
    if [ -z "$COOKIE" ]; then
        printf "a secret cookie must be provided in one of the following ways:\n  - with vm.args using the -setcookie parameter,\n  or\n  by writing the cookie to '$DEFAULT_COOKIE_FILE', with permissions set to 0400"
        exit 1
    fi
}

# Private. For determining the version of ERTS available to the release
_detect_erts_vsn() {
    "$BINDIR/erl" -boot start_clean -eval 'Ver = erlang:system_info(version), io:format("~s~n", [Ver]), halt()' -noshell
}

# Private. Gets a list of code paths for this release
_get_code_paths() {
    "$ERTS_DIR/bin/escript" "$ROOTDIR/bin/release_utils.escript" "get_code_paths" "$ROOTDIR" "$ERTS_DIR" "$REL_NAME" "$REL_VSN"
}

# Private. For setting ERTS_DIR and ROOTDIR
_find_erts_dir() {
    __erts_dir="$RELEASE_ROOT_DIR/erts-$ERTS_VSN"
    if [ -d "$__erts_dir" ]; then
        ERTS_DIR="$__erts_dir";
        ROOTDIR="$RELEASE_ROOT_DIR"
    else
        set +e # temporarily disable so we can display a nice error message to the user
        __erl="$(which erl)"
        set -e
        if [ -z "${__erl}"  ]; then
          echo 'erlang runtime not found. If erlang is installed check your $PATH. aborting.'
          exit 1
        fi
        code="io:format(\"~s\", [code:root_dir()]), halt()."
        __erl_root="$("$__erl" -noshell -eval "$code")"
        ERTS_DIR="$__erl_root/erts-$ERTS_VSN"
        ROOTDIR="$__erl_root"
    fi
}

# Private. Open a shell on a remote node
_rem_sh() {
    # Generate a unique id used to allow multiple remsh to the same node
    # transparently
    id="remsh$(gen_id)-${NAME}"

    # Get the node's ticktime so that we use the same thing.
    TICKTIME="$(nodetool rpcterms net_kernel get_net_ticktime)"

    # Setup remote shell command to control node
    if [ ! -z "$USE_ERL_SHELL" ]; then
        exec "$BINDIR/erl" \
            -hidden \
            -boot start_clean -boot_var ERTS_LIB_DIR "$ERTS_LIB_DIR" \
            -kernel net_ticktime $TICKTIME \
            "$NAME_TYPE" "$id" -remsh "$NAME" -setcookie "$COOKIE"
    else
        __code_paths=$(_get_code_paths)
        exec "$BINDIR/erl" \
            -pa "$CONSOLIDATED_DIR" \
            ${__code_paths} \
            -hidden -noshell \
            -boot start_clean -boot_var ERTS_LIB_DIR "$ERTS_LIB_DIR" \
            -kernel net_ticktime $TICKTIME \
            -user Elixir.IEx.CLI "$NAME_TYPE" "$id" -setcookie "$COOKIE" \
            -extra --no-halt +iex -"$NAME_TYPE" "$id" --cookie "$COOKIE" --remsh "$NAME"
    fi
}

# Output a start command for the last argument of run_erl
_start_command() {
    printf "exec \"%s\" \"%s\" -- %s %s" "$RELEASE_ROOT_DIR/bin/$REL_NAME" \
           "$START_OPTION" "${ARGS}" "${EXTRA_OPTS}"
}

# Do a textual replacement of ${VAR} occurances in $1 and pipe to $2
_replace_os_vars() {
    awk '
        function escape(s) {
            gsub(/'\&'/, "\\\\&", s);
            return s;
        }
        {
            while(match($0,"[$]{[^}]*}")) {
                var=substr($0,RSTART+2,RLENGTH-3);
                gsub("[$]{"var"}", escape(ENVIRON[var]))
            }
        }1' < "$1" > "$1.bak"
    mv -- "$1.bak" "$1"
}

# Private. Run hooks from directory.
_run_hooks_from_dir() {
   if [ -d "$1" ]; then
      for file in $1/[0-9a-zA-Z]*.sh; do
        [ -f "$file" ] || continue
        . "$file"
      done
   fi
}

# If TERM is not set, set it to xterm
if [ -z "$TERM" ]; then
    export TERM=xterm
fi

# If ERTS_VSN is not set, detect it
if [ -z "$ERTS_VSN" ]; then
    ERTS_VSN="$(_detect_erts_vsn)"
    echo "$ERTS_VSN $REL_VSN" > "$START_ERL_DATA"
fi

# Allow override of where to read configuration from
# By default it's RELEASE_ROOT_DIR
if [ -z "$RELEASE_CONFIG_DIR" ]; then
    RELEASE_CONFIG_DIR="$RELEASE_ROOT_DIR"
fi

# Make sure directories exist
mkdir -p "$RELEASE_MUTABLE_DIR"
echo "Files in this directory are regenerated frequently, edits will be lost" \
    > "$RELEASE_MUTABLE_DIR/WARNING_README"
mkdir -p "$RUNNER_LOG_DIR"

# Set VMARGS_PATH, the path to the vm.args file to use
# Use $RELEASE_CONFIG_DIR/vm.args if exists, otherwise releases/VSN/vm.args
if [ -z "$VMARGS_PATH" ]; then
    if [ -f "$RELEASE_CONFIG_DIR/vm.args" ]; then
        VMARGS_PATH="$RELEASE_CONFIG_DIR/vm.args"
    else
        VMARGS_PATH="$REL_DIR/vm.args"
    fi
fi
if [ "$VMARGS_PATH" != "$RELEASE_MUTABLE_DIR/vm.args" ]; then
    echo "#### Generated - edit/create $RELEASE_CONFIG_DIR/vm.args instead." \
        >  "$RELEASE_MUTABLE_DIR/vm.args"
    cat  "$VMARGS_PATH"                              \
        >> "$RELEASE_MUTABLE_DIR/vm.args"
    VMARGS_PATH=$RELEASE_MUTABLE_DIR/vm.args
fi
if [ $REPLACE_OS_VARS ]; then
    _replace_os_vars "$VMARGS_PATH"
fi

# Set SYS_CONFIG_PATH, the path to the sys.config file to use
# Use $RELEASE_CONFIG_DIR/sys.config if exists, otherwise releases/VSN/sys.config
if [ -z "$SYS_CONFIG_PATH" ]; then
    if [ -f "$RELEASE_CONFIG_DIR/sys.config" ]; then
        SYS_CONFIG_PATH="$RELEASE_CONFIG_DIR/sys.config"
    else
        SYS_CONFIG_PATH="$REL_DIR/sys.config"
    fi
fi
if [ "$SYS_CONFIG_PATH" != "$RELEASE_MUTABLE_DIR/sys.config" ]; then
    echo "%% Generated - edit/create $RELEASE_CONFIG_DIR/sys.config instead." \
        >  "$RELEASE_MUTABLE_DIR/sys.config"
    cat  "$SYS_CONFIG_PATH"                              \
        >> "$RELEASE_MUTABLE_DIR/sys.config"
    SYS_CONFIG_PATH=$RELEASE_MUTABLE_DIR/sys.config
fi
if [ $REPLACE_OS_VARS ]; then
    _replace_os_vars "$SYS_CONFIG_PATH"
fi

# Extract the target node name from node.args
# Should be `-sname somename` or `-name somename@somehost`
NAME_ARG=$(egrep '^-s?name' "$VMARGS_PATH" || true)
if [ -z "$NAME_ARG" ]; then
    echo "vm.args needs to have either -name or -sname parameter."
    exit 1
fi

# Extract the name type and name from the NAME_ARG for REMSH
# NAME_TYPE should be -name or -sname
NAME_TYPE="$(echo "$NAME_ARG" | awk '{print $1}')"
# NAME will be either `somename` or `somename@somehost`
NAME="$(echo "$NAME_ARG" | awk '{print $2}')"

# Where the pipe will be stored when using `start`
# This is used so you can attach the running application to the current
# shell using `attach`
PIPE_DIR="${PIPE_DIR:-$RELEASE_MUTABLE_DIR/erl_pipes/$NAME/}"

# Extract the target cookie
_load_cookie

_find_erts_dir
export ROOTDIR="$RELEASE_ROOT_DIR"
export BINDIR="$ERTS_DIR/bin"
export EMU="beam"
export PROGNAME="erl"
export LD_LIBRARY_PATH="$ERTS_DIR/lib:$LD_LIBRARY_PATH"
ERTS_LIB_DIR="$ERTS_DIR/../lib"
CONSOLIDATED_DIR="$ROOTDIR/lib/${REL_NAME}-${REL_VSN}/consolidated"

cd "$ROOTDIR"

# User can specify an sname without @hostname
# This will fail when creating remote shell
# So here we check for @ and add @hostname if missing
case $NAME in
    *@*)
        # Nothing to do
        ;;
    *)
        NAME=$NAME@$(gen_nodename)
        ;;
esac

# Check the first argument for instructions
case "$1" in
    start|start_boot)

        # Make sure there is not already a node running
        #RES=`$NODETOOL ping`
        #if [ "$RES" = "pong" ]; then
        #    echo "Node is already running!"
        #    exit 1
        #fi
        # Save this for later.
        CMD=$1
        case "$1" in
            start)
                shift
                START_OPTION="console"
                HEART_OPTION="start"
                ;;
            start_boot)
                shift
                START_OPTION="console_boot"
                HEART_OPTION="start_boot"
                ;;
        esac
        ARGS="$@"
        RUN_PARAM="$@"

        _check_cookie
        _run_hooks_from_dir "$PRE_START_HOOKS"

        # Set arguments for the heart command
        set -- "$SCRIPT_DIR/$REL_NAME" "$HEART_OPTION"
        [ "$RUN_PARAM" ] && set -- "$@" "$RUN_PARAM"

        # Export the HEART_COMMAND
        HEART_COMMAND="$RELEASE_ROOT_DIR/bin/$REL_NAME $CMD"
        export HEART_COMMAND

        mkdir -p "$PIPE_DIR"

        "$BINDIR/run_erl" -daemon "$PIPE_DIR" "$RUNNER_LOG_DIR" \
                          "$(_start_command)"

        _run_hooks_from_dir "$POST_START_HOOKS"
        ;;

    stop)
        _check_cookie
        _run_hooks_from_dir "$PRE_STOP_HOOKS"
        # Wait for the node to completely stop...
        PID="$(get_pid)"
        if ! nodetool "stop"; then
            exit 1
        fi
        while $(kill -s 0 "$PID" 2>/dev/null);
        do
            sleep 1
        done
        _run_hooks_from_dir "$POST_STOP_HOOKS"
        ;;

    restart)
        _check_cookie
        _run_hooks_from_dir "$PRE_START_HOOKS"
        ## Restart the VM without exiting the process
        if ! nodetool "restart"; then
            exit 1
        fi
        ;;

    reboot)
        _check_cookie
        _run_hooks_from_dir "$PRE_START_HOOKS"
        ## Restart the VM completely (uses heart to restart it)
        if ! nodetool "reboot"; then
            exit 1
        fi
        ;;

    pid)
        ## Get the VM's pid
        _check_cookie
        if ! get_pid; then
            exit 1
        fi
        ;;

    ping)
        _check_cookie
        ## See if the VM is alive
        if ! nodetool "ping"; then
            exit 1
        fi
        ;;

    escript)
        _check_cookie
        ## Run an escript under the node's environment
        if ! escript $@; then
            exit 1
        fi
        ;;

    attach)
        _check_cookie

        # Make sure a node IS running
        if ! nodetool "ping" > /dev/null; then
            echo "Node is not running!"
            exit 1
        fi

        shift
        exec "$BINDIR/to_erl" "$PIPE_DIR"
        ;;

    remote_console)
        _check_cookie

        # Make sure a node IS running
        if ! nodetool "ping" > /dev/null; then
            echo "Node is not running!"
            exit 1
        fi

        shift
        _rem_sh
        ;;

    upgrade|downgrade|install)
        if [ -z "$2" ]; then
            echo "Missing package argument"
            echo "Usage: $REL_NAME $1 {package base name}"
            echo "NOTE {package base name} MUST NOT include the .tar.gz suffix"
            exit 1
        fi

        _check_cookie

        # Make sure a node IS running
        if ! nodetool "ping" > /dev/null; then
            echo "Node is not running!"
            exit 1
        fi

        exec "$BINDIR/escript" "$ROOTDIR/bin/release_utils.escript" \
             "install_release" "$REL_NAME" "$NAME_TYPE"  "$NAME" "$COOKIE" "$2"
        ;;

    unpack)
        if [ -z "$2" ]; then
            echo "Missing package argument"
            echo "Usage: $REL_NAME $1 {package base name}"
            echo "NOTE {package base name} MUST NOT include the .tar.gz suffix"
            exit 1
        fi

        _check_cookie

        # Make sure a node IS running
        if ! nodetool "ping" > /dev/null; then
            echo "Node is not running!"
            exit 1
        fi

        exec "$BINDIR/escript" "$ROOTDIR/bin/release_utils.escript" \
             "unpack_release" "$REL_NAME" "$NAME_TYPE" "$NAME" "$COOKIE" "$2"
        ;;

    console|console_clean|console_boot)
        # .boot file typically just $REL_NAME (ie, the app name)
        # however, for debugging, sometimes start_clean.boot is useful.
        # For e.g. 'setup', one may even want to name another boot script.
        case "$1" in
            console)
                if [ -f "$REL_DIR/$REL_NAME.boot" ]; then
                  BOOTFILE="$REL_DIR/$REL_NAME"
                else
                  BOOTFILE="$REL_DIR/start"
                fi
                ;;
            console_clean)
                BOOTFILE="$ROOTDIR/bin/start_clean"
                __code_paths=$(_get_code_paths)
                EXTRA_CODE_PATHS=${_code_paths}
                ;;
            console_boot)
                shift
                BOOTFILE="$1"
                shift
                ;;
        esac
        # Setup beam-required vars
        EMU="beam"
        PROGNAME="${0#*/}"

        export EMU
        export PROGNAME

        # Store passed arguments since they will be erased by `set`
        ARGS="$@"

        _check_cookie

        # Start the VM, executing pre_start hook along
        # the way. We can't run the post_start hook because
        # the console will crash with no TTY attached
        _run_hooks_from_dir "$PRE_START_HOOKS"

        # Build an array of arguments to pass to exec later on
        # Build it here because this command will be used for logging.
        set -- "$BINDIR/erlexec" \
            -boot "$BOOTFILE" \
            -boot_var ERTS_LIB_DIR "$ERTS_LIB_DIR" \
            -env ERL_LIBS "$REL_LIB_DIR" \
            -pa "$CONSOLIDATED_DIR" \
            ${EXTRA_CODE_PATHS} \
            -args_file "$VMARGS_PATH" \
            -config "$SYS_CONFIG_PATH" \
            -mode "$CODE_LOADING_MODE" \
            ${ERL_OPTS} \
            -user Elixir.IEx.CLI \
            -extra --no-halt +iex

        # Dump environment info for logging purposes
        if [ ! -z $VERBOSE ]; then
            echo "Exec: $@" -- ${1+$ARGS} ${EXTRA_OPTS}
            echo "Root: $ROOTDIR"

            # Log the startup
            echo "$RELEASE_ROOT_DIR"
        fi;

        logger -t "$REL_NAME[$$]" "Starting up"

        exec "$@" -- ${1+$ARGS} ${EXTRA_OPTS}
        ;;

    foreground)
        # start up the release in the foreground for use by runit
        # or other supervision services

        [ -f "$REL_DIR/$REL_NAME.boot" ] && BOOTFILE="$REL_NAME" || BOOTFILE=start
        FOREGROUNDOPTIONS="-noshell -noinput +Bd"

        # Setup beam-required vars
        EMU=beam
        PROGNAME="${0#*/}"

        export EMU
        export PROGNAME

        # Store passed arguments since they will be erased by `set`
        ARGS="$@"

        _check_cookie

        # Start the VM, executing pre and post start hooks
        _run_hooks_from_dir "$PRE_START_HOOKS"

        # Build an array of arguments to pass to exec later on
        # Build it here because this command will be used for logging.
        set -- "$BINDIR/erlexec" $FOREGROUNDOPTIONS \
            -boot "$REL_DIR/$BOOTFILE" \
            -boot_var ERTS_LIB_DIR "$ERTS_LIB_DIR" \
            -env ERL_LIBS "$REL_LIB_DIR" \
            -pa "$CONSOLIDATED_DIR" \
            -args_file "$VMARGS_PATH" \
            -config "$SYS_CONFIG_PATH" \
            -mode "$CODE_LOADING_MODE" \
            ${ERL_OPTS} \
            -extra ${EXTRA_OPTS}

        # Dump environment info for logging purposes
        if [ ! -z $VERBOSE ]; then
            echo "Exec: $@" -- ${1+$ARGS}
            echo "Root: $ROOTDIR"
        fi;

        "$@" -- ${1+$ARGS} &
        __bg_pid=$!
        _run_hooks_from_dir "$POST_START_HOOKS"
        wait $__bg_pid
        ;;
    rpc)
        _check_cookie

        # Make sure a node IS running
        if ! nodetool "ping" > /dev/null; then
            echo "Node is not running!"
            exit 1
        fi

        shift

        nodetool rpc $@
        ;;
    rpcterms)
        _check_cookie

        # Make sure a node IS running
        if ! nodetool "ping" > /dev/null; then
            echo "Node is not running!"
            exit 1
        fi

        shift

        nodetool rpcterms $@
        ;;
    eval)
        _check_cookie

        # Make sure a node IS running
        if ! nodetool "ping" > /dev/null; then
            echo "Node is not running!"
            exit 1
        fi

        shift
        nodetool "eval" $@
        ;;
    command)
        # Execute as command-line utility
        #
        # Like the escript command, this does not start the OTP application.
        # If your command depends on a running OTP application,
        # use
        #
        #     {:ok, _} = Application.ensure_all_started(:your_app)

        _check_cookie

        shift
        MODULE="$1"; shift
        FUNCTION="$1"; shift

        # Save extra arguments
        ARGS="$@"

        # Build arguments for erlexec
        __code_paths=$(_get_code_paths)
        set -- "$ERL_OPTS"
        [ "$SYS_CONFIG_PATH" ] && set -- "$@" -config "$SYS_CONFIG_PATH"
        set -- "$@" -boot_var ERTS_LIB_DIR "$ERTS_LIB_DIR"
        set -- "$@" -noshell
        set -- "$@" -boot start_clean
        set -- "$@" -pa "$CONSOLIDATED_DIR"
        set -- "$@" ${__code_paths}
        set -- "$@" -s "$MODULE" "$FUNCTION"

        $BINDIR/erlexec $@ -extra $ARGS
        exit "$?"
        ;;
    *)
        __command_path="$REL_DIR/commands/$1"
        if [ -f "$__command_path" ]; then
            _check_cookie
            . "$__command_path"
        else
            echo "Usage: $REL_NAME {start|start_boot <file>|foreground|stop|restart|reboot|pid|ping|console|console_clean|console_boot <file>|attach|remote_console|upgrade|downgrade|install|escript|rpc|rpcterms|eval|command <module> <function> <args>}"
            exit 1
        fi
        ;;
esac

exit 0

#!/bin/sh

set -e

# Set VERBOSE to enable verbose logging of the boot script
#VERBOSE=true
if [ ! -z $DEBUG_BOOT ]; then
    set -x
fi;

my_readlink_f() {
    TARGET_FILE="$1"
    cd $(dirname "$TARGET_FILE")
    TARGET_FILE=$(basename "$TARGET_FILE")

    # Iterate down a (possible) chain of symlinks
    while [ -L "$TARGET_FILE" ]
    do
        TARGET_FILE=$(readlink "$TARGET_FILE")
        cd $(dirname "$TARGET_FILE")
        TARGET_FILE=$(basename "$TARGET_FILE")
    done
    # Compute the canonicalized name by finding the physical path
    # for the directory we're in and appending the target file.
    PHYS_DIR=$(pwd -P)
    RESULT="$PHYS_DIR/$TARGET_FILE"
    echo "$RESULT"
}

# Path to this script
if uname | grep -q 'Darwin'; then
  # on OSX, best to install coreutils from homebrew or similar
  # to get greadlink
  if command -v greadlink >/dev/null 2>&1; then
    SCRIPT=$(greadlink -f "$0")
  else
    SCRIPT=$(my_readlink_f "$0" )
  fi
else
  SCRIPT=$(readlink -f "$0" )
fi

# Parent directory of this script
SCRIPT_DIR=$(dirname "${SCRIPT}")
# Root directory of all releases
RELEASE_ROOT_DIR=$(dirname $(dirname ${SCRIPT_DIR}))

# Disable flow control for run_erl
# Flow control can cause several problems. On Linux, if you
# accidentally hit Ctrl-S (instead of Ctrl-D to detach) and
# then some other key, the entire beam process will hang when
# attempting to write to stdout. On Solaris, the beam process
# will hang on writing if ScrollLock is on.
RUN_ERL_DISABLE_FLOWCNTRL="${RUN_ERL_DISABLE_FLOWCNTRL:-true}"
# Name of the release
REL_NAME="<%= release_name %>"
# Current version of the release
REL_VSN="<%= release_version %>"
# Current version of ERTS being used
# If this is not present/unset, it will be detected
<%= unless is_nil(erts_vsn) do %>ERTS_VSN="<%= erts_vsn %>"<% end %>
# VM code loading mode, embedded by default, can also be interactive
# See http://erlang.org/doc/man/code.html
CODE_LOADING_MODE="${CODE_LOADING_MODE:-embedded}"
# Path to start_erl.data
START_ERL_DATA="$RELEASE_ROOT_DIR/releases/start_erl.data"
# Directory containing the current version of this release
REL_DIR="$RELEASE_ROOT_DIR/releases/$REL_VSN"
# The lib directory for this release
REL_LIB_DIR="$RELEASE_ROOT_DIR/lib"
# Options passed to erl
ERL_OPTS="<%= release.profile.erl_opts %>"
# When stdout is piped to a file, this is the directory those files will
# be stored in. defaults to /log in the release root directory

# RELEASE_MUTABLE_DIR
RELEASE_MUTABLE_DIR="${RELEASE_MUTABLE_DIR:-$RELEASE_ROOT_DIR/var}"

RUNNER_LOG_DIR="${RUNNER_LOG_DIR:-$RELEASE_MUTABLE_DIR/log}"
# A string of extra options to pass to erl, here for plugins
EXTRA_OPTS=""

# Pre-start/stop event hook paths
PRE_START_HOOKS="$REL_DIR/hooks/pre_start.d"
POST_START_HOOKS="$REL_DIR/hooks/post_start.d"
PRE_STOP_HOOKS="$REL_DIR/hooks/pre_stop.d"
POST_STOP_HOOKS="$REL_DIR/hooks/post_stop.d"

# Get node pid
get_pid() {
    if output="$(nodetool rpcterms os getpid)"
    then
        echo "$output" | sed -e 's/"//g'
        return 0
    else
        echo "$output"
        return 1
    fi
}

# Generate a unique nodename
gen_nodename() {
    id="longname$(gen_id)-${NAME}"
    "$BINDIR/erl" -boot start_clean -eval '[Host] = tl(string:tokens(atom_to_list(node()),"@")), io:format("~s~n", [Host]), halt()' -noshell ${NAME_TYPE} $id
}

# Generate a random id
gen_id() {
    od -t x -N 4 /dev/urandom | head -n1 | awk '{print $2}'
}

# Control a node
# Use like `nodetool "ping"`
nodetool() {
    command="$1"; shift

    "$ERTS_DIR/bin/escript" "$ROOTDIR/bin/nodetool" "$NAME_TYPE" "$NAME" \
                            -setcookie "$COOKIE" "$command" $@
}

# Run an escript in the node's environment
# Use like `escript "path/to/escript"`
escript() {
    shift; scriptpath="$1"; shift
    export RELEASE_ROOT_DIR

    "$ERTS_DIR/bin/escript" "$ROOTDIR/$scriptpath" $@
}

# Private. Load target cookie, either from vm.args or $HOME/.cookie
_load_cookie() {
    COOKIE_ARG="$(grep '^-setcookie' "$VMARGS_PATH" || true)"
    DEFAULT_COOKIE_FILE="$HOME/.erlang.cookie"
    if [ -z "$COOKIE_ARG" ]; then
        if [ -f "$DEFAULT_COOKIE_FILE" ]; then
            COOKIE="$(cat "$DEFAULT_COOKIE_FILE")"
        fi
    else
        # Extract cookie name from COOKIE_ARG
        COOKIE="$(echo "$COOKIE_ARG" | awk '{print $2}')"
    fi
}

# Private. Ensure that cookie is set.
_check_cookie() {
    # Attempt reloading cookie in case it has been set in a hook
    if [ -z "$COOKIE" ]; then
        _load_cookie
    fi
    # Die if cookie is still not set, as connecting via distribution will fail
    if [ -z "$COOKIE" ]; then
        printf "a secret cookie must be provided in one of the following ways:\n  - with vm.args using the -setcookie parameter,\n  or\n  by writing the cookie to '$DEFAULT_COOKIE_FILE', with permissions set to 0400"
        exit 1
    fi
}

# Private. For determining the version of ERTS available to the release
_detect_erts_vsn() {
    "$BINDIR/erl" -boot start_clean -eval 'Ver = erlang:system_info(version), io:format("~s~n", [Ver]), halt()' -noshell
}

# Private. Gets a list of code paths for this release
_get_code_paths() {
    "$ERTS_DIR/bin/escript" "$ROOTDIR/bin/release_utils.escript" "get_code_paths" "$ROOTDIR" "$ERTS_DIR" "$REL_NAME" "$REL_VSN"
}

# Private. For setting ERTS_DIR and ROOTDIR
_find_erts_dir() {
    __erts_dir="$RELEASE_ROOT_DIR/erts-$ERTS_VSN"
    if [ -d "$__erts_dir" ]; then
        ERTS_DIR="$__erts_dir";
        ROOTDIR="$RELEASE_ROOT_DIR"
    else
        set +e # temporarily disable so we can display a nice error message to the user
        __erl="$(which erl)"
        set -e
        if [ -z "${__erl}"  ]; then
          echo 'erlang runtime not found. If erlang is installed check your $PATH. aborting.'
          exit 1
        fi
        code="io:format(\"~s\", [code:root_dir()]), halt()."
        __erl_root="$("$__erl" -noshell -eval "$code")"
        ERTS_DIR="$__erl_root/erts-$ERTS_VSN"
        ROOTDIR="$__erl_root"
    fi
}

# Private. Open a shell on a remote node
_rem_sh() {
    # Generate a unique id used to allow multiple remsh to the same node
    # transparently
    id="remsh$(gen_id)-${NAME}"

    # Get the node's ticktime so that we use the same thing.
    TICKTIME="$(nodetool rpcterms net_kernel get_net_ticktime)"

    # Setup remote shell command to control node
    if [ ! -z "$USE_ERL_SHELL" ]; then
        exec "$BINDIR/erl" \
            -hidden \
            -boot start_clean -boot_var ERTS_LIB_DIR "$ERTS_LIB_DIR" \
            -kernel net_ticktime $TICKTIME \
            "$NAME_TYPE" "$id" -remsh "$NAME" -setcookie "$COOKIE"
    else
        __code_paths=$(_get_code_paths)
        exec "$BINDIR/erl" \
            -pa "$CONSOLIDATED_DIR" \
            ${__code_paths} \
            -hidden -noshell \
            -boot start_clean -boot_var ERTS_LIB_DIR "$ERTS_LIB_DIR" \
            -kernel net_ticktime $TICKTIME \
            -user Elixir.IEx.CLI "$NAME_TYPE" "$id" -setcookie "$COOKIE" \
            -extra --no-halt +iex -"$NAME_TYPE" "$id" --cookie "$COOKIE" --remsh "$NAME"
    fi
}

# Output a start command for the last argument of run_erl
_start_command() {
    printf "exec \"%s\" \"%s\" -- %s %s" "$RELEASE_ROOT_DIR/bin/$REL_NAME" \
           "$START_OPTION" "${ARGS}" "${EXTRA_OPTS}"
}

# Do a textual replacement of ${VAR} occurances in $1 and pipe to $2
_replace_os_vars() {
    awk '
        function escape(s) {
            gsub(/'\&'/, "\\\\&", s);
            return s;
        }
        {
            while(match($0,"[$]{[^}]*}")) {
                var=substr($0,RSTART+2,RLENGTH-3);
                gsub("[$]{"var"}", escape(ENVIRON[var]))
            }
        }1' < "$1" > "$1.bak"
    mv -- "$1.bak" "$1"
}

# Private. Run hooks from directory.
_run_hooks_from_dir() {
   if [ -d "$1" ]; then
      find "$1" -name "[0-9a-zA-Z]*.sh" -type f -print0 | while read -d $'\0' s; do
            . "$s"
      done
   fi
}

# If TERM is not set, set it to xterm
if [ -z "$TERM" ]; then
    export TERM=xterm
fi

# If ERTS_VSN is not set, detect it
if [ -z "$ERTS_VSN" ]; then
    ERTS_VSN="$(_detect_erts_vsn)"
    echo "$ERTS_VSN $REL_VSN" > "$START_ERL_DATA"
fi

# Allow override of where to read configuration from
# By default it's RELEASE_ROOT_DIR
if [ -z "$RELEASE_CONFIG_DIR" ]; then
    RELEASE_CONFIG_DIR="$RELEASE_ROOT_DIR"
fi

# Make sure directories exist
mkdir -p "$RELEASE_MUTABLE_DIR"
echo "Files in this directory are regenerated frequently, edits will be lost" \
    > "$RELEASE_MUTABLE_DIR/WARNING_README"
mkdir -p "$RUNNER_LOG_DIR"

# Set VMARGS_PATH, the path to the vm.args file to use
# Use $RELEASE_CONFIG_DIR/vm.args if exists, otherwise releases/VSN/vm.args
if [ -z "$VMARGS_PATH" ]; then
    if [ -f "$RELEASE_CONFIG_DIR/vm.args" ]; then
        VMARGS_PATH="$RELEASE_CONFIG_DIR/vm.args"
    else
        VMARGS_PATH="$REL_DIR/vm.args"
    fi
fi
if [ "$VMARGS_PATH" != "$RELEASE_MUTABLE_DIR/vm.args" ]; then
    echo "#### Generated - edit/create $RELEASE_CONFIG_DIR/vm.args instead." \
        >  "$RELEASE_MUTABLE_DIR/vm.args"
    cat  "$VMARGS_PATH"                              \
        >> "$RELEASE_MUTABLE_DIR/vm.args"
    VMARGS_PATH=$RELEASE_MUTABLE_DIR/vm.args
fi
if [ $REPLACE_OS_VARS ]; then
    _replace_os_vars "$VMARGS_PATH"
fi

# Set SYS_CONFIG_PATH, the path to the sys.config file to use
# Use $RELEASE_CONFIG_DIR/sys.config if exists, otherwise releases/VSN/sys.config
if [ -z "$SYS_CONFIG_PATH" ]; then
    if [ -f "$RELEASE_CONFIG_DIR/sys.config" ]; then
        SYS_CONFIG_PATH="$RELEASE_CONFIG_DIR/sys.config"
    else
        SYS_CONFIG_PATH="$REL_DIR/sys.config"
    fi
fi
if [ "$SYS_CONFIG_PATH" != "$RELEASE_MUTABLE_DIR/sys.config" ]; then
    echo "%% Generated - edit/create $RELEASE_CONFIG_DIR/sys.config instead." \
        >  "$RELEASE_MUTABLE_DIR/sys.config"
    cat  "$SYS_CONFIG_PATH"                              \
        >> "$RELEASE_MUTABLE_DIR/sys.config"
    SYS_CONFIG_PATH=$RELEASE_MUTABLE_DIR/sys.config
fi
if [ $REPLACE_OS_VARS ]; then
    _replace_os_vars "$SYS_CONFIG_PATH"
fi

# Extract the target node name from node.args
# Should be `-sname somename` or `-name somename@somehost`
NAME_ARG=$(egrep '^-s?name' "$VMARGS_PATH" || true)
if [ -z "$NAME_ARG" ]; then
    echo "vm.args needs to have either -name or -sname parameter."
    exit 1
fi

# Extract the name type and name from the NAME_ARG for REMSH
# NAME_TYPE should be -name or -sname
NAME_TYPE="$(echo "$NAME_ARG" | awk '{print $1}')"
# NAME will be either `somename` or `somename@somehost`
NAME="$(echo "$NAME_ARG" | awk '{print $2}')"

# Where the pipe will be stored when using `start`
# This is used so you can attach the running application to the current
# shell using `attach`
PIPE_DIR="${PIPE_DIR:-$RELEASE_MUTABLE_DIR/erl_pipes/$NAME/}"

# Extract the target cookie
_load_cookie

_find_erts_dir
export ROOTDIR="$RELEASE_ROOT_DIR"
export BINDIR="$ERTS_DIR/bin"
export EMU="beam"
export PROGNAME="erl"
export LD_LIBRARY_PATH="$ERTS_DIR/lib:$LD_LIBRARY_PATH"
ERTS_LIB_DIR="$ERTS_DIR/../lib"
CONSOLIDATED_DIR="$ROOTDIR/lib/${REL_NAME}-${REL_VSN}/consolidated"

cd "$ROOTDIR"

# User can specify an sname without @hostname
# This will fail when creating remote shell
# So here we check for @ and add @hostname if missing
case $NAME in
    *@*)
        # Nothing to do
        ;;
    *)
        NAME=$NAME@$(gen_nodename)
        ;;
esac

# Check the first argument for instructions
case "$1" in
    start|start_boot)

        # Make sure there is not already a node running
        #RES=`$NODETOOL ping`
        #if [ "$RES" = "pong" ]; then
        #    echo "Node is already running!"
        #    exit 1
        #fi
        # Save this for later.
        CMD=$1
        case "$1" in
            start)
                shift
                START_OPTION="console"
                HEART_OPTION="start"
                ;;
            start_boot)
                shift
                START_OPTION="console_boot"
                HEART_OPTION="start_boot"
                ;;
        esac
        ARGS="$@"
        RUN_PARAM="$@"

        _check_cookie
        _run_hooks_from_dir "$PRE_START_HOOKS"

        # Set arguments for the heart command
        set -- "$SCRIPT_DIR/$REL_NAME" "$HEART_OPTION"
        [ "$RUN_PARAM" ] && set -- "$@" "$RUN_PARAM"

        # Export the HEART_COMMAND
        HEART_COMMAND="$RELEASE_ROOT_DIR/bin/$REL_NAME $CMD"
        export HEART_COMMAND

        mkdir -p "$PIPE_DIR"

        "$BINDIR/run_erl" -daemon "$PIPE_DIR" "$RUNNER_LOG_DIR" \
                          "$(_start_command)"

        _run_hooks_from_dir "$POST_START_HOOKS"
        ;;

    stop)
        _check_cookie
        _run_hooks_from_dir "$PRE_STOP_HOOKS"
        # Wait for the node to completely stop...
        PID="$(get_pid)"
        if ! nodetool "stop"; then
            exit 1
        fi
        while $(kill -s 0 "$PID" 2>/dev/null);
        do
            sleep 1
        done
        _run_hooks_from_dir "$POST_STOP_HOOKS"
        ;;

    restart)
        _check_cookie
        _run_hooks_from_dir "$PRE_START_HOOKS"
        ## Restart the VM without exiting the process
        if ! nodetool "restart"; then
            exit 1
        fi
        ;;

    reboot)
        _check_cookie
        _run_hooks_from_dir "$PRE_START_HOOKS"
        ## Restart the VM completely (uses heart to restart it)
        if ! nodetool "reboot"; then
            exit 1
        fi
        ;;

    pid)
        ## Get the VM's pid
        _check_cookie
        if ! get_pid; then
            exit 1
        fi
        ;;

    ping)
        _check_cookie
        ## See if the VM is alive
        if ! nodetool "ping"; then
            exit 1
        fi
        ;;

    escript)
        _check_cookie
        ## Run an escript under the node's environment
        if ! escript $@; then
            exit 1
        fi
        ;;

    attach)
        _check_cookie

        # Make sure a node IS running
        if ! nodetool "ping" > /dev/null; then
            echo "Node is not running!"
            exit 1
        fi

        shift
        exec "$BINDIR/to_erl" "$PIPE_DIR"
        ;;

    remote_console)
        _check_cookie

        # Make sure a node IS running
        if ! nodetool "ping" > /dev/null; then
            echo "Node is not running!"
            exit 1
        fi

        shift
        _rem_sh
        ;;

    upgrade|downgrade|install)
        if [ -z "$2" ]; then
            echo "Missing package argument"
            echo "Usage: $REL_NAME $1 {package base name}"
            echo "NOTE {package base name} MUST NOT include the .tar.gz suffix"
            exit 1
        fi

        _check_cookie

        # Make sure a node IS running
        if ! nodetool "ping" > /dev/null; then
            echo "Node is not running!"
            exit 1
        fi

        exec "$BINDIR/escript" "$ROOTDIR/bin/release_utils.escript" \
             "install_release" "$REL_NAME" "$NAME_TYPE"  "$NAME" "$COOKIE" "$2"
        ;;

    unpack)
        if [ -z "$2" ]; then
            echo "Missing package argument"
            echo "Usage: $REL_NAME $1 {package base name}"
            echo "NOTE {package base name} MUST NOT include the .tar.gz suffix"
            exit 1
        fi

        _check_cookie

        # Make sure a node IS running
        if ! nodetool "ping" > /dev/null; then
            echo "Node is not running!"
            exit 1
        fi

        exec "$BINDIR/escript" "$ROOTDIR/bin/release_utils.escript" \
             "unpack_release" "$REL_NAME" "$NAME_TYPE" "$NAME" "$COOKIE" "$2"
        ;;

    console|console_clean|console_boot)
        # .boot file typically just $REL_NAME (ie, the app name)
        # however, for debugging, sometimes start_clean.boot is useful.
        # For e.g. 'setup', one may even want to name another boot script.
        case "$1" in
            console)
                if [ -f "$REL_DIR/$REL_NAME.boot" ]; then
                  BOOTFILE="$REL_DIR/$REL_NAME"
                else
                  BOOTFILE="$REL_DIR/start"
                fi
                ;;
            console_clean)
                BOOTFILE="$ROOTDIR/bin/start_clean"
                __code_paths=$(_get_code_paths)
                EXTRA_CODE_PATHS=${_code_paths}
                ;;
            console_boot)
                shift
                BOOTFILE="$1"
                shift
                ;;
        esac
        # Setup beam-required vars
        EMU="beam"
        PROGNAME="${0#*/}"

        export EMU
        export PROGNAME

        # Store passed arguments since they will be erased by `set`
        ARGS="$@"

        _check_cookie

        # Start the VM, executing pre_start hook along
        # the way. We can't run the post_start hook because
        # the console will crash with no TTY attached
        _run_hooks_from_dir "$PRE_START_HOOKS"

        # Build an array of arguments to pass to exec later on
        # Build it here because this command will be used for logging.
        set -- "$BINDIR/erlexec" \
            -boot "$BOOTFILE" \
            -boot_var ERTS_LIB_DIR "$ERTS_LIB_DIR" \
            -env ERL_LIBS "$REL_LIB_DIR" \
            -pa "$CONSOLIDATED_DIR" \
            ${EXTRA_CODE_PATHS} \
            -args_file "$VMARGS_PATH" \
            -config "$SYS_CONFIG_PATH" \
            -mode "$CODE_LOADING_MODE" \
            ${ERL_OPTS} \
            -user Elixir.IEx.CLI \
            -extra --no-halt +iex

        # Dump environment info for logging purposes
        if [ ! -z $VERBOSE ]; then
            echo "Exec: $@" -- ${1+$ARGS} ${EXTRA_OPTS}
            echo "Root: $ROOTDIR"

            # Log the startup
            echo "$RELEASE_ROOT_DIR"
        fi;

        logger -t "$REL_NAME[$$]" "Starting up"

        exec "$@" -- ${1+$ARGS} ${EXTRA_OPTS}
        ;;

    foreground)
        # start up the release in the foreground for use by runit
        # or other supervision services

        [ -f "$REL_DIR/$REL_NAME.boot" ] && BOOTFILE="$REL_NAME" || BOOTFILE=start
        FOREGROUNDOPTIONS="-noshell -noinput +Bd"

        # Setup beam-required vars
        EMU=beam
        PROGNAME="${0#*/}"

        export EMU
        export PROGNAME

        # Store passed arguments since they will be erased by `set`
        ARGS="$@"

        _check_cookie

        # Start the VM, executing pre and post start hooks
        _run_hooks_from_dir "$PRE_START_HOOKS"

        # Build an array of arguments to pass to exec later on
        # Build it here because this command will be used for logging.
        set -- "$BINDIR/erlexec" $FOREGROUNDOPTIONS \
            -boot "$REL_DIR/$BOOTFILE" \
            -boot_var ERTS_LIB_DIR "$ERTS_LIB_DIR" \
            -env ERL_LIBS "$REL_LIB_DIR" \
            -pa "$CONSOLIDATED_DIR" \
            -args_file "$VMARGS_PATH" \
            -config "$SYS_CONFIG_PATH" \
            -mode "$CODE_LOADING_MODE" \
            ${ERL_OPTS} \
            -extra ${EXTRA_OPTS}

        # Dump environment info for logging purposes
        if [ ! -z $VERBOSE ]; then
            echo "Exec: $@" -- ${1+$ARGS}
            echo "Root: $ROOTDIR"
        fi;

        "$@" -- ${1+$ARGS} &
        __bg_pid=$!
        _run_hooks_from_dir "$POST_START_HOOKS"
        wait $__bg_pid
        ;;
    rpc)
        _check_cookie

        # Make sure a node IS running
        if ! nodetool "ping" > /dev/null; then
            echo "Node is not running!"
            exit 1
        fi

        shift

        nodetool rpc $@
        ;;
    rpcterms)
        _check_cookie

        # Make sure a node IS running
        if ! nodetool "ping" > /dev/null; then
            echo "Node is not running!"
            exit 1
        fi

        shift

        nodetool rpcterms $@
        ;;
    eval)
        _check_cookie

        # Make sure a node IS running
        if ! nodetool "ping" > /dev/null; then
            echo "Node is not running!"
            exit 1
        fi

        shift
        nodetool "eval" $@
        ;;
    command)
        # Execute as command-line utility
        #
        # Like the escript command, this does not start the OTP application.
        # If your command depends on a running OTP application,
        # use
        #
        #     {:ok, _} = Application.ensure_all_started(:your_app)

        _check_cookie

        shift
        MODULE="$1"; shift
        FUNCTION="$1"; shift

        # Save extra arguments
        ARGS="$@"

        # Build arguments for erlexec
        __code_paths=$(_get_code_paths)
        set -- "$ERL_OPTS"
        [ "$SYS_CONFIG_PATH" ] && set -- "$@" -config "$SYS_CONFIG_PATH"
        set -- "$@" -boot_var ERTS_LIB_DIR "$ERTS_LIB_DIR"
        set -- "$@" -noshell
        set -- "$@" -boot start_clean
        set -- "$@" -pa "$CONSOLIDATED_DIR"
        set -- "$@" ${__code_paths}
        set -- "$@" -s "$MODULE" "$FUNCTION"

        $BINDIR/erlexec $@ -extra $ARGS
        exit "$?"
        ;;
    *)
        __command_path="$REL_DIR/commands/$1"
        if [ -f "$__command_path" ]; then
            _check_cookie
            . "$__command_path"
        else
            echo "Usage: $REL_NAME {start|start_boot <file>|foreground|stop|restart|reboot|pid|ping|console|console_clean|console_boot <file>|attach|remote_console|upgrade|downgrade|install|escript|rpc|rpcterms|eval|command <module> <function> <args>}"
            exit 1
        fi
        ;;
esac

exit 0


catch non existing dates in show view

catch non existing dates in show view

Show podcast

Title

<%= @podcast.title %>

Website

<%= @podcast.website %>

Description

<%= @podcast.description %>

Summary

<%= @podcast.summary %>

Image title

<%= @podcast.image_title %>

Image url

<%= @podcast.image_url %>

Last build date

<%= @podcast.last_build_date && Timex.format!(@podcast.last_build_date, "{ISOdate} {h24}:{m}:{s}") %>

Updated at

<%= @podcast.updated_at && Timex.format!(@podcast.updated_at, "{ISOdate} {h24}:{m}:{s}") %>

Update intervall

<%= @podcast.update_intervall %>

Next update

<%= @podcast.next_update && Timex.format!(@podcast.next_update, "{ISOdate} {h24}:{m}:{s}") %>

Inserted at

<%= @podcast.inserted_at && Timex.format!(@podcast.inserted_at, "{ISOdate} {h24}:{m}:{s}") %>

Payment link title

<%= @podcast.payment_link_title %>

Payment link url

<%= @podcast.payment_link_url %>

Blocked

<%= @podcast.blocked %>

Contributors

  <%= for owner <- @podcast.contributors do %>

<%= owner.name %>

  <% end %>

Explicit

<%= @podcast.explicit %>

Unique identifier

<%= @podcast.unique_identifier %>

Update paused

<%= @podcast.update_paused %>

Failure count

<%= @podcast.failure_count %>

Retired

<%= @podcast.retired %>

Episodes count

<%= @podcast.episodes_count %>

Followers count

<%= @podcast.followers_count %>

Likes_count

<%= @podcast.likes_count %>

Subscriptions_count

<%= @podcast.subscriptions_count %>

Latest episode publishing date

<%= @podcast.latest_episode_publishing_date && Timex.format!(@podcast.latest_episode_publishing_date, "{ISOdate} {h24}:{m}:{s}") %>

Publication frequency

<%= if @podcast.publication_frequency, do: Float.round(@podcast.publication_frequency, 2) %> days

Manually updated at

<%= @podcast.manually_updated_at && Timex.format!(@podcast.manually_updated_at, "{ISOdate} {h24}:{m}:{s}") %>

Indexed by Elatsicsearch

<%= @podcast.elastic %>

Last error message

<%= @podcast.last_error_message %>

Last error occured

<%= @podcast.last_error_occured && Timex.format!(@podcast.last_error_occured, "{ISOdate} {h24}:{m}:{s}") %>

<%= link "Show", to: podcast_path(@conn, :show, @podcast) %>

Show podcast

Title

<%= @podcast.title %>

Website

<%= @podcast.website %>

Description

<%= @podcast.description %>

Summary

<%= @podcast.summary %>

Image title

<%= @podcast.image_title %>

Image url

<%= @podcast.image_url %>

Last build date

<%= Timex.format!(@podcast.last_build_date, "{ISOdate} {h24}:{m}:{s}") %>

Updated at

<%= Timex.format!(@podcast.updated_at, "{ISOdate} {h24}:{m}:{s}") %>

Update intervall

<%= @podcast.update_intervall %>

Next update

<%= Timex.format!(@podcast.next_update, "{ISOdate} {h24}:{m}:{s}") %>

Inserted at

<%= Timex.format!(@podcast.inserted_at, "{ISOdate} {h24}:{m}:{s}") %>

Payment link title

<%= @podcast.payment_link_title %>

Payment link url

<%= @podcast.payment_link_url %>

Blocked

<%= @podcast.blocked %>

Contributors

  <%= for owner <- @podcast.contributors do %>

<%= owner.name %>

  <% end %>

Explicit

<%= @podcast.explicit %>

Unique identifier

<%= @podcast.unique_identifier %>

Update paused

<%= @podcast.update_paused %>

Failure count

<%= @podcast.failure_count %>

Retired

<%= @podcast.retired %>

Episodes count

<%= @podcast.episodes_count %>

Followers count

<%= @podcast.followers_count %>

Likes_count

<%= @podcast.likes_count %>

Subscriptions_count

<%= @podcast.subscriptions_count %>

Latest episode publishing date

<%= Timex.format!(@podcast.latest_episode_publishing_date, "{ISOdate} {h24}:{m}:{s}") %>

Publication frequency

<%= if @podcast.publication_frequency, do: Float.round(@podcast.publication_frequency, 2) %> days

Manually updated at

<%= Timex.format!(@podcast.manually_updated_at, "{ISOdate} {h24}:{m}:{s}") %>

Indexed by Elatsicsearch

<%= @podcast.elastic %>

Last error message

<%= @podcast.last_error_message %>

Last error occured

<%= Timex.format!(@podcast.last_error_occured, "{ISOdate} {h24}:{m}:{s}") %>

<%= link "Show", to: podcast_path(@conn, :show, @podcast) %>


More styles for the admin sidebar

More styles for the admin sidebar

<!DOCTYPE html>

    <meta charset="utf-8"/>
    <meta http-equiv="X-UA-Compatible" content="IE=edge"/>
    <meta name="viewport" content="width=device-width, initial-scale=1.0"/>
    <title><%= gettext("ExVenture") %></title>
    <link rel="stylesheet" href="<%= Routes.static_path(@conn, "/css/app.css") %>"/>

    <meta property="og:site_name" content="<%= gettext("ExVenture") %>" />

    <%= if Map.has_key?(assigns, :open_graph_title) do %>
      <meta property="og:title" content="<%= @open_graph_title %>" />
    <% end %>

    <%= if Map.has_key?(assigns, :open_graph_description) do %>
      <meta property="og:description" content="<%= @open_graph_description %>" />
    <% end %>

    <%= if Map.has_key?(assigns, :open_graph_url) do %>
      <meta property="og:url" content="<%= @open_graph_url %>" />
    <% end %>

    <meta name="twitter:card" content="summary" />

        <%= link(to: Routes.admin_dashboard_path(@conn, :index), class: "flex-1 font-bold") do %>
          <%= img_tag(Routes.static_path(@conn, "/images/exventure.png"), class: "h-8 inline-block") %>
          ExVenture
        <% end %>

<%= @current_user.email %>

          <%= link(to: Routes.admin_dashboard_path(@conn, :index), class: tab_active?(@active_tab, :dashboard)) do %>
             Dashboard
          <% end %>
          <%= link(to: Routes.page_path(@conn, :index), target: "_blank") do %>
             Home Page »
          <% end %>
          System
          <%= link(to: Routes.admin_staged_changes_path(@conn, :index), class: tab_active?(@active_tab, :staged_changes)) do %>
             Staged Changes
          <% end %>
          The World
          <%= link(to: Routes.admin_zone_path(@conn, :index), class: tab_active?(@active_tab, :zones)) do %>
             Zones
          <% end %>

        <%= @inner_content %>

<!DOCTYPE html>

    <meta charset="utf-8"/>
    <meta http-equiv="X-UA-Compatible" content="IE=edge"/>
    <meta name="viewport" content="width=device-width, initial-scale=1.0"/>
    <title><%= gettext("ExVenture") %></title>
    <link rel="stylesheet" href="<%= Routes.static_path(@conn, "/css/app.css") %>"/>

    <meta property="og:site_name" content="<%= gettext("ExVenture") %>" />

    <%= if Map.has_key?(assigns, :open_graph_title) do %>
      <meta property="og:title" content="<%= @open_graph_title %>" />
    <% end %>

    <%= if Map.has_key?(assigns, :open_graph_description) do %>
      <meta property="og:description" content="<%= @open_graph_description %>" />
    <% end %>

    <%= if Map.has_key?(assigns, :open_graph_url) do %>
      <meta property="og:url" content="<%= @open_graph_url %>" />
    <% end %>

    <meta name="twitter:card" content="summary" />

        <%= link("ExVenture", to: Routes.admin_dashboard_path(@conn, :index), class: "ml-1 flex-1 font-bold") %>

<%= @current_user.email %>

          <%= link(to: Routes.admin_dashboard_path(@conn, :index), class: tab_active?(@active_tab, :dashboard)) do %>
             Dashboard
          <% end %>
          <%= link(to: Routes.admin_staged_changes_path(@conn, :index), class: tab_active?(@active_tab, :staged_changes)) do %>
             Staged Changes
          <% end %>
          <%= link(to: Routes.admin_zone_path(@conn, :index), class: tab_active?(@active_tab, :zones)) do %>
             Zones
          <% end %>

        <%= @inner_content %>


More configurator tweaks

More configurator tweaks

<meta name="viewport" content="width=device-width, initial-scale=1.0">

  <title> Configure your firmware </title>
  <link rel="stylesheet" href="/styles.css">

Configure your FarmBot

Firmware

      Your firmware is flashing.

      <form action=configure_firmware method=post>
        <fieldset>
          <select id="firmware_hardware" name="firmware_hardware">
            <option value="arduino"> Arduino/RAMPS (Genesis v1.2) </option>
            <option value="farmduino"> Farmduino (Genesis v1.3) </option>
          </select>
        </fieldset>

    <input type=submit value="next">

  </form>

<meta name="viewport" content="width=device-width, initial-scale=1.0">

  <title> Configure your firmware </title>
  <link rel="stylesheet" href="/styles.css">

Configure your FarmBot

Firmware

      Your firmware is flashing.

      <form action=configure_firmware method=post>
        <fieldset>
          <select name=firmware_hardware>
            <option value="arduino"> Arduino/RAMPS (Genesis v1.2) </option>
            <option value="farmduino"> Farmduino (Genesis v1.3) </option>
          </select>
        </fieldset>

    <input type=submit value="next">

  </form>


Add Plausible analytics

Add Plausible analytics

This doesn’t require cookies or track individual users across devices (or in any other way) and is therefore much more privacy-friendly.

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="utf-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <%= csrf_meta_tag() %>

    <%= live_title_tag assigns[:page_title] || title() %>

    <link rel="stylesheet" href="<%= Routes.static_path(@conn, "/css/app.css") %>"/>
    <script defer type="text/javascript" src="<%= Routes.static_path(@conn, "/js/app.js") %>"></script>

    <!-- Privacy-friendly analytics via Plausible -->
    <script async defer data-domain="jumu-weltweit.org" src="https://plausible.io/js/plausible.js"></script>

    <%= render "favicons.html" %>
  </head>
  <body>
    <%= @inner_content %>
  </body>
</html>

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="utf-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <%= csrf_meta_tag() %>

    <%= live_title_tag assigns[:page_title] || title() %>

    <link rel="stylesheet" href="<%= Routes.static_path(@conn, "/css/app.css") %>"/>
    <script defer type="text/javascript" src="<%= Routes.static_path(@conn, "/js/app.js") %>"></script>

    <%= render "favicons.html" %>
  </head>
  <body>
    <%= @inner_content %>
  </body>
</html>


Drop templates/auth/request

Drop templates/auth/request

<%= form_tag @callback_url, method: "post" do %>

    Email
    <input class='form-control' type='email' name='email' value='<%= @conn.params["email"] %>' />

    First Name
    <input class='form-control' type='text' name='first_name' value='<%= @conn.params["first_name"] %>' />

    Last Name
    <input class='form-control' type='text' name='last_name' value='<%= @conn.params["last_name"] %>' />

    Username
    <input class='form-control' type='text' name='username' value='<%= @conn.params["username"] %>' />

    Phone
    <input class='form-control' type='tel' name='phone' value='<%= @conn.params["phone"] %>' />

    Location
    <input class='form-control' type='text' name='location' value='<%= @conn.params["location"] %>' />

    Description
    <textarea class='form-control' name='description' value='<%= @conn.params["description"] %>'></textarea>

    Password
    <input class='form-control' type='password' name='password' />

    Confirm Password
    <input class='form-control' type='password' name='password_confirmation' />

    <button class='btn btn-primary'>Login</button>

<% end %>