From c731e15365150e7f6db75edc5fffc91787cac602 Mon Sep 17 00:00:00 2001
From: Leo Kacenjar
Date: Mon, 28 Jul 2025 07:56:15 -0600
Subject: [PATCH 1/4] Asap 204 change all file handling to requests (#239)
* Use requests library for both file downloads. Add errors to logs as well as API response.
* Locally mount code for inference container.
* Add some tests for file related issues.
* Install local module during github setup.
---
.github/workflows/python_components.yml | 1 +
docker-compose.yml | 2 +
python_components/ci/requirements.txt | 18 ++--
python_components/ci/scripts/entrypoint.sh | 1 +
.../document_inference/helpers.py | 16 +++-
.../document_inference/lambda_function.py | 1 +
.../tests/test_file_downloads.py | 89 +++++++++++++++++++
.../evaluation/evaluation/utility/document.py | 17 +++-
.../evaluation/lambda_function.py | 1 +
9 files changed, 134 insertions(+), 12 deletions(-)
create mode 100644 python_components/document_inference/tests/test_file_downloads.py
diff --git a/.github/workflows/python_components.yml b/.github/workflows/python_components.yml
index b97ba750..da4becc1 100644
--- a/.github/workflows/python_components.yml
+++ b/.github/workflows/python_components.yml
@@ -26,6 +26,7 @@ jobs:
python -m pip install --upgrade pip
if [ -f ./python_components/ci/requirements.txt ]; then pip install -r ./python_components/ci/requirements.txt; fi
pip install python_components/evaluation
+ pip install python_components/document_inference
python -m spacy download en_core_web_sm
- name: Run Linting
run: |
diff --git a/docker-compose.yml b/docker-compose.yml
index b6af5838..438d54fa 100644
--- a/docker-compose.yml
+++ b/docker-compose.yml
@@ -38,6 +38,8 @@ services:
- "9002:8080"
environment:
- ASAP_LOCAL_MODE=True
+ volumes:
+ - ./python_components/document_inference:/var/task
lambda_evaluation:
container_name: lambda_evaluation
build:
diff --git a/python_components/ci/requirements.txt b/python_components/ci/requirements.txt
index 49bc2a8e..198e37d8 100644
--- a/python_components/ci/requirements.txt
+++ b/python_components/ci/requirements.txt
@@ -2,23 +2,25 @@ backports.tarfile==1.2.0
beautifulsoup4==4.13.3
black==25.1.0
boto3==1.38.30
-deepeval @ git+https://github.com/codeforamerica/deepeval.git@fix/support-read-only-file-systems
+deepeval==3.1.4
flake8==7.1.2
google-api-python-client==2.171.0
google-auth-oauthlib==1.2.2
-importlib-metadata==8.0.0
+inflect==7.3.1
isort==6.0.1
+jaraco-functools==4.2.1
jaraco.collections==5.1.0
-requests-aws4auth==1.3.1
+llm==0.26
pandas==2.2.3
pip-chill==1.0.3
-PyMuPDF==1.25.5
-pytest==8.3.5
-pydantic==2.11.5
+pymupdf==1.25.5
+pypdf==5.8.0
+pysocks==1.7.1
+pytest-httpserver==1.1.3
+requests-aws4auth==1.3.1
scikit-learn==1.6.1
selenium==4.34.2
spacy==3.8.7
tldextract==5.1.3
tomli==2.0.1
-tqdm==4.67.1
-xgboost==2.1.4
+xgboost==2.1.4
\ No newline at end of file
diff --git a/python_components/ci/scripts/entrypoint.sh b/python_components/ci/scripts/entrypoint.sh
index 73624f00..f4ff6a3e 100755
--- a/python_components/ci/scripts/entrypoint.sh
+++ b/python_components/ci/scripts/entrypoint.sh
@@ -3,6 +3,7 @@ set -e
# Install any local packages.
pip install python_components/evaluation
+pip install python_components/document_inference
# Execute the main command
exec "$@"
\ No newline at end of file
diff --git a/python_components/document_inference/document_inference/helpers.py b/python_components/document_inference/document_inference/helpers.py
index 548e28b1..5fc15f02 100644
--- a/python_components/document_inference/document_inference/helpers.py
+++ b/python_components/document_inference/document_inference/helpers.py
@@ -1,7 +1,7 @@
import json
import logging
import os
-import urllib
+import shutil
import boto3
import fitz
@@ -49,7 +49,19 @@ def get_secret(secret_name: str, local_mode: bool) -> str:
def get_file(url: str, output_path: str) -> str:
file_name = os.path.basename(url)
local_path = f"{output_path}/{file_name}"
- urllib.request.urlretrieve(url, local_path)
+ headers = {
+ "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36",
+ "Accept": "application/pdf,application/octet-stream,*/*",
+ "Accept-Language": "en-US,en;q=0.9",
+ "Accept-Encoding": "gzip, deflate",
+ "DNT": "1",
+ "Connection": "keep-alive",
+ "Upgrade-Insecure-Requests": "1",
+ }
+ with requests.get(url, headers=headers, stream=True) as response:
+ response.raise_for_status()
+ with open(f"{output_path}/{file_name}", "wb") as file:
+ shutil.copyfileobj(response.raw, file)
return local_path
diff --git a/python_components/document_inference/lambda_function.py b/python_components/document_inference/lambda_function.py
index 2affcfb0..ef097be3 100644
--- a/python_components/document_inference/lambda_function.py
+++ b/python_components/document_inference/lambda_function.py
@@ -76,4 +76,5 @@ def handler(event, context):
else:
return {"statusCode": 200, "body": helpers.json_dump_collection()}
except Exception as e:
+ helpers.logger.error(f"Error during execution: {e}")
return {"statusCode": 500, "body": str(e)}
diff --git a/python_components/document_inference/tests/test_file_downloads.py b/python_components/document_inference/tests/test_file_downloads.py
new file mode 100644
index 00000000..252830de
--- /dev/null
+++ b/python_components/document_inference/tests/test_file_downloads.py
@@ -0,0 +1,89 @@
+import os
+
+from document_inference.helpers import get_file
+from pytest_httpserver import HTTPServer
+from werkzeug import Request, Response
+
+"""
+Tests to assert that our file getting method works with some known curveballs.
+"""
+
+
+def test_default_behavior(httpserver: HTTPServer):
+ def handler(request: Request):
+ return Response("Plain pdf content!", 200)
+
+ httpserver.expect_request("/test.pdf").respond_with_handler(handler)
+ _remove_file_if_exists("/tmp/test.pdf")
+ get_file(httpserver.url_for("/test.pdf"), "/tmp")
+ _assert_file_contents("/tmp/test.pdf", "Plain pdf content!")
+
+
+def test_content_disposition(httpserver: HTTPServer):
+ def handler(request: Request):
+ return Response(
+ "Great pdf content!",
+ 200,
+ headers={"Content-Disposition": "attachment", "filename": "mypdf.pdf"},
+ )
+
+ httpserver.expect_request("/test.pdf").respond_with_handler(handler)
+ _remove_file_if_exists("/tmp/test.pdf")
+ get_file(httpserver.url_for("/test.pdf"), "/tmp")
+ _assert_file_contents("/tmp/test.pdf", "Great pdf content!")
+
+
+def test_header_assertion(httpserver: HTTPServer):
+ def handler(request: Request):
+ try:
+ headers_as_text = str(request.headers)
+ assert (
+ "python" not in headers_as_text
+ ), f"Headers contain 'python': {headers_as_text}"
+ assert (
+ "urllib" not in headers_as_text
+ ), f"Headers contain 'urllib': {headers_as_text}"
+ assert (
+ "Mozilla" in headers_as_text
+ ), f"Headers missing 'Mozilla': {headers_as_text}"
+ return Response("Great pdf validated by headers content!", 200)
+ except AssertionError as e:
+ print(f"Assertion failed: {e}")
+ return Response(f"Assertion failed: {e}", 500)
+
+ httpserver.expect_request("/test.pdf").respond_with_handler(handler)
+ _remove_file_if_exists("/tmp/test.pdf")
+ get_file(httpserver.url_for("/test.pdf"), "/tmp")
+ _assert_file_contents("/tmp/test.pdf", "Great pdf validated by headers content!")
+
+
+def test_308_redirect(httpserver: HTTPServer):
+ def redirect_handler(request: Request):
+ return Response(
+ "",
+ status=308,
+ headers={
+ "Location": httpserver.url_for("/redirected.pdf"),
+ "Cache-Control": "max-age=3600",
+ },
+ )
+
+ def final_handler(request: Request):
+ return Response("Great redirected content!", 200)
+
+ httpserver.expect_request("/original.pdf").respond_with_handler(redirect_handler)
+ httpserver.expect_request("/redirected.pdf").respond_with_handler(final_handler)
+ _remove_file_if_exists("/tmp/original.pdf")
+ get_file(httpserver.url_for("/original.pdf"), "/tmp")
+ _assert_file_contents("/tmp/original.pdf", "Great redirected content!")
+
+
+def _remove_file_if_exists(path: str):
+ if os.path.exists(path):
+ os.remove(path)
+ assert not os.path.exists(path)
+
+
+def _assert_file_contents(path: str, contents: str):
+ with open(path, "r") as f:
+ assert f.read() == contents
diff --git a/python_components/evaluation/evaluation/utility/document.py b/python_components/evaluation/evaluation/utility/document.py
index cd4abb49..116795ae 100644
--- a/python_components/evaluation/evaluation/utility/document.py
+++ b/python_components/evaluation/evaluation/utility/document.py
@@ -1,6 +1,6 @@
import datetime
import os
-import urllib
+import shutil
from abc import ABC, abstractmethod
from pathlib import Path
from typing import Any, List
@@ -8,6 +8,7 @@
import boto3
import fitz
import pandas as pd
+import requests
from deepeval.models import DeepEvalBaseMLLM
from deepeval.test_case import MLLMImage
from evaluation.utility.helpers import logger
@@ -81,7 +82,19 @@ def add_images_to_document(
def get_file(url: str, output_path: str) -> str:
file_name = os.path.basename(url)
local_path = f"{output_path}/{file_name}"
- urllib.request.urlretrieve(url, local_path)
+ headers = {
+ "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36",
+ "Accept": "application/pdf,application/octet-stream,*/*",
+ "Accept-Language": "en-US,en;q=0.9",
+ "Accept-Encoding": "gzip, deflate",
+ "DNT": "1",
+ "Connection": "keep-alive",
+ "Upgrade-Insecure-Requests": "1",
+ }
+ with requests.get(url, headers=headers, stream=True) as response:
+ response.raise_for_status()
+ with open(f"{output_path}/{file_name}", "wb") as file:
+ shutil.copyfileobj(response.raw, file)
return local_path
diff --git a/python_components/evaluation/lambda_function.py b/python_components/evaluation/lambda_function.py
index 7e0964cd..dcc791a2 100644
--- a/python_components/evaluation/lambda_function.py
+++ b/python_components/evaluation/lambda_function.py
@@ -107,4 +107,5 @@ def handler(event, context):
output = str(e)
if local_mode:
output = traceback.format_exc()
+ utility.helpers.logger.error(f"Error during execution: {output}")
return {"statusCode": 500, "body": output}
From 0caf1681a0dd36dd7c46be6a5365569475766bd8 Mon Sep 17 00:00:00 2001
From: Leo Kacenjar
Date: Mon, 28 Jul 2025 12:08:36 -0600
Subject: [PATCH 2/4] Asap 173 admin user UI (#222)
* Devise working for login.
* Migrate away from custom session table.
* Use database for session management.
* Bring back the tests and fix API authentication.
* Get inline validation going.
* Return success flash, update tests and lint away.
* Remove obsolete test.
* Fix accessibility scan.
* Theme user profile form. Rename is_admin to is_site_admin. Add is_user_admin.
* Start working on admin list.
* Remove development creds and add to gitignore.
* Fix linting.
* Admin views for list, add and edit with validation.
* Add admin fields and site picker.
* Clean up admin table.
* Block admin pages for non-admins.
* Add a test.
* Fix linting.
* Fix broken tests.
* Improve mobile experience.
* Try braking up tests into two batches.
* Make password labels and validation more clear.
* Improve current password language.
---
.github/workflows/ci.yml | 10 +-
app/controllers/admin/users_controller.rb | 68 ++++++++++++++
app/controllers/concerns/access.rb | 14 ++-
app/controllers/configurations_controller.rb | 2 +-
app/controllers/sites_controller.rb | 2 +-
.../users/registrations_controller.rb | 1 -
.../controllers/modal_controller.js | 4 +-
app/views/admin/users/edit.html.erb | 82 ++++++++++++++++
app/views/admin/users/index.html.erb | 41 ++++++++
app/views/admin/users/new.html.erb | 67 +++++++++++++
app/views/layouts/application.html.erb | 10 +-
app/views/sites/index.html.erb | 4 +-
app/views/users/registrations/edit.html.erb | 94 ++++++++++++-------
app/views/users/sessions/new.html.erb | 4 +-
config/locales/en.yml | 37 ++------
config/routes.rb | 4 +-
.../20250711141619_change_admin_roles.rb | 8 ++
lib/tasks/users.rake | 3 +-
spec/factories/users.rb | 4 +-
spec/features/admin_spec.rb | 64 ++++++++++++-
spec/features/site_spec.rb | 2 +-
spec/requests/api/sites_spec.rb | 2 +-
22 files changed, 436 insertions(+), 91 deletions(-)
create mode 100644 app/controllers/admin/users_controller.rb
create mode 100644 app/views/admin/users/edit.html.erb
create mode 100644 app/views/admin/users/index.html.erb
create mode 100644 app/views/admin/users/new.html.erb
create mode 100644 db/migrate/20250711141619_change_admin_roles.rb
diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index 95347033..c0872fcf 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -52,11 +52,17 @@ jobs:
bundle exec rails db:create
bundle exec rails db:migrate
- - name: Run tests
+ - name: Run Non-JS tests
env:
RAILS_ENV: test
DATABASE_URL: postgres://postgres:postgres@localhost:5432/access_pdf_test
- run: bundle exec rspec
+ run: bundle exec rspec spec/models spec/requests
+
+ - name: Run JS tests
+ env:
+ RAILS_ENV: test
+ DATABASE_URL: postgres://postgres:postgres@localhost:5432/access_pdf_test
+ run: bundle exec rspec spec/features
- name: Keep screenshots from failed system tests
uses: actions/upload-artifact@v4
diff --git a/app/controllers/admin/users_controller.rb b/app/controllers/admin/users_controller.rb
new file mode 100644
index 00000000..33d10023
--- /dev/null
+++ b/app/controllers/admin/users_controller.rb
@@ -0,0 +1,68 @@
+class Admin::UsersController < ApplicationController
+ include Access
+
+ before_action :ensure_user_user_admin
+
+ before_action :set_user, only: [:new, :edit, :update]
+ before_action :site_list, only: [:new, :create, :edit, :update]
+ before_action :set_minimum_password_length, only: [:new, :edit, :update]
+
+ def index
+ @users = User.all
+ end
+
+ def new
+ render "/admin/users/new"
+ end
+
+ def create
+ @user = User.new(user_params)
+ if @user.save
+ redirect_to admin_users_path, notice: "User added successfully"
+ else
+ render :new, status: 422
+ end
+ end
+
+ def edit
+ render "/admin/users/edit"
+ end
+
+ def update
+ if params[:user][:password].blank?
+ params[:user].delete(:password)
+ params[:user].delete(:password_confirmation)
+ success = @user.update_without_password(user_params)
+ elsif @user.id == current_user.id
+ bypass_sign_in @user, scope: "user"
+ success = @user.update_with_password(user_params)
+ else
+ success = @user.update(user_params)
+ end
+ if success
+ redirect_to admin_users_path, notice: "User updated successfully"
+ else
+ render :edit, status: 422
+ end
+ end
+
+ private
+
+ def site_list
+ @sites = Site.all.order(:location, :name).group_by(&:location).map do |location, sites|
+ [location, sites.map { |site| [site.name, site.id] }]
+ end
+ end
+
+ def set_user
+ @user = params[:id].present? ? User.find(params[:id]) : User.new
+ end
+
+ def user_params
+ params.require(:user).permit(:email, :password, :password_confirmation, :current_password, :is_site_admin, :is_user_admin, :site_id)
+ end
+
+ def set_minimum_password_length
+ @minimum_password_length = User.password_length.min
+ end
+end
diff --git a/app/controllers/concerns/access.rb b/app/controllers/concerns/access.rb
index 07554a99..cef179ea 100644
--- a/app/controllers/concerns/access.rb
+++ b/app/controllers/concerns/access.rb
@@ -1,18 +1,24 @@
module Access
- def ensure_user_admin
- unless current_user.is_admin?
+ def ensure_user_site_admin
+ unless current_user.is_site_admin?
+ redirect_to sites_path, alert: "You don't have permission to access that page."
+ end
+ end
+
+ def ensure_user_user_admin
+ unless current_user.is_user_admin?
redirect_to sites_path, alert: "You don't have permission to access that page."
end
end
def ensure_user_site_access
- if !current_user.is_admin? && (current_user.site.nil? || current_user.site.id != @site.id)
+ if !current_user.is_site_admin? && (current_user.site.nil? || current_user.site.id != @site.id)
redirect_to sites_path, alert: "You don't have permission to access that site."
end
end
def ensure_user_document_access
- if !current_user.is_admin? && (current_user.site.nil? || current_user.site.documents.find(@document.id).nil?)
+ if !current_user.is_site_admin? && (current_user.site.nil? || current_user.site.documents.find(@document.id).nil?)
redirect_to sites_path, alert: "You don't have permission to perform that action on document."
end
end
diff --git a/app/controllers/configurations_controller.rb b/app/controllers/configurations_controller.rb
index a516be67..bf13c4c5 100644
--- a/app/controllers/configurations_controller.rb
+++ b/app/controllers/configurations_controller.rb
@@ -1,7 +1,7 @@
class ConfigurationsController < AuthenticatedController
include Access
- before_action :ensure_user_admin
+ before_action :ensure_user_site_admin
ASAP_API_USER = "/asap-pdf/production/RAILS_API_USER-20250613220933079900000001"
ASAP_API_PASSWORD = "/asap-pdf/production/RAILS_API_PASSWORD-20250613220933080000000003"
diff --git a/app/controllers/sites_controller.rb b/app/controllers/sites_controller.rb
index 8aa19d16..fde11e78 100644
--- a/app/controllers/sites_controller.rb
+++ b/app/controllers/sites_controller.rb
@@ -6,7 +6,7 @@ class SitesController < AuthenticatedController
before_action :ensure_user_site_access, only: [:insights, :show, :edit, :update, :destroy]
def index
- @sites = if current_user.is_admin?
+ @sites = if current_user.is_site_admin?
Site.all
else
current_user.site.nil? ? [] : [current_user.site]
diff --git a/app/controllers/users/registrations_controller.rb b/app/controllers/users/registrations_controller.rb
index c266662e..b9e664fe 100644
--- a/app/controllers/users/registrations_controller.rb
+++ b/app/controllers/users/registrations_controller.rb
@@ -1,7 +1,6 @@
# frozen_string_literal: true
class Users::RegistrationsController < Devise::RegistrationsController
- layout "centered"
# before_action :configure_sign_up_params, only: [:create]
# before_action :configure_account_update_params, only: [:update]
diff --git a/app/javascript/controllers/modal_controller.js b/app/javascript/controllers/modal_controller.js
index 8304a8d3..37554b8f 100644
--- a/app/javascript/controllers/modal_controller.js
+++ b/app/javascript/controllers/modal_controller.js
@@ -5,7 +5,9 @@ export default class extends Controller {
connect() {
super.connect();
- this.wrapperTarget.addEventListener('close', this.onModalClose.bind(this))
+ if (this.hasWrapperTarget) {
+ this.wrapperTarget.addEventListener('close', this.onModalClose.bind(this))
+ }
}
submitAndClose(event) {
diff --git a/app/views/admin/users/edit.html.erb b/app/views/admin/users/edit.html.erb
new file mode 100644
index 00000000..607a53bb
--- /dev/null
+++ b/app/views/admin/users/edit.html.erb
@@ -0,0 +1,82 @@
+
+
+
Edit <%= @user.email %>
+
+ <%= form_with(model: [:admin, @user]) do |f| %>
+
+
+
+ <% if @user.id == current_user.id %>
+
+ <% end %>
+
+
+
+
+
+
+ <%= f.check_box :is_site_admin %>
+ <%= f.label :is_site_admin, class: "label-text text-black mb-2 font-semibold" %>
+
User should be able to see and edit all sites and documents.
+
+
+
+ <%= f.check_box :is_user_admin %>
+ <%= f.label :is_user_admin, class: "label-text text-black mb-2 font-semibold" %>
+
User should be able to edit other users and create admins.
+
+
+
+ <%= f.label :site_id, "Site", class: "label-text text-black mb-2 font-semibold" %>
+ <%= f.select :site_id, grouped_options_for_select(@sites, @user.site_id),
+ { include_blank: "None" },
+ { class: "input input-bordered w-full select" } %>
+
+
+
+ <%= button_tag type: "submit", class: "btn btn-primary text-white", id: "submit-user-form" do %>
+
+ Update
+ <% end %>
+ <%= link_to "Back", :back %>
+
+ <% end %>
+
+
diff --git a/app/views/admin/users/index.html.erb b/app/views/admin/users/index.html.erb
new file mode 100644
index 00000000..7cadcf62
--- /dev/null
+++ b/app/views/admin/users/index.html.erb
@@ -0,0 +1,41 @@
+
\ No newline at end of file
diff --git a/app/views/admin/users/new.html.erb b/app/views/admin/users/new.html.erb
new file mode 100644
index 00000000..5bdec638
--- /dev/null
+++ b/app/views/admin/users/new.html.erb
@@ -0,0 +1,67 @@
+
+
+
Add New User
+
+ <%= form_for(@user, url: admin_users_path) do |f| %>
+
+
+
+
+
+
+
+ <%= f.check_box :is_site_admin %>
+ <%= f.label :is_site_admin, class: "label-text text-black mb-2 font-semibold" %>
+
User should be able to see and edit all sites and documents.
+
+
+
+ <%= f.check_box :is_user_admin %>
+ <%= f.label :is_user_admin, class: "label-text text-black mb-2 font-semibold" %>
+
User should be able to edit other users and create admins.
+
+
+
+ <%= f.label :site_id, "Site", class: "label-text text-black mb-2 font-semibold" %>
+ <%= f.select :site_id, grouped_options_for_select(@sites, @user.site_id),
+ { include_blank: "None" },
+ { class: "input input-bordered w-full select" } %>
+
+
+
+ <%= button_tag type: "submit", class: "btn btn-primary text-white", id: "submit-user-form" do %>
+
+ Save
+ <% end %>
+ <%= link_to "Back", :back %>
+
+ <% end %>
+
+
\ No newline at end of file
diff --git a/app/views/layouts/application.html.erb b/app/views/layouts/application.html.erb
index 1d01d144..26d65bdb 100644
--- a/app/views/layouts/application.html.erb
+++ b/app/views/layouts/application.html.erb
@@ -36,14 +36,22 @@
+ <%= link_to edit_user_registration_path, class: "block px-4 py-2 text-sm text-gray-700 hover:bg-gray-100" do %>
+
My Account
+ <% end %>
<%= link_to sites_path, class: "block px-4 py-2 text-sm text-gray-700 hover:bg-gray-100" do %>
My Sites
<% end %>
- <% if current_user.present? && current_user.is_admin? %>
+ <% if current_user.present? && current_user.is_site_admin? %>
<%= link_to edit_configuration_path, class: "block px-4 py-2 text-sm text-gray-700 hover:bg-gray-100" do %>
AI Settings
<% end %>
<% end %>
+ <% if current_user.present? && current_user.is_user_admin? %>
+ <%= link_to admin_users_path, class: "block px-4 py-2 text-sm text-gray-700 hover:bg-gray-100" do %>
+
Admin Users
+ <% end %>
+ <% end %>
<%= button_to destroy_user_session_path, method: :delete, class: "w-full text-left px-4 py-2 text-sm text-gray-700 hover:bg-gray-100" do %>
Logout
<% end %>
diff --git a/app/views/sites/index.html.erb b/app/views/sites/index.html.erb
index f6d03ea0..90179239 100644
--- a/app/views/sites/index.html.erb
+++ b/app/views/sites/index.html.erb
@@ -3,7 +3,7 @@
My Sites
- <% if current_user.is_admin? %>
+ <% if current_user.is_site_admin? %>
Add Site
<% end %>
@@ -44,7 +44,7 @@
-<% if current_user.is_admin? %>
+<% if current_user.is_site_admin? %>
diff --git a/app/views/users/registrations/edit.html.erb b/app/views/users/registrations/edit.html.erb
index 54a7c4a9..10c720b4 100644
--- a/app/views/users/registrations/edit.html.erb
+++ b/app/views/users/registrations/edit.html.erb
@@ -1,43 +1,67 @@
-
Edit <%= resource_name.to_s.humanize %>
+
+
+
Edit <%= resource_name.to_s.humanize %>
-<%= form_for(resource, as: resource_name, url: registration_path(resource_name), html: { method: :put }) do |f| %>
- <%= render "users/shared/error_messages", resource: resource %>
+ <%= form_for(resource, as: resource_name, url: registration_path(resource_name), html: { method: :put }) do |f| %>
-
- <%= f.label :email %>
- <%= f.email_field :email, autofocus: true, autocomplete: "email" %>
-
+
- <% if devise_mapping.confirmable? && resource.pending_reconfirmation? %>
-
Currently waiting confirmation for: <%= resource.unconfirmed_email %>
- <% end %>
+ <% if devise_mapping.confirmable? && resource.pending_reconfirmation? %>
+
Currently waiting confirmation for: <%= resource.unconfirmed_email %>
+ <% end %>
-
- <%= f.label :password %> (leave blank if you don't want to change it)
- <%= f.password_field :password, autocomplete: "new-password" %>
- <% if @minimum_password_length %>
-
- <%= @minimum_password_length %> characters minimum
- <% end %>
-
+ <% if resource.id == current_user.id %>
+
+ <% end %>
-
- <%= f.label :password_confirmation %>
- <%= f.password_field :password_confirmation, autocomplete: "new-password" %>
-
+
-
- <%= f.label :current_password %> (we need your current password to confirm your changes)
- <%= f.password_field :current_password, autocomplete: "current-password" %>
-
+
-
- <%= f.submit "Update" %>
+
+ <%= button_tag type: "submit", class: "btn btn-primary text-white", id: "submit-session-form" do %>
+
+ Update
+ <% end %>
+ <%= link_to "Back", :back %>
+
+ <% end %>
-<% end %>
-
-
Cancel my account
-
-
Unhappy? <%= button_to "Cancel my account", registration_path(resource_name), data: { confirm: "Are you sure?", turbo_confirm: "Are you sure?" }, method: :delete %>
-
-<%= link_to "Back", :back %>
+
diff --git a/app/views/users/sessions/new.html.erb b/app/views/users/sessions/new.html.erb
index bb6e1673..6cd48baa 100644
--- a/app/views/users/sessions/new.html.erb
+++ b/app/views/users/sessions/new.html.erb
@@ -8,7 +8,7 @@
<%= form_for(resource, as: resource_name, url: session_path(resource_name)) do |f| %>
- <%= yield %>
+
diff --git a/app/views/layouts/mailer.html.erb b/app/views/layouts/mailer.html.erb
index 3aac9002..d1ba3f75 100644
--- a/app/views/layouts/mailer.html.erb
+++ b/app/views/layouts/mailer.html.erb
@@ -1,13 +1,71 @@
-
+