add entry requests, invite new users
CI / scan_ruby (push) Failing after 12s
CI / scan_js (push) Successful in 11s
CI / lint (push) Failing after 19s
CI / test (push) Successful in 34s

This commit is contained in:
2026-01-30 01:28:53 +01:00
parent b64ad52d30
commit 530021960e
35 changed files with 1838 additions and 118 deletions
@@ -12,6 +12,7 @@ class Admin::DashboardController < Admin::BaseController
@pending_suggestions_count = SuggestedMeaning.pending.count
@accepted_suggestions_count = SuggestedMeaning.accepted.count
@rejected_suggestions_count = SuggestedMeaning.rejected.count
@requested_entries_count = Entry.requested.count
@comment_count = Comment.count
@@ -0,0 +1,64 @@
class Admin::RequestsController < Admin::BaseController
def index
@requested_entries = Entry.requested
.includes(:requested_by)
.order(created_at: :desc)
@approved_entries = Entry.approved
.includes(:requested_by)
.order(updated_at: :desc)
end
def show
@entry = Entry.find(params[:id])
end
def edit
@entry = Entry.find(params[:id])
end
def update
@entry = Entry.find(params[:id])
if @entry.update(entry_params)
redirect_to admin_request_path(@entry), notice: "Request updated successfully."
else
flash.now[:alert] = "Error updating request."
render :edit, status: :unprocessable_entity
end
end
def approve
@entry = Entry.find(params[:id])
@user = @entry.requested_by
@user.update!(
invitation_token: SecureRandom.urlsafe_base64(32),
invitation_sent_at: Time.current,
invited_by: current_user
)
@entry.update!(status: :approved)
InvitationMailer.invite(@user, approved_entry: @entry).deliver_later
redirect_to admin_requests_path, notice: "Request approved and invitation sent to #{@user.email}."
end
def reject
@entry = Entry.find(params[:id])
@user = @entry.requested_by
entry_preview = [@entry.fi, @entry.en, @entry.sv, @entry.no, @entry.ru, @entry.de].compact.first || "Entry"
@entry.destroy!
@user.destroy! if @user.requested_entries.count.zero?
redirect_to admin_requests_path, notice: "Request '#{entry_preview}' has been rejected and deleted."
end
private
def entry_params
params.require(:entry).permit(:category, :fi, :en, :sv, :no, :ru, :de, :notes)
end
end
+7 -6
View File
@@ -9,7 +9,7 @@ class EntriesController < ApplicationController
@page = [ params[:page].to_i, 1 ].max
@per_page = 25
entries_scope = Entry.all
entries_scope = Entry.active_entries
entries_scope = entries_scope.with_category(@category)
entries_scope = entries_scope.search(@query, language_code: @language_code)
entries_scope = entries_scope.starts_with(@starts_with, language_code: @language_code) if @starts_with.present?
@@ -20,17 +20,18 @@ class EntriesController < ApplicationController
@total_pages = (@total_entries.to_f / @per_page).ceil
@entries = entries_scope.offset((@page - 1) * @per_page).limit(@per_page)
@entry_count = Entry.count
@verified_count = Entry.where(verified: true).count
@entry_count = Entry.active_entries.count
@requested_count = Entry.requested.count
@verified_count = Entry.active_entries.where(verified: true).count
@needs_review_count = @entry_count - @verified_count
@complete_entries_count = supported_languages.reduce(Entry.all) do |scope, language|
@complete_entries_count = supported_languages.reduce(Entry.active_entries) do |scope, language|
scope.where.not(language.code => [ nil, "" ])
end.count
@missing_entries_count = @entry_count - @complete_entries_count
@language_completion = supported_languages.index_with do |language|
next 0 if @entry_count.zero?
(Entry.where.not(language.code => [ nil, "" ]).count * 100.0 / @entry_count).round
(Entry.active_entries.where.not(language.code => [ nil, "" ]).count * 100.0 / @entry_count).round
end
if @language_code.present?
@@ -61,7 +62,7 @@ class EntriesController < ApplicationController
end
def download
@entries = Entry.order(:id)
@entries = Entry.active_entries.order(:id)
respond_to do |format|
format.xlsx do
filename = "sanasto-entries-#{Time.zone.today}.xlsx"
@@ -21,6 +21,9 @@ class InvitationsController < ApplicationController
invitation_token: nil
)
# Activate approved entries by this user
Entry.where(requested_by: @user, status: :approved).update_all(status: :active)
session[:user_id] = @user.id
redirect_to admin? ? admin_root_path : root_path, notice: "Welcome to Sanasto Wiki, #{@user.name}!"
else
+75
View File
@@ -0,0 +1,75 @@
class RequestsController < ApplicationController
def new
@entry = Entry.new
if current_user
@pending_count = current_user.requested_entries.where(status: [ :requested, :approved ]).count
elsif params[:email].present?
@pending_count = User.find_by(email: params[:email])&.requested_entries&.where(status: [ :requested, :approved ])&.count || 0
else
@pending_count = 0
end
end
def create
# If user is logged in, use their account
if current_user
@user = current_user
else
# Anonymous submission - need to find or create user
email = request_params[:email]
existing_user = User.find_by(email: email)
# Check if user has already accepted an invitation
if existing_user&.invitation_accepted_at.present?
redirect_to login_path, alert: "An account with this email already exists. Please log in."
return
end
# Use existing pending user or create new one
@user = existing_user || User.new(
name: request_params[:name],
email: email,
password: SecureRandom.alphanumeric(32),
role: :contributor
)
end
# Create entry in a transaction
ActiveRecord::Base.transaction do
# Save user only if it's a new record
if @user.new_record? && !@user.save
@pending_count = 0
@entry = Entry.new(entry_params)
flash.now[:alert] = "There was an error submitting your request. Please check the form."
render :new, status: :unprocessable_entity
raise ActiveRecord::Rollback
return
end
# Create entry
@entry = Entry.new(entry_params)
@entry.status = :requested
@entry.requested_by = @user
if @entry.save
redirect_to root_path, notice: "Thank you for your request! We'll review it and get back to you soon."
else
@pending_count = 0
flash.now[:alert] = "There was an error submitting your request. Please check the form."
render :new, status: :unprocessable_entity
raise ActiveRecord::Rollback
end
end
end
private
def request_params
params.require(:entry).permit(:name, :email, :category, :fi, :en, :sv, :no, :ru, :de, :notes)
end
def entry_params
request_params.except(:name, :email)
end
end