post comments, refactor post loading, hide load more btn
This commit is contained in:
parent
2968ee5280
commit
1f647374a8
22 changed files with 647 additions and 98 deletions
15
db/rest/comment/api_comment.sql
Normal file
15
db/rest/comment/api_comment.sql
Normal file
|
@ -0,0 +1,15 @@
|
||||||
|
CREATE VIEW api.comment AS
|
||||||
|
SELECT
|
||||||
|
c.id,
|
||||||
|
c.user_id,
|
||||||
|
c.post_id,
|
||||||
|
c.content,
|
||||||
|
c.date
|
||||||
|
FROM
|
||||||
|
admin.comment c
|
||||||
|
ORDER BY id ASC;
|
||||||
|
|
||||||
|
GRANT SELECT ON TABLE api.comment
|
||||||
|
TO rest_anon, rest_user;
|
||||||
|
GRANT SELECT ON TABLE admin.comment
|
||||||
|
TO rest_anon, rest_user;
|
31
db/rest/comment/api_comment_delete.sql
Normal file
31
db/rest/comment/api_comment_delete.sql
Normal file
|
@ -0,0 +1,31 @@
|
||||||
|
CREATE FUNCTION _api.comment_delete()
|
||||||
|
RETURNS TRIGGER
|
||||||
|
LANGUAGE plpgsql VOLATILE
|
||||||
|
AS $BODY$
|
||||||
|
DECLARE
|
||||||
|
_user_id INTEGER;
|
||||||
|
BEGIN
|
||||||
|
_user_id = _api.get_user_id();
|
||||||
|
|
||||||
|
IF OLD.user_id <> _user_id THEN
|
||||||
|
PERFORM _api.raise_deny();
|
||||||
|
END IF;
|
||||||
|
|
||||||
|
DELETE FROM admin.comment
|
||||||
|
WHERE user_id = _user_id
|
||||||
|
AND id = OLD.id;
|
||||||
|
END
|
||||||
|
$BODY$;
|
||||||
|
|
||||||
|
GRANT EXECUTE ON FUNCTION _api.comment_delete()
|
||||||
|
TO rest_user;
|
||||||
|
GRANT DELETE ON TABLE api.comment
|
||||||
|
TO rest_user;
|
||||||
|
GRANT DELETE ON TABLE admin.comment
|
||||||
|
TO rest_user;
|
||||||
|
|
||||||
|
CREATE TRIGGER api_comment_delete_trgr
|
||||||
|
INSTEAD OF DELETE
|
||||||
|
ON api.comment
|
||||||
|
FOR EACH ROW
|
||||||
|
EXECUTE PROCEDURE _api.comment_delete();
|
56
db/rest/comment/api_comment_insert.sql
Normal file
56
db/rest/comment/api_comment_insert.sql
Normal file
|
@ -0,0 +1,56 @@
|
||||||
|
CREATE FUNCTION _api.comment_insert()
|
||||||
|
RETURNS TRIGGER
|
||||||
|
LANGUAGE plpgsql VOLATILE
|
||||||
|
AS $BODY$
|
||||||
|
DECLARE
|
||||||
|
_user_id INTEGER;
|
||||||
|
BEGIN
|
||||||
|
_user_id = _api.get_user_id();
|
||||||
|
|
||||||
|
NEW.content := _api.trim(NEW.content);
|
||||||
|
PERFORM _api.validate_text(
|
||||||
|
_text => NEW.content,
|
||||||
|
_column => 'content',
|
||||||
|
_min => 1,
|
||||||
|
_max => 1024
|
||||||
|
);
|
||||||
|
|
||||||
|
PERFORM TRUE
|
||||||
|
FROM admin.post
|
||||||
|
WHERE id = NEW.post_id;
|
||||||
|
|
||||||
|
IF NOT FOUND THEN
|
||||||
|
PERFORM _api.raise(
|
||||||
|
_msg => 'api_null_post',
|
||||||
|
_err => 400
|
||||||
|
);
|
||||||
|
END IF;
|
||||||
|
|
||||||
|
INSERT INTO admin.comment (
|
||||||
|
user_id,
|
||||||
|
post_id,
|
||||||
|
content
|
||||||
|
) VALUES (
|
||||||
|
_user_id,
|
||||||
|
NEW.post_id,
|
||||||
|
NEW.content
|
||||||
|
);
|
||||||
|
|
||||||
|
RETURN NEW;
|
||||||
|
END
|
||||||
|
$BODY$;
|
||||||
|
|
||||||
|
GRANT EXECUTE ON FUNCTION _api.comment_insert()
|
||||||
|
TO rest_user;
|
||||||
|
GRANT INSERT ON TABLE api.comment
|
||||||
|
TO rest_user;
|
||||||
|
GRANT INSERT ON TABLE admin.comment
|
||||||
|
TO rest_user;
|
||||||
|
GRANT UPDATE ON TABLE sys.comment_id_seq
|
||||||
|
TO rest_user;
|
||||||
|
|
||||||
|
CREATE TRIGGER api_comment_insert_trgr
|
||||||
|
INSTEAD OF INSERT
|
||||||
|
ON api.comment
|
||||||
|
FOR EACH ROW
|
||||||
|
EXECUTE PROCEDURE _api.comment_insert();
|
50
db/rest/comment/api_comment_update.sql
Normal file
50
db/rest/comment/api_comment_update.sql
Normal file
|
@ -0,0 +1,50 @@
|
||||||
|
CREATE FUNCTION _api.comment_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.user_id <> _user_id THEN
|
||||||
|
PERFORM _api.raise_deny();
|
||||||
|
END IF;
|
||||||
|
|
||||||
|
NEW.content = COALESCE(NEW.content, OLD.content);
|
||||||
|
NEW.content := _api.trim(NEW.content);
|
||||||
|
PERFORM _api.validate_text(
|
||||||
|
_text => NEW.content,
|
||||||
|
_column => 'content',
|
||||||
|
_min => 1,
|
||||||
|
_max => 1024
|
||||||
|
);
|
||||||
|
|
||||||
|
IF NEW.content IS DISTINCT FROM OLD.content THEN
|
||||||
|
_changed = TRUE;
|
||||||
|
END IF;
|
||||||
|
|
||||||
|
IF _changed THEN
|
||||||
|
UPDATE admin.comment
|
||||||
|
SET content = NEW.content
|
||||||
|
WHERE id = OLD.id;
|
||||||
|
END IF;
|
||||||
|
|
||||||
|
RETURN NEW;
|
||||||
|
END
|
||||||
|
$BODY$;
|
||||||
|
|
||||||
|
GRANT EXECUTE ON FUNCTION _api.comment_update()
|
||||||
|
TO rest_user;
|
||||||
|
GRANT UPDATE ON TABLE api.comment
|
||||||
|
TO rest_user;
|
||||||
|
GRANT UPDATE ON TABLE admin.comment
|
||||||
|
TO rest_user;
|
||||||
|
|
||||||
|
CREATE TRIGGER api_comment_update_trgr
|
||||||
|
INSTEAD OF UPDATE
|
||||||
|
ON api.comment
|
||||||
|
FOR EACH ROW
|
||||||
|
EXECUTE PROCEDURE _api.comment_update();
|
|
@ -3,10 +3,21 @@ CREATE VIEW api.post AS
|
||||||
p.id,
|
p.id,
|
||||||
p.user_id,
|
p.user_id,
|
||||||
p.content,
|
p.content,
|
||||||
p.date
|
p.date,
|
||||||
|
COALESCE(c.cc, 0)
|
||||||
|
AS comment_count
|
||||||
FROM
|
FROM
|
||||||
admin.post p
|
admin.post p
|
||||||
ORDER BY id DESC;
|
LEFT JOIN (
|
||||||
|
SELECT
|
||||||
|
COUNT(c.id) as cc,
|
||||||
|
c.post_id
|
||||||
|
FROM
|
||||||
|
admin.comment c
|
||||||
|
GROUP BY
|
||||||
|
c.post_id
|
||||||
|
) c ON p.id = c.post_id
|
||||||
|
ORDER BY p.id DESC;
|
||||||
|
|
||||||
GRANT SELECT ON TABLE api.post
|
GRANT SELECT ON TABLE api.post
|
||||||
TO rest_anon, rest_user;
|
TO rest_anon, rest_user;
|
||||||
|
|
|
@ -7,6 +7,8 @@ DECLARE
|
||||||
BEGIN
|
BEGIN
|
||||||
_user_id = _api.get_user_id();
|
_user_id = _api.get_user_id();
|
||||||
|
|
||||||
|
NEW.content := _api.trim(NEW.content);
|
||||||
|
|
||||||
PERFORM _api.validate_text(
|
PERFORM _api.validate_text(
|
||||||
_text => NEW.content,
|
_text => NEW.content,
|
||||||
_column => 'content',
|
_column => 'content',
|
||||||
|
|
|
@ -3,13 +3,45 @@ RETURNS TRIGGER
|
||||||
LANGUAGE plpgsql VOLATILE
|
LANGUAGE plpgsql VOLATILE
|
||||||
AS $BODY$
|
AS $BODY$
|
||||||
DECLARE
|
DECLARE
|
||||||
_length INTEGER;
|
_user_id INTEGER;
|
||||||
|
_changed BOOLEAN;
|
||||||
BEGIN
|
BEGIN
|
||||||
|
_user_id = _api.get_user_id();
|
||||||
|
_changed = FALSE;
|
||||||
|
|
||||||
|
IF OLD.user_id <> _user_id THEN
|
||||||
|
PERFORM _api.raise_deny();
|
||||||
|
END IF;
|
||||||
|
|
||||||
|
NEW.content = COALESCE(NEW.content, OLD.content);
|
||||||
|
NEW.content := _api.trim(NEW.content);
|
||||||
|
PERFORM _api.validate_text(
|
||||||
|
_text => NEW.content,
|
||||||
|
_column => 'content',
|
||||||
|
_min => 1,
|
||||||
|
_max => 4096
|
||||||
|
);
|
||||||
|
|
||||||
|
IF NEW.content IS DISTINCT FROM OLD.content THEN
|
||||||
|
_changed = TRUE;
|
||||||
|
END IF;
|
||||||
|
|
||||||
|
IF _changed THEN
|
||||||
|
UPDATE admin.post
|
||||||
|
SET content = NEW.content
|
||||||
|
WHERE id = OLD.id;
|
||||||
|
END IF;
|
||||||
|
|
||||||
RETURN NEW;
|
RETURN NEW;
|
||||||
END
|
END
|
||||||
$BODY$;
|
$BODY$;
|
||||||
|
|
||||||
GRANT EXECUTE ON FUNCTION _api.post_update() TO rest_user;
|
GRANT EXECUTE ON FUNCTION _api.post_update()
|
||||||
|
TO rest_user;
|
||||||
|
GRANT UPDATE ON TABLE api.post
|
||||||
|
TO rest_user;
|
||||||
|
GRANT UPDATE ON TABLE admin.post
|
||||||
|
TO rest_user;
|
||||||
|
|
||||||
CREATE TRIGGER api_post_update_trgr
|
CREATE TRIGGER api_post_update_trgr
|
||||||
INSTEAD OF UPDATE
|
INSTEAD OF UPDATE
|
||||||
|
|
|
@ -14,6 +14,7 @@ GRANT USAGE ON SCHEMA api TO rest_anon, rest_user;
|
||||||
GRANT USAGE ON SCHEMA _api TO rest_anon, rest_user;
|
GRANT USAGE ON SCHEMA _api TO rest_anon, rest_user;
|
||||||
|
|
||||||
-- util
|
-- util
|
||||||
|
\i /db/rest/util/_api_trim.sql;
|
||||||
\i /db/rest/util/_api_serve_media.sql;
|
\i /db/rest/util/_api_serve_media.sql;
|
||||||
\i /db/rest/util/_api_raise.sql;
|
\i /db/rest/util/_api_raise.sql;
|
||||||
\i /db/rest/util/_api_raise_null.sql;
|
\i /db/rest/util/_api_raise_null.sql;
|
||||||
|
@ -34,6 +35,12 @@ GRANT USAGE ON SCHEMA _api TO rest_anon, rest_user;
|
||||||
\i /db/rest/post/api_post_update.sql;
|
\i /db/rest/post/api_post_update.sql;
|
||||||
\i /db/rest/post/api_post_delete.sql;
|
\i /db/rest/post/api_post_delete.sql;
|
||||||
|
|
||||||
|
-- comment
|
||||||
|
\i /db/rest/comment/api_comment.sql;
|
||||||
|
\i /db/rest/comment/api_comment_insert.sql;
|
||||||
|
\i /db/rest/comment/api_comment_update.sql;
|
||||||
|
\i /db/rest/comment/api_comment_delete.sql;
|
||||||
|
|
||||||
-- login
|
-- login
|
||||||
\i /db/rest/login/_api_sign_jwt.sql;
|
\i /db/rest/login/_api_sign_jwt.sql;
|
||||||
\i /db/rest/login/_api_verify_jwt.sql;
|
\i /db/rest/login/_api_verify_jwt.sql;
|
||||||
|
|
|
@ -5,6 +5,16 @@ AS $BODY$
|
||||||
DECLARE
|
DECLARE
|
||||||
_length INTEGER;
|
_length INTEGER;
|
||||||
BEGIN
|
BEGIN
|
||||||
|
|
||||||
|
NEW.username := _api.trim(NEW.username);
|
||||||
|
NEW.password := _api.trim(NEW.password);
|
||||||
|
NEW.first_name := _api.trim(NEW.first_name);
|
||||||
|
NEW.last_name := _api.trim(NEW.last_name);
|
||||||
|
NEW.middle_name := _api.trim(NEW.middle_name);
|
||||||
|
NEW.email := _api.trim(NEW.email);
|
||||||
|
NEW.gender := _api.trim(NEW.gender);
|
||||||
|
NEW.profile_bio := _api.trim(NEW.profile_bio);
|
||||||
|
|
||||||
PERFORM _api.validate_text(
|
PERFORM _api.validate_text(
|
||||||
_text => NEW.username,
|
_text => NEW.username,
|
||||||
_column => 'username',
|
_column => 'username',
|
||||||
|
@ -22,6 +32,7 @@ BEGIN
|
||||||
PERFORM _api.validate_text(
|
PERFORM _api.validate_text(
|
||||||
_text => NEW.password,
|
_text => NEW.password,
|
||||||
_column => 'password',
|
_column => 'password',
|
||||||
|
_min => 1,
|
||||||
_max => 256
|
_max => 256
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -82,8 +93,6 @@ BEGIN
|
||||||
email,
|
email,
|
||||||
gender,
|
gender,
|
||||||
birth_date,
|
birth_date,
|
||||||
profile_avatar,
|
|
||||||
profile_banner,
|
|
||||||
profile_bio
|
profile_bio
|
||||||
) VALUES (
|
) VALUES (
|
||||||
NEW.username,
|
NEW.username,
|
||||||
|
@ -94,8 +103,6 @@ BEGIN
|
||||||
NEW.email,
|
NEW.email,
|
||||||
NEW.gender,
|
NEW.gender,
|
||||||
NEW.birth_date,
|
NEW.birth_date,
|
||||||
NEW.profile_avatar,
|
|
||||||
NEW.profile_banner,
|
|
||||||
NEW.profile_bio
|
NEW.profile_bio
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
|
@ -3,15 +3,161 @@ RETURNS TRIGGER
|
||||||
LANGUAGE plpgsql VOLATILE
|
LANGUAGE plpgsql VOLATILE
|
||||||
AS $BODY$
|
AS $BODY$
|
||||||
DECLARE
|
DECLARE
|
||||||
_length INTEGER;
|
_user_id INTEGER;
|
||||||
|
_changed BOOLEAN;
|
||||||
BEGIN
|
BEGIN
|
||||||
|
_user_id = _api.get_user_id();
|
||||||
|
_changed = FALSE;
|
||||||
|
|
||||||
|
IF OLD.id <> _user_id THEN
|
||||||
|
PERFORM _api.raise_deny();
|
||||||
|
END IF;
|
||||||
|
|
||||||
|
-- username
|
||||||
|
NEW.username = COALESCE(NEW.username, OLD.username);
|
||||||
|
NEW.username := _api.trim(NEW.username);
|
||||||
|
PERFORM _api.validate_text(
|
||||||
|
_text => NEW.username,
|
||||||
|
_column => 'username',
|
||||||
|
_min => 1,
|
||||||
|
_max => 24
|
||||||
|
);
|
||||||
|
|
||||||
|
IF NEW.username IS DISTINCT FROM OLD.username THEN
|
||||||
|
PERFORM TRUE FROM admin.user
|
||||||
|
WHERE username = NEW.username;
|
||||||
|
IF FOUND THEN
|
||||||
|
PERFORM _api.raise_unique('username');
|
||||||
|
END IF;
|
||||||
|
_changed = TRUE;
|
||||||
|
END IF;
|
||||||
|
|
||||||
|
-- password
|
||||||
|
SELECT password
|
||||||
|
INTO OLD.password
|
||||||
|
FROM admin.user
|
||||||
|
WHERE id = OLD.id;
|
||||||
|
|
||||||
|
NEW.password = COALESCE(NEW.password, OLD.password);
|
||||||
|
NEW.password := _api.trim(NEW.password);
|
||||||
|
PERFORM _api.validate_text(
|
||||||
|
_text => NEW.password,
|
||||||
|
_column => 'password',
|
||||||
|
_min => 1,
|
||||||
|
_max => 256
|
||||||
|
);
|
||||||
|
|
||||||
|
IF NEW.password IS DISTINCT FROM OLD.password THEN
|
||||||
|
_changed = TRUE;
|
||||||
|
END IF;
|
||||||
|
|
||||||
|
-- first name
|
||||||
|
NEW.first_name = COALESCE(NEW.first_name, OLD.first_name);
|
||||||
|
NEW.first_name := _api.trim(NEW.first_name);
|
||||||
|
PERFORM _api.validate_text(
|
||||||
|
_text => NEW.first_name,
|
||||||
|
_column => 'first_name',
|
||||||
|
_max => 256
|
||||||
|
);
|
||||||
|
|
||||||
|
IF NEW.first_name IS DISTINCT FROM OLD.first_name THEN
|
||||||
|
_changed = TRUE;
|
||||||
|
END IF;
|
||||||
|
|
||||||
|
-- last name
|
||||||
|
NEW.last_name = COALESCE(NEW.last_name, OLD.last_name);
|
||||||
|
NEW.last_name := _api.trim(NEW.last_name);
|
||||||
|
PERFORM _api.validate_text(
|
||||||
|
_text => NEW.last_name,
|
||||||
|
_column => 'last_name',
|
||||||
|
_max => 256
|
||||||
|
);
|
||||||
|
|
||||||
|
IF NEW.last_name IS DISTINCT FROM OLD.last_name THEN
|
||||||
|
_changed = TRUE;
|
||||||
|
END IF;
|
||||||
|
|
||||||
|
-- middle name
|
||||||
|
NEW.middle_name = COALESCE(NEW.middle_name, OLD.middle_name);
|
||||||
|
NEW.middle_name := _api.trim(NEW.middle_name);
|
||||||
|
PERFORM _api.validate_text(
|
||||||
|
_text => NEW.middle_name,
|
||||||
|
_column => 'middle_name',
|
||||||
|
_max => 256
|
||||||
|
);
|
||||||
|
|
||||||
|
IF NEW.middle_name IS DISTINCT FROM OLD.middle_name THEN
|
||||||
|
_changed = TRUE;
|
||||||
|
END IF;
|
||||||
|
|
||||||
|
-- email
|
||||||
|
NEW.email = COALESCE(NEW.email, OLD.email);
|
||||||
|
NEW.email := _api.trim(NEW.email);
|
||||||
|
PERFORM _api.validate_text(
|
||||||
|
_text => NEW.email,
|
||||||
|
_column => 'email',
|
||||||
|
_max => 256
|
||||||
|
);
|
||||||
|
|
||||||
|
IF NEW.email IS DISTINCT FROM OLD.email THEN
|
||||||
|
_changed = TRUE;
|
||||||
|
END IF;
|
||||||
|
|
||||||
|
-- gender
|
||||||
|
NEW.gender = COALESCE(NEW.gender, OLD.gender);
|
||||||
|
NEW.gender := _api.trim(NEW.gender);
|
||||||
|
PERFORM _api.validate_text(
|
||||||
|
_text => NEW.gender,
|
||||||
|
_column => 'gender',
|
||||||
|
_max => 256
|
||||||
|
);
|
||||||
|
|
||||||
|
IF NEW.gender IS DISTINCT FROM OLD.gender THEN
|
||||||
|
_changed = TRUE;
|
||||||
|
END IF;
|
||||||
|
|
||||||
|
-- birth date
|
||||||
|
NEW.birth_date = COALESCE(NEW.birth_date, OLD.birth_date);
|
||||||
|
IF NEW.birth_date IS DISTINCT FROM OLD.birth_date THEN
|
||||||
|
_changed = TRUE;
|
||||||
|
END IF;
|
||||||
|
|
||||||
|
-- profile bio
|
||||||
|
NEW.profile_bio = COALESCE(NEW.profile_bio, OLD.profile_bio);
|
||||||
|
NEW.profile_bio := _api.trim(NEW.profile_bio);
|
||||||
|
PERFORM _api.validate_text(
|
||||||
|
_text => NEW.profile_bio,
|
||||||
|
_column => 'profile_bio',
|
||||||
|
_max => 2048
|
||||||
|
);
|
||||||
|
|
||||||
|
IF NEW.profile_bio IS DISTINCT FROM OLD.profile_bio THEN
|
||||||
|
_changed = TRUE;
|
||||||
|
END IF;
|
||||||
|
|
||||||
|
IF _changed THEN
|
||||||
|
UPDATE admin.user SET
|
||||||
|
username = NEW.username,
|
||||||
|
password = NEW.password,
|
||||||
|
first_name = NEW.first_name,
|
||||||
|
last_name = NEW.last_name,
|
||||||
|
middle_name = NEW.middle_name,
|
||||||
|
email = NEW.email,
|
||||||
|
gender = NEW.gender,
|
||||||
|
birth_date = NEW.birth_date,
|
||||||
|
profile_bio = NEW.profile_bio
|
||||||
|
WHERE id = OLD.id;
|
||||||
|
END IF;
|
||||||
|
|
||||||
RETURN NEW;
|
RETURN NEW;
|
||||||
END
|
END
|
||||||
$BODY$;
|
$BODY$;
|
||||||
|
|
||||||
GRANT EXECUTE ON FUNCTION _api.user_update()
|
GRANT EXECUTE ON FUNCTION _api.user_update()
|
||||||
TO rest_user;
|
TO rest_user;
|
||||||
GRANT DELETE ON TABLE api.user
|
GRANT UPDATE ON TABLE api.user
|
||||||
|
TO rest_user;
|
||||||
|
GRANT UPDATE ON TABLE admin.user
|
||||||
TO rest_user;
|
TO rest_user;
|
||||||
|
|
||||||
CREATE TRIGGER api_user_update_trgr
|
CREATE TRIGGER api_user_update_trgr
|
||||||
|
|
25
db/rest/util/_api_trim.sql
Normal file
25
db/rest/util/_api_trim.sql
Normal file
|
@ -0,0 +1,25 @@
|
||||||
|
CREATE FUNCTION _api.trim(
|
||||||
|
_text TEXT
|
||||||
|
)
|
||||||
|
RETURNS TEXT
|
||||||
|
LANGUAGE plpgsql VOLATILE
|
||||||
|
AS $BODY$
|
||||||
|
DECLARE
|
||||||
|
_new TEXT;
|
||||||
|
BEGIN
|
||||||
|
|
||||||
|
IF _text IS NULL THEN
|
||||||
|
RETURN NULL;
|
||||||
|
END IF;
|
||||||
|
|
||||||
|
_new = _text;
|
||||||
|
_new = TRIM(_new);
|
||||||
|
_new = REGEXP_REPLACE(_new, '^(?: |\r|\n)*', '');
|
||||||
|
_new = REGEXP_REPLACE(_new, '(?: |\r|\n)*$', '');
|
||||||
|
|
||||||
|
RETURN _new;
|
||||||
|
END
|
||||||
|
$BODY$;
|
||||||
|
|
||||||
|
GRANT EXECUTE ON FUNCTION _api.trim(TEXT)
|
||||||
|
TO rest_anon, rest_user;
|
|
@ -33,6 +33,7 @@ BEGIN
|
||||||
_detail => _column,
|
_detail => _column,
|
||||||
_hint => _min || ''
|
_hint => _min || ''
|
||||||
);
|
);
|
||||||
|
RETURN FALSE;
|
||||||
END IF;
|
END IF;
|
||||||
|
|
||||||
IF _max IS NOT NULL AND _length > _max THEN
|
IF _max IS NOT NULL AND _length > _max THEN
|
||||||
|
@ -41,6 +42,7 @@ BEGIN
|
||||||
_detail => _column,
|
_detail => _column,
|
||||||
_hint => _max || ''
|
_hint => _max || ''
|
||||||
);
|
);
|
||||||
|
RETURN FALSE;
|
||||||
END IF;
|
END IF;
|
||||||
|
|
||||||
RETURN TRUE;
|
RETURN TRUE;
|
||||||
|
|
129
web/_controller/_util/post.php
Normal file
129
web/_controller/_util/post.php
Normal file
|
@ -0,0 +1,129 @@
|
||||||
|
<?php /* Copyright (c) 2024 Freya Murphy */
|
||||||
|
class Post_controller extends Controller {
|
||||||
|
|
||||||
|
// the request model
|
||||||
|
private $request_model;
|
||||||
|
|
||||||
|
// the caceh model
|
||||||
|
private $cache_model;
|
||||||
|
|
||||||
|
// page size
|
||||||
|
private $page_size;
|
||||||
|
|
||||||
|
function __construct($load) {
|
||||||
|
parent::__construct($load);
|
||||||
|
$this->request_model = $this->load->model('request');
|
||||||
|
$this->cache_model = $this->load->model('cache');
|
||||||
|
$this->page_size = 10;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function index(): void {
|
||||||
|
$this->view('template/posts');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return array<string,mixed>
|
||||||
|
*/
|
||||||
|
public function posts(): array {
|
||||||
|
$page = $this->request_model->get_int('page', 0);
|
||||||
|
$max = $this->request_model->get_int('max');
|
||||||
|
$offset = $page * $this->page_size;
|
||||||
|
|
||||||
|
$user = $this->main->user();
|
||||||
|
|
||||||
|
$query = $this->db;
|
||||||
|
|
||||||
|
if ($user) {
|
||||||
|
$query = $query->select('p.*, l.post_id IS NOT NULL as liked');
|
||||||
|
} else {
|
||||||
|
$query = $query->select('p.*, FALSE as liked');
|
||||||
|
}
|
||||||
|
|
||||||
|
$query = $query->from('api.post p');
|
||||||
|
|
||||||
|
if ($user) {
|
||||||
|
$query = $query->join('admin.like l', 'p.id = l.post_id AND l.user_id')
|
||||||
|
->eq($user['id']);
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($max) {
|
||||||
|
$query = $query
|
||||||
|
->where('id')->le($max);
|
||||||
|
}
|
||||||
|
|
||||||
|
$posts = $query
|
||||||
|
->limit($this->page_size)
|
||||||
|
->offset($offset)
|
||||||
|
->rows();
|
||||||
|
|
||||||
|
$users = $this->cache_model->get_users($posts);
|
||||||
|
$max = 0;
|
||||||
|
|
||||||
|
foreach ($posts as $post) {
|
||||||
|
$max = max($max, $post['id']);
|
||||||
|
$data = array();
|
||||||
|
$data['page_size'] = $this->page_size;
|
||||||
|
$data['user'] = $users[$post['user_id']];
|
||||||
|
$data['post'] = $post;
|
||||||
|
$this->view('template/post', $data);
|
||||||
|
}
|
||||||
|
|
||||||
|
$pc = $this->db
|
||||||
|
->select('COUNT(p.id) as pc')
|
||||||
|
->from('api.post p')
|
||||||
|
->row()['pc'];
|
||||||
|
|
||||||
|
|
||||||
|
return array(
|
||||||
|
'loaded' => count($posts),
|
||||||
|
'total' => $pc,
|
||||||
|
'page_size' => $this->page_size,
|
||||||
|
'max' => $max,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return array<string,mixed>
|
||||||
|
*/
|
||||||
|
public function comments(): array {
|
||||||
|
$page = $this->request_model->get_int('page', 0);
|
||||||
|
$max = $this->request_model->get_int('max');
|
||||||
|
$id = $this->request_model->get_int('id', 0);
|
||||||
|
$offset = $page * $this->page_size;
|
||||||
|
|
||||||
|
$query = $this->db
|
||||||
|
->select('*')
|
||||||
|
->from('api.comment')
|
||||||
|
->where('post_id')
|
||||||
|
->eq($id);
|
||||||
|
|
||||||
|
if ($max) {
|
||||||
|
$query = $query
|
||||||
|
->and()
|
||||||
|
->where('id')
|
||||||
|
->le($max);
|
||||||
|
}
|
||||||
|
|
||||||
|
$comments = $query
|
||||||
|
->limit($this->page_size)
|
||||||
|
->offset($offset)
|
||||||
|
->rows();
|
||||||
|
|
||||||
|
$users = $this->cache_model->get_users($comments);
|
||||||
|
$max = 0;
|
||||||
|
|
||||||
|
foreach ($comments as $comment) {
|
||||||
|
$max = max($max, $comment['id']);
|
||||||
|
$data = array();
|
||||||
|
$data['user'] = $users[$comment['user_id']];
|
||||||
|
$data['comment'] = $comment;
|
||||||
|
$this->view('template/comment', $data);
|
||||||
|
}
|
||||||
|
|
||||||
|
return array(
|
||||||
|
'loaded' => count($comments),
|
||||||
|
'page_size' => $this->page_size,
|
||||||
|
'max' => $max,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
|
@ -4,17 +4,13 @@ class Home_controller extends Controller {
|
||||||
// the home model
|
// the home model
|
||||||
private $home_model;
|
private $home_model;
|
||||||
|
|
||||||
// the request model
|
// the post controller
|
||||||
private $request_model;
|
protected $post_controller;
|
||||||
|
|
||||||
// the caceh model
|
|
||||||
private $cache_model;
|
|
||||||
|
|
||||||
function __construct($load) {
|
function __construct($load) {
|
||||||
parent::__construct($load);
|
parent::__construct($load);
|
||||||
$this->home_model = $this->load->model('apps/home');
|
$this->home_model = $this->load->model('apps/home');
|
||||||
$this->request_model = $this->load->model('request');
|
$this->post_controller = $this->load->controller('_util/post');
|
||||||
$this->cache_model = $this->load->model('cache');
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public function index(): void {
|
public function index(): void {
|
||||||
|
@ -24,66 +20,6 @@ class Home_controller extends Controller {
|
||||||
$this->view('apps/home/main', $data);
|
$this->view('apps/home/main', $data);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function posts(): void {
|
|
||||||
$page = $this->request_model->get_int('page', 0);
|
|
||||||
$page_size = 20;
|
|
||||||
$offset = $page * $page_size;
|
|
||||||
|
|
||||||
$user = $this->main->user();
|
|
||||||
|
|
||||||
$query = $this->db;
|
|
||||||
|
|
||||||
if ($user) {
|
|
||||||
$query = $query->select('p.*, l.post_id IS NOT NULL as liked');
|
|
||||||
} else {
|
|
||||||
$query = $query->select('p.*, FALSE as liked');
|
|
||||||
}
|
|
||||||
|
|
||||||
$query = $query->from('api.post p');
|
|
||||||
|
|
||||||
if ($user) {
|
|
||||||
$query = $query->join('admin.like l', 'p.id = l.post_id')
|
|
||||||
->where('l.user_id')->eq($user['id'])
|
|
||||||
->or()->where('l.user_id IS NULL');
|
|
||||||
}
|
|
||||||
|
|
||||||
$posts = $query->limit($page_size)
|
|
||||||
->offset($offset)
|
|
||||||
->rows();
|
|
||||||
|
|
||||||
$users = $this->cache_model->get_users($posts);
|
|
||||||
|
|
||||||
foreach ($posts as $post) {
|
|
||||||
$data = array();
|
|
||||||
$data['user'] = $users[$post['user_id']];
|
|
||||||
$data['post'] = $post;
|
|
||||||
$this->view('template/post', $data);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public function comments(): void {
|
|
||||||
$page = $this->request_model->get_int('page', 0);
|
|
||||||
$id = $this->request_model->get_int('id');
|
|
||||||
$page_size = 20;
|
|
||||||
$offset = $page * $page_size;
|
|
||||||
|
|
||||||
$comments = $this->db
|
|
||||||
->select('*')
|
|
||||||
->from('admin.comment')
|
|
||||||
->limit($page_size)
|
|
||||||
->offset($offset)
|
|
||||||
->rows();
|
|
||||||
|
|
||||||
$users = $this->cache_model->get_users($comments);
|
|
||||||
|
|
||||||
foreach ($comments as $comment) {
|
|
||||||
$data = array();
|
|
||||||
$data['user'] = $users[$comment['user_id']];
|
|
||||||
$data['comment'] = $comment;
|
|
||||||
$this->view('template/comment', $data);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
?>
|
?>
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
<?php /* Copyright (c) 2024 Freya Murphy */
|
<?php /* Copyright (c) 2024 Freya Murphy */
|
||||||
class Format_model extends Modal {
|
class Format_model extends Model {
|
||||||
|
|
||||||
function __construct($load) {
|
function __construct($load) {
|
||||||
parent::__construct($load);
|
parent::__construct($load);
|
||||||
|
|
|
@ -23,8 +23,5 @@
|
||||||
</script>
|
</script>
|
||||||
</div>
|
</div>
|
||||||
<?php endif; ?>
|
<?php endif; ?>
|
||||||
<div id="post-container">
|
<?php $this->post_controller->index(); ?>
|
||||||
<?=$this->posts()?>
|
|
||||||
</div>
|
|
||||||
<?=ilang('action_load_posts', id: 'action-load-posts', class: 'btn btn-line')?>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -1,11 +1,14 @@
|
||||||
<?php /* Copyright (c) 2024 Freya Murphy */ ?>
|
<?php /* Copyright (c) 2024 Freya Murphy */ ?>
|
||||||
<?php /* vi: syntax=php */ ?>
|
<?php /* vi: syntax=php */ ?>
|
||||||
|
<?php
|
||||||
|
$format_model = $this->load->model('format');
|
||||||
|
?>
|
||||||
<div class="comment row mt">
|
<div class="comment row mt">
|
||||||
<?php $this->view('template/pfp', array('user' => $user))?>
|
<?php $this->view('template/pfp', array('user' => $user))?>
|
||||||
<div class="ml col sub-card">
|
<div class="ml col sub-card">
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<strong><?=$this->main->display_name($user)?></strong>
|
<strong><?=$format_model->name($user)?></strong>
|
||||||
<span class="dim ml"><?=$this->main->display_date($comment['date'])?></span>
|
<span class="dim ml"><?=$format_model->date($comment['date'])?></span>
|
||||||
</div>
|
</div>
|
||||||
<?=$comment['content']?>
|
<?=$comment['content']?>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -29,25 +29,40 @@
|
||||||
<div class="col comments">
|
<div class="col comments">
|
||||||
<?php
|
<?php
|
||||||
$_GET['id'] = $post['id'];
|
$_GET['id'] = $post['id'];
|
||||||
$this->comments();
|
$cdata = $this->comments();
|
||||||
ilang('action_load_comments',
|
|
||||||
class: 'action-load-comments btn btn-line mt',
|
$loaded = $cdata['loaded'];
|
||||||
attrs: array('postId' => $post['id'])
|
$max = $cdata['max'];
|
||||||
);
|
$page_size = $cdata['page_size'];
|
||||||
|
$total = $post['comment_count'];
|
||||||
|
|
||||||
|
if ($loaded >= $page_size && $page_size < $total) {
|
||||||
|
ilang('action_load_comments',
|
||||||
|
class: 'action-load-comments btn btn-line mt',
|
||||||
|
attrs: array(
|
||||||
|
'postId' => $post['id'],
|
||||||
|
'loaded' => $loaded,
|
||||||
|
'pageSize' => $page_size,
|
||||||
|
'commentCount' => $total,
|
||||||
|
'commentMax' => $max,
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
?>
|
?>
|
||||||
</div>
|
</div>
|
||||||
<?php if ($self): ?>
|
<?php if ($self): ?>
|
||||||
<div class="row grow mt">
|
<div class="row grow mt">
|
||||||
<?php $this->view('template/pfp', array('user' => $user))?>
|
<?php $this->view('template/pfp', array('user' => $user))?>
|
||||||
<form class="ml">
|
<form class="ml action-new-comment-form">
|
||||||
<input
|
<input
|
||||||
type="hidden"
|
type="hidden"
|
||||||
name="id"
|
name="id"
|
||||||
value="<?=$post['id']?>"
|
value="<?=$post['id']?>"
|
||||||
>
|
>
|
||||||
<input
|
<input
|
||||||
id="new-comment-<?=$post['id']?>"
|
class="action-new-comment input"
|
||||||
class="input"
|
postId="<?=$post['id']?>"
|
||||||
autocomplete="off"
|
autocomplete="off"
|
||||||
type="text"
|
type="text"
|
||||||
name="text"
|
name="text"
|
||||||
|
|
23
web/_views/template/posts.php
Normal file
23
web/_views/template/posts.php
Normal file
|
@ -0,0 +1,23 @@
|
||||||
|
<div id="post-container">
|
||||||
|
<?php
|
||||||
|
$pdata = $this->posts();
|
||||||
|
|
||||||
|
$loaded = $pdata['loaded'];
|
||||||
|
$page_size = $pdata['page_size'];
|
||||||
|
$total = $pdata['total'];
|
||||||
|
$max = $pdata['max'];
|
||||||
|
|
||||||
|
if ($loaded >= $page_size && $page_size < $total) {
|
||||||
|
ilang('action_load_posts',
|
||||||
|
id: 'action-load-posts',
|
||||||
|
class: 'btn btn-line mb',
|
||||||
|
attrs: array(
|
||||||
|
'loaded' => $loaded,
|
||||||
|
'pageSize' => $page_size,
|
||||||
|
'postCount' => $total,
|
||||||
|
'postMax' => $max,
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
?>
|
||||||
|
</div>
|
|
@ -69,6 +69,18 @@ class DatabaseQuery {
|
||||||
return $this;
|
return $this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function lt($item) {
|
||||||
|
$this->query .= "< ?\n";
|
||||||
|
array_push($this->param, $item);
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function le($item) {
|
||||||
|
$this->query .= "<= ?\n";
|
||||||
|
array_push($this->param, $item);
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
public function where_in($column, $array) {
|
public function where_in($column, $array) {
|
||||||
if (!$this->where) {
|
if (!$this->where) {
|
||||||
$this->where = TRUE;
|
$this->where = TRUE;
|
||||||
|
|
|
@ -10,3 +10,7 @@
|
||||||
.action-load-comments {
|
.action-load-comments {
|
||||||
margin-left: 4rem;
|
margin-left: 4rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#action-load-posts {
|
||||||
|
justify-content: center;
|
||||||
|
}
|
||||||
|
|
|
@ -5,14 +5,28 @@ observe('#main-content', '.action-load-comments', function(me) {
|
||||||
page = '1';
|
page = '1';
|
||||||
}
|
}
|
||||||
let newPage = Number(page) + 1;
|
let newPage = Number(page) + 1;
|
||||||
let id = me.attr('postId');
|
|
||||||
me.attr('page', newPage + '');
|
me.attr('page', newPage + '');
|
||||||
let url = '/home/comments?page=' + page + '&id=' + id;
|
|
||||||
|
let postId = me.attr('postId');
|
||||||
|
let loaded = Number(me.attr('loaded'));
|
||||||
|
let pageSize = Number(me.attr('pageSize'));
|
||||||
|
let commmentCount = Number(me.attr('commentCount'));
|
||||||
|
let commentMax = Number(me.attr('commentMax'));
|
||||||
|
|
||||||
|
let url = '/_util/post/comments?page=' + page + '&id=' + postId + '&max' + commentMax;
|
||||||
$.get(url, function (data) {
|
$.get(url, function (data) {
|
||||||
if (data === '') {
|
if (data === '') {
|
||||||
me.remove();
|
me.remove();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
$(data).insertBefore(me);
|
||||||
|
|
||||||
|
loaded += pageSize;
|
||||||
|
if (loaded >= commmentCount) {
|
||||||
|
me.remove();
|
||||||
} else {
|
} else {
|
||||||
$(me).prepend(data);
|
me.attr('loaded', loaded + '');
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -26,13 +40,45 @@ observe('#main-content', '#action-load-posts', function(me) {
|
||||||
}
|
}
|
||||||
let newPage = Number(page) + 1;
|
let newPage = Number(page) + 1;
|
||||||
me.attr('page', newPage + '');
|
me.attr('page', newPage + '');
|
||||||
let url = '/home/posts?page=' + page;
|
|
||||||
$.get(url, function (data) {
|
let loaded = Number(me.attr('loaded'));
|
||||||
|
let pageSize = Number(me.attr('pageSize'));
|
||||||
|
let postCount = Number(me.attr('postCount'));
|
||||||
|
let postMax = Number(me.attr('postMax'));
|
||||||
|
|
||||||
|
let url = '/_util/post/posts?page=' + page + '&max=' + postMax;
|
||||||
|
$.get(url, function (data) {
|
||||||
if (data === '') {
|
if (data === '') {
|
||||||
me.remove();
|
me.remove();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
$(data).insertBefore(me);
|
||||||
|
|
||||||
|
loaded += pageSize;
|
||||||
|
if (loaded >= postCount) {
|
||||||
|
me.remove();
|
||||||
} else {
|
} else {
|
||||||
$('#post-container').append(data);
|
me.attr('loaded', loaded + '');
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
observe('#main-content', '.action-new-comment-form', function(me) {
|
||||||
|
me.on('submit', function(e) {
|
||||||
|
e.preventDefault();
|
||||||
|
let input = me.find('.action-new-comment');
|
||||||
|
let content = input.val();
|
||||||
|
let post_id = input.attr('postId');
|
||||||
|
$.ajax({
|
||||||
|
url: '/api/comment',
|
||||||
|
method: 'POST',
|
||||||
|
data: JSON.stringify({ post_id, content }),
|
||||||
|
success: function(_data) {
|
||||||
|
window.location.reload();
|
||||||
|
},
|
||||||
|
error: errorToast
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
Loading…
Reference in a new issue