Compare commits

..

8 Commits

Author SHA1 Message Date
d06f24b1fa upd: utils
Add method to get file permissions
2026-02-16 14:16:37 -05:00
ed774a5a37 upd: profile twig
add saveProfile method and required support
2026-02-16 14:15:51 -05:00
6664a7c71e upd: user
add home church rss field
2026-02-16 14:15:10 -05:00
4be33834d4 upd: ajaxcontroller
add method to save profile and include home church rss feed url
2026-02-16 14:14:42 -05:00
b445295959 upd: database
add home church rss url for users
2026-02-16 14:13:56 -05:00
20ba17c684 git: ignore
ignore composer.lock file
2026-02-16 14:12:44 -05:00
323e668ac9 upd: dockerfile
install logrotate and cron to accomplish scripting to retrieve audio recording links for sermon
2026-02-16 14:12:09 -05:00
0d384a8fa3 fix: install
fix typo
2026-02-16 14:10:51 -05:00
8 changed files with 221 additions and 6 deletions

3
.gitignore vendored
View File

@@ -22,4 +22,5 @@
/assets/vendor/ /assets/vendor/
###< symfony/asset-mapper ### ###< symfony/asset-mapper ###
/references/ /references/
composer.lock

View File

@@ -1,4 +1,4 @@
FROM php:8.4-apache FROM php:8.5-apache
RUN apt update && \ RUN apt update && \
apt upgrade -y && \ apt upgrade -y && \
@@ -14,8 +14,13 @@ RUN apt update && \
sqlite3 \ sqlite3 \
curl \ curl \
git \ git \
cron \
logrotate \
nano nano
RUN service start cron
RUN service enable cron
RUN docker-php-ext-configure gd --with-jpeg RUN docker-php-ext-configure gd --with-jpeg
RUN docker-php-ext-configure zip RUN docker-php-ext-configure zip
@@ -46,6 +51,19 @@ RUN rm -rf /var/www/html/vendor
RUN rm -rf /var/www/html/tests RUN rm -rf /var/www/html/tests
RUN rm -rf /var/www/html/translations RUN rm -rf /var/www/html/translations
RUN echo "20 1 * * 6 root cd /var/www/html && /usr/local/bin/php bin/console app:get-audio > /var/log/sermon-notes.log 2>&1" > /etc/cron.d/get-audio
RUN chmod 644 /etc/cron.d/get-audio
RUN echo "/var/log/sermon-notes.log {
monthly
rotate 12
compress
delaycompress
missingok
notifempty
create 644 root root
}" > /etc/logrotate.d/sermon-notes
RUN COMPOSER_ALLOW_SUPERUSER=1 composer install --no-scripts --no-dev --optimize-autoloader RUN COMPOSER_ALLOW_SUPERUSER=1 composer install --no-scripts --no-dev --optimize-autoloader
RUN mkdir /data RUN mkdir /data

View File

@@ -17,7 +17,7 @@ print "Updating migrations and setting permissions for data folder".PHP_EOL;
// import reference material // import reference material
print "Importing Bible and Eccumenical Creeds".PHP_EOL; print "Importing Bible and Eccumenical Creeds".PHP_EOL;
`symfony console app:ingest-bible /var/www/html/reference/esv-bible`; `symfony console app:ingest-bible /var/www/html/references/esv-bible`;
`symfony console app:import-ref /var/www/html/references/creeds/Apostles 'Apostles Creed' creed apc`; `symfony console app:import-ref /var/www/html/references/creeds/Apostles 'Apostles Creed' creed apc`;
`symfony console app:import-ref /var/www/html/references/creeds/Athanasian 'Athanasian Creed' creed ath`; `symfony console app:import-ref /var/www/html/references/creeds/Athanasian 'Athanasian Creed' creed ath`;
`symfony console app:import-ref /var/www/html/references/creeds/Chalcedon 'Definition of Chalcedon' creed dc`; `symfony console app:import-ref /var/www/html/references/creeds/Chalcedon 'Definition of Chalcedon' creed dc`;
@@ -50,13 +50,13 @@ if ($westminsterStandards) {
`symfony console app:import-wlc /var/www/html/references/wlc 'Westminster Larger' wlc WLC{\$ndx}`; `symfony console app:import-wlc /var/www/html/references/wlc 'Westminster Larger' wlc WLC{\$ndx}`;
} }
$helviticConfessions = ( $helveticConfessions = (
strtolower( strtolower(
readline("Do you want to import the Helvetic Confessions (1st & 2nd) (y/n)? ") readline("Do you want to import the Helvetic Confessions (1st & 2nd) (y/n)? ")
) == 'y' ) == 'y'
); );
if ($helviticConfessions) { if ($helveticConfessions) {
print "Importing Helvitic standards".PHP_EOL; print "Importing Helvitic standards".PHP_EOL;
`symfony console app:import-ref /var/www/html/references/fhc 'First Helvetic Confession' 1hc 1HC{\$ndx}`; `symfony console app:import-ref /var/www/html/references/fhc 'First Helvetic Confession' 1hc 1HC{\$ndx}`;
`symfony console app:import-ref /var/www/html/references/shc 'Second Helvetic Confession' 2hc 2HC{\$ndx}`; `symfony console app:import-ref /var/www/html/references/shc 'Second Helvetic Confession' 2hc 2HC{\$ndx}`;

View File

@@ -0,0 +1,39 @@
<?php
declare(strict_types=1);
namespace DoctrineMigrations;
use Doctrine\DBAL\Schema\Schema;
use Doctrine\Migrations\AbstractMigration;
/**
* Auto-generated Migration: Please modify to your needs!
*/
final class Version20260114224910 extends AbstractMigration
{
public function getDescription(): string
{
return '';
}
public function up(Schema $schema): void
{
// this up() migration is auto-generated, please modify it to your needs
$this->addSql('ALTER TABLE user ADD COLUMN home_church_rss VARCHAR(255) DEFAULT NULL');
}
public function down(Schema $schema): void
{
// this down() migration is auto-generated, please modify it to your needs
$this->addSql('CREATE TEMPORARY TABLE __temp__user AS SELECT id, email, roles, password, name, meta_data FROM user');
$this->addSql('DROP TABLE user');
$this->addSql('CREATE TABLE user (id BLOB NOT NULL --(DC2Type:uuid)
, email VARCHAR(180) NOT NULL, roles CLOB NOT NULL --(DC2Type:json)
, password VARCHAR(255) NOT NULL, name VARCHAR(255) NOT NULL, meta_data CLOB DEFAULT NULL --(DC2Type:json)
, PRIMARY KEY(id))');
$this->addSql('INSERT INTO user (id, email, roles, password, name, meta_data) SELECT id, email, roles, password, name, meta_data FROM __temp__user');
$this->addSql('DROP TABLE __temp__user');
$this->addSql('CREATE UNIQUE INDEX UNIQ_IDENTIFIER_EMAIL ON user (email)');
}
}

View File

@@ -20,6 +20,7 @@ use Exception;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController; use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response; use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\HttpFoundation\JsonResponse;
use Symfony\Component\Mailer\MailerInterface; use Symfony\Component\Mailer\MailerInterface;
use Symfony\Component\Mime\Address; use Symfony\Component\Mime\Address;
use Symfony\Component\Routing\Attribute\Route; use Symfony\Component\Routing\Attribute\Route;
@@ -453,6 +454,47 @@ class AjaxController extends AbstractController
return $res; return $res;
} }
#[Route('/save-profile', name: 'app_save_profile', methods: ['POST'])]
public function saveProfile(Request $req, EntityManagerInterface $emi): Response
{
$data = json_decode($req->getContent());
/** @var App\Entity\User $user */
$user = $this->getUser();
if (!$user) {
return new JsonResponse(['msg' => 'No User']);
}
if ($data->passChange) {
if(!$data->password) {
return new JsonResponse(['msg' => 'Blank password']);
}
// @todo check that password matches current password
if ($data->password != $user->getPassword()) {
return new JsonResponse(['msg' => 'Invalid password']);
}
if ($data->newPassword != $data->confPassword) {
return new JsonResponse(['msg' => 'Passwords don\'t match']);
}
}
$user->setName($data->name);
$user->setEmail($data->email);
$user->setHomeChurchRSS($data->homeChurch);
$emi->persist($user);
try {
$emi->flush();
} catch (Exception $e) {
return new JsonResponse();
}
return new JsonResponse(['msg' => 'Updated']);
}
#[Route('/save-settings', name: 'app_save_settings', methods: ['POST'])] #[Route('/save-settings', name: 'app_save_settings', methods: ['POST'])]
public function saveSettings(Request $req, EntityManagerInterface $emi): Response public function saveSettings(Request $req, EntityManagerInterface $emi): Response
{ {

View File

@@ -71,6 +71,9 @@ class User implements UserInterface, PasswordAuthenticatedUserInterface, JsonSer
#[ORM\Column(nullable: true)] #[ORM\Column(nullable: true)]
private ?array $metaData = null; private ?array $metaData = null;
#[ORM\Column(length: 255, nullable: true)]
private ?string $homeChurchRSS = null;
public function __construct() public function __construct()
{ {
$this->series = new ArrayCollection(); $this->series = new ArrayCollection();
@@ -323,4 +326,16 @@ class User implements UserInterface, PasswordAuthenticatedUserInterface, JsonSer
return $this; return $this;
} }
public function getHomeChurchRSS(): ?string
{
return $this->homeChurchRSS;
}
public function setHomeChurchRSS(?string $homeChurchRSS): static
{
$this->homeChurchRSS = $homeChurchRSS;
return $this;
}
} }

View File

@@ -32,4 +32,58 @@ class Utils
// error message or try to resend the message // error message or try to resend the message
} }
} }
public static function filePerms($file): string
{
$perms = fileperms($file);
switch ($perms & 0xF000) {
case 0xC000: // socket
$info = 's';
break;
case 0xA000: // symbolic link
$info = 'l';
break;
case 0x8000: // regular
$info = 'r';
break;
case 0x6000: // block special
$info = 'b';
break;
case 0x4000: // directory
$info = 'd';
break;
case 0x2000: // character special
$info = 'c';
break;
case 0x1000: // FIFO pipe
$info = 'p';
break;
default: // unknown
$info = 'u';
}
// Owner
$info .= (($perms & 0x0100) ? 'r' : '-');
$info .= (($perms & 0x0080) ? 'w' : '-');
$info .= (($perms & 0x0040) ?
(($perms & 0x0800) ? 's' : 'x' ) :
(($perms & 0x0800) ? 'S' : '-'));
// Group
$info .= (($perms & 0x0020) ? 'r' : '-');
$info .= (($perms & 0x0010) ? 'w' : '-');
$info .= (($perms & 0x0008) ?
(($perms & 0x0400) ? 's' : 'x' ) :
(($perms & 0x0400) ? 'S' : '-'));
// World
$info .= (($perms & 0x0004) ? 'r' : '-');
$info .= (($perms & 0x0002) ? 'w' : '-');
$info .= (($perms & 0x0001) ?
(($perms & 0x0200) ? 't' : 'x' ) :
(($perms & 0x0200) ? 'T' : '-'));
return $info;
}
} }

View File

@@ -6,7 +6,7 @@
<link href="{{ asset('css/main.css') }}" rel="stylesheet" /> <link href="{{ asset('css/main.css') }}" rel="stylesheet" />
<link href="{{ asset('css/jquery-ui.theme.css') }}" rel='stylesheet' /> <link href="{{ asset('css/jquery-ui.theme.css') }}" rel='stylesheet' />
<link href="{{ asset('css/jquery-ui.structure.css') }}" rel='stylesheet' /> <link href="{{ asset('css/jquery-ui.structure.css') }}" rel='stylesheet' />
<link href="{{ asset('css/style.css') }}" rel='stylesheet' /> <link href="{{ asset('styles/style.css') }}" rel='stylesheet' />
<link href='//cdn.datatables.net/2.0.8/css/dataTables.dataTables.min.css' rel='stylesheet' /> <link href='//cdn.datatables.net/2.0.8/css/dataTables.dataTables.min.css' rel='stylesheet' />
<style> <style>
.flex-container { .flex-container {
@@ -80,6 +80,49 @@ $(function() {
}); });
function saveProfile() {
const name = $('#name');
const email = $('#email');
const homeChurch = $('#home-church');
const password = $('#password');
const newPassword = $('#new-password');
const confPassword = $('#conf-password');
let passChange = false;
if (newPassword.val().length > 0) {
if (password.val().length == 0) {
alert('If you want to change your password you need to put in the current password as well');
return;
}
if (newPassword.val() != confPassword.val()) {
alert('New password and confirm passwords do not match');
return;
}
passChange = true;
}
fetch('/save-profile', {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify({
'name': name.val(),
'email': email.val(),
'homeChurch': homeChurch.val(),
'passChange': passChange,
'password': password.val(),
'newPassword': newPassword.val(),
'confPassword': confPassword.val()
})
})
.then(response => response.json())
.then(results => {
alert(results.msg);
});
}
function saveSettings() { function saveSettings() {
var saveInterval = $('#save-interval'); var saveInterval = $('#save-interval');
var saveReferences = $('#save-references'); var saveReferences = $('#save-references');
@@ -128,6 +171,9 @@ function rollUp(cont) {
<label for='email'>Email: </label> <label for='email'>Email: </label>
<input type='email' id='email' name='email' value='{{ app.user.email }}' /><br /> <input type='email' id='email' name='email' value='{{ app.user.email }}' /><br />
<label for='home-church'>Home Church RSS Feed: </label>
<input type='text' id='home-church' name='home-church' value='{{ app.user.homeChurchRSS }}' /><br />
<label for='password'>Password: </label> <label for='password'>Password: </label>
<input type='password' id='password' name='password' /><br/> <input type='password' id='password' name='password' /><br/>