wip: wcag structure
This commit is contained in:
parent
4c31dbbed0
commit
4dd445be57
48 changed files with 461 additions and 137 deletions
|
|
@ -1,3 +1,3 @@
|
|||
web: RUBY_DEBUG_OPEN=true bin/rails server -b 0
|
||||
js: yarn build --watch
|
||||
js: yarn build --watch=forever
|
||||
css: yarn watch:css
|
||||
|
|
|
|||
|
|
@ -2,3 +2,7 @@
|
|||
|
||||
`rails new -n a11yist -d sqlite3 --skip-action-mailbox --css bootstrap --js esbuild .`
|
||||
|
||||
|
||||
## Installation
|
||||
|
||||
To [install](readme)
|
||||
|
|
|
|||
|
|
@ -25,10 +25,6 @@ class ApplicationController < ActionController::Base
|
|||
label: Project.model_name.human(count: 2),
|
||||
icon: :'folder',
|
||||
path: :projects },
|
||||
# {
|
||||
# label: Report.model_name.human(count: 2),
|
||||
# icon: :'journal-text',
|
||||
# path: :reports },
|
||||
{
|
||||
label: I18n.t("backoffice"),
|
||||
icon: :gear,
|
||||
|
|
@ -36,7 +32,8 @@ class ApplicationController < ActionController::Base
|
|||
active: %w[backoffice checklists checks links link_categories].include?(controller_name) },
|
||||
{
|
||||
label: "Konto",
|
||||
path: profile_path
|
||||
path: profile_path,
|
||||
icon: "person-circle"
|
||||
}
|
||||
]
|
||||
else
|
||||
|
|
|
|||
|
|
@ -81,7 +81,7 @@ class ChecksController < ApplicationController
|
|||
|
||||
# Only allow a list of trusted parameters through.
|
||||
def check_params
|
||||
params.require(:check).permit(:principle_id,
|
||||
params.require(:check).permit(:guideline_id,
|
||||
:number,
|
||||
:name_de,
|
||||
:name_en,
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@ module BackofficeMenu
|
|||
[
|
||||
{ label: "Einstellungen", icon: :sliders, path: :backoffice },
|
||||
{ label: Checklist.model_name.human(count: 2), icon: :'list-check', path: :checklists },
|
||||
{ label: Guideline.model_name.human(count: 2), icon: :'rulers', path: :guidelines },
|
||||
{ label: Check.model_name.human(count: 2), icon: :check2, path: :checks },
|
||||
{ label: Link.model_name.human(count: 2), icon: :link, path: :links },
|
||||
{ label: LinkCategory.model_name.human(count: 2), icon: :folder, path: :link_categories } ]
|
||||
|
|
|
|||
58
app/controllers/guidelines_controller.rb
Normal file
58
app/controllers/guidelines_controller.rb
Normal file
|
|
@ -0,0 +1,58 @@
|
|||
class GuidelinesController < BackofficeController
|
||||
before_action :set_guideline, only: %i[ show edit update destroy ]
|
||||
|
||||
# GET /guidelines
|
||||
def index
|
||||
@guidelines = Guideline.all
|
||||
end
|
||||
|
||||
# GET /guidelines/1
|
||||
def show
|
||||
end
|
||||
|
||||
# GET /guidelines/new
|
||||
def new
|
||||
@guideline = Guideline.new
|
||||
end
|
||||
|
||||
# GET /guidelines/1/edit
|
||||
def edit
|
||||
end
|
||||
|
||||
# POST /guidelines
|
||||
def create
|
||||
@guideline = Guideline.new(guideline_params)
|
||||
|
||||
if @guideline.save
|
||||
redirect_to @guideline, notice: "Guideline was successfully created."
|
||||
else
|
||||
render :new, status: :unprocessable_entity
|
||||
end
|
||||
end
|
||||
|
||||
# PATCH/PUT /guidelines/1
|
||||
def update
|
||||
if @guideline.update(guideline_params)
|
||||
redirect_to @guideline, notice: "Guideline was successfully updated.", status: :see_other
|
||||
else
|
||||
render :edit, status: :unprocessable_entity
|
||||
end
|
||||
end
|
||||
|
||||
# DELETE /guidelines/1
|
||||
def destroy
|
||||
@guideline.destroy!
|
||||
redirect_to guidelines_url, notice: "Guideline was successfully destroyed.", status: :see_other
|
||||
end
|
||||
|
||||
private
|
||||
# Use callbacks to share common setup or constraints between actions.
|
||||
def set_guideline
|
||||
@guideline = Guideline.find(params[:id])
|
||||
end
|
||||
|
||||
# Only allow a list of trusted parameters through.
|
||||
def guideline_params
|
||||
params.require(:guideline).permit(:principle_id, :number, :name_de, :name_en, :description_de, :description_en)
|
||||
end
|
||||
end
|
||||
|
|
@ -4,7 +4,11 @@ import * as bootstrap from "bootstrap"
|
|||
// Connects to data-controller="toast"
|
||||
export default class extends Controller {
|
||||
connect() {
|
||||
const toastBootstrap = bootstrap.Toast.getOrCreateInstance(this.element)
|
||||
toastBootstrap.show()
|
||||
const shownKey = `toastsShown[${this.element.getAttribute("data-ts")}]`
|
||||
if(!window.sessionStorage.getItem(shownKey)) {
|
||||
window.sessionStorage.setItem(shownKey, Date.now());
|
||||
const toastBootstrap = bootstrap.Toast.getOrCreateInstance(this.element)
|
||||
toastBootstrap.show()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
class Check < ApplicationRecord
|
||||
belongs_to :principle
|
||||
belongs_to :guideline
|
||||
|
||||
has_and_belongs_to_many :links
|
||||
has_and_belongs_to_many :standards
|
||||
|
|
@ -51,9 +51,9 @@ class Check < ApplicationRecord
|
|||
:standard_text,
|
||||
:powerpoint_text
|
||||
|
||||
validates :number, uniqueness: true, presence: true
|
||||
validates :number, uniqueness: { scope: :guideline_id }, presence: true
|
||||
|
||||
before_validation { self.number = self.class.maximum(:id).to_i + 1 unless self.number.present? }
|
||||
# before_validation { self.number = self.class.maximum(:id).to_i + 1 unless self.number.present? }
|
||||
|
||||
scope(:search, lambda { |terms|
|
||||
# TODO: Search only fields for current locale.
|
||||
|
|
@ -129,4 +129,20 @@ class Check < ApplicationRecord
|
|||
def external_number
|
||||
[ external_number_1, external_number_2, external_number_3 ].compact_blank.join(".")
|
||||
end
|
||||
|
||||
def external_number
|
||||
[
|
||||
guideline.principle.id,
|
||||
guideline.number,
|
||||
number
|
||||
].compact.join(".")
|
||||
end
|
||||
|
||||
def full_number
|
||||
external_number
|
||||
end
|
||||
|
||||
def to_s
|
||||
display_label
|
||||
end
|
||||
end
|
||||
|
|
|
|||
18
app/models/guideline.rb
Normal file
18
app/models/guideline.rb
Normal file
|
|
@ -0,0 +1,18 @@
|
|||
class Guideline < ApplicationRecord
|
||||
belongs_to :principle
|
||||
|
||||
has_many :checks
|
||||
|
||||
has_rich_text :description_de
|
||||
has_rich_text :description_en
|
||||
|
||||
translates_attributes :name, :description
|
||||
|
||||
def to_s
|
||||
"#{full_number} #{t_name}"
|
||||
end
|
||||
|
||||
def full_number
|
||||
[ principle.id, number ].join(".")
|
||||
end
|
||||
end
|
||||
|
|
@ -21,4 +21,8 @@ class Report < ApplicationRecord
|
|||
success_criteria: export_success_criteria
|
||||
}
|
||||
end
|
||||
|
||||
def test
|
||||
139
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -7,6 +7,12 @@
|
|||
<%= link_to Checklist.model_name.human(count: Checklist.count), :checklists %>
|
||||
</p>
|
||||
|
||||
<p>
|
||||
<i class="bi bi-rulers"></i>
|
||||
<%= Guideline.count %>
|
||||
<%= link_to Guideline.model_name.human(count: Guideline.count), :guidelines %>
|
||||
</p>
|
||||
|
||||
<p>
|
||||
<i class="bi bi-check2"></i>
|
||||
<%= Check.count %>
|
||||
|
|
|
|||
|
|
@ -11,8 +11,8 @@ div id=dom_id(check)
|
|||
th = Check.human_attribute_name(:number)
|
||||
td = check.number
|
||||
tr
|
||||
th = Principle.model_name.human
|
||||
td = check.principle&.t_name
|
||||
th = Guideline.model_name.human
|
||||
td = check.guideline&.name_en
|
||||
tr
|
||||
th = Standard.model_name.human(count: check.standard_ids.size)
|
||||
td = check.standards.map(&:t_name).sort_by(&:downcase).join(", ")
|
||||
|
|
|
|||
|
|
@ -1,9 +1,9 @@
|
|||
= bootstrap_form_with(model: check, remote: true, data: { controller: "unsaved-changes" }) do |form|
|
||||
h2 Details
|
||||
= multilang_form_field(form, :name)
|
||||
= form.text_field :number, required: false
|
||||
.row
|
||||
= form.collection_radio_buttons(:principle_id, Principle.all.sort_by(&:t_name), :id, :t_name) { |b| b.label(class: "col-md-2") { b.radio_button + b.text } }
|
||||
= form.collection_radio_buttons(:guideline_id, Guideline.all.sort_by(&:full_number), :id, :to_s) { |b| b.label(class: "col-md-2") { b.radio_button + b.text } }
|
||||
= form.text_field :number, required: false
|
||||
= form.collection_check_boxes :standard_ids, Standard.all.sort_by{ _1.t_name.downcase }, :id, :t_name, include_blank: true
|
||||
|
||||
h2 Einschränkung/Zugänglichkeit
|
||||
|
|
|
|||
|
|
@ -6,4 +6,5 @@
|
|||
- if element.persisted?
|
||||
= safe_display(element.screenshot) { tag.div(link_to(_1.filename.to_s, _1), class: "mb-3") }
|
||||
= form.submit class: "btn btn-primary"
|
||||
= link_to("Abbrechen", element.persisted? ? element : element.report, class: "btn btn-outline-secondary")
|
||||
- unless modal?
|
||||
= link_to("Abbrechen", element.persisted? ? element : element.report, class: "btn btn-outline-secondary")
|
||||
|
|
|
|||
|
|
@ -1,26 +1,3 @@
|
|||
/nav
|
||||
= link_to(@report.name, "##{dom_id(@report)}")
|
||||
ul
|
||||
li = link_to("Inhaltsverzeichnis", "#toc")
|
||||
li
|
||||
= link_to('Testbericht')
|
||||
ul
|
||||
- @report.pages.select { |p| p.elements.any? { |e| e.success_criteria.any?(&:failed?) } }.each do |page|
|
||||
li
|
||||
= link_to("#{page.position} #{page.path}", "##{dom_id(page)}")
|
||||
ul
|
||||
- page.elements.select { |e| e.success_criteria.any?(&:failed?) }.each do |element|
|
||||
li
|
||||
= link_to("#{element.number} #{element.title}")
|
||||
ul
|
||||
- element.success_criteria.select(&:failed?).each do |sc|
|
||||
li = link_to("#{sc.number} #{sc.title}", "##{dom_id(sc)}")
|
||||
li
|
||||
= link_to("Anhang")
|
||||
ul
|
||||
- @failed_success_criteria.group_by(&:check).each do |check, scs|
|
||||
li = link_to(check.display_label)
|
||||
|
||||
h1.title id=dom_id(@report) = @report.name
|
||||
|
||||
h2 1 Einschätzung
|
||||
|
|
@ -42,18 +19,18 @@ h2 2 Protokoll
|
|||
- current_page_pos += 1
|
||||
- current_element_pos = 0
|
||||
h3 = "2.#{current_page_pos} #{page.path}"
|
||||
p
|
||||
strong URL
|
||||
= page.url
|
||||
- page.elements.select { |e| e.success_criteria.any? { _1.failed? } }.each do |element|
|
||||
- current_element_pos += 1
|
||||
- current_abs_element_pos += 1
|
||||
- current_sc_pos = 0
|
||||
h4 = "2.#{current_page_pos}.#{current_element_pos} #{element.title}"
|
||||
/h3 = "2.#{current_abs_element_pos} #{element.title}"
|
||||
p
|
||||
strong Pfad:
|
||||
span =< page.path
|
||||
= safe_display(element.screenshot) { image_tag(_1.representation(resize_to_fit: [250, 250]))}
|
||||
= element.description
|
||||
- element.success_criteria.select{ _1.failed? }.each do |sc|
|
||||
- element.success_criteria.select(&:failed?).each do |sc|
|
||||
- current_sc_pos += 1
|
||||
/h4
|
||||
= "2.#{current_abs_element_pos}.#{current_sc_pos} #{sc.title}"
|
||||
|
|
|
|||
7
app/views/guidelines/_form.html.erb
Normal file
7
app/views/guidelines/_form.html.erb
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
<%= bootstrap_form_with(model: guideline) do |form| %>
|
||||
<%= form.collection_select :principle_id, Principle.all.sort_by(&:id), :id, :t_name %>
|
||||
<%= form.number_field :number %>
|
||||
<%= form.text_field :name_de %>
|
||||
<%= form.rich_text_area :description_de %>
|
||||
<%= form.submit %>
|
||||
<% end %>
|
||||
15
app/views/guidelines/_guideline.html.erb
Normal file
15
app/views/guidelines/_guideline.html.erb
Normal file
|
|
@ -0,0 +1,15 @@
|
|||
<div id="<%= dom_id guideline %>">
|
||||
<p>
|
||||
<strong>Principle:</strong>
|
||||
<%= guideline.principle.t_name %>
|
||||
</p>
|
||||
|
||||
<%= guideline.description_de %>
|
||||
<h2 class="mt-4">Richtlinien</h2>
|
||||
<% guideline.checks.each do |check| %>
|
||||
<section class="pt-3">
|
||||
<h3 class="fs-5"><%= link_to check %></h3>
|
||||
<%= check.t_criterion %>
|
||||
</section>
|
||||
<% end %>
|
||||
</div>
|
||||
2
app/views/guidelines/_guideline.json.jbuilder
Normal file
2
app/views/guidelines/_guideline.json.jbuilder
Normal file
|
|
@ -0,0 +1,2 @@
|
|||
json.extract! guideline, :id, :principle_id, :number, :name_de, :created_at, :updated_at
|
||||
json.url guideline_url(guideline, format: :json)
|
||||
8
app/views/guidelines/edit.html.erb
Normal file
8
app/views/guidelines/edit.html.erb
Normal file
|
|
@ -0,0 +1,8 @@
|
|||
<h1><%= t("scaffold.pagetitle_edit", model: Guideline.model_name.human) %></h1>
|
||||
|
||||
<%= render "form", guideline: @guideline %>
|
||||
|
||||
<div class="action-row">
|
||||
<%= link_to t("scaffold.link_show", model: Guideline.model_name.human), @guideline %>
|
||||
<%= link_to t("scaffold.link_index", model: Guideline.model_name.human(count: 2)), guidelines_path %>
|
||||
</div>
|
||||
28
app/views/guidelines/index.html.erb
Normal file
28
app/views/guidelines/index.html.erb
Normal file
|
|
@ -0,0 +1,28 @@
|
|||
<h1><%= t("scaffold.pagetitle_index", model: Guideline.model_name.human(count: 2)) %></h1>
|
||||
|
||||
<table class="table table-striped">
|
||||
<thead>
|
||||
<tr>
|
||||
|
||||
<th><%= Guideline.human_attribute_name(:name_de) %></th>
|
||||
|
||||
<th><%= Guideline.human_attribute_name(:description_de) %></th>
|
||||
|
||||
</thead>
|
||||
<tbody>
|
||||
<% @guidelines.each do |guideline| %>
|
||||
<tr>
|
||||
|
||||
|
||||
|
||||
<td><%= link_to(guideline, url_for(guideline)) %></td>
|
||||
<td><%= link_to(guideline.description_de, url_for(guideline)) %></td>
|
||||
|
||||
</tr>
|
||||
<% end %>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
<div class="action-row">
|
||||
<%= link_to t("scaffold.link_new", model: Guideline.model_name.human), new_guideline_path %>
|
||||
</div>
|
||||
1
app/views/guidelines/index.json.jbuilder
Normal file
1
app/views/guidelines/index.json.jbuilder
Normal file
|
|
@ -0,0 +1 @@
|
|||
json.array! @guidelines, partial: "guidelines/guideline", as: :guideline
|
||||
7
app/views/guidelines/new.html.erb
Normal file
7
app/views/guidelines/new.html.erb
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
<h1><%= t("scaffold.pagetitle_new", model: Guideline.model_name.human) %></h1>
|
||||
|
||||
<%= render "form", guideline: @guideline %>
|
||||
|
||||
<div class="action-row">
|
||||
<%= link_to t("scaffold.link_index", model: Guideline.model_name.human(count: 2)), guidelines_path %>
|
||||
</div>
|
||||
9
app/views/guidelines/show.html.erb
Normal file
9
app/views/guidelines/show.html.erb
Normal file
|
|
@ -0,0 +1,9 @@
|
|||
<h1><%= @guideline %></h1>
|
||||
|
||||
<%= render @guideline %>
|
||||
|
||||
<div class="action-row">
|
||||
<%= link_to t("scaffold.link_edit", model: @guideline.model_name.human), edit_guideline_path(@guideline) %>
|
||||
<%= link_to t("scaffold.link_index", model: @guideline.model_name.human(count: 2)), guidelines_path %>
|
||||
<%= button_to t("scaffold.link_destroy", model: @guideline.model_name.human), @guideline, method: :delete, class: "btn btn-outline-danger" %>
|
||||
</div>
|
||||
1
app/views/guidelines/show.json.jbuilder
Normal file
1
app/views/guidelines/show.json.jbuilder
Normal file
|
|
@ -0,0 +1 @@
|
|||
json.partial! "guidelines/guideline", guideline: @guideline
|
||||
|
|
@ -19,6 +19,12 @@ h1 Dashboard
|
|||
li = link_to(r.name, r)
|
||||
|
||||
.col-md-6
|
||||
h2 Browser Erweiterungen
|
||||
p = link_to "Language Tools KI Korrektur", "https://languagetool.org/services#browsers"
|
||||
p = link_to "DeepL Firefox Extension", "https://www.deepl.com/en/firefox-extension"
|
||||
p = link_to "DeepL Chrome Extension", "https://www.deepl.com/en/chrome-extension"
|
||||
p = link_to "DeepL Edge Extension", "https://www.deepl.com/en/edge-extension"
|
||||
|
||||
h2 Hotkeys
|
||||
p Auf der Bericht-Ausfüllen Seite können folgende Shortcuts verwendet werden:
|
||||
dl
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
.toast class="#{alert ? "text-bg-danger" : "text-bg-info"}" role="alert" aria-live="assertive" aria-atomic="true" data={ controller: "toast" }
|
||||
.toast class="#{alert ? "text-bg-danger" : "text-bg-info"}" role="alert" aria-live="assertive" aria-atomic="true" data={ controller: "toast", ts: Time.now.to_f }
|
||||
.toast-header
|
||||
/img src="..." class="rounded me-2" alt="...">
|
||||
/strong.me-auto = heading
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
doctype html
|
||||
html data-bs-theme="#{cookies[:"modeTheme"] || "light"}" data-controller="set-theme"
|
||||
html lang=:de data-bs-theme="#{cookies[:"modeTheme"] || "light"}" data-controller="set-theme"
|
||||
head
|
||||
title a11ydive
|
||||
meta[name="viewport" content="width=device-width,initial-scale=1"]
|
||||
|
|
|
|||
|
|
@ -2,4 +2,5 @@
|
|||
= form.text_field :path
|
||||
= form.text_field :url
|
||||
= form.submit
|
||||
= link_to("Abbrechen", report_path(@page.report), class: "btn btn-outline-secondary")
|
||||
- unless modal?
|
||||
= link_to("Abbrechen", report_path(@page.report), class: "btn btn-outline-secondary")
|
||||
|
|
|
|||
|
|
@ -7,4 +7,5 @@
|
|||
= form.rich_text_area :test_comment
|
||||
= form.submit class: "btn btn-primary"
|
||||
- unless modal?
|
||||
p Not MODAL
|
||||
=< link_to "Abbrechen", success_criterion.persisted? ? success_criterion : success_criterion.element, class: "btn btn-outline-secondary"
|
||||
|
|
|
|||
2
bin/dev
2
bin/dev
|
|
@ -8,4 +8,4 @@ fi
|
|||
# Default to port 3000 if not specified
|
||||
export PORT="${PORT:-3000}"
|
||||
|
||||
exec foreman start -f /app/Procfile.dev "$@"
|
||||
exec foreman start -f Procfile.dev "$@"
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
#!/bin/bash
|
||||
|
||||
if [ -f /app/tmp/pids/server.pid ]; then
|
||||
rm /app/tmp/pids/server.pid
|
||||
if [ -f tmp/pids/server.pid ]; then
|
||||
rm tmp/pids/server.pid
|
||||
fi
|
||||
|
||||
exec "$@"
|
||||
|
|
|
|||
|
|
@ -1 +1 @@
|
|||
vRtWWWCynlFuZljZsCQOV5D6xZlwp/LkuGZ2bPp1IuZiCRfJ2F8CXbubRavkqM0xxpHWkC7hiQ0lSv/dCPtaujG+aJ2Q0+KSw81kJ3xiYd/AiR3ZxO9prk5Zsf0LPLoONGBbYurJIb37hAWQ7h1r1qORLbtQGLb9pYlynObhXvkaYQR5E7Jr0Rcnon0d2uKJ13ukDAHUiIgwCE5/f+Y6UVllTLBCk0a+YDZvkv7xsYNpTTD9bgjpAU/pg6jv1xY0k/gZ8SFRsSQaJs8CYtbhMsyZBvImtguGfc7U7B4cFrgdzUPY473vBKBqm6O0StNagS5muP3/YLLN7xcrRDTBi9n3qllATlfJ9cxZ+JCWZJmyqINI4X+T/sc3lpzlo/grW0W+HwQ/4CZ3LkCN/OOqZlG1HL1u--NhnYNqgGpqSnh7Ut--KGndHl8kyj/uAjRG12R3zg==
|
||||
TjpgJe/DwNAzOraDbzXUo20hxCLkOn72DlXYkIouG0crCD3m4/LAiwDOlOWwsCO5Je6FzqKZniLUqSfVthChxewvZn+PY7XGTLECbB9gjSpRC6hERUxKIirLot+CH7lkMlM3f4o3NPf2I4vs8j6hooXcc8Vd8l1uMHOU1RHd+8EfPmTetqd0IETnEUdXigL50yjcbpxy8jHGdaeA8hHU3F+jwk7gRv11uVgEfn069qD9tc5AlAFWdnYJj/ZadAX2+bimHyne5Y12gmRAiqu95KXzUs0OlI+Vx9lpFyMQNacuKPPx0AeeBafjnMBozXOSU7MMCBUGzig07Kg+/tJSiekeLX0X2VEj3Ecqe9nL54fAGVSqJmwT19KvjeS4WfjvFDff9KAY4H+vKzfkBQzMFOTJjnMTNHa+dtNly0kUphwDtsFWDhF3PaZQIRCeI7RIXKhFMgrRFE6UL4AFFzhocQ1DAwaZElXFJaKjiCWWCY+acLCRz2AnriR08KMsEGN54nMtcGcWnUS2ewr/txLQDkipjADYmjoxsz+rDqp2IH+4y4ZFJf8=--jX6TlsuL0nwliAuN--lnbA1H3K7bwrmIjgJzAT9A==
|
||||
6
config/initializers/deepl.rb
Normal file
6
config/initializers/deepl.rb
Normal file
|
|
@ -0,0 +1,6 @@
|
|||
if Rails.application.credentials[:deepl_api_key]
|
||||
DeepL.configure do |config|
|
||||
config.auth_key = Rails.application.credentials[:deepl_api_key]
|
||||
config.host = "https://api-free.deepl.com"
|
||||
end
|
||||
end
|
||||
|
|
@ -1,5 +1,6 @@
|
|||
# Define your application routes per the DSL in https://guides.rubyonrails.org/routing.html
|
||||
Rails.application.routes.draw do
|
||||
resources :guidelines
|
||||
resource :session
|
||||
resources :passwords, param: :token
|
||||
namespace :admin do
|
||||
|
|
|
|||
12
db/migrate/20250101163808_create_guidelines.rb
Normal file
12
db/migrate/20250101163808_create_guidelines.rb
Normal file
|
|
@ -0,0 +1,12 @@
|
|||
class CreateGuidelines < ActiveRecord::Migration[8.0]
|
||||
def change
|
||||
create_table :guidelines do |t|
|
||||
t.references :principle, null: false, foreign_key: true
|
||||
t.integer :number
|
||||
t.string :name_de
|
||||
t.string :name_en
|
||||
|
||||
t.timestamps
|
||||
end
|
||||
end
|
||||
end
|
||||
6
db/migrate/20250101163835_add_guideline_id_to_checks.rb
Normal file
6
db/migrate/20250101163835_add_guideline_id_to_checks.rb
Normal file
|
|
@ -0,0 +1,6 @@
|
|||
class AddGuidelineIdToChecks < ActiveRecord::Migration[8.0]
|
||||
def change
|
||||
add_reference :checks, :guideline, null: false, foreign_key: true
|
||||
remove_reference :checks, :principle
|
||||
end
|
||||
end
|
||||
|
|
@ -0,0 +1,5 @@
|
|||
class RemoveUniqueNumberConstraintOnChecks < ActiveRecord::Migration[8.0]
|
||||
def change
|
||||
remove_index :checks, :number
|
||||
end
|
||||
end
|
||||
20
db/schema.rb
generated
20
db/schema.rb
generated
|
|
@ -10,7 +10,7 @@
|
|||
#
|
||||
# It's strongly recommended that you check this file into your version control system.
|
||||
|
||||
ActiveRecord::Schema[8.0].define(version: 2024_11_24_183406) do
|
||||
ActiveRecord::Schema[8.0].define(version: 2025_01_01_171836) do
|
||||
create_table "account_remember_keys", force: :cascade do |t|
|
||||
t.string "key", null: false
|
||||
t.datetime "deadline", null: false
|
||||
|
|
@ -94,7 +94,6 @@ ActiveRecord::Schema[8.0].define(version: 2024_11_24_183406) do
|
|||
t.boolean "cognitive", default: false, null: false
|
||||
t.boolean "applicable_to_web", default: false, null: false
|
||||
t.boolean "applicable_to_app", default: false, null: false
|
||||
t.integer "principle_id"
|
||||
t.integer "conformity_level"
|
||||
t.integer "priority"
|
||||
t.boolean "manual_test", default: true, null: false
|
||||
|
|
@ -106,8 +105,8 @@ ActiveRecord::Schema[8.0].define(version: 2024_11_24_183406) do
|
|||
t.integer "external_number_1"
|
||||
t.integer "external_number_2"
|
||||
t.integer "external_number_3"
|
||||
t.index ["number"], name: "index_checks_on_number", unique: true
|
||||
t.index ["principle_id"], name: "index_checks_on_principle_id"
|
||||
t.integer "guideline_id", null: false
|
||||
t.index ["guideline_id"], name: "index_checks_on_guideline_id"
|
||||
end
|
||||
|
||||
create_table "checks_links", id: false, force: :cascade do |t|
|
||||
|
|
@ -131,6 +130,16 @@ ActiveRecord::Schema[8.0].define(version: 2024_11_24_183406) do
|
|||
t.index ["page_id"], name: "index_elements_on_page_id"
|
||||
end
|
||||
|
||||
create_table "guidelines", force: :cascade do |t|
|
||||
t.integer "principle_id", null: false
|
||||
t.integer "number"
|
||||
t.string "name_de"
|
||||
t.string "name_en"
|
||||
t.datetime "created_at", null: false
|
||||
t.datetime "updated_at", null: false
|
||||
t.index ["principle_id"], name: "index_guidelines_on_principle_id"
|
||||
end
|
||||
|
||||
create_table "link_categories", force: :cascade do |t|
|
||||
t.string "name"
|
||||
t.text "description"
|
||||
|
|
@ -230,8 +239,9 @@ ActiveRecord::Schema[8.0].define(version: 2024_11_24_183406) do
|
|||
add_foreign_key "active_storage_variant_records", "active_storage_blobs", column: "blob_id"
|
||||
add_foreign_key "checklist_entries", "checklists"
|
||||
add_foreign_key "checklist_entries", "checks"
|
||||
add_foreign_key "checks", "principles"
|
||||
add_foreign_key "checks", "guidelines"
|
||||
add_foreign_key "elements", "pages"
|
||||
add_foreign_key "guidelines", "principles"
|
||||
add_foreign_key "links", "link_categories"
|
||||
add_foreign_key "pages", "reports"
|
||||
add_foreign_key "reports", "projects"
|
||||
|
|
|
|||
|
|
@ -72,6 +72,8 @@ Principle.create!(name_de: "Verständlich", name_en: "Understandable")
|
|||
Principle.create!(name_de: "Robust", name_en: "Robust")
|
||||
Principle.create!(name_de: "Sonstige", name_en: "Other")
|
||||
|
||||
LinkCategory.create!(name: "Verstehen")
|
||||
LinkCategory.create!(name: "WCAG Quick Reference")
|
||||
LinkCategory.create!(name: "Tools")
|
||||
LinkCategory.create!(name: "Beispiele")
|
||||
LinkCategory.create!(name: "Artikel")
|
||||
|
|
@ -92,4 +94,3 @@ Link.create!(url: "https://www.a11yproject.com/",
|
|||
link_category: LinkCategory.find_by(name: "Artikel"))
|
||||
|
||||
User.find_or_initialize_by(email_address: "admin@example.com").update!(password: "password")
|
||||
User.find_or_initialize_by(email_address: "goran@quiet.ch").update!(password: "password")
|
||||
|
|
@ -1,62 +0,0 @@
|
|||
# This is available in $COMPOSE_PROJECT_NAME
|
||||
name: a11yist
|
||||
|
||||
networks:
|
||||
traefik:
|
||||
external: true
|
||||
|
||||
services:
|
||||
app: &app
|
||||
build:
|
||||
context: .
|
||||
restart: "unless-stopped"
|
||||
command: ["/bin/sh", "-c", "/app/bin/dev"]
|
||||
volumes:
|
||||
- ./:/app:cached
|
||||
- ${PWD}:${PWD}
|
||||
- ${SSH_AUTH_SOCK}:/ssh-agent
|
||||
- .devenv/helix:/home/app/.config/helix
|
||||
- .devenv/fish:/home/app/.config/fish
|
||||
env_file:
|
||||
- .env
|
||||
environment:
|
||||
RAILS_ENV: development
|
||||
LOG_LEVEL: debug
|
||||
TRUSTED_IP: 172.16.0.0/12,192.168.0.0/16,10.0.0.0/24
|
||||
SSH_AUTH_SOCK: /ssh-agent
|
||||
RAILS_SERVE_STATIC_FILES: 1
|
||||
APP_HOST: ${COMPOSE_PROJECT_NAME}.localhost
|
||||
HISTFILE: /app/tmp/.bash_history
|
||||
PSQL_HISTORY: /app/tmp/.psql_history
|
||||
IRBRC: /app/.irbrc
|
||||
SELENIUM_REMOTE_URL: http://chrome:4444/wd/hub
|
||||
labels:
|
||||
- traefik.http.routers.app-${COMPOSE_PROJECT_NAME}.entrypoints=http
|
||||
- traefik.http.routers.app-${COMPOSE_PROJECT_NAME}.rule=Host(`${COMPOSE_PROJECT_NAME}.localhost`)
|
||||
- traefik.http.services.app-${COMPOSE_PROJECT_NAME}.loadbalancer.server.port=3000
|
||||
- traefik.docker.network=traefik
|
||||
networks:
|
||||
- traefik
|
||||
- default
|
||||
|
||||
edit:
|
||||
<<: *app
|
||||
restart: "no"
|
||||
labels: []
|
||||
depends_on: []
|
||||
command: ["hx", "."]
|
||||
entrypoint: null
|
||||
networks:
|
||||
- default
|
||||
|
||||
chrome:
|
||||
image: selenium/standalone-chrome
|
||||
shm_size: 2g
|
||||
labels:
|
||||
- traefik.http.routers.chrome-${COMPOSE_PROJECT_NAME}.entrypoints=http
|
||||
- traefik.http.routers.chrome-${COMPOSE_PROJECT_NAME}.rule=Host(`chrome.${COMPOSE_PROJECT_NAME}.localhost`)
|
||||
- traefik.http.services.chrome-${COMPOSE_PROJECT_NAME}.loadbalancer.server.port=4444
|
||||
- traefik.docker.network=traefik
|
||||
networks:
|
||||
- traefik
|
||||
- default
|
||||
|
|
@ -1,20 +1,66 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
URL = "https://outline-rocks.github.io/wcag/translations/WCAG21-de/"
|
||||
WCAG_22_EN_URL ="https://www.w3.org/TR/WCAG22/"
|
||||
WCAG_22_EN_QREF_URL = "https://www.w3.org/WAI/WCAG22/quickref/"
|
||||
|
||||
def translate(input)
|
||||
DeepL.translate(input, "EN", "DE")
|
||||
end
|
||||
|
||||
def import_wcag22en
|
||||
doc = Nokogiri::HTML5(URI.open(WCAG_22_EN_URL))
|
||||
standards = [Standard.find_by(name_de: "WCAG 2.2"), Standard.find_by(name_de: "EN 301 549")]
|
||||
qrefdoc = Nokogiri::HTML5(URI.open(WCAG_22_EN_QREF_URL))
|
||||
quick_criteria = qrefdoc.css(".guidelines section:has(h4)").each_with_object({}) do |node, h|
|
||||
h[node.css("h4 strong").first.content] = {
|
||||
quick_criterion_en: node.css(".sc-content .sc-text p").first.content,
|
||||
link_url: node.css(".understanding a").first.attribute("href").value,
|
||||
perm_url: "#{WCAG_22_EN_QREF_URL}##{node.css("article").first.attribute("id").value}"
|
||||
}
|
||||
rescue
|
||||
debugger
|
||||
end
|
||||
link_category = LinkCategory.find_by(name: "Verstehen")
|
||||
qr_category = LinkCategory.find_by(name: "WCAG Quick Reference")
|
||||
# debugger
|
||||
# raise ActiveRecord::Rollback
|
||||
|
||||
standards = [ Standard.find_by(name_de: "WCAG 2.2"), Standard.find_by(name_de: "EN 301 549") ]
|
||||
doc.css("section.principle").each do |principle_node|
|
||||
_principle_id = principle_node.attributes["id"].value
|
||||
principle_title = principle_node.css("h2").first.content.scan(/([a-zA-Z]+)/)
|
||||
principle = Principle.find_by!(name_en: principle_title)
|
||||
principle = Principle.find_or_create_by!(name_en: principle_title)
|
||||
|
||||
principle_node.css("section.guideline").each do |guideline_node|
|
||||
next unless guideline_node.css("h3").first
|
||||
|
||||
puts guideline_node.css("h3").first&.content
|
||||
puts guideline_node.css_path
|
||||
|
||||
g_title = guideline_node.css("h3")
|
||||
.first
|
||||
.content
|
||||
.scan(/Guideline \d+\.\d+ (.*)/)
|
||||
.first
|
||||
.first
|
||||
g_number = guideline_node.css("h3 bdi")
|
||||
.first
|
||||
.content
|
||||
.scan(/\d+\.(\d+)/)
|
||||
.first
|
||||
.first
|
||||
g_text = guideline_node.css("> p").first.content
|
||||
|
||||
guideline = Guideline.find_or_create_by(principle: principle, number: g_number, name_en: g_title)
|
||||
guideline.update(description_en: g_text, description_de: translate(g_text), name_de: translate(g_title)) if guideline.description_de.blank?
|
||||
|
||||
guideline_node.css("section.guideline").each do |sc|
|
||||
puts sc.css_path
|
||||
puts sc.css("h4 bdi")
|
||||
sc_number, sc_title = sc.css("h4")
|
||||
.first
|
||||
.content
|
||||
.scan(/Success Criterion (\d+\.\d+\.\d+) (.*)/)
|
||||
.scan(/Success Criterion \d+\.\d+\.(\d+) (.*)/)
|
||||
.first
|
||||
sc_level = sc.css("p.conformance-level").first&.content&.scan(/\(Level (A+)\)/)&.first&.last
|
||||
sc_url = sc.css("a.self-link").first.attr("href")
|
||||
|
|
@ -24,19 +70,34 @@ def import_wcag22en
|
|||
.select { _1.attr("class").nil? || _1.attr("class") == "note" }
|
||||
.reduce("") { |str, node| str + node.to_s }
|
||||
|
||||
check = Check.find_or_initialize_by(external_number: sc_number)
|
||||
check = Check.find_or_initialize_by(guideline_id: guideline.id, number: sc_number)
|
||||
new_standards = Set.new(check.standards)
|
||||
new_standards += standards
|
||||
check.name_de = sc_title
|
||||
check.name_de = translate sc_title if check.name_de.blank?
|
||||
check.name_en = sc_title
|
||||
check.principle = principle
|
||||
check.standards = new_standards
|
||||
check.applicable_to_app = check.applicable_to_web = true
|
||||
check.external_number = sc_number
|
||||
check.external_url = "#{WCAG_22_EN_URL}#{sc_url}"
|
||||
check.conformity_level = sc_level&.to_sym
|
||||
check.conformity_notice_de = sc_conformity_notice
|
||||
check.criterion_de = "<div>#{full_text}</div>".gsub('href="#', %(href="#{WCAG_22_EN_URL}#))
|
||||
check.conformity_notice_de = translate sc_conformity_notice
|
||||
check.criterion_de = "<div>#{translate full_text}</div>".gsub('href="#', %(href="#{WCAG_22_EN_URL}#)) if check.criterion_de.blank?
|
||||
qref_attrs = quick_criteria["#{principle.id}.#{guideline.number}.#{sc_number}"]
|
||||
check.quick_criterion_en = qref_attrs&.dig :quick_criterion_en
|
||||
check.quick_criterion_de = qref_attrs&.dig :quick_criterion_en
|
||||
|
||||
if qref_attrs&.dig(:link_url)
|
||||
link = Link.find_or_create_by(link_category:, url: qref_attrs[:link_url])
|
||||
link.update(text: "#{check.full_number} Verstehen")
|
||||
check.links << link
|
||||
end
|
||||
|
||||
if qref_attrs&.dig(:perm_url)
|
||||
link = Link.find_or_create_by(link_category: qr_category, url: qref_attrs[:perm_url])
|
||||
link.update(text: "#{check.full_number} Quick Reference")
|
||||
check.links << link
|
||||
end
|
||||
# check.criterion_details_en = qref_attrs&.dig :criterion_details_en
|
||||
# check.criterion_details_de = qref_attrs&.dig :criterion_details_en
|
||||
check.save!
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@ class ChecksControllerTest < ::ControllerTest
|
|||
end
|
||||
|
||||
setup do
|
||||
@principle = principles(:one)
|
||||
@guideline = guidelines(:one)
|
||||
@check = checks(:deletable)
|
||||
User.create!(email_address: "test@example.com", password: "password")
|
||||
login("test@example.com", "password")
|
||||
|
|
@ -27,7 +27,7 @@ class ChecksControllerTest < ::ControllerTest
|
|||
test "should create check" do
|
||||
assert_difference("Check.count") do
|
||||
post checks_url,
|
||||
params: { check: { principle_id: @principle.id, number: Check.maximum(:number) + 1, level: @check.level, name_de: @check.name_de, position: @check.position,
|
||||
params: { check: { guideline_id: @guideline.id, number: Check.maximum(:number) + 1, level: @check.level, name_de: @check.name_de, position: @check.position,
|
||||
criterion_de: @check.criterion_de } }
|
||||
end
|
||||
|
||||
|
|
@ -46,7 +46,7 @@ class ChecksControllerTest < ::ControllerTest
|
|||
|
||||
test "should update check" do
|
||||
patch check_url(@check),
|
||||
params: { check: { principle_id: @principle.id, level: @check.level, name_de: @check.t_name, position: @check.position,
|
||||
params: { check: { guideline_id: @guideline.id, level: @check.level, name_de: @check.t_name, position: @check.position,
|
||||
criterion_de: @check.criterion_de } }
|
||||
assert_redirected_to check_url(@check)
|
||||
end
|
||||
|
|
|
|||
49
test/controllers/guidelines_controller_test.rb
Normal file
49
test/controllers/guidelines_controller_test.rb
Normal file
|
|
@ -0,0 +1,49 @@
|
|||
require "test_helper"
|
||||
|
||||
class GuidelinesControllerTest < ActionDispatch::IntegrationTest
|
||||
setup do
|
||||
skip "login"
|
||||
@guideline = guidelines(:one)
|
||||
end
|
||||
|
||||
test "should get index" do
|
||||
get guidelines_url
|
||||
assert_response :success
|
||||
end
|
||||
|
||||
test "should get new" do
|
||||
get new_guideline_url
|
||||
assert_response :success
|
||||
end
|
||||
|
||||
test "should create guideline" do
|
||||
assert_difference("Guideline.count") do
|
||||
post guidelines_url, params: { guideline: { name_de: @guideline.name_de, number: @guideline.number, principle_id: @guideline.principle_id } }
|
||||
end
|
||||
|
||||
assert_redirected_to guideline_url(Guideline.last)
|
||||
end
|
||||
|
||||
test "should show guideline" do
|
||||
get guideline_url(@guideline)
|
||||
assert_response :success
|
||||
end
|
||||
|
||||
test "should get edit" do
|
||||
get edit_guideline_url(@guideline)
|
||||
assert_response :success
|
||||
end
|
||||
|
||||
test "should update guideline" do
|
||||
patch guideline_url(@guideline), params: { guideline: { name_de: @guideline.name_de, number: @guideline.number, principle_id: @guideline.principle_id } }
|
||||
assert_redirected_to guideline_url(@guideline)
|
||||
end
|
||||
|
||||
test "should destroy guideline" do
|
||||
assert_difference("Guideline.count", -1) do
|
||||
delete guideline_url(@guideline)
|
||||
end
|
||||
|
||||
assert_redirected_to guidelines_url
|
||||
end
|
||||
end
|
||||
6
test/fixtures/checks.yml
vendored
6
test/fixtures/checks.yml
vendored
|
|
@ -5,18 +5,18 @@ one:
|
|||
name_de: MyString
|
||||
level: 1
|
||||
number: 1
|
||||
principle: one
|
||||
guideline: one
|
||||
|
||||
two:
|
||||
position: MyString
|
||||
name_de: MyString
|
||||
level: 1
|
||||
number: 2
|
||||
principle: one
|
||||
guideline: one
|
||||
|
||||
deletable:
|
||||
position: MyString
|
||||
name_de: MyString
|
||||
level: 1
|
||||
number: 3
|
||||
principle: one
|
||||
guideline: one
|
||||
|
|
|
|||
11
test/fixtures/guidelines.yml
vendored
Normal file
11
test/fixtures/guidelines.yml
vendored
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
# Read about fixtures at https://api.rubyonrails.org/classes/ActiveRecord/FixtureSet.html
|
||||
|
||||
one:
|
||||
principle: one
|
||||
number: 1
|
||||
name_de: MyString
|
||||
|
||||
two:
|
||||
principle: two
|
||||
number: 1
|
||||
name_de: MyString
|
||||
7
test/models/guideline_test.rb
Normal file
7
test/models/guideline_test.rb
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
require "test_helper"
|
||||
|
||||
class GuidelineTest < ActiveSupport::TestCase
|
||||
# test "the truth" do
|
||||
# assert true
|
||||
# end
|
||||
end
|
||||
45
test/system/guidelines_test.rb
Normal file
45
test/system/guidelines_test.rb
Normal file
|
|
@ -0,0 +1,45 @@
|
|||
require "application_system_test_case"
|
||||
|
||||
class GuidelinesTest < ApplicationSystemTestCase
|
||||
setup do
|
||||
@guideline = guidelines(:one)
|
||||
end
|
||||
|
||||
test "visiting the index" do
|
||||
visit guidelines_url
|
||||
assert_selector "h1", text: "Guidelines"
|
||||
end
|
||||
|
||||
test "should create guideline" do
|
||||
visit guidelines_url
|
||||
click_on "New guideline"
|
||||
|
||||
fill_in "Name de", with: @guideline.name_de
|
||||
fill_in "Number", with: @guideline.number
|
||||
fill_in "Principle", with: @guideline.principle_id
|
||||
click_on "Create Guideline"
|
||||
|
||||
assert_text "Guideline was successfully created"
|
||||
click_on "Back"
|
||||
end
|
||||
|
||||
test "should update Guideline" do
|
||||
visit guideline_url(@guideline)
|
||||
click_on "Edit this guideline", match: :first
|
||||
|
||||
fill_in "Name de", with: @guideline.name_de
|
||||
fill_in "Number", with: @guideline.number
|
||||
fill_in "Principle", with: @guideline.principle_id
|
||||
click_on "Update Guideline"
|
||||
|
||||
assert_text "Guideline was successfully updated"
|
||||
click_on "Back"
|
||||
end
|
||||
|
||||
test "should destroy Guideline" do
|
||||
visit guideline_url(@guideline)
|
||||
click_on "Destroy this guideline", match: :first
|
||||
|
||||
assert_text "Guideline was successfully destroyed"
|
||||
end
|
||||
end
|
||||
Loading…
Add table
Add a link
Reference in a new issue