Compare commits

...

3 commits

Author SHA1 Message Date
4c8d58b646
add en_CAT makefile and use ucfirst/ucwords 2024-04-05 15:00:27 -04:00
adc545a0cb
user menu 2024-04-05 13:46:33 -04:00
b6ae609ee3
follow ppl 2024-04-05 12:58:11 -04:00
31 changed files with 604 additions and 135 deletions

1
.gitignore vendored
View file

@ -1 +1,2 @@
/data
/src/web/lang/en_CAT

View file

@ -0,0 +1,15 @@
CREATE VIEW api.follow AS
SELECT
f.id,
f.follower_id,
f.followee_id,
f.value,
f.created,
f.modified
FROM
admin.follow f;
GRANT SELECT ON TABLE api.follow
TO rest_anon, rest_user;
GRANT SELECT ON TABLE admin.follow
TO rest_anon, rest_user;

View file

@ -0,0 +1,32 @@
CREATE FUNCTION _api.follow_delete()
RETURNS TRIGGER
LANGUAGE plpgsql VOLATILE
AS $BODY$
DECLARE
_user_id INTEGER;
BEGIN
_user_id = _api.get_user_id();
IF OLD.follower_id <> _user_id THEN
PERFORM _api.raise_deny();
END IF;
UPDATE admin.follow SET
value = FALSE,
modified = clock_timestamp()
WHERE id = OLD.id;
END
$BODY$;
GRANT EXECUTE ON FUNCTION _api.follow_delete()
TO rest_user;
GRANT DELETE ON TABLE api.follow
TO rest_user;
GRANT UPDATE ON TABLE admin.follow
TO rest_user;
CREATE TRIGGER api_follow_delete_trgr
INSTEAD OF DELETE
ON api.follow
FOR EACH ROW
EXECUTE PROCEDURE _api.follow_delete();

View file

@ -0,0 +1,46 @@
CREATE FUNCTION _api.follow_insert()
RETURNS TRIGGER
LANGUAGE plpgsql VOLATILE
AS $BODY$
DECLARE
_user_id INTEGER;
BEGIN
_user_id = _api.get_user_id();
IF NEW.followee_id IS NULL THEN
-- for now
PERFORM _api.raise_deny();
END IF;
NEW.value := COALESCE(NEW.value, TRUE);
INSERT INTO admin.follow (
follower_id,
followee_id,
value
) VALUES (
_user_id,
NEW.followee_id,
NEW.value
)
RETURNING id
INTO NEW.id;
RETURN NEW;
END
$BODY$;
GRANT EXECUTE ON FUNCTION _api.follow_insert()
TO rest_user;
GRANT INSERT ON TABLE api.follow
TO rest_user;
GRANT INSERT ON TABLE admin.follow
TO rest_user;
GRANT UPDATE ON TABLE sys.follow_id_seq
TO rest_user;
CREATE TRIGGER api_follow_insert_trgr
INSTEAD OF INSERT
ON api.follow
FOR EACH ROW
EXECUTE PROCEDURE _api.follow_insert();

View file

@ -0,0 +1,44 @@
CREATE FUNCTION _api.follow_update()
RETURNS TRIGGER
LANGUAGE plpgsql VOLATILE
AS $BODY$
DECLARE
_user_id INTEGER;
_changed BOOLEAN;
BEGIN
_user_id = _api.get_user_id();
_changed = FALSE;
IF OLD.follower_id <> _user_id THEN
PERFORM _api.raise_deny();
END IF;
NEW.value = COALESCE(NEW.value, OLD.value);
IF NEW.value IS DISTINCT FROM OLD.value THEN
_changed = TRUE;
END IF;
IF _changed THEN
UPDATE admin.follow SET
value = NEW.value,
modified = clock_timestamp()
WHERE id = OLD.id;
END IF;
RETURN NEW;
END
$BODY$;
GRANT EXECUTE ON FUNCTION _api.follow_update()
TO rest_user;
GRANT UPDATE ON TABLE api.follow
TO rest_user;
GRANT UPDATE ON TABLE admin.follow
TO rest_user;
CREATE TRIGGER api_follow_update_trgr
INSTEAD OF UPDATE
ON api.follow
FOR EACH ROW
EXECUTE PROCEDURE _api.follow_update();

View file

@ -45,6 +45,12 @@ GRANT USAGE ON SCHEMA _api TO rest_anon, rest_user;
\i /db/rest/like/api_like_update.sql;
\i /db/rest/like/api_like_delete.sql;
-- follow
\i /db/rest/follow/api_follow.sql;
\i /db/rest/follow/api_follow_insert.sql;
\i /db/rest/follow/api_follow_update.sql;
\i /db/rest/follow/api_follow_delete.sql;
-- media
\i /db/rest/media/_api_serve_user_media.sql;
\i /db/rest/media/_api_serve_system_media.sql;

View file

@ -155,6 +155,15 @@ a, button {
background-color: var(--surface2);
}
.btn-alt.btn-blue {
background-color: var(--blue);
color: var(--white);
}
.btn-alt.btn-blue:hover {
background-color: var(--blue-alt);
}
.btn-wide {
width: auto;
flex-grow: 1;
@ -437,8 +446,8 @@ input.btn:focus {
@keyframes slideIn {
0% {
animation-timing-function: ease-out;
transform: translate(0, -50%);
animation-timing-function: ease-in;
transform: translate(0, -20%);
}
}
@ -604,3 +613,35 @@ input[type=radio] {
width: fit-content;
outline: none !important;
}
.container {
padding: 1rem;
}
.grow {
flex-grow: 1;
}
#user-menu {
position: fixed;
right: .5rem;
top: 4rem;
min-width: fit-content;
animation: fadeIn .1s, slideIn .1s linear;
}
#user-menu .btn {
width: 100%;
}
#user-menu-header {
align-items: center;
}
#user-menu-header .pfp {
margin-right: 1rem;
}
.hidden {
display: none;
}

View file

@ -10,19 +10,14 @@
.post, #new-post {
margin-bottom: 1rem;
width: 40rem;
max-width: 40rem;
width: 100%;
}
.post {
padding-bottom: 0;
}
@media(max-width: 40rem) {
.post, #new-post {
width: 100%;
}
}
.post .likes {
display: block;
padding-top: .25rem;

View file

@ -10,7 +10,6 @@
flex-direction: row;
justify-content: center;
background-color: var(--surface0);
margin-bottom: 1rem;
border-bottom: 1px solid var(--surface1);
}
@ -85,10 +84,9 @@
.tab {
max-width: 80rem;
width: 100%;
height: 100%;
margin-left: auto;
margin-right: auto;
padding: 0 1rem;
margin-bottom: 1rem;
}
#post-container {
@ -106,3 +104,22 @@
td:nth-child(1) {
padding-right: 2rem;
}
#about-tab {
width: auto;
}
#follow-container {
margin-left: auto;
margin-right: 2rem;
}
#follow-container > a {
width: 10rem;
padding: .5rem;
}
#follow-container > a > span {
width: 100%;
text-align: center;
}

View file

@ -11,7 +11,7 @@ class Modal_controller extends Controller {
* @param array $data
*/
private function modal($name, $data = array()): void {
$title = lang($name . '_modal_title');
$title = ucwords(lang($name . '_modal_title'));
$data['title'] = $title;
$data['content'] = $name;
$this->view('template/modal', $data);

View file

@ -7,7 +7,7 @@ class Auth_model extends Model {
public function get_data(): ?array {
$data = parent::get_data();
$data['title'] = lang('login');
$data['title'] = ucfirst(lang('login'));
return $data;
}
}

View file

@ -8,15 +8,15 @@ class Error_model extends Model {
private function get_msg(&$data) {
if (!array_key_exists('code', $_GET)) {
http_response_code(500);
$data['msg'] = lang('error');
$data['msg'] = ucfirst(lang('error'));
$data['title'] = '500';
} else {
$code = $_GET['code'];
http_response_code($code);
$data['title'] = $code;
$msg = lang('error_' . $code, FALSE);
$msg = ucfirst(lang('error_' . $code, FALSE));
if (!$msg) {
$msg = lang('error');
$msg = ucfirst(lang('error'));
}
$data['msg'] = $msg;
}

View file

@ -15,7 +15,7 @@ class Home_model extends Model {
public function get_data(): ?array {
$data = parent::get_data();
$data['title'] = lang('title');
$data['title'] = ucfirst(lang('title'));
$data['posts'] = $this->get_posts();
return $data;
}

View file

@ -24,13 +24,15 @@ class People_model extends Model {
case 'follower': {
$query = $query
->join('admin.follow f', 'f.follower_id = u.id AND f.followee_id', 'INNER')
->eq($filter_uid);
->eq($filter_uid)
->where('f.value = TRUE');
} break;
case 'followee': {
$query = $query
->join('admin.follow f', 'f.followee_id = u.id AND f.follower_id', 'INNER')
->eq($filter_uid);
->eq($filter_uid)
->where('f.value = TRUE');
} break;
}
}
@ -82,7 +84,7 @@ class People_model extends Model {
public function get_data(): ?array {
$data = parent::get_data();
$data['title'] = lang('title');
$data['title'] = ucfirst(lang('title'));
return $data;
}
}

View file

@ -29,9 +29,37 @@ class Profile_model extends Model {
return NULL;
}
$following = FALSE;
$followed = FALSE;
$follow_id = NULL;
if ($this->main->session) {
$sid = $this->main->user()['id'];
$res = $this->db->select('f.value, f.id')
->from('admin.follow f')
->where('f.follower_id')
->eq($sid)
->where('f.followee_id')
->eq($uid)
->row();
$following = $res ? $res['value'] : FALSE;
$follow_id = $res ? $res['id'] : NULL;
$res = $this->db->select('f.value')
->from('admin.follow f')
->where('f.follower_id')
->eq($uid)
->where('f.followee_id')
->eq($sid)
->row();
$followed = $res ? $res['value'] : FALSE;
}
$data = parent::get_data();
$data['user'] = $user;
$data['title'] = lang('title', sub: [$user['first_name']]);
$data['following'] = $following;
$data['followed'] = $followed;
$data['follow_id'] = $follow_id;
$data['title'] = ucfirst(lang('title', sub: [$user['first_name']]));
return $data;
}
}

View file

@ -3,7 +3,7 @@
<div id="main-content">
<div class="branding col">
<h1>xssbook</h1>
<span><?=lang('login_branding')?></span>
<span><?=ucfirst(lang('login_branding'))?></span>
</div>
<div class="form card col">
<form id="action-login" class="col" action="">
@ -13,9 +13,10 @@
name="username"
id="login-username"
placeholder=" "
autofocus="true"
>
<label for="username">
<?=lang('ph_username')?>
<?=ucfirst(lang('ph_username'))?>
</label>
</div>
<div class="rel mb">
@ -26,7 +27,7 @@
placeholder=" "
>
<label for="password">
<?=lang('ph_password')?>
<?=ucfirst(lang('ph_password'))?>
</label>
</div>
<?=ilang('action_login',

View file

@ -2,5 +2,5 @@
<?php /* vi: syntax=php */ ?>
<div id="main-content">
<h1><?=$title?></h1>
<span><?=$msg?></span>
<span><?=ucfirst($msg)?></span>
</div>

View file

@ -1,6 +1,6 @@
<?php /* Copyright (c) 2024 Freya Murphy */ ?>
<?php /* vi: syntax=php */ ?>
<div id="main-content">
<div id="main-content" class="container">
<?php if ($self): ?>
<div id="new-post" class="card">
<div class="row grow">
@ -9,9 +9,9 @@
id="action-new-post"
class="btn btn-alt btn-wide ml"
autocomplete="off"
aria-label="<?=lang('action_new_post_tip')?>"
aria-label="<?=ucfirst(lang('action_new_post_tip'))?>"
>
<?=lang('action_new_post_text', sub: [$self['first_name']])?>
<?=ucfirst(lang('action_new_post_text', sub: [$self['first_name']]))?>
</a>
</div>
<script>

View file

@ -1,6 +1,6 @@
<?php /* Copyright (c) 2024 Freya Murphy */ ?>
<?php /* vi: syntax=php */ ?>
<div id="main-content" class="col">
<h1 class="title"><?=lang('title')?></h1>
<h3 class="desc"><?=lang('desc')?></h3>
<h1 class="title"><?=ucfirst(lang('title'))?></h1>
<h3 class="desc"><?=ucfirst(lang('desc'))?></h3>
<hr>

View file

@ -8,12 +8,122 @@
<div class="pfp-wrapper">
<?=pfp($user)?>
</div>
<div class="col content">
<strong class="name"><?=$this->format_model->name($user)?></strong>
<span class="dim"><?=$user['follower_count'] . ' ' . lang('followers')?></span>
<div class="col content grow">
<div class="row grow">
<div class="col">
<strong class="name"><?=$this->format_model->name($user)?></strong>
<span class="dim"><?=$user['follower_count'] . ' ' . ucfirst(lang('followers'))?></span>
</div>
<?php if (
$this->main->session &&
(!isset($self) || $self['id'] != $user['id'])
): ?>
<div id="follow-container">
<?=ilang(
'action_follow',
id: 'action-follow-follow',
class: 'btn btn-alt',
style: (!$following && !$followed) ? '' : 'display: none',
sub: [$user['first_name']]
)?>
<?=ilang(
'action_follow_back',
id: 'action-follow-follow-back',
class: 'btn btn-alt',
style: (!$following && $followed) ? '' : 'display: none',
sub: [$user['first_name']]
)?>
<?=ilang(
'action_following',
id: 'action-follow-following',
class: 'btn btn-alt btn-blue',
style: ($following && !$followed) ? '' : 'display: none',
sub: [$user['first_name']]
)?>
<?=ilang(
'action_friends',
id: 'action-follow-friends',
class: 'btn btn-alt btn-blue',
style: ($following && $followed) ? '' : 'display: none',
sub: [$user['first_name']]
)?>
</div>
<script>
let following = <?=json_encode($following)?>;
let followed = <?=json_encode($followed)?>;
let followId = <?=json_encode($follow_id)?>;
let followee_id = <?=json_encode($user['id'])?>;
let btns = {};
const disableBtn = (btn) => {
btn.css('display', 'none');
};
const enableBtn = (btn) => {
btn.css('display', '');
};
const updateFollow = () => {
for (let btn of Object.values(btns)) {
disableBtn(btn);
}
if (!following && !followed) {
enableBtn(btns['follow']);
} else if (!following && followed) {
enableBtn(btns['follow-back']);
} else if (following && !followed) {
enableBtn(btns['following']);
} else if (following && followed) {
enableBtn(btns['friends']);
}
}
const onPatchFollow = (data) => {
following = data[0].value;
updateFollow();
}
const onPostFollow = (data) => {
followId = data[0].id;
following = true;
updateFollow();
}
const onClickFollow = () => {
if (followId) {
$.ajax({
url: '/api/follow?id=eq.' + followId,
method: 'PATCH',
data: JSON.stringify({ followee_id, value: !following }),
success: onPatchFollow
});
} else {
$.ajax({
url: '/api/follow',
method: 'POST',
data: JSON.stringify({ followee_id, value: !following }),
success: onPostFollow,
});
}
}
const loadBtn = (name) => {
let btn = $('#action-follow-' + name);
btn.on('click', onClickFollow);
btns[name] = btn;
};
loadBtn('follow');
loadBtn('follow-back');
loadBtn('following');
loadBtn('friends');
</script>
<?php endif; ?>
</div>
<?php if(strlen($user['profile_bio']) > 0): ?>
<br>
<strong><?=lang('bio')?></strong>
<strong><?=ucfirst(lang('bio'))?></strong>
<span class="dim"><?=$user['profile_bio']?></span>
<?php endif; ?>
</div>
@ -43,78 +153,79 @@
</div>
</div>
</div>
<div id="tab-posts" class="tab">
<div id="tab-container" class="container">
<div id="tab-posts" class="tab">
<?php
$_GET['user_id'] = $user['id'];
$this->post_controller->index();
?>
</div>
<div id="tab-about" class="tab card">
<h1><?=ucfirst(lang('about_general'))?></h1>
<table>
<tr>
<td><strong><?=ucfirst(lang('about_general_username'))?></strong></td>
<td><?=$user['username']?></td>
</tr>
<tr>
<td><strong><?=ucfirst(lang('about_general_full_name'))?></strong></td>
<td><?=$user['first_name'] . ' ' . $user['last_name']?></td>
</tr>
<tr>
<td><strong><?=ucfirst(lang('about_general_email'))?></strong></td>
<td><?=$user['email']?></td>
</tr>
<tr>
<td><strong><?=ucfirst(lang('about_general_gender'))?></strong></td>
<td><?=$user['gender']?></td>
</tr>
<tr>
<td><strong><?=ucfirst(lang('about_general_birth_date'))?></strong></td>
<td><?=$user['birth_date']?></td>
</tr>
</table>
<h1><?=ucfirst(lang('about_stats'))?></h1>
<table>
<tr>
<td><strong><?=ucfirst(lang('about_stats_posts'))?></strong></td>
<td><?=$user['post_count']?></td>
</tr>
<tr>
<td><strong><?=ucfirst(lang('about_stats_like'))?></strong></td>
<td><?=$user['like_count']?></td>
</tr>
<tr>
<td><strong><?=ucfirst(lang('about_stats_comments'))?></strong></td>
<td><?=$user['comment_count']?></td>
</tr>
<tr>
<td><strong><?=ucfirst(lang('about_stats_following'))?></strong></td>
<td><?=$user['followed_count']?></td>
</tr>
<tr>
<td><strong><?=ucfirst(lang('about_stats_joined'))?></strong></td>
<td><?=$user['created']?></td>
</tr>
<tr>
<td><strong><?=ucfirst(lang('about_stats_seen'))?></strong></td>
<td><?=$user['seen']?></td>
</tr>
</table>
</div>
<div id="tab-followers" class="tab">
<?php
$_GET['user_id'] = $user['id'];
$this->post_controller->index();
$_GET['filter'] = 'follower';
$_GET['uid'] = $user['id'];
$this->people_controller->content();
?>
</div>
<div id="tab-about" class="tab">
<h1><?=lang('about_general')?></h1>
<table>
<tr>
<td><strong><?=lang('about_general_username')?></strong></td>
<td><?=$user['username']?></td>
</tr>
<tr>
<td><strong><?=lang('about_general_full_name')?></strong></td>
<td><?=$user['first_name'] . ' ' . $user['last_name']?></td>
</tr>
<tr>
<td><strong><?=lang('about_general_email')?></strong></td>
<td><?=$user['email']?></td>
</tr>
<tr>
<td><strong><?=lang('about_general_gender')?></strong></td>
<td><?=$user['gender']?></td>
</tr>
<tr>
<td><strong><?=lang('about_general_birth_date')?></strong></td>
<td><?=$user['birth_date']?></td>
</tr>
</table>
<h1><?=lang('about_stats')?></h1>
<table>
<tr>
<td><strong><?=lang('about_stats_posts')?></strong></td>
<td><?=$user['post_count']?></td>
</tr>
<tr>
<td><strong><?=lang('about_stats_like')?></strong></td>
<td><?=$user['like_count']?></td>
</tr>
<tr>
<td><strong><?=lang('about_stats_comments')?></strong></td>
<td><?=$user['comment_count']?></td>
</tr>
<tr>
<td><strong><?=lang('about_stats_following')?></strong></td>
<td><?=$user['followed_count']?></td>
</tr>
<tr>
<td><strong><?=lang('about_stats_joined')?></strong></td>
<td><?=$user['created']?></td>
</tr>
<tr>
<td><strong><?=lang('about_stats_seen')?></strong></td>
<td><?=$user['seen']?></td>
</tr>
</table>
</div>
<div id="tab-followers" class="tab">
<?php
$_GET['filter'] = 'follower';
$_GET['uid'] = $user['id'];
$this->people_controller->content();
?>
</div>
<div id="tab-following" class="tab">
<?php
$_GET['filter'] = 'followee';
$_GET['uid'] = $user['id'];
$this->people_controller->content();
?>
</div>
</div>
<div id="tab-following" class="tab">
<?php
$_GET['filter'] = 'followee';
$_GET['uid'] = $user['id'];
$this->people_controller->content();
?>
</div>
</div>
<script>
let tabs = {};

View file

@ -13,19 +13,19 @@
id="action-home"
class="btn<?=$this->main->info['app'] == 'home' ? ' btn-blue btn-border' : ''?>"
href="/home"
title="<?=lang('action_home_tip')?>"
title="<?=ucfirst(lang('action_home_tip'))?>"
>
<i class="mi mi-lg">home</i>
<span><?=lang('action_home_text')?></span>
<span><?=ucfirst(lang('action_home_text'))?></span>
</a>
<a
id="action-people"
class="btn<?=$this->main->info['app'] == 'people' ? ' btn-blue btn-border' : ''?>"
href="/people"
title="<?=lang('action_people_tip')?>"
title="<?=ucfirst(lang('action_people_tip'))?>"
>
<i class="mi mi-lg">people</i>
<span><?=lang('action_people_text')?></span>
<span><?=ucfirst(lang('action_people_text'))?></span>
</a>
<!--a
id="action-chat"
@ -40,13 +40,45 @@
<div class="nav-right">
<button
id="action-hamburger"
title="<?=lang('action_hamburger_tip')?>"
title="<?=ucfirst(lang('action_hamburger_tip'))?>"
class="btn mr"
>
<i class="mi mi-lg">menu</i>
</button>
<?php if($self): ?>
<?=pfp($self)?>
<script>
var userMenu = null;
var toggleUserMenu = () => {
userMenu.toggleClass('hidden');
}
</script>
<?=pfp($self, FALSE, 'toggleUserMenu()')?>
<div class="card col hidden" id="user-menu">
<span class="row" id="user-menu-header">
<?=pfp($self, FALSE)?>
<span class="col">
<strong><?=$this->format_model->name($self)?></strong>
<span class="dim"><?=$self['username']?></span>
</span>
</span>
<hr>
<?=ilang(
'action_profile',
class: 'btn',
href: '/profile?id=' . $self['id']
)?>
<?=ilang(
'action_settings',
class: 'btn',
href: '/settings'
)?>
<?=ilang(
'action_logout',
class: 'btn',
href: '/auth/logout'
)?>
</div>
<?php else: ?>
<?=ilang('action_login', class: 'btn', href: '/auth/login')?>
<?php endif; ?>
@ -58,3 +90,14 @@
});
</script>
</header>
<script>
userMenu = $('#user-menu');
var nav = $('.nav');
document.onclick = function(event) {
console.log(event.target, nav[0]);
let outside = !(nav[0].contains(event.target));
if (outside) {
userMenu.addClass('hidden');
}
};
</script>

View file

@ -9,14 +9,14 @@
<?=pfp($user)?>
<div class="col ml">
<strong><?=$user['first_name'] . ' ' . $user['last_name']?></strong>
<span class="dim"><?=lang('now')?></span>
<span class="dim"><?=ucfirst(lang('now'))?></span>
</div>
</div>
<textarea
type="text"
name="content"
id="new-post-content"
placeholder="<?=lang('action_new_post_text', sub: [$user['first_name']])?>"
placeholder="<?=ucfirst(lang('action_new_post_text', sub: [$user['first_name']]))?>"
></textarea>
</div>
<div class="modal-footer">

View file

@ -4,7 +4,7 @@
<form id="register-form">
<div class="modal-content register-modal col">
<label class="static">
<?=lang('ph_basic_info')?>
<?=ucwords(lang('ph_basic_info'))?>
</label>
<div class="row mt">
<div class="rel btn-wide">
@ -15,7 +15,7 @@
placeholder=" "
>
<label for="first_name">
<?=lang('ph_first_name')?>
<?=ucwords(lang('ph_first_name'))?>
</label>
</div>
<div class="rel ml btn-wide">
@ -26,7 +26,7 @@
placeholder=" "
>
<label for="last_name">
<?=lang('ph_last_name')?>
<?=ucwords(lang('ph_last_name'))?>
</label>
</div>
</div>
@ -38,7 +38,7 @@
placeholder=" "
>
<label for="username">
<?=lang('ph_username')?>
<?=ucwords(lang('ph_username'))?>
</label>
</div>
<div class="rel mt">
@ -49,7 +49,7 @@
placeholder=" "
>
<label for="password">
<?=lang('ph_password')?>
<?=ucwords(lang('ph_password'))?>
</label>
</div>
<div class="rel mt">
@ -60,11 +60,11 @@
placeholder=" "
>
<label for="email">
<?=lang('ph_email')?>
<?=ucwords(lang('ph_email'))?>
</label>
</div>
<label for="birth_date" class="mt static">
<?=lang('ph_birth_date')?>
<?=ucwords(lang('ph_birth_date'))?>
</label>
<input
class="mt"
@ -73,7 +73,7 @@
id="register-birth-date"
>
<label for="gender" class="mt static">
<?=lang('ph_gender')?>
<?=ucwords(lang('ph_gender'))?>
</label>
<div class="row mt" data-type="radio" data-name="gender-wrapper">
<div class="rel radio mr">
@ -81,13 +81,13 @@
type="radio"
id="register-gender-male"
name="gender"
value="male"
value="Male"
>
<label
for="register-gender-male"
class="static"
>
<?=lang('ph_gender_male')?>
<?=ucwords(lang('ph_gender_male'))?>
</label>
</div>
<div class="rel radio mr">
@ -95,13 +95,13 @@
type="radio"
id="register-gender-female"
name="gender"
value="female"
value="Female"
>
<label
for="register-gender-female"
class="static"
>
<?=lang('ph_gender_female')?>
<?=ucwords(lang('ph_gender_female'))?>
</label>
</div>
<div class="rel radio">
@ -109,13 +109,13 @@
type="radio"
id="register-gender-lettuce"
name="gender"
value="lettuce"
value="Lettuce"
>
<label
for="register-gender-lettuce"
class="static"
>
<?=lang('ph_gender_lettuce')?>
<?=ucwords(lang('ph_gender_lettuce'))?>
</label>
</div>
</div>

View file

@ -21,7 +21,7 @@
$post_attrs['likeId'] = $post['like_id'];
}
?>
<span class="likes dim"><span class="count"><?=$post['like_count']?></span><?=' ' . lang('likes')?></span>
<span class="likes dim"><span class="count"><?=$post['like_count']?></span><?=' ' . ucfirst(lang('likes'))?></span>
<?php if ($self): ?>
<hr>
<div class="row">
@ -76,8 +76,8 @@
autocomplete="off"
type="text"
name="text"
placeholder="<?=lang('action_new_comment_text')?>"
aria-label="<?=lang('action_new_comment_tip')?>"
placeholder="<?=ucfirst(lang('action_new_comment_text'))?>"
aria-label="<?=ucfirst(lang('action_new_comment_tip'))?>"
>
</form>
</div>

View file

@ -10,6 +10,9 @@ abstract class Controller {
// the database
public $db;
// the format model
protected $format_model;
/**
* Creates a constructor
* @param Loader $load - the website loaded object
@ -26,6 +29,8 @@ abstract class Controller {
if ($app) {
$this->load->app_lang($lang, $app);
}
$this->format_model = $this->load->model('format');
}
public function index() {}

View file

@ -1,6 +1,6 @@
<?php /* Copyright (c) 2024 Freya Murphy */
function image($src, $class = NULL, $link = NULL): string {
function image($src, $class = NULL, $link = NULL, $click = NULL): string {
if ($class) {
$class = 'image-loading ' . $class;
} else {
@ -11,12 +11,16 @@ function image($src, $class = NULL, $link = NULL): string {
if ($link) {
$content .= '<a class="' . $class . '" href="' . $link . '">';
} else if ($click) {
$content .= '<button class="' . $class . '" onclick="' . $click . '">';
} else {
$content .= '<span class="' . $class . '">';
}
$content .= '<img src="' . $src . '" onerror="onImgError(this)" onload="onImgLoad(this)"/>';
if ($link) {
$content .= '</a>';
} else if ($click) {
$content .= '</button>';
} else {
$content .= '</span>';
}
@ -26,8 +30,11 @@ function image($src, $class = NULL, $link = NULL): string {
function pfp(
$user,
$embedLink = TRUE,
$link = TRUE,
$click = NULL
): string {
$link = $embedLink ? '/profile?id=' . $user['id'] : NULL;
return image('/api/rpc/profile_avatar?user_id=' . $user['id'], 'pfp', link: $link);
if ($link === TRUE) {
$link = '/profile?id=' . $user['id'];
}
return image('/api/rpc/profile_avatar?user_id=' . $user['id'], 'pfp', link: $link, click: $click);
}

View file

@ -18,6 +18,7 @@ function lang($key, $default = NULL, $sub = NULL) {
function ilang($key,
$class = NULL,
$style = NULL,
$id = NULL,
$href = NULL,
$click = NULL,
@ -25,7 +26,7 @@ function ilang($key,
$sub = NULL,
$button = FALSE,
) {
$text = lang($key . "_text", FALSE, sub: $sub);
$text = ucfirst(lang($key . "_text", FALSE, sub: $sub));
$tip = lang($key . "_tip", FALSE, sub: $sub);
$icon = lang($key . "_icon", FALSE);
$content = lang($key . "_content", FALSE);
@ -42,6 +43,9 @@ function ilang($key,
if ($class) {
echo 'class="' . $class . '" ';
}
if ($style) {
echo 'style="' . $style . '" ';
}
if ($id) {
echo 'id="' . $id . '" ';
}

19
src/web/lang/Makefile Normal file
View file

@ -0,0 +1,19 @@
#
# en_CAT generator from en_US
# uwuify and awk required
#
LANG_SRC = $(shell find en_US -type f)
LANG_OBJ = $(patsubst en_US/%,en_CAT/%,$(LANG_SRC))
.PHONY: all
all: $(LANG_OBJ)
$(LANG_OBJ): en_CAT/% : en_US/%
@printf "\033[35m UWU \033[0m%s\n" $<
@mkdir -p $(@D)
@./_bin/transpile.sh $< $@
clean:
@rm -fr "en_CAT"

29
src/web/lang/_bin/transpile.sh Executable file
View file

@ -0,0 +1,29 @@
#!/bin/sh
lang_part() {
echo "$1" | awk "{split(\$0,a,\" = \"); print a[$2]}"
}
export -f lang_part
handle_line() {
line="$1"
left=$(lang_part "$line" 1)
right=$(lang_part "$line" 2)
echo "$left" | grep -Ev '_content|_line' > /dev/null
if [ "$?" -eq 0 ]; then
right=$(echo "$right" | uwuify)
fi;
right=${right%;};
echo "$left = $right;"
}
export -f handle_line
transpile() {
file="$1"
out="$2"
printf "" > "$out"
printf "<?php /* Copyright (c) 2024 Freya Murphy */\n\n" > "$out";
cat "$file" | grep '$lang' | xargs -d'\n' -I {} bash -c 'handle_line "$@"' _ {} >> "$out"
}
transpile "$1" "$2"

View file

@ -16,6 +16,15 @@ $lang['action_followers_tip'] = 'View %s\'s followres';
$lang['action_following_text'] = 'Following';
$lang['action_following_tip'] = 'View who %s is following';
$lang['action_follow_text'] = 'Follow';
$lang['action_follow_tip'] = 'Follow %s';
$lang['action_follow_back_text'] = 'Follow Back';
$lang['action_follow_back_tip'] = 'Follow %s';
$lang['action_following_text'] = 'Followed';
$lang['action_following_tip'] = 'Unfollow %s';
$lang['action_friends_text'] = 'Friends';
$lang['action_friends_tip'] = 'Unfollow %s';
$lang['action_load_posts_text'] = 'Load more posts';
$lang['action_load_posts_tip'] = 'Load more posts';

View file

@ -44,6 +44,20 @@ $lang['new_post_modal_title'] = 'Author New Post';
$lang['action_new_post_text'] = 'What\'s on your mind, %s';
$lang['action_new_post_tip'] = 'Author a new post.';
// User Menu
$lang['action_logout_text'] = 'Logout';
$lang['action_logout_tip'] = 'Logout';
$lang['action_logout_icon'] = 'mi mi-sm';
$lang['action_logout_content'] = 'logout';
$lang['action_settings_text'] = 'Settings';
$lang['action_settings_tip'] = 'Edit account settings';
$lang['action_settings_icon'] = 'mi mi-sm';
$lang['action_settings_content'] = 'settings';
$lang['action_profile_text'] = 'Profile';
$lang['action_profile_tip'] = 'View your profile';
$lang['action_profile_icon'] = 'mi mi-sm';
$lang['action_profile_content'] = 'account_circle';
// Words
$lang['now'] = 'Now';
$lang['likes'] = 'Likes';