Compare commits
No commits in common. "7e2553646c27cae8baaca1cc5c13d980661b5d90" and "ef7b0e26fadb882e026f1b447b6d18259057c040" have entirely different histories.
7e2553646c
...
ef7b0e26fa
109 changed files with 333 additions and 1712 deletions
|
@ -1,4 +1,4 @@
|
||||||
FROM php:fpm-alpine
|
FROM php:fpm-alpine
|
||||||
RUN apk add --no-cache postgresql-dev runuser
|
RUN apk add --no-cache postgresql-dev
|
||||||
RUN docker-php-ext-configure pgsql -with-pgsql=/usr/local/pgsql
|
RUN docker-php-ext-configure pgsql -with-pgsql=/usr/local/pgsql
|
||||||
RUN docker-php-ext-install pdo pdo_pgsql
|
RUN docker-php-ext-install pdo pdo_pgsql
|
||||||
|
|
|
@ -87,7 +87,6 @@ server {
|
||||||
}
|
}
|
||||||
|
|
||||||
location / {
|
location / {
|
||||||
root /opt/xssbook/web;
|
|
||||||
include fastcgi_params;
|
include fastcgi_params;
|
||||||
fastcgi_pass php:9000;
|
fastcgi_pass php:9000;
|
||||||
fastcgi_param SCRIPT_FILENAME $document_root/index.php;
|
fastcgi_param SCRIPT_FILENAME $document_root/index.php;
|
||||||
|
|
|
@ -50,12 +50,11 @@ CREATE TABLE admin.user (
|
||||||
middle_name TEXT DEFAULT ''::text NOT NULL,
|
middle_name TEXT DEFAULT ''::text NOT NULL,
|
||||||
email TEXT DEFAULT ''::text NOT NULL,
|
email TEXT DEFAULT ''::text NOT NULL,
|
||||||
gender TEXT DEFAULT ''::text NOT NULL,
|
gender TEXT DEFAULT ''::text NOT NULL,
|
||||||
|
join_date TIMESTAMP WITH TIME ZONE DEFAULT clock_timestamp() NOT NULL,
|
||||||
birth_date TIMESTAMP WITH TIME ZONE NOT NULL,
|
birth_date TIMESTAMP WITH TIME ZONE NOT NULL,
|
||||||
profile_bio TEXT DEFAULT ''::text NOT NULL,
|
profile_avatar BYTEA,
|
||||||
created TIMESTAMP WITH TIME ZONE DEFAULT clock_timestamp() NOT NULL,
|
profile_banner BYTEA,
|
||||||
modified TIMESTAMP WITH TIME ZONE DEFAULT clock_timestamp() NOT NULL,
|
profile_bio TEXT DEFAULT ''::text NOT NULL
|
||||||
seen TIMESTAMP WITH TIME ZONE DEFAULT clock_timestamp() NOT NULL,
|
|
||||||
deleted BOOLEAN DEFAULT FALSE NOT NULL
|
|
||||||
);
|
);
|
||||||
|
|
||||||
ALTER TABLE admin.user OWNER TO xssbook;
|
ALTER TABLE admin.user OWNER TO xssbook;
|
||||||
|
@ -79,9 +78,7 @@ CREATE TABLE admin.post (
|
||||||
id INTEGER DEFAULT nextval('sys.post_id_seq'::regclass) NOT NULL,
|
id INTEGER DEFAULT nextval('sys.post_id_seq'::regclass) NOT NULL,
|
||||||
user_id INTEGER NOT NULL,
|
user_id INTEGER NOT NULL,
|
||||||
content TEXT DEFAULT ''::text NOT NULL,
|
content TEXT DEFAULT ''::text NOT NULL,
|
||||||
created TIMESTAMP WITH TIME ZONE DEFAULT clock_timestamp() NOT NULL,
|
date TIMESTAMP WITH TIME ZONE DEFAULT clock_timestamp() NOT NULL
|
||||||
modified TIMESTAMP WITH TIME ZONE DEFAULT clock_timestamp() NOT NULL,
|
|
||||||
deleted BOOLEAN DEFAULT FALSE NOT NULL
|
|
||||||
);
|
);
|
||||||
|
|
||||||
ALTER TABLE admin.post OWNER TO xssbook;
|
ALTER TABLE admin.post OWNER TO xssbook;
|
||||||
|
@ -104,9 +101,7 @@ CREATE TABLE admin.comment (
|
||||||
user_id INTEGER NOT NULL,
|
user_id INTEGER NOT NULL,
|
||||||
post_id INTEGER NOT NULL,
|
post_id INTEGER NOT NULL,
|
||||||
content TEXT DEFAULT ''::text NOT NULL,
|
content TEXT DEFAULT ''::text NOT NULL,
|
||||||
created TIMESTAMP WITH TIME ZONE DEFAULT clock_timestamp() NOT NULL,
|
date TIMESTAMP WITH TIME ZONE DEFAULT clock_timestamp() NOT NULL
|
||||||
modified TIMESTAMP WITH TIME ZONE DEFAULT clock_timestamp() NOT NULL,
|
|
||||||
deleted BOOLEAN DEFAULT FALSE NOT NULL
|
|
||||||
);
|
);
|
||||||
|
|
||||||
ALTER TABLE admin.comment OWNER TO xssbook;
|
ALTER TABLE admin.comment OWNER TO xssbook;
|
||||||
|
@ -120,28 +115,15 @@ ALTER TABLE ONLY admin.comment
|
||||||
ALTER TABLE ONLY admin.comment
|
ALTER TABLE ONLY admin.comment
|
||||||
ADD CONSTRAINT comment_post_id_fkey FOREIGN KEY (post_id) REFERENCES admin.post (id) ON DELETE CASCADE;
|
ADD CONSTRAINT comment_post_id_fkey FOREIGN KEY (post_id) REFERENCES admin.post (id) ON DELETE CASCADE;
|
||||||
|
|
||||||
CREATE SEQUENCE IF NOT EXISTS sys.like_id_seq
|
|
||||||
START WITH 1
|
|
||||||
INCREMENT BY 1
|
|
||||||
NO MINVALUE
|
|
||||||
NO MAXVALUE
|
|
||||||
CACHE 1;
|
|
||||||
|
|
||||||
CREATE TABLE admin.like (
|
CREATE TABLE admin.like (
|
||||||
id INTEGER DEFAULT nextval('sys.like_id_seq'::regclass) NOT NULL,
|
|
||||||
user_id INTEGER NOT NULL,
|
user_id INTEGER NOT NULL,
|
||||||
post_id INTEGER,
|
post_id INTEGER,
|
||||||
comment_id INTEGER,
|
comment_id INTEGER,
|
||||||
value BOOLEAN NOT NULL DEFAULT TRUE,
|
date TIMESTAMP WITH TIME ZONE DEFAULT clock_timestamp() NOT NULL
|
||||||
created TIMESTAMP WITH TIME ZONE DEFAULT clock_timestamp() NOT NULL,
|
|
||||||
modified TIMESTAMP WITH TIME ZONE DEFAULT clock_timestamp() NOT NULL
|
|
||||||
);
|
);
|
||||||
|
|
||||||
ALTER TABLE admin.like OWNER TO xssbook;
|
ALTER TABLE admin.like OWNER TO xssbook;
|
||||||
|
|
||||||
ALTER TABLE ONLY admin.like
|
|
||||||
ADD CONSTRAINT like_pkey PRIMARY KEY (id);
|
|
||||||
|
|
||||||
ALTER TABLE ONLY admin.like
|
ALTER TABLE ONLY admin.like
|
||||||
ADD CONSTRAINT like_user_id_fkey FOREIGN KEY (user_id) REFERENCES admin.user (id) ON DELETE CASCADE;
|
ADD CONSTRAINT like_user_id_fkey FOREIGN KEY (user_id) REFERENCES admin.user (id) ON DELETE CASCADE;
|
||||||
|
|
||||||
|
@ -151,32 +133,16 @@ ALTER TABLE ONLY admin.like
|
||||||
ALTER TABLE ONLY admin.like
|
ALTER TABLE ONLY admin.like
|
||||||
ADD CONSTRAINT like_comment_id_fkey FOREIGN KEY (comment_id) REFERENCES admin.comment (id) ON DELETE CASCADE;
|
ADD CONSTRAINT like_comment_id_fkey FOREIGN KEY (comment_id) REFERENCES admin.comment (id) ON DELETE CASCADE;
|
||||||
|
|
||||||
ALTER TABLE ONLY admin.like
|
|
||||||
ADD CONSTRAINT like_post_id_unique UNIQUE (user_id, post_id);
|
|
||||||
|
|
||||||
ALTER TABLE ONLY admin.like
|
|
||||||
ADD CONSTRAINT like_comment_id_unique UNIQUE (user_id, comment_id);
|
|
||||||
|
|
||||||
CREATE SEQUENCE IF NOT EXISTS sys.follow_id_seq
|
|
||||||
START WITH 1
|
|
||||||
INCREMENT BY 1
|
|
||||||
NO MINVALUE
|
|
||||||
NO MAXVALUE
|
|
||||||
CACHE 1;
|
|
||||||
|
|
||||||
CREATE TABLE admin.follow (
|
CREATE TABLE admin.follow (
|
||||||
id INTEGER DEFAULT nextval('sys.follow_id_seq'::regclass) NOT NULL,
|
|
||||||
follower_id INTEGER NOT NULL,
|
follower_id INTEGER NOT NULL,
|
||||||
followee_id INTEGER NOT NULL,
|
followee_id INTEGER NOT NULL,
|
||||||
value BOOLEAN NOT NULL DEFAULT TRUE,
|
date TIMESTAMP WITH TIME ZONE DEFAULT clock_timestamp() NOT NULL
|
||||||
created TIMESTAMP WITH TIME ZONE DEFAULT clock_timestamp() NOT NULL,
|
|
||||||
modified TIMESTAMP WITH TIME ZONE DEFAULT clock_timestamp() NOT NULL
|
|
||||||
);
|
);
|
||||||
|
|
||||||
ALTER TABLE admin.follow OWNER TO xssbook;
|
ALTER TABLE admin.follow OWNER TO xssbook;
|
||||||
|
|
||||||
ALTER TABLE ONLY admin.follow
|
ALTER TABLE ONLY admin.follow
|
||||||
ADD CONSTRAINT follow_pkey PRIMARY KEY (id);
|
ADD CONSTRAINT follow_pkey PRIMARY KEY (follower_id, followee_id);
|
||||||
|
|
||||||
ALTER TABLE ONLY admin.follow
|
ALTER TABLE ONLY admin.follow
|
||||||
ADD CONSTRAINT follow_follower_id FOREIGN KEY (follower_id) REFERENCES admin.user (id) ON DELETE CASCADE;
|
ADD CONSTRAINT follow_follower_id FOREIGN KEY (follower_id) REFERENCES admin.user (id) ON DELETE CASCADE;
|
||||||
|
@ -184,64 +150,16 @@ ALTER TABLE ONLY admin.follow
|
||||||
ALTER TABLE ONLY admin.follow
|
ALTER TABLE ONLY admin.follow
|
||||||
ADD CONSTRAINT follow_followee_id FOREIGN KEY (followee_id) REFERENCES admin.user (id) ON DELETE CASCADE;
|
ADD CONSTRAINT follow_followee_id FOREIGN KEY (followee_id) REFERENCES admin.user (id) ON DELETE CASCADE;
|
||||||
|
|
||||||
ALTER TABLE ONLY admin.follow
|
|
||||||
ADD CONSTRAINT follow_follower_unique UNIQUE (follower_id, followee_id);
|
|
||||||
|
|
||||||
CREATE SEQUENCE IF NOT EXISTS sys.media_id_seq
|
|
||||||
START WITH 1
|
|
||||||
INCREMENT BY 1
|
|
||||||
NO MINVALUE
|
|
||||||
NO MAXVALUE
|
|
||||||
CACHE 1;
|
|
||||||
|
|
||||||
CREATE TABLE admin.media (
|
CREATE TABLE admin.media (
|
||||||
id INTEGER DEFAULT nextval('sys.media_id_seq'::regclass) NOT NULL,
|
|
||||||
name TEXT NOT NULL,
|
name TEXT NOT NULL,
|
||||||
content BYTEA NOT NULL,
|
content BYTEA NOT NULL,
|
||||||
type TEXT NOT NULL,
|
type TEXT NOT NULL
|
||||||
created TIMESTAMP WITH TIME ZONE DEFAULT clock_timestamp() NOT NULL,
|
|
||||||
modified TIMESTAMP WITH TIME ZONE DEFAULT clock_timestamp() NOT NULL
|
|
||||||
);
|
);
|
||||||
|
|
||||||
ALTER TABLE admin.media OWNER TO xssbook;
|
ALTER TABLE admin.media OWNER TO xssbook;
|
||||||
|
|
||||||
ALTER TABLE ONLY admin.media
|
ALTER TABLE ONLY admin.media
|
||||||
ADD CONSTRAINT media_pkey PRIMARY KEY (id);
|
ADD CONSTRAINT media_pkey PRIMARY KEY (name);
|
||||||
|
|
||||||
ALTER TABLE ONLY admin.media
|
|
||||||
ADD CONSTRAINT media_name_unique UNIQUE (name);
|
|
||||||
|
|
||||||
CREATE SEQUENCE IF NOT EXISTS sys.user_media_id_seq
|
|
||||||
START WITH 1
|
|
||||||
INCREMENT BY 1
|
|
||||||
NO MINVALUE
|
|
||||||
NO MAXVALUE
|
|
||||||
CACHE 1;
|
|
||||||
|
|
||||||
CREATE TYPE admin.user_media_type AS ENUM (
|
|
||||||
'avatar', 'banner'
|
|
||||||
);
|
|
||||||
|
|
||||||
CREATE TABLE admin.user_media (
|
|
||||||
id INTEGER DEFAULT nextval('sys.user_media_id_seq'::regclass) NOT NULL,
|
|
||||||
media_id INTEGER NOT NULL,
|
|
||||||
user_id INTEGER NOT NULL,
|
|
||||||
type admin.user_media_type NOT NULL
|
|
||||||
);
|
|
||||||
|
|
||||||
ALTER TABLE admin.user_media OWNER TO xssbook;
|
|
||||||
|
|
||||||
ALTER TABLE ONLY admin.user_media
|
|
||||||
ADD CONSTRAINT user_media_pkey PRIMARY KEY (id);
|
|
||||||
|
|
||||||
ALTER TABLE ONLY admin.user_media
|
|
||||||
ADD CONSTRAINT user_media_media_id_fkey FOREIGN KEY (media_id) REFERENCES admin.media (id) ON DELETE CASCADE;
|
|
||||||
|
|
||||||
ALTER TABLE ONLY admin.user_media
|
|
||||||
ADD CONSTRAINT user_media_user_id_fkey FOREIGN KEY (user_id) REFERENCES admin.user (id) ON DELETE CASCADE;
|
|
||||||
|
|
||||||
ALTER TABLE ONLY admin.user_media
|
|
||||||
ADD CONSTRAINT user_media_type_unique UNIQUE (user_id, type);
|
|
||||||
|
|
||||||
ALTER DATABASE xssbook SET search_path = admin,public;
|
ALTER DATABASE xssbook SET search_path = admin,public;
|
||||||
ALTER DATABASE xssbook SET bytea_output = 'hex';
|
ALTER DATABASE xssbook SET bytea_output = 'hex';
|
|
@ -4,24 +4,10 @@ CREATE VIEW api.comment AS
|
||||||
c.user_id,
|
c.user_id,
|
||||||
c.post_id,
|
c.post_id,
|
||||||
c.content,
|
c.content,
|
||||||
c.created,
|
c.date
|
||||||
c.modified
|
|
||||||
FROM
|
FROM
|
||||||
admin.comment c
|
admin.comment c
|
||||||
LEFT JOIN
|
ORDER BY id ASC;
|
||||||
admin.post p
|
|
||||||
ON
|
|
||||||
p.id = c.post_id
|
|
||||||
LEFT JOIN
|
|
||||||
admin.user u
|
|
||||||
ON
|
|
||||||
u.id = c.user_id
|
|
||||||
WHERE
|
|
||||||
c.deleted <> TRUE AND
|
|
||||||
p.deleted <> TRUE AND
|
|
||||||
u.deleted <> TRUE
|
|
||||||
ORDER BY
|
|
||||||
id ASC;
|
|
||||||
|
|
||||||
GRANT SELECT ON TABLE api.comment
|
GRANT SELECT ON TABLE api.comment
|
||||||
TO rest_anon, rest_user;
|
TO rest_anon, rest_user;
|
|
@ -11,10 +11,9 @@ BEGIN
|
||||||
PERFORM _api.raise_deny();
|
PERFORM _api.raise_deny();
|
||||||
END IF;
|
END IF;
|
||||||
|
|
||||||
UPDATE admin.comment SET
|
DELETE FROM admin.comment
|
||||||
deleted = TRUE,
|
WHERE user_id = _user_id
|
||||||
modified = clock_timestamp()
|
AND id = OLD.id;
|
||||||
WHERE id = OLD.id;
|
|
||||||
END
|
END
|
||||||
$BODY$;
|
$BODY$;
|
||||||
|
|
||||||
|
@ -22,7 +21,7 @@ GRANT EXECUTE ON FUNCTION _api.comment_delete()
|
||||||
TO rest_user;
|
TO rest_user;
|
||||||
GRANT DELETE ON TABLE api.comment
|
GRANT DELETE ON TABLE api.comment
|
||||||
TO rest_user;
|
TO rest_user;
|
||||||
GRANT UPDATE ON TABLE admin.comment
|
GRANT DELETE ON TABLE admin.comment
|
||||||
TO rest_user;
|
TO rest_user;
|
||||||
|
|
||||||
CREATE TRIGGER api_comment_delete_trgr
|
CREATE TRIGGER api_comment_delete_trgr
|
|
@ -34,9 +34,7 @@ BEGIN
|
||||||
_user_id,
|
_user_id,
|
||||||
NEW.post_id,
|
NEW.post_id,
|
||||||
NEW.content
|
NEW.content
|
||||||
)
|
);
|
||||||
RETURNING id
|
|
||||||
INTO NEW.id;
|
|
||||||
|
|
||||||
RETURN NEW;
|
RETURN NEW;
|
||||||
END
|
END
|
|
@ -27,9 +27,8 @@ BEGIN
|
||||||
END IF;
|
END IF;
|
||||||
|
|
||||||
IF _changed THEN
|
IF _changed THEN
|
||||||
UPDATE admin.comment SET
|
UPDATE admin.comment
|
||||||
content = NEW.content,
|
SET content = NEW.content
|
||||||
modified = clock_timestamp()
|
|
||||||
WHERE id = OLD.id;
|
WHERE id = OLD.id;
|
||||||
END IF;
|
END IF;
|
||||||
|
|
|
@ -8,7 +8,6 @@ DECLARE
|
||||||
_payload JSON;
|
_payload JSON;
|
||||||
_valid BOOLEAN;
|
_valid BOOLEAN;
|
||||||
_jwt_secret TEXT;
|
_jwt_secret TEXT;
|
||||||
_user_id INTEGER;
|
|
||||||
BEGIN
|
BEGIN
|
||||||
SELECT jwt_secret INTO _jwt_secret
|
SELECT jwt_secret INTO _jwt_secret
|
||||||
FROM sys.database_info
|
FROM sys.database_info
|
||||||
|
@ -29,13 +28,7 @@ BEGIN
|
||||||
RETURN NULL;
|
RETURN NULL;
|
||||||
END IF;
|
END IF;
|
||||||
|
|
||||||
_user_id = _payload->>'user_id';
|
RETURN _payload->>'user_id';
|
||||||
|
|
||||||
UPDATE admin.user
|
|
||||||
SET seen = clock_timestamp()
|
|
||||||
WHERE id = _user_id;
|
|
||||||
|
|
||||||
RETURN _user_id;
|
|
||||||
END
|
END
|
||||||
$BODY$;
|
$BODY$;
|
||||||
|
|
||||||
|
@ -43,5 +36,3 @@ GRANT EXECUTE ON FUNCTION _api.verify_jwt(TEXT)
|
||||||
TO rest_anon, rest_user;
|
TO rest_anon, rest_user;
|
||||||
GRANT SELECT ON TABLE sys.database_info
|
GRANT SELECT ON TABLE sys.database_info
|
||||||
TO rest_anon, rest_user;
|
TO rest_anon, rest_user;
|
||||||
GRANT UPDATE ON TABLE admin.user
|
|
||||||
TO rest_anon, rest_user;
|
|
|
@ -3,8 +3,7 @@ CREATE VIEW api.post AS
|
||||||
p.id,
|
p.id,
|
||||||
p.user_id,
|
p.user_id,
|
||||||
p.content,
|
p.content,
|
||||||
p.created,
|
p.date,
|
||||||
p.modified,
|
|
||||||
COALESCE(c.cc, 0)
|
COALESCE(c.cc, 0)
|
||||||
AS comment_count
|
AS comment_count
|
||||||
FROM
|
FROM
|
||||||
|
@ -17,19 +16,8 @@ CREATE VIEW api.post AS
|
||||||
admin.comment c
|
admin.comment c
|
||||||
GROUP BY
|
GROUP BY
|
||||||
c.post_id
|
c.post_id
|
||||||
) c
|
) c ON p.id = c.post_id
|
||||||
ON
|
ORDER BY p.id DESC;
|
||||||
p.id = c.post_id
|
|
||||||
LEFT JOIN
|
|
||||||
admin.user u
|
|
||||||
ON
|
|
||||||
u.id = p.user_id
|
|
||||||
WHERE
|
|
||||||
p.deleted <> TRUE
|
|
||||||
AND
|
|
||||||
u.deleted <> TRUE
|
|
||||||
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;
|
|
@ -11,10 +11,9 @@ BEGIN
|
||||||
PERFORM _api.raise_deny();
|
PERFORM _api.raise_deny();
|
||||||
END IF;
|
END IF;
|
||||||
|
|
||||||
UPDATE admin.post SET
|
DELETE FROM admin.post
|
||||||
deleted = TRUE,
|
WHERE user_id = _user_id
|
||||||
modified = clock_timestamp()
|
AND id = OLD.id;
|
||||||
WHERE id = OLD.id;
|
|
||||||
END
|
END
|
||||||
$BODY$;
|
$BODY$;
|
||||||
|
|
||||||
|
@ -22,7 +21,7 @@ GRANT EXECUTE ON FUNCTION _api.post_delete()
|
||||||
TO rest_user;
|
TO rest_user;
|
||||||
GRANT DELETE ON TABLE api.post
|
GRANT DELETE ON TABLE api.post
|
||||||
TO rest_user;
|
TO rest_user;
|
||||||
GRANT UPDATE ON TABLE admin.post
|
GRANT DELETE ON TABLE admin.post
|
||||||
TO rest_user;
|
TO rest_user;
|
||||||
|
|
||||||
CREATE TRIGGER api_post_delete_trgr
|
CREATE TRIGGER api_post_delete_trgr
|
|
@ -22,9 +22,7 @@ BEGIN
|
||||||
) VALUES (
|
) VALUES (
|
||||||
_user_id,
|
_user_id,
|
||||||
NEW.content
|
NEW.content
|
||||||
)
|
);
|
||||||
RETURNING id
|
|
||||||
INTO NEW.id;
|
|
||||||
|
|
||||||
RETURN NEW;
|
RETURN NEW;
|
||||||
END
|
END
|
|
@ -27,9 +27,8 @@ BEGIN
|
||||||
END IF;
|
END IF;
|
||||||
|
|
||||||
IF _changed THEN
|
IF _changed THEN
|
||||||
UPDATE admin.post SET
|
UPDATE admin.post
|
||||||
content = NEW.content,
|
SET content = NEW.content
|
||||||
modified = clock_timestamp()
|
|
||||||
WHERE id = OLD.id;
|
WHERE id = OLD.id;
|
||||||
END IF;
|
END IF;
|
||||||
|
|
|
@ -27,6 +27,7 @@ GRANT USAGE ON SCHEMA _api TO rest_anon, rest_user;
|
||||||
\i /db/rest/user/api_user_insert.sql;
|
\i /db/rest/user/api_user_insert.sql;
|
||||||
\i /db/rest/user/api_user_update.sql;
|
\i /db/rest/user/api_user_update.sql;
|
||||||
\i /db/rest/user/api_user_delete.sql;
|
\i /db/rest/user/api_user_delete.sql;
|
||||||
|
\i /db/rest/user/api_avatar.sql;
|
||||||
|
|
||||||
-- post
|
-- post
|
||||||
\i /db/rest/post/api_post.sql;
|
\i /db/rest/post/api_post.sql;
|
||||||
|
@ -40,16 +41,6 @@ GRANT USAGE ON SCHEMA _api TO rest_anon, rest_user;
|
||||||
\i /db/rest/comment/api_comment_update.sql;
|
\i /db/rest/comment/api_comment_update.sql;
|
||||||
\i /db/rest/comment/api_comment_delete.sql;
|
\i /db/rest/comment/api_comment_delete.sql;
|
||||||
|
|
||||||
-- like
|
|
||||||
\i /db/rest/like/api_like.sql;
|
|
||||||
\i /db/rest/like/api_like_insert.sql;
|
|
||||||
\i /db/rest/like/api_like_update.sql;
|
|
||||||
\i /db/rest/like/api_like_delete.sql;
|
|
||||||
|
|
||||||
-- media
|
|
||||||
\i /db/rest/media/api_profile_avatar.sql;
|
|
||||||
\i /db/rest/media/api_profile_banner.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;
|
22
db/rest/user/api_avatar.sql
Normal file
22
db/rest/user/api_avatar.sql
Normal file
|
@ -0,0 +1,22 @@
|
||||||
|
CREATE FUNCTION api.avatar(
|
||||||
|
user_id INTEGER DEFAULT 0
|
||||||
|
)
|
||||||
|
RETURNS sys."*/*"
|
||||||
|
LANGUAGE plpgsql VOLATILE
|
||||||
|
AS $BODY$
|
||||||
|
DECLARE
|
||||||
|
_mod INTEGER;
|
||||||
|
_name TEXT;
|
||||||
|
BEGIN
|
||||||
|
_mod = MOD(user_id, 24);
|
||||||
|
_name = 'default_avatar_' || _mod || '.png';
|
||||||
|
RETURN _api.serve_media(_name);
|
||||||
|
END
|
||||||
|
$BODY$;
|
||||||
|
|
||||||
|
GRANT EXECUTE ON FUNCTION api.avatar(INTEGER)
|
||||||
|
TO rest_anon, rest_user;
|
||||||
|
GRANT SELECT ON TABLE admin.user
|
||||||
|
TO rest_anon, rest_user;
|
||||||
|
GRANT SELECT ON TABLE admin.media
|
||||||
|
TO rest_anon, rest_user;
|
|
@ -9,15 +9,13 @@ CREATE VIEW api.user AS
|
||||||
u.middle_name,
|
u.middle_name,
|
||||||
u.email,
|
u.email,
|
||||||
u.gender,
|
u.gender,
|
||||||
|
u.join_date,
|
||||||
u.birth_date,
|
u.birth_date,
|
||||||
u.profile_bio,
|
u.profile_avatar,
|
||||||
u.created,
|
u.profile_banner,
|
||||||
u.modified,
|
u.profile_bio
|
||||||
u.seen
|
|
||||||
FROM
|
FROM
|
||||||
admin.user u
|
admin.user u;
|
||||||
WHERE
|
|
||||||
u.deleted <> TRUE;
|
|
||||||
|
|
||||||
GRANT SELECT ON TABLE api.user
|
GRANT SELECT ON TABLE api.user
|
||||||
TO rest_anon, rest_user;
|
TO rest_anon, rest_user;
|
|
@ -11,9 +11,7 @@ BEGIN
|
||||||
PERFORM _api.raise_deny();
|
PERFORM _api.raise_deny();
|
||||||
END IF;
|
END IF;
|
||||||
|
|
||||||
UPDATE admin.user SET
|
DELETE FROM admin.user
|
||||||
deleted = TRUE,
|
|
||||||
modified = clock_timestamp()
|
|
||||||
WHERE id = _user_id;
|
WHERE id = _user_id;
|
||||||
END
|
END
|
||||||
$BODY$;
|
$BODY$;
|
||||||
|
@ -22,7 +20,7 @@ GRANT EXECUTE ON FUNCTION _api.user_delete()
|
||||||
TO rest_user;
|
TO rest_user;
|
||||||
GRANT DELETE ON TABLE api.user
|
GRANT DELETE ON TABLE api.user
|
||||||
TO rest_user;
|
TO rest_user;
|
||||||
GRANT UPDATE ON TABLE admin.user
|
GRANT DELETE ON TABLE admin.user
|
||||||
TO rest_user;
|
TO rest_user;
|
||||||
|
|
||||||
CREATE TRIGGER api_user_delete_trgr
|
CREATE TRIGGER api_user_delete_trgr
|
|
@ -104,9 +104,7 @@ BEGIN
|
||||||
NEW.gender,
|
NEW.gender,
|
||||||
NEW.birth_date,
|
NEW.birth_date,
|
||||||
NEW.profile_bio
|
NEW.profile_bio
|
||||||
)
|
);
|
||||||
RETURNING id
|
|
||||||
INTO NEW.id;
|
|
||||||
|
|
||||||
NEW.password := NULL;
|
NEW.password := NULL;
|
||||||
|
|
|
@ -145,8 +145,7 @@ BEGIN
|
||||||
email = NEW.email,
|
email = NEW.email,
|
||||||
gender = NEW.gender,
|
gender = NEW.gender,
|
||||||
birth_date = NEW.birth_date,
|
birth_date = NEW.birth_date,
|
||||||
profile_bio = NEW.profile_bio,
|
profile_bio = NEW.profile_bio
|
||||||
modified = clock_timestamp()
|
|
||||||
WHERE id = OLD.id;
|
WHERE id = OLD.id;
|
||||||
END IF;
|
END IF;
|
||||||
|
|
11
db/rest/util/_api_get_user_id.sql
Normal file
11
db/rest/util/_api_get_user_id.sql
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
CREATE FUNCTION _api.get_user_id()
|
||||||
|
RETURNS INTEGER
|
||||||
|
LANGUAGE plpgsql VOLATILE
|
||||||
|
AS $BODY$
|
||||||
|
BEGIN
|
||||||
|
RETURN CURRENT_SETTING(
|
||||||
|
'request.jwt.claims',
|
||||||
|
TRUE
|
||||||
|
)::JSON->>'user_id';
|
||||||
|
END
|
||||||
|
$BODY$;
|
|
@ -1,5 +1,5 @@
|
||||||
CREATE FUNCTION _api.serve_media(
|
CREATE FUNCTION _api.serve_media(
|
||||||
_media_id INTEGER
|
_name TEXT
|
||||||
)
|
)
|
||||||
RETURNS sys."*/*"
|
RETURNS sys."*/*"
|
||||||
LANGUAGE plpgsql VOLATILE
|
LANGUAGE plpgsql VOLATILE
|
||||||
|
@ -15,13 +15,13 @@ BEGIN
|
||||||
'{"Cache-Control": "max-age=259200"}]'
|
'{"Cache-Control": "max-age=259200"}]'
|
||||||
, m.type, m.name)
|
, m.type, m.name)
|
||||||
FROM admin.media m
|
FROM admin.media m
|
||||||
WHERE m.id = _media_id INTO _headers;
|
WHERE m.name = _name INTO _headers;
|
||||||
|
|
||||||
PERFORM SET_CONFIG('response.headers', _headers, true);
|
PERFORM SET_CONFIG('response.headers', _headers, true);
|
||||||
|
|
||||||
SELECT m.content
|
SELECT m.content
|
||||||
FROM admin.media m
|
FROM admin.media m
|
||||||
WHERE m.id = _media_id
|
WHERE m.name = _name
|
||||||
INTO _data;
|
INTO _data;
|
||||||
|
|
||||||
IF FOUND THEN
|
IF FOUND THEN
|
||||||
|
@ -35,7 +35,7 @@ BEGIN
|
||||||
END
|
END
|
||||||
$BODY$;
|
$BODY$;
|
||||||
|
|
||||||
GRANT EXECUTE ON FUNCTION _api.serve_media(INTEGER)
|
GRANT EXECUTE ON FUNCTION _api.serve_media(TEXT)
|
||||||
TO rest_anon, rest_user;
|
TO rest_anon, rest_user;
|
||||||
GRANT SELECT ON TABLE admin.media
|
GRANT SELECT ON TABLE admin.media
|
||||||
TO rest_anon, rest_user;
|
TO rest_anon, rest_user;
|
|
@ -5,7 +5,7 @@ services:
|
||||||
ports:
|
ports:
|
||||||
- '80:80'
|
- '80:80'
|
||||||
volumes:
|
volumes:
|
||||||
- ./src:/opt/xssbook:ro
|
- ./web:/opt/xssbook
|
||||||
- ./conf/nginx:/etc/nginx/conf.d:ro
|
- ./conf/nginx:/etc/nginx/conf.d:ro
|
||||||
depends_on:
|
depends_on:
|
||||||
- rest
|
- rest
|
||||||
|
@ -18,9 +18,8 @@ services:
|
||||||
env_file:
|
env_file:
|
||||||
- ./conf/postgres/database.env
|
- ./conf/postgres/database.env
|
||||||
volumes:
|
volumes:
|
||||||
- ./src:/opt/xssbook:ro
|
- ./web:/opt/xssbook
|
||||||
- ./data/status:/status:ro
|
- ./data/status:/status
|
||||||
- ./data/session:/var/lib/php/session
|
|
||||||
depends_on:
|
depends_on:
|
||||||
- db
|
- db
|
||||||
|
|
||||||
|
@ -34,7 +33,7 @@ services:
|
||||||
- POSTGRES_INITDB_ARGS=--encoding=UTF-8 --lc-collate=C --lc-ctype=C
|
- POSTGRES_INITDB_ARGS=--encoding=UTF-8 --lc-collate=C --lc-ctype=C
|
||||||
volumes:
|
volumes:
|
||||||
- './data/schemas:/var/lib/postgresql/data'
|
- './data/schemas:/var/lib/postgresql/data'
|
||||||
- ./src/db:/db:ro
|
- ./db:/db:ro
|
||||||
|
|
||||||
rest:
|
rest:
|
||||||
build: ./build/postgrest
|
build: ./build/postgrest
|
||||||
|
@ -48,7 +47,7 @@ services:
|
||||||
env_file:
|
env_file:
|
||||||
- ./conf/postgres/database.env
|
- ./conf/postgres/database.env
|
||||||
volumes:
|
volumes:
|
||||||
- ./src/db:/db:ro
|
- ./db:/db:ro
|
||||||
- ./data/status:/status
|
- ./data/status:/status
|
||||||
depends_on:
|
depends_on:
|
||||||
- db
|
- db
|
||||||
|
|
|
@ -1,16 +0,0 @@
|
||||||
CREATE VIEW api.like AS
|
|
||||||
SELECT
|
|
||||||
l.id,
|
|
||||||
l.user_id,
|
|
||||||
l.post_id,
|
|
||||||
l.comment_id,
|
|
||||||
l.value,
|
|
||||||
l.created,
|
|
||||||
l.modified
|
|
||||||
FROM
|
|
||||||
admin.like l;
|
|
||||||
|
|
||||||
GRANT SELECT ON TABLE api.like
|
|
||||||
TO rest_anon, rest_user;
|
|
||||||
GRANT SELECT ON TABLE admin.like
|
|
||||||
TO rest_anon, rest_user;
|
|
|
@ -1,32 +0,0 @@
|
||||||
CREATE FUNCTION _api.like_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;
|
|
||||||
|
|
||||||
UPDATE admin.like SET
|
|
||||||
value = FALSE,
|
|
||||||
modified = clock_timestamp()
|
|
||||||
WHERE id = OLD.id;
|
|
||||||
END
|
|
||||||
$BODY$;
|
|
||||||
|
|
||||||
GRANT EXECUTE ON FUNCTION _api.like_delete()
|
|
||||||
TO rest_user;
|
|
||||||
GRANT DELETE ON TABLE api.like
|
|
||||||
TO rest_user;
|
|
||||||
GRANT UPDATE ON TABLE admin.like
|
|
||||||
TO rest_user;
|
|
||||||
|
|
||||||
CREATE TRIGGER api_like_delete_trgr
|
|
||||||
INSTEAD OF DELETE
|
|
||||||
ON api.like
|
|
||||||
FOR EACH ROW
|
|
||||||
EXECUTE PROCEDURE _api.like_delete();
|
|
|
@ -1,51 +0,0 @@
|
||||||
CREATE FUNCTION _api.like_insert()
|
|
||||||
RETURNS TRIGGER
|
|
||||||
LANGUAGE plpgsql VOLATILE
|
|
||||||
AS $BODY$
|
|
||||||
DECLARE
|
|
||||||
_user_id INTEGER;
|
|
||||||
BEGIN
|
|
||||||
_user_id = _api.get_user_id();
|
|
||||||
|
|
||||||
IF
|
|
||||||
NEW.post_id IS NULL AND
|
|
||||||
NEW.comment_id IS NULL
|
|
||||||
THEN
|
|
||||||
-- for now
|
|
||||||
PERFORM _api.raise_deny();
|
|
||||||
END IF;
|
|
||||||
|
|
||||||
NEW.value := COALESCE(NEW.value, TRUE);
|
|
||||||
|
|
||||||
INSERT INTO admin.like (
|
|
||||||
user_id,
|
|
||||||
post_id,
|
|
||||||
comment_id,
|
|
||||||
value
|
|
||||||
) VALUES (
|
|
||||||
_user_id,
|
|
||||||
NEW.post_id,
|
|
||||||
NEW.comment_id,
|
|
||||||
NEW.value
|
|
||||||
)
|
|
||||||
RETURNING id
|
|
||||||
INTO NEW.id;
|
|
||||||
|
|
||||||
RETURN NEW;
|
|
||||||
END
|
|
||||||
$BODY$;
|
|
||||||
|
|
||||||
GRANT EXECUTE ON FUNCTION _api.like_insert()
|
|
||||||
TO rest_user;
|
|
||||||
GRANT INSERT ON TABLE api.like
|
|
||||||
TO rest_user;
|
|
||||||
GRANT INSERT ON TABLE admin.like
|
|
||||||
TO rest_user;
|
|
||||||
GRANT UPDATE ON TABLE sys.like_id_seq
|
|
||||||
TO rest_user;
|
|
||||||
|
|
||||||
CREATE TRIGGER api_like_insert_trgr
|
|
||||||
INSTEAD OF INSERT
|
|
||||||
ON api.like
|
|
||||||
FOR EACH ROW
|
|
||||||
EXECUTE PROCEDURE _api.like_insert();
|
|
|
@ -1,44 +0,0 @@
|
||||||
CREATE FUNCTION _api.like_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.value = COALESCE(NEW.value, OLD.value);
|
|
||||||
|
|
||||||
IF NEW.value IS DISTINCT FROM OLD.value THEN
|
|
||||||
_changed = TRUE;
|
|
||||||
END IF;
|
|
||||||
|
|
||||||
IF _changed THEN
|
|
||||||
UPDATE admin.like SET
|
|
||||||
value = NEW.value,
|
|
||||||
modified = clock_timestamp()
|
|
||||||
WHERE id = OLD.id;
|
|
||||||
END IF;
|
|
||||||
|
|
||||||
RETURN NEW;
|
|
||||||
END
|
|
||||||
$BODY$;
|
|
||||||
|
|
||||||
GRANT EXECUTE ON FUNCTION _api.like_update()
|
|
||||||
TO rest_user;
|
|
||||||
GRANT UPDATE ON TABLE api.like
|
|
||||||
TO rest_user;
|
|
||||||
GRANT UPDATE ON TABLE admin.like
|
|
||||||
TO rest_user;
|
|
||||||
|
|
||||||
CREATE TRIGGER api_like_update_trgr
|
|
||||||
INSTEAD OF UPDATE
|
|
||||||
ON api.like
|
|
||||||
FOR EACH ROW
|
|
||||||
EXECUTE PROCEDURE _api.like_update();
|
|
|
@ -1,36 +0,0 @@
|
||||||
CREATE FUNCTION api.profile_avatar(
|
|
||||||
user_id INTEGER DEFAULT 0
|
|
||||||
)
|
|
||||||
RETURNS sys."*/*"
|
|
||||||
LANGUAGE plpgsql VOLATILE
|
|
||||||
AS $BODY$
|
|
||||||
DECLARE
|
|
||||||
_id INTEGER;
|
|
||||||
_mod INTEGER;
|
|
||||||
_name TEXT;
|
|
||||||
BEGIN
|
|
||||||
SELECT media_id INTO _id
|
|
||||||
FROM admin.user_media m
|
|
||||||
WHERE m.user_id = profile_avatar.user_id
|
|
||||||
AND type = 'avatar'::admin.user_media_type;
|
|
||||||
|
|
||||||
-- get default if not exists
|
|
||||||
IF NOT FOUND THEN
|
|
||||||
_mod = MOD(user_id, 24);
|
|
||||||
_name = 'default_avatar_' || _mod || '.png';
|
|
||||||
|
|
||||||
SELECT id INTO _id
|
|
||||||
FROM admin.media
|
|
||||||
WHERE name = _name;
|
|
||||||
END IF;
|
|
||||||
|
|
||||||
RETURN _api.serve_media(_id);
|
|
||||||
END
|
|
||||||
$BODY$;
|
|
||||||
|
|
||||||
GRANT EXECUTE ON FUNCTION api.profile_avatar(INTEGER)
|
|
||||||
TO rest_anon, rest_user;
|
|
||||||
GRANT SELECT ON TABLE admin.user_media
|
|
||||||
TO rest_anon, rest_user;
|
|
||||||
GRANT SELECT ON TABLE admin.media
|
|
||||||
TO rest_anon, rest_user;
|
|
|
@ -1,13 +0,0 @@
|
||||||
CREATE FUNCTION api.profile_banner(
|
|
||||||
user_id INTEGER DEFAULT 0
|
|
||||||
)
|
|
||||||
RETURNS sys."*/*"
|
|
||||||
LANGUAGE plpgsql VOLATILE
|
|
||||||
AS $BODY$
|
|
||||||
BEGIN
|
|
||||||
PERFORM _api.raise_deny();
|
|
||||||
END
|
|
||||||
$BODY$;
|
|
||||||
|
|
||||||
GRANT EXECUTE ON FUNCTION api.profile_banner(INTEGER)
|
|
||||||
TO rest_anon, rest_user;
|
|
|
@ -1,22 +0,0 @@
|
||||||
CREATE FUNCTION _api.get_user_id()
|
|
||||||
RETURNS INTEGER
|
|
||||||
LANGUAGE plpgsql VOLATILE
|
|
||||||
AS $BODY$
|
|
||||||
DECLARE
|
|
||||||
_user_id INTEGER;
|
|
||||||
BEGIN
|
|
||||||
_user_id = CURRENT_SETTING(
|
|
||||||
'request.jwt.claims',
|
|
||||||
TRUE
|
|
||||||
)::JSON->>'user_id';
|
|
||||||
|
|
||||||
UPDATE admin.user
|
|
||||||
SET seen = clock_timestamp()
|
|
||||||
WHERE id = _user_id;
|
|
||||||
|
|
||||||
RETURN _user_id;
|
|
||||||
END
|
|
||||||
$BODY$;
|
|
||||||
|
|
||||||
GRANT UPDATE ON TABLE admin.user
|
|
||||||
TO rest_anon, rest_user;
|
|
|
@ -1,45 +0,0 @@
|
||||||
#main-content {
|
|
||||||
padding-top: 20rem;
|
|
||||||
padding-bottom: 5rem;
|
|
||||||
display: flex;
|
|
||||||
flex-direction: row;
|
|
||||||
justify-content: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
.branding {
|
|
||||||
max-width: 30rem;
|
|
||||||
margin-right: 5rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
.branding h1 {
|
|
||||||
color: var(--blue);
|
|
||||||
font-family: facebook;
|
|
||||||
font-size: 3.5rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
.branding span {
|
|
||||||
font-size: 1.5rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
.form {
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
width: 30rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
@media(max-width: 1200px) {
|
|
||||||
#main-content {
|
|
||||||
flex-direction: column;
|
|
||||||
align-items: center;
|
|
||||||
width: 100%;
|
|
||||||
padding: 10rem 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.branding {
|
|
||||||
margin: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.form {
|
|
||||||
margin-top: 4rem;
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,69 +0,0 @@
|
||||||
|
|
||||||
.title {
|
|
||||||
margin-top: 2rem;
|
|
||||||
margin-left: 3rem;
|
|
||||||
font-size: 3rem;
|
|
||||||
margin-bottom: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.desc {
|
|
||||||
margin-left: 3rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
#people-container {
|
|
||||||
margin-left: auto;
|
|
||||||
margin-right: auto;
|
|
||||||
padding: 1rem 2rem;
|
|
||||||
display: grid;
|
|
||||||
grid-template-columns: 1fr 1fr 1fr;
|
|
||||||
grid-row-gap: 2rem;
|
|
||||||
grid-auto-rows: 1fr;
|
|
||||||
flex-direction: column;
|
|
||||||
align-items: center;
|
|
||||||
max-width: 90rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
.profile {
|
|
||||||
margin: 1rem;
|
|
||||||
text-decoration: none;
|
|
||||||
height: 100%;
|
|
||||||
}
|
|
||||||
|
|
||||||
.profile:hover {
|
|
||||||
outline: 1px solid var(--blue);
|
|
||||||
}
|
|
||||||
|
|
||||||
.profile strong {
|
|
||||||
font-size: 2rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
.profile .pfp, .profile .pfp img {
|
|
||||||
padding: none;
|
|
||||||
margin: none;
|
|
||||||
height: 6rem;
|
|
||||||
border-radius: .3rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
@media(max-width: 1400px) {
|
|
||||||
#people-container {
|
|
||||||
max-width: 90rem;
|
|
||||||
grid-template-columns: 1fr 1fr;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@media(max-width: 1000px) {
|
|
||||||
#people-container {
|
|
||||||
max-width: 50rem;
|
|
||||||
grid-template-columns: 1fr;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
td:nth-child(1) {
|
|
||||||
font-weight: bold;
|
|
||||||
color: var(--subtext);
|
|
||||||
padding-right: 1rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
td:nth-child(2) {
|
|
||||||
color: var(--text);
|
|
||||||
}
|
|
Binary file not shown.
|
@ -1,56 +0,0 @@
|
||||||
<?php /* Copyright (c) 2024 Freya Murphy */
|
|
||||||
class Auth_controller extends Controller {
|
|
||||||
|
|
||||||
// the home model
|
|
||||||
private $auth_model;
|
|
||||||
|
|
||||||
// the post controller
|
|
||||||
protected $post_controller;
|
|
||||||
|
|
||||||
function __construct($load) {
|
|
||||||
parent::__construct($load);
|
|
||||||
$this->auth_model = $this->load->model('apps/auth');
|
|
||||||
}
|
|
||||||
|
|
||||||
public function index(): void {
|
|
||||||
if ($this->main->session) {
|
|
||||||
$this->redirect('/home');
|
|
||||||
} else {
|
|
||||||
$this->redirect('/auth/login');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public function login(): void {
|
|
||||||
if ($this->main->session) {
|
|
||||||
$this->redirect('/home');
|
|
||||||
}
|
|
||||||
|
|
||||||
parent::index();
|
|
||||||
$data = $this->auth_model->get_data();
|
|
||||||
$this->view('header_empty', $data);
|
|
||||||
$this->view('apps/auth/login', $data);
|
|
||||||
$this->view('footer', $data);
|
|
||||||
}
|
|
||||||
|
|
||||||
public function logout(): void {
|
|
||||||
if ($this->main->session) {
|
|
||||||
$_SESSION['jwt'] = NULL;
|
|
||||||
}
|
|
||||||
$this->redirect('/auth/login');
|
|
||||||
}
|
|
||||||
|
|
||||||
public function update(): void {
|
|
||||||
if (!$this->is_ajax()) {
|
|
||||||
$this->error(400);
|
|
||||||
}
|
|
||||||
if (!isset($_POST['key']) || !isset($_POST['value'])) {
|
|
||||||
$this->error(400);
|
|
||||||
}
|
|
||||||
$key = $_POST['key'];
|
|
||||||
$value = $_POST['value'];
|
|
||||||
$_SESSION[$key] = $value;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
?>
|
|
|
@ -1,41 +0,0 @@
|
||||||
<?php /* Copyright (c) 2024 Freya Murphy */
|
|
||||||
class People_controller extends Controller {
|
|
||||||
|
|
||||||
// the people model
|
|
||||||
private $people_model;
|
|
||||||
|
|
||||||
// format model
|
|
||||||
protected $format_model;
|
|
||||||
|
|
||||||
function __construct($load) {
|
|
||||||
parent::__construct($load);
|
|
||||||
$this->people_model = $this->load->model('apps/people');
|
|
||||||
$this->format_model = $this->load->model('format');
|
|
||||||
}
|
|
||||||
|
|
||||||
public function index(): void {
|
|
||||||
parent::index();
|
|
||||||
$data = $this->people_model->get_data();
|
|
||||||
$this->view('header', $data);
|
|
||||||
$this->view('apps/people/main', $data);
|
|
||||||
$this->view('footer', $data);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return array<string,mixed>
|
|
||||||
*/
|
|
||||||
public function people(): array {
|
|
||||||
$data = $this->people_model->get_users();
|
|
||||||
|
|
||||||
$this->view('apps/people/people', $data);
|
|
||||||
|
|
||||||
$max = 0;
|
|
||||||
foreach ($data['users'] as $user) {
|
|
||||||
$max = max($max, $user['id']);
|
|
||||||
}
|
|
||||||
|
|
||||||
return $data;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
?>
|
|
|
@ -1,13 +0,0 @@
|
||||||
<?php /* Copyright (c) 2024 Freya Murphy */
|
|
||||||
class Auth_model extends Model {
|
|
||||||
|
|
||||||
function __construct($load) {
|
|
||||||
parent::__construct($load);
|
|
||||||
}
|
|
||||||
|
|
||||||
public function get_data(): array {
|
|
||||||
$data = parent::get_data();
|
|
||||||
$data['title'] = lang('login');
|
|
||||||
return $data;
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,88 +0,0 @@
|
||||||
<?php /* Copyright (c) 2024 Freya Murphy */
|
|
||||||
class People_model extends Model {
|
|
||||||
|
|
||||||
private $request_model;
|
|
||||||
|
|
||||||
function __construct($load) {
|
|
||||||
parent::__construct($load);
|
|
||||||
$this->request_model = $this->load->model('request');
|
|
||||||
}
|
|
||||||
|
|
||||||
private function get_filted_query($select) {
|
|
||||||
$filter_username = $this->request_model->get_str('filter_username', FALSE);
|
|
||||||
$filter_fisrt_name = $this->request_model->get_str('filter_first_name', FALSE);
|
|
||||||
$filter_last_name = $this->request_model->get_str('filter_last_name', FALSE);
|
|
||||||
$filter_email = $this->request_model->get_str('filter_email', FALSE);
|
|
||||||
$max = $this->request_model->get_int('max', FALSE);
|
|
||||||
|
|
||||||
$query = $this->db
|
|
||||||
->select($select)
|
|
||||||
->from('api.user u');
|
|
||||||
|
|
||||||
if ($filter_username) {
|
|
||||||
$query = $query
|
|
||||||
->where('u.username')
|
|
||||||
->like('%' . $filter_username . '%');
|
|
||||||
}
|
|
||||||
|
|
||||||
if ($filter_fisrt_name) {
|
|
||||||
$query = $query
|
|
||||||
->where('u.first_name')
|
|
||||||
->like('%'. $filter_fisrt_name . '%');
|
|
||||||
}
|
|
||||||
|
|
||||||
if ($filter_last_name) {
|
|
||||||
$query = $query
|
|
||||||
->where('u.last_name')
|
|
||||||
->like('%' . $filter_last_name . '%');
|
|
||||||
}
|
|
||||||
|
|
||||||
if ($filter_email) {
|
|
||||||
$query = $query
|
|
||||||
->where('u.email')
|
|
||||||
->like('%' . $filter_email . '%');
|
|
||||||
}
|
|
||||||
|
|
||||||
if ($max) {
|
|
||||||
$query = $query
|
|
||||||
->where('u.id')
|
|
||||||
->le($max);
|
|
||||||
}
|
|
||||||
|
|
||||||
return $query;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function get_users(): array {
|
|
||||||
$page = $this->request_model->get_int('page', 0);
|
|
||||||
$page_size = 24;
|
|
||||||
$offset = $page_size * $page;
|
|
||||||
|
|
||||||
$users = $this->get_filted_query('*')
|
|
||||||
->order_by('u.id', 'DESC')
|
|
||||||
->offset($offset)
|
|
||||||
->limit($page_size)
|
|
||||||
->rows();
|
|
||||||
|
|
||||||
$count = $this->get_filted_query('COUNT(u.id) AS count')
|
|
||||||
->row()['count'];
|
|
||||||
|
|
||||||
$max = 0;
|
|
||||||
|
|
||||||
foreach ($users as $user) {
|
|
||||||
$max = max($max, $user['id']);
|
|
||||||
}
|
|
||||||
|
|
||||||
return array(
|
|
||||||
'users' => $users,
|
|
||||||
'count' => $count,
|
|
||||||
'page_size' => $page_size,
|
|
||||||
'max_id' => $max
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
public function get_data(): array {
|
|
||||||
$data = parent::get_data();
|
|
||||||
$data['title'] = lang('title');
|
|
||||||
return $data;
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,86 +0,0 @@
|
||||||
<?php /* Copyright (c) 2024 Freya Murphy */ ?>
|
|
||||||
<?php /* vi: syntax=php */ ?>
|
|
||||||
<div id="main-content">
|
|
||||||
<div class="branding col">
|
|
||||||
<h1>xssbook</h1>
|
|
||||||
<span><?=lang('login_branding')?></span>
|
|
||||||
</div>
|
|
||||||
<div class="form card col">
|
|
||||||
<form id="action-login" class="col" action="">
|
|
||||||
<div class="rel mb">
|
|
||||||
<input
|
|
||||||
type="text"
|
|
||||||
name="username"
|
|
||||||
id="login-username"
|
|
||||||
placeholder=" "
|
|
||||||
>
|
|
||||||
<label for="username">
|
|
||||||
<?=lang('ph_username')?>
|
|
||||||
</label>
|
|
||||||
</div>
|
|
||||||
<div class="rel mb">
|
|
||||||
<input
|
|
||||||
type="password"
|
|
||||||
name="password"
|
|
||||||
id="login-password"
|
|
||||||
placeholder=" "
|
|
||||||
>
|
|
||||||
<label for="password">
|
|
||||||
<?=lang('ph_password')?>
|
|
||||||
</label>
|
|
||||||
</div>
|
|
||||||
<?=ilang('action_login',
|
|
||||||
class: 'btn btn-submit btn-wide',
|
|
||||||
button: TRUE,
|
|
||||||
attrs: array('type' => 'submit')
|
|
||||||
)?>
|
|
||||||
<?=ilang('action_forgot_passwd',
|
|
||||||
class: 'btn btn-line btn-blue btn-wide mt'
|
|
||||||
)?>
|
|
||||||
</form>
|
|
||||||
<hr>
|
|
||||||
<?=ilang('action_create_account',
|
|
||||||
id: 'action-register',
|
|
||||||
class: 'btn btn-success btn-wide',
|
|
||||||
button: TRUE,
|
|
||||||
attrs: array('type' => 'submit')
|
|
||||||
)?>
|
|
||||||
</div>
|
|
||||||
<script>
|
|
||||||
|
|
||||||
var onLogin = function(data) {
|
|
||||||
let jwt = data.token;
|
|
||||||
|
|
||||||
$.ajax({
|
|
||||||
url: '/auth/update',
|
|
||||||
method: 'POST',
|
|
||||||
data: JSON.stringify({
|
|
||||||
key: 'jwt',
|
|
||||||
value: jwt
|
|
||||||
}),
|
|
||||||
success: function (_) {
|
|
||||||
window.location = '/home';
|
|
||||||
}
|
|
||||||
})
|
|
||||||
};
|
|
||||||
|
|
||||||
$('#action-login').on('submit', function(e) {
|
|
||||||
e.preventDefault();
|
|
||||||
let username = $('#login-username').val();
|
|
||||||
let password = $('#login-password').val();
|
|
||||||
|
|
||||||
$.ajax({
|
|
||||||
url: '/api/rpc/login',
|
|
||||||
method: 'POST',
|
|
||||||
data: JSON.stringify({ username, password }),
|
|
||||||
success: onLogin
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
$('#action-register').on('click', function() {
|
|
||||||
$.get( "/modal/register", function (data) {
|
|
||||||
$(document.body).append(data);
|
|
||||||
});
|
|
||||||
})
|
|
||||||
</script>
|
|
||||||
</div>
|
|
|
@ -1,35 +0,0 @@
|
||||||
<?php /* Copyright (c) 2024 Freya Murphy */ ?>
|
|
||||||
<?php /* vi: syntax=php */ ?>
|
|
||||||
<a
|
|
||||||
class="card profile"
|
|
||||||
href="/profile?id=<?=$user['id']?>"
|
|
||||||
>
|
|
||||||
<div class="row">
|
|
||||||
<?php $this->view('template/pfp', array('user' => $user, 'link' => FALSE)); ?>
|
|
||||||
<div class="col ml">
|
|
||||||
<strong class=""><?=$this->format_model->name($user)?></strong>
|
|
||||||
<span class="dim"><?=lang('joined') . ' ' . $this->format_model->date($user['created'])?></span>
|
|
||||||
<span class="dim"><?=lang('seen') . ' ' . $this->format_model->date($user['seen'])?></span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<hr>
|
|
||||||
<table>
|
|
||||||
<tr>
|
|
||||||
<td><?=lang('tbl_username')?></td>
|
|
||||||
<td><?=$user['username']?></td>
|
|
||||||
<tr>
|
|
||||||
<tr>
|
|
||||||
<td><?=lang('tbl_email')?></td>
|
|
||||||
<td><?=$user['email']?></td>
|
|
||||||
<tr>
|
|
||||||
<tr>
|
|
||||||
<td><?=lang('tbl_gender')?></td>
|
|
||||||
<td><?=$user['gender']?></td>
|
|
||||||
<tr>
|
|
||||||
<tr>
|
|
||||||
<td><?=lang('tbl_uid')?></td>
|
|
||||||
<td><?=$user['id']?></td>
|
|
||||||
<tr>
|
|
||||||
</table>
|
|
||||||
</a>
|
|
||||||
<?
|
|
|
@ -1,67 +0,0 @@
|
||||||
<?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>
|
|
||||||
<hr>
|
|
||||||
<div id="people-container" class="col">
|
|
||||||
<?php
|
|
||||||
$pdata = $this->people();
|
|
||||||
?>
|
|
||||||
</div>
|
|
||||||
<?php
|
|
||||||
$loaded = count($pdata['users']);
|
|
||||||
$page_size = $pdata['page_size'];
|
|
||||||
$total = $pdata['count'];
|
|
||||||
$max = $pdata['max_id'];
|
|
||||||
?>
|
|
||||||
<?php if ($loaded >= $page_size && $page_size < $total): ?>
|
|
||||||
<?=ilang('action_load_users',
|
|
||||||
id: 'action-load-users',
|
|
||||||
class: 'btn btn-line btn-wide mt mb',
|
|
||||||
attrs: array(
|
|
||||||
'loaded' => $loaded,
|
|
||||||
'pageSize' => $page_size,
|
|
||||||
'userCount' => $total,
|
|
||||||
'userMax' => $max
|
|
||||||
)
|
|
||||||
)?>
|
|
||||||
<script>
|
|
||||||
|
|
||||||
var urlParams = new URLSearchParams(window.location.search).toString();
|
|
||||||
|
|
||||||
$('#action-load-users').on('click', function() {
|
|
||||||
let me = $(this);
|
|
||||||
let page = me.attr('page');
|
|
||||||
if (!page) {
|
|
||||||
page = '1';
|
|
||||||
}
|
|
||||||
let newPage = Number(page) + 1;
|
|
||||||
me.attr('page', newPage + '');
|
|
||||||
|
|
||||||
let loaded = Number(me.attr('loaded'));
|
|
||||||
let pageSize = Number(me.attr('pageSize'));
|
|
||||||
let userCount = Number(me.attr('userCount'));
|
|
||||||
let userMax = Number(me.attr('userMax'));
|
|
||||||
|
|
||||||
let url = '/people/people?page=' + page + '&max=' + userMax + '&' + urlParams;
|
|
||||||
$.get(url, function (data) {
|
|
||||||
if (data === '') {
|
|
||||||
me.remove();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
let container = $('#people-container');
|
|
||||||
container.append(data);
|
|
||||||
|
|
||||||
loaded += pageSize;
|
|
||||||
if (loaded >= userCount) {
|
|
||||||
me.remove();
|
|
||||||
} else {
|
|
||||||
me.attr('loaded', loaded + '');
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
|
||||||
</script>
|
|
||||||
<?php endif ?>
|
|
||||||
</div>
|
|
|
@ -1,7 +0,0 @@
|
||||||
<?php /* Copyright (c) 2024 Freya Murphy */ ?>
|
|
||||||
<?php /* vi: syntax=php */ ?>
|
|
||||||
<?php
|
|
||||||
foreach($users as $user) {
|
|
||||||
$this->view('apps/people/card', array('user' => $user));
|
|
||||||
}
|
|
||||||
?>
|
|
|
@ -1,23 +0,0 @@
|
||||||
<!DOCTYPE html>
|
|
||||||
<html>
|
|
||||||
<head>
|
|
||||||
<script>
|
|
||||||
<?php if ($this->main->session): ?>
|
|
||||||
var jwtStr = <?=json_encode($this->main->session['jwt'])?>;
|
|
||||||
<?php else: ?>
|
|
||||||
var jwtStr = null;
|
|
||||||
<?php endif; ?>
|
|
||||||
</script>
|
|
||||||
<?php
|
|
||||||
foreach ($js_files as $js) {
|
|
||||||
echo $this->main->link_js($js);
|
|
||||||
}
|
|
||||||
foreach ($css_files as $css) {
|
|
||||||
echo $this->main->link_css($css);
|
|
||||||
}
|
|
||||||
?>
|
|
||||||
<title><?=$title?></title>
|
|
||||||
</head>
|
|
||||||
<body>
|
|
||||||
<div id="toast-container">
|
|
||||||
</div>
|
|
|
@ -1,173 +0,0 @@
|
||||||
|
|
||||||
<?php /* Copyright (c) 2024 Freya Murphy */ ?>
|
|
||||||
<?php /* vi: syntax=php */ ?>
|
|
||||||
<form id="register-form">
|
|
||||||
<div class="modal-content register-modal col">
|
|
||||||
<label class="static">
|
|
||||||
<?=lang('ph_basic_info')?>
|
|
||||||
</label>
|
|
||||||
<div class="row mt">
|
|
||||||
<div class="rel btn-wide">
|
|
||||||
<input
|
|
||||||
type="text"
|
|
||||||
name="first_name"
|
|
||||||
id="register-first-name"
|
|
||||||
placeholder=" "
|
|
||||||
>
|
|
||||||
<label for="first_name">
|
|
||||||
<?=lang('ph_first_name')?>
|
|
||||||
</label>
|
|
||||||
</div>
|
|
||||||
<div class="rel ml btn-wide">
|
|
||||||
<input
|
|
||||||
type="text"
|
|
||||||
name="last_name"
|
|
||||||
id="register-last-name"
|
|
||||||
placeholder=" "
|
|
||||||
>
|
|
||||||
<label for="last_name">
|
|
||||||
<?=lang('ph_last_name')?>
|
|
||||||
</label>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="rel mt">
|
|
||||||
<input
|
|
||||||
type="text"
|
|
||||||
name="username"
|
|
||||||
id="register-username"
|
|
||||||
placeholder=" "
|
|
||||||
>
|
|
||||||
<label for="username">
|
|
||||||
<?=lang('ph_username')?>
|
|
||||||
</label>
|
|
||||||
</div>
|
|
||||||
<div class="rel mt">
|
|
||||||
<input
|
|
||||||
type="password"
|
|
||||||
name="password"
|
|
||||||
id="register-password"
|
|
||||||
placeholder=" "
|
|
||||||
>
|
|
||||||
<label for="password">
|
|
||||||
<?=lang('ph_password')?>
|
|
||||||
</label>
|
|
||||||
</div>
|
|
||||||
<div class="rel mt">
|
|
||||||
<input
|
|
||||||
type="text"
|
|
||||||
name="email"
|
|
||||||
id="register-email"
|
|
||||||
placeholder=" "
|
|
||||||
>
|
|
||||||
<label for="email">
|
|
||||||
<?=lang('ph_email')?>
|
|
||||||
</label>
|
|
||||||
</div>
|
|
||||||
<label for="birth_date" class="mt static">
|
|
||||||
<?=lang('ph_birth_date')?>
|
|
||||||
</label>
|
|
||||||
<input
|
|
||||||
class="mt"
|
|
||||||
type="date"
|
|
||||||
name="birth_date"
|
|
||||||
id="register-birth-date"
|
|
||||||
>
|
|
||||||
<label for="gender" class="mt static">
|
|
||||||
<?=lang('ph_gender')?>
|
|
||||||
</label>
|
|
||||||
<div class="row mt" data-type="radio" data-name="gender-wrapper">
|
|
||||||
<div class="rel radio mr">
|
|
||||||
<input
|
|
||||||
type="radio"
|
|
||||||
id="register-gender-male"
|
|
||||||
name="gender"
|
|
||||||
value="male"
|
|
||||||
>
|
|
||||||
<label
|
|
||||||
for="register-gender-male"
|
|
||||||
class="static"
|
|
||||||
>
|
|
||||||
<?=lang('ph_gender_male')?>
|
|
||||||
</label>
|
|
||||||
</div>
|
|
||||||
<div class="rel radio mr">
|
|
||||||
<input
|
|
||||||
type="radio"
|
|
||||||
id="register-gender-female"
|
|
||||||
name="gender"
|
|
||||||
value="female"
|
|
||||||
>
|
|
||||||
<label
|
|
||||||
for="register-gender-female"
|
|
||||||
class="static"
|
|
||||||
>
|
|
||||||
<?=lang('ph_gender_female')?>
|
|
||||||
</label>
|
|
||||||
</div>
|
|
||||||
<div class="rel radio">
|
|
||||||
<input
|
|
||||||
type="radio"
|
|
||||||
id="register-gender-lettuce"
|
|
||||||
name="gender"
|
|
||||||
value="lettuce"
|
|
||||||
>
|
|
||||||
<label
|
|
||||||
for="register-gender-lettuce"
|
|
||||||
class="static"
|
|
||||||
>
|
|
||||||
<?=lang('ph_gender_lettuce')?>
|
|
||||||
</label>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="modal-footer">
|
|
||||||
<?=ilang('action_register',
|
|
||||||
id: 'register-submit',
|
|
||||||
class: 'btn btn-wide btn-success',
|
|
||||||
attrs: array('type' => 'submit'),
|
|
||||||
button: TRUE
|
|
||||||
)?>
|
|
||||||
</div>
|
|
||||||
</form>
|
|
||||||
<script>
|
|
||||||
$('#register-form').submit(function(e) {
|
|
||||||
e.preventDefault();
|
|
||||||
|
|
||||||
const form = event.target;
|
|
||||||
const formFields = form.elements;
|
|
||||||
|
|
||||||
let first_name = formFields.first_name.value.trim();
|
|
||||||
let last_name = formFields.last_name.value.trim();
|
|
||||||
let username = formFields.username.value.trim();
|
|
||||||
let password = formFields.password.value.trim();
|
|
||||||
let email = formFields.email.value.trim();
|
|
||||||
let birth_date = formFields.birth_date.value.trim();
|
|
||||||
let gender = formFields.gender.value.trim();
|
|
||||||
|
|
||||||
if(birth_date === '') {
|
|
||||||
errorToast('toast_date_empty');
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const onSuccess = () => {
|
|
||||||
$.ajax({
|
|
||||||
url: '/api/rpc/login',
|
|
||||||
method: 'POST',
|
|
||||||
data: JSON.stringify({
|
|
||||||
username, password
|
|
||||||
}),
|
|
||||||
success: onLogin
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
$.ajax({
|
|
||||||
url: '/api/user',
|
|
||||||
method: 'POST',
|
|
||||||
data: JSON.stringify({
|
|
||||||
first_name, last_name, username, password,
|
|
||||||
email, birth_date, gender
|
|
||||||
}),
|
|
||||||
success: onSuccess
|
|
||||||
});
|
|
||||||
});
|
|
||||||
</script>
|
|
|
@ -1,17 +0,0 @@
|
||||||
<?php /* Copyright (c) 2024 Freya Murphy */ ?>
|
|
||||||
<?php /* vi: syntax=php */ ?>
|
|
||||||
<?php
|
|
||||||
$class = isset($class) ? $class : '';
|
|
||||||
$link = isset($link) ? $link : TRUE;
|
|
||||||
?>
|
|
||||||
<?php if($link): ?>
|
|
||||||
<a class="image-loading pfp <?=$class?>" href="/profile?id=<?=$user['id']?>">
|
|
||||||
<?php else: ?>
|
|
||||||
<div class="image-loading pfp <?=$class?>">
|
|
||||||
<?php endif; ?>
|
|
||||||
<img src="/api/rpc/profile_avatar?user_id=<?=$user['id']?>"/>
|
|
||||||
<?php if ($link): ?>
|
|
||||||
</a>
|
|
||||||
<?php else: ?>
|
|
||||||
</div>
|
|
||||||
<?php endif; ?>
|
|
|
@ -1,34 +0,0 @@
|
||||||
<?php
|
|
||||||
|
|
||||||
$lang['login'] = 'Login';
|
|
||||||
$lang['login_branding'] = 'Connect with javascript and the world around you on XSSBook.';
|
|
||||||
|
|
||||||
$lang['ph_username'] = 'Username';
|
|
||||||
$lang['ph_password'] = 'Password';
|
|
||||||
$lang['ph_first_name'] = 'First Name';
|
|
||||||
$lang['ph_last_name'] = 'Last Name';
|
|
||||||
$lang['ph_middle_name'] = 'Middle Name';
|
|
||||||
$lang['ph_username'] = 'Username';
|
|
||||||
$lang['ph_email'] = 'Email';
|
|
||||||
$lang['ph_password'] = 'Password';
|
|
||||||
$lang['ph_birth_date'] = 'Birthday';
|
|
||||||
$lang['ph_gender'] = 'Gender';
|
|
||||||
$lang['ph_gender_male'] = 'Male';
|
|
||||||
$lang['ph_gender_female'] = 'Female';
|
|
||||||
$lang['ph_gender_lettuce'] = 'Lettuce';
|
|
||||||
$lang['ph_basic_info'] = 'General Information';
|
|
||||||
|
|
||||||
$lang['action_login_tip'] = 'Login';
|
|
||||||
$lang['action_login_text'] = 'Login';
|
|
||||||
$lang['action_register_tip'] = 'Register';
|
|
||||||
$lang['action_register_text'] = 'Register';
|
|
||||||
$lang['action_create_account_tip'] = 'Create a new account';
|
|
||||||
$lang['action_create_account_text'] = 'Create new account';
|
|
||||||
$lang['action_forgot_passwd_tip'] = 'Reset your password';
|
|
||||||
$lang['action_forgot_passwd_text'] = 'Forgot password?';
|
|
||||||
|
|
||||||
$lang['register_modal_title'] = 'Create New Account';
|
|
||||||
$lang['action_register_text'] = 'Register';
|
|
||||||
$lang['action_register_tip'] = 'Register';
|
|
||||||
|
|
||||||
?>
|
|
|
@ -1,17 +0,0 @@
|
||||||
<?php
|
|
||||||
|
|
||||||
$lang['title'] = 'Directory';
|
|
||||||
$lang['desc'] = 'Explore other people on xssbook!';
|
|
||||||
|
|
||||||
$lang['joined'] = 'Joined: ';
|
|
||||||
$lang['seen'] = 'Seen: ';
|
|
||||||
|
|
||||||
$lang['tbl_username'] = 'Username';
|
|
||||||
$lang['tbl_email'] = 'Email ';
|
|
||||||
$lang['tbl_uid'] = 'User ID ';
|
|
||||||
$lang['tbl_gender'] = 'Gender ';
|
|
||||||
|
|
||||||
$lang['action_load_users_text'] = 'Load more users';
|
|
||||||
$lang['action_load_users_tip'] = 'Load more users';
|
|
||||||
|
|
||||||
?>
|
|
|
@ -14,7 +14,7 @@ class _index_controller extends Controller {
|
||||||
if ($this->main->session) {
|
if ($this->main->session) {
|
||||||
$this->redirect('/home');
|
$this->redirect('/home');
|
||||||
} else {
|
} else {
|
||||||
$this->redirect('/auth/login');
|
$this->redirect('/login');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -21,39 +21,6 @@ class Post_controller extends Controller {
|
||||||
$this->view('template/posts');
|
$this->view('template/posts');
|
||||||
}
|
}
|
||||||
|
|
||||||
public function post(): void {
|
|
||||||
$pid = $this->request_model->get_int('id', 0);
|
|
||||||
|
|
||||||
$post = $this->db
|
|
||||||
->select('p.*, l.id as like_id')
|
|
||||||
->from('api.post p')
|
|
||||||
->join('api.like l', 'p.id = l.post_id AND l.user_id')
|
|
||||||
->eq($pid)
|
|
||||||
->where('p.id')
|
|
||||||
->eq($pid)
|
|
||||||
->row();
|
|
||||||
|
|
||||||
if (!$post) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
$users = $this->cache_model->get_users([$post]);
|
|
||||||
$uid = $post['user_id'];
|
|
||||||
|
|
||||||
if (!array_key_exists($uid, $users)) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
$user = $users[$uid];
|
|
||||||
|
|
||||||
$data = array(
|
|
||||||
'user' => $user,
|
|
||||||
'page_size' => $this->page_size,
|
|
||||||
'post' => $post
|
|
||||||
);
|
|
||||||
$this->view('template/post', $data);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return array<string,mixed>
|
* @return array<string,mixed>
|
||||||
*/
|
*/
|
||||||
|
@ -63,23 +30,28 @@ class Post_controller extends Controller {
|
||||||
$offset = $page * $this->page_size;
|
$offset = $page * $this->page_size;
|
||||||
|
|
||||||
$user = $this->main->user();
|
$user = $this->main->user();
|
||||||
$uid = isset($user) ? $user['id'] : NULL;
|
|
||||||
|
|
||||||
$query = $this->db;
|
$query = $this->db;
|
||||||
|
|
||||||
$query = $this->db
|
if ($user) {
|
||||||
->select('p.*, l.id as like_id')
|
$query = $query->select('p.*, l.post_id IS NOT NULL as liked');
|
||||||
->from('api.post p')
|
} else {
|
||||||
->join('api.like l', 'p.id = l.post_id AND l.user_id')
|
$query = $query->select('p.*, FALSE as liked');
|
||||||
->eq($uid);
|
}
|
||||||
|
|
||||||
|
$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) {
|
if ($max) {
|
||||||
$query = $query
|
$query = $query
|
||||||
->where('p.id')->le($max);
|
->where('id')->le($max);
|
||||||
}
|
}
|
||||||
|
|
||||||
$posts = $query
|
$posts = $query
|
||||||
->order_by('p.id', 'DESC')
|
|
||||||
->limit($this->page_size)
|
->limit($this->page_size)
|
||||||
->offset($offset)
|
->offset($offset)
|
||||||
->rows();
|
->rows();
|
||||||
|
@ -101,6 +73,7 @@ class Post_controller extends Controller {
|
||||||
->from('api.post p')
|
->from('api.post p')
|
||||||
->row()['pc'];
|
->row()['pc'];
|
||||||
|
|
||||||
|
|
||||||
return array(
|
return array(
|
||||||
'loaded' => count($posts),
|
'loaded' => count($posts),
|
||||||
'total' => $pc,
|
'total' => $pc,
|
||||||
|
@ -109,36 +82,6 @@ class Post_controller extends Controller {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function comment(): void {
|
|
||||||
$cid = $this->request_model->get_int('id', 0);
|
|
||||||
|
|
||||||
$comment = $this->db
|
|
||||||
->select('*')
|
|
||||||
->from('api.comment')
|
|
||||||
->where('id')
|
|
||||||
->eq($cid)
|
|
||||||
->row();
|
|
||||||
|
|
||||||
if (!$comment) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
$users = $this->cache_model->get_users([$comment]);
|
|
||||||
$uid = $comment['user_id'];
|
|
||||||
|
|
||||||
if (!array_key_exists($uid, $users)) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
$user = $users[$uid];
|
|
||||||
|
|
||||||
$data = array(
|
|
||||||
'user' => $user,
|
|
||||||
'comment' => $comment
|
|
||||||
);
|
|
||||||
$this->view('template/comment', $data);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return array<string,mixed>
|
* @return array<string,mixed>
|
||||||
*/
|
*/
|
||||||
|
@ -162,7 +105,6 @@ class Post_controller extends Controller {
|
||||||
}
|
}
|
||||||
|
|
||||||
$comments = $query
|
$comments = $query
|
||||||
->order_by('id', 'ASC')
|
|
||||||
->limit($this->page_size)
|
->limit($this->page_size)
|
||||||
->offset($offset)
|
->offset($offset)
|
||||||
->rows();
|
->rows();
|
||||||
|
@ -170,17 +112,6 @@ class Post_controller extends Controller {
|
||||||
$users = $this->cache_model->get_users($comments);
|
$users = $this->cache_model->get_users($comments);
|
||||||
$max = 0;
|
$max = 0;
|
||||||
|
|
||||||
// only add this hr when not logged in
|
|
||||||
// otherwise its added automatically by
|
|
||||||
// the like and comment buttons
|
|
||||||
if (
|
|
||||||
count($comments) &&
|
|
||||||
$page == 0 &&
|
|
||||||
$this->main->session === NULL
|
|
||||||
) {
|
|
||||||
echo '<hr>';
|
|
||||||
}
|
|
||||||
|
|
||||||
foreach ($comments as $comment) {
|
foreach ($comments as $comment) {
|
||||||
$max = max($max, $comment['id']);
|
$max = max($max, $comment['id']);
|
||||||
$data = array();
|
$data = array();
|
|
@ -8,12 +8,11 @@ class Error_controller extends Controller {
|
||||||
$this->error_model = $this->load->model('apps/error');
|
$this->error_model = $this->load->model('apps/error');
|
||||||
}
|
}
|
||||||
|
|
||||||
public function index(): void {
|
public function index() {
|
||||||
parent::index();
|
parent::index();
|
||||||
$data = $this->error_model->get_data();
|
$data = $this->error_model->get_data();
|
||||||
$this->view('header', $data);
|
$this->view('header', $data);
|
||||||
$this->view('apps/error/main', $data);
|
$this->view('apps/error/main', $data);
|
||||||
$this->view('footer', $data);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
|
@ -18,7 +18,6 @@ class Home_controller extends Controller {
|
||||||
$data = $this->home_model->get_data();
|
$data = $this->home_model->get_data();
|
||||||
$this->view('header', $data);
|
$this->view('header', $data);
|
||||||
$this->view('apps/home/main', $data);
|
$this->view('apps/home/main', $data);
|
||||||
$this->view('footer', $data);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
|
@ -20,14 +20,6 @@ class Modal_controller extends Controller {
|
||||||
public function new_post(): void {
|
public function new_post(): void {
|
||||||
$this->modal('new_post');
|
$this->modal('new_post');
|
||||||
}
|
}
|
||||||
|
|
||||||
public function register(): void {
|
|
||||||
$this->load->app_lang(
|
|
||||||
$this->main->info['lang'],
|
|
||||||
'auth'
|
|
||||||
);
|
|
||||||
$this->modal('register');
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
?>
|
?>
|
|
@ -7,18 +7,22 @@ class Error_model extends Model {
|
||||||
|
|
||||||
private function get_msg(&$data) {
|
private function get_msg(&$data) {
|
||||||
if (!array_key_exists('code', $_GET)) {
|
if (!array_key_exists('code', $_GET)) {
|
||||||
http_response_code(500);
|
|
||||||
$data['msg'] = lang('error');
|
$data['msg'] = lang('error');
|
||||||
$data['title'] = '500';
|
$data['title'] = '500';
|
||||||
} else {
|
} else {
|
||||||
$code = $_GET['code'];
|
$code = $_GET['code'];
|
||||||
http_response_code($code);
|
|
||||||
$data['title'] = $code;
|
$data['title'] = $code;
|
||||||
$msg = lang('error_' . $code, FALSE);
|
switch ($code) {
|
||||||
if (!$msg) {
|
case '404':
|
||||||
$msg = lang('error');
|
$data['msg'] = lang('error_404');
|
||||||
|
break;
|
||||||
|
case '500':
|
||||||
|
$data['msg'] = lang('error_500');
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
$data['msg'] = lang('error');
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
$data['msg'] = $msg;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -39,8 +39,7 @@ class Format_model extends Model {
|
||||||
* @returns the formatted date
|
* @returns the formatted date
|
||||||
*/
|
*/
|
||||||
public function date($date) {
|
public function date($date) {
|
||||||
$date=date_create($date);
|
return $date;
|
||||||
return date_format($date, "Y-m-d H:i");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
|
@ -53,7 +53,7 @@ class Main_model {
|
||||||
*/
|
*/
|
||||||
private function asset_stamp($path): int {
|
private function asset_stamp($path): int {
|
||||||
$root = $GLOBALS['webroot'];
|
$root = $GLOBALS['webroot'];
|
||||||
$path = $root . '/../public/' . $path;
|
$path = $root . '/public/' . $path;
|
||||||
return filemtime($path);
|
return filemtime($path);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
<?php /* Copyright (c) 2024 Freya Murphy */ ?>
|
<?php /* Copyright (c) 2024 Freya Murphy */ ?>
|
||||||
<?php /* vi: syntax=php */ ?>
|
<?php /* vi: syntax=php */ ?>
|
||||||
<div id="main-content">
|
<div id="error">
|
||||||
<h1><?=$title?></h1>
|
<h1><?=$title?></h1>
|
||||||
<span><?=$msg?></span>
|
<span><?=$msg?></span>
|
||||||
</div>
|
</div>
|
|
@ -7,7 +7,7 @@
|
||||||
<?php $this->view('template/pfp', array('user' => $self))?>
|
<?php $this->view('template/pfp', array('user' => $self))?>
|
||||||
<a
|
<a
|
||||||
id="action-new-post"
|
id="action-new-post"
|
||||||
class="btn btn-alt btn-wide ml"
|
class="input btn-fake ml"
|
||||||
autocomplete="off"
|
autocomplete="off"
|
||||||
aria-label="<?=lang('action_new_post_tip')?>"
|
aria-label="<?=lang('action_new_post_tip')?>"
|
||||||
>
|
>
|
|
@ -1,8 +1,4 @@
|
||||||
<?php /* Copyright (c) 2024 Freya Murphy */ ?>
|
<?php /* Copyright (c) 2024 Freya Murphy */ ?>
|
||||||
<?php /* vi: syntax=php */ ?>
|
<?php /* vi: syntax=php */ ?>
|
||||||
<footer>
|
|
||||||
Freya Murphy © 2023 | <a href="https://freya.cat">freya.cat</a>
|
|
||||||
</footer>
|
|
||||||
<body>
|
<body>
|
||||||
|
|
||||||
</html>
|
</html>
|
|
@ -2,8 +2,28 @@
|
||||||
<?php /* vi: syntax=php */ ?>
|
<?php /* vi: syntax=php */ ?>
|
||||||
<?php
|
<?php
|
||||||
$self = $this->main->user();
|
$self = $this->main->user();
|
||||||
$this->view('header_empty', $data);
|
|
||||||
?>
|
?>
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<script>
|
||||||
|
<?php if ($this->main->session): ?>
|
||||||
|
var jwtStr = <?=json_encode($this->main->session['jwt'])?>;
|
||||||
|
<?php else: ?>
|
||||||
|
var jwtStr = null;
|
||||||
|
<?php endif; ?>
|
||||||
|
</script>
|
||||||
|
<?php
|
||||||
|
foreach ($js_files as $js) {
|
||||||
|
echo $this->main->link_js($js);
|
||||||
|
}
|
||||||
|
foreach ($css_files as $css) {
|
||||||
|
echo $this->main->link_css($css);
|
||||||
|
}
|
||||||
|
?>
|
||||||
|
<title><?=$title?></title>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
<header class="nav">
|
<header class="nav">
|
||||||
<div class="nav-left">
|
<div class="nav-left">
|
||||||
<span class="logo">xssbook</span>
|
<span class="logo">xssbook</span>
|
||||||
|
@ -11,7 +31,7 @@
|
||||||
<div class="nav-center" :class="{hidden: !visible}">
|
<div class="nav-center" :class="{hidden: !visible}">
|
||||||
<a
|
<a
|
||||||
id="action-home"
|
id="action-home"
|
||||||
class="btn"
|
class="header-entry btn btn-hover btn-action btn-blue"
|
||||||
href="/home"
|
href="/home"
|
||||||
title="<?=lang('action_home_tip')?>"
|
title="<?=lang('action_home_tip')?>"
|
||||||
>
|
>
|
||||||
|
@ -20,7 +40,7 @@
|
||||||
</a>
|
</a>
|
||||||
<a
|
<a
|
||||||
id="action-people"
|
id="action-people"
|
||||||
class="btn"
|
class="header-entry btn btn-hover btn-action btn-blue"
|
||||||
href="/people"
|
href="/people"
|
||||||
title="<?=lang('action_people_tip')?>"
|
title="<?=lang('action_people_tip')?>"
|
||||||
>
|
>
|
||||||
|
@ -29,7 +49,7 @@
|
||||||
</a>
|
</a>
|
||||||
<a
|
<a
|
||||||
id="action-chat"
|
id="action-chat"
|
||||||
class="btn"
|
class="header-entry btn btn-hover btn-action btn-blue"
|
||||||
href="/chat"
|
href="/chat"
|
||||||
title="<?=lang('action_chat_tip')?>"
|
title="<?=lang('action_chat_tip')?>"
|
||||||
>
|
>
|
||||||
|
@ -50,7 +70,7 @@
|
||||||
'class' => 'pfp-sm ml',
|
'class' => 'pfp-sm ml',
|
||||||
)); ?>
|
)); ?>
|
||||||
<?php else: ?>
|
<?php else: ?>
|
||||||
<?=ilang('action_login', class: 'btn', href: '/auth/login')?>
|
<?=ilang('action_login', class: 'btn btn-action', href: '/auth/login')?>
|
||||||
<?php endif; ?>
|
<?php endif; ?>
|
||||||
</div>
|
</div>
|
||||||
<script>
|
<script>
|
||||||
|
@ -60,3 +80,5 @@
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
</header>
|
</header>
|
||||||
|
<div id="toast-container">
|
||||||
|
</div>
|
|
@ -22,7 +22,7 @@
|
||||||
<div class="modal-footer">
|
<div class="modal-footer">
|
||||||
<?=ilang('action_submit',
|
<?=ilang('action_submit',
|
||||||
id: 'new-post-submit',
|
id: 'new-post-submit',
|
||||||
class: 'btn btn-wide btn-submit',
|
class: 'btn-action',
|
||||||
attrs: array('type' => 'submit'),
|
attrs: array('type' => 'submit'),
|
||||||
button: TRUE
|
button: TRUE
|
||||||
)?>
|
)?>
|
||||||
|
@ -32,28 +32,14 @@
|
||||||
$('#new-post-form').submit(function(e) {
|
$('#new-post-form').submit(function(e) {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
let content = $('#new-post-content').val();
|
let content = $('#new-post-content').val();
|
||||||
let me = $(this);
|
|
||||||
|
|
||||||
const getPost = function(data) {
|
|
||||||
if (data) {
|
|
||||||
$('#post-container').prepend(data);
|
|
||||||
}
|
|
||||||
me.closest('.modal-container').remove();
|
|
||||||
}
|
|
||||||
|
|
||||||
const onPost = function(data) {
|
|
||||||
let id = data[0].id;
|
|
||||||
$.get({
|
|
||||||
url: '/_util/post/post?id=' + id,
|
|
||||||
success: getPost
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
$.ajax({
|
$.ajax({
|
||||||
url: '/api/post',
|
url: '/api/post',
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
data: JSON.stringify({ content }),
|
data: JSON.stringify({ content }),
|
||||||
success: onPost
|
success: function(data) {
|
||||||
|
window.location.reload();
|
||||||
|
},
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
|
@ -8,7 +8,7 @@
|
||||||
<div class="ml col sub-card">
|
<div class="ml col sub-card">
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<strong><?=$format_model->name($user)?></strong>
|
<strong><?=$format_model->name($user)?></strong>
|
||||||
<span class="dim ml"><?=$format_model->date($comment['created'])?></span>
|
<span class="dim ml"><?=$format_model->date($comment['date'])?></span>
|
||||||
</div>
|
</div>
|
||||||
<?=$comment['content']?>
|
<?=$comment['content']?>
|
||||||
</div>
|
</div>
|
8
web/_views/template/pfp.php
Normal file
8
web/_views/template/pfp.php
Normal file
|
@ -0,0 +1,8 @@
|
||||||
|
<?php /* Copyright (c) 2024 Freya Murphy */ ?>
|
||||||
|
<?php /* vi: syntax=php */ ?>
|
||||||
|
<?php
|
||||||
|
$class = isset($class) ? $class : '';
|
||||||
|
?>
|
||||||
|
<a class="image-loading pfp <?=$class?>" href="/profile?id=<?=$user['id']?>">
|
||||||
|
<img src="/api/rpc/avatar?user_id=<?=$user['id']?>" />
|
||||||
|
</a>
|
|
@ -5,7 +5,7 @@
|
||||||
<?php $this->view('template/pfp', array('user' => $user))?>
|
<?php $this->view('template/pfp', array('user' => $user))?>
|
||||||
<div class="col ml">
|
<div class="col ml">
|
||||||
<strong><?=$user['first_name'] . ' ' . $user['last_name']?></strong>
|
<strong><?=$user['first_name'] . ' ' . $user['last_name']?></strong>
|
||||||
<span class="dim"><?=$post['created']?></span>
|
<span class="dim"><?=$post['date']?></span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<p>
|
<p>
|
||||||
|
@ -13,28 +13,20 @@
|
||||||
</p>
|
</p>
|
||||||
<?php
|
<?php
|
||||||
$self = $this->main->user();
|
$self = $this->main->user();
|
||||||
$liked = $post['like_id'] ? 'btn-blue' : '';
|
|
||||||
$post_attrs = array(
|
|
||||||
'postId' => $post['id']
|
|
||||||
);
|
|
||||||
if ($post['like_id'] !== NULL) {
|
|
||||||
$post_attrs['likeId'] = $post['like_id'];
|
|
||||||
}
|
|
||||||
?>
|
?>
|
||||||
<?php if ($self): ?>
|
<?php if ($self): ?>
|
||||||
<hr>
|
<hr>
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<?=ilang('action_like',
|
<?=ilang('action_like', class: 'grow btn btn-hover btn-action')?>
|
||||||
class: 'btn btn-wide action-like ' . $liked,
|
<?=ilang('action_comment', class: 'grow btn btn-hover btn-action action-comment',
|
||||||
attrs: $post_attrs
|
click: '$(\'#new-comment-' . $post['id'] . '\').focus()'
|
||||||
)?>
|
|
||||||
<?=ilang('action_comment', class: 'btn btn-wide action-comment',
|
|
||||||
click: '$(\'#action-new-comment-' . $post['id'] . '\').focus()'
|
|
||||||
)?>
|
)?>
|
||||||
</div>
|
</div>
|
||||||
<hr>
|
<hr>
|
||||||
|
<?php else: ?>
|
||||||
|
<hr>
|
||||||
<?php endif; ?>
|
<?php endif; ?>
|
||||||
<div class="col comments pb">
|
<div class="col comments">
|
||||||
<?php
|
<?php
|
||||||
$_GET = array('id' => $post['id']);
|
$_GET = array('id' => $post['id']);
|
||||||
$cdata = $this->comments();
|
$cdata = $this->comments();
|
||||||
|
@ -60,17 +52,16 @@
|
||||||
?>
|
?>
|
||||||
</div>
|
</div>
|
||||||
<?php if ($self): ?>
|
<?php if ($self): ?>
|
||||||
<div class="row pb">
|
<div class="row grow mt">
|
||||||
<?php $this->view('template/pfp', array('user' => $self))?>
|
<?php $this->view('template/pfp', array('user' => $user))?>
|
||||||
<form class="ml action-new-comment-form row">
|
<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="action-new-comment-<?=$post['id']?>"
|
class="action-new-comment input"
|
||||||
class="action-new-comment btn btn-wide btn-alt"
|
|
||||||
postId="<?=$post['id']?>"
|
postId="<?=$post['id']?>"
|
||||||
autocomplete="off"
|
autocomplete="off"
|
||||||
type="text"
|
type="text"
|
||||||
|
@ -82,5 +73,3 @@
|
||||||
</div>
|
</div>
|
||||||
<?php endif; ?>
|
<?php endif; ?>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
|
@ -10,7 +10,7 @@
|
||||||
if ($loaded >= $page_size && $page_size < $total) {
|
if ($loaded >= $page_size && $page_size < $total) {
|
||||||
ilang('action_load_posts',
|
ilang('action_load_posts',
|
||||||
id: 'action-load-posts',
|
id: 'action-load-posts',
|
||||||
class: 'btn btn-line btn-wide mb',
|
class: 'btn btn-line mb',
|
||||||
attrs: array(
|
attrs: array(
|
||||||
'loaded' => $loaded,
|
'loaded' => $loaded,
|
||||||
'pageSize' => $page_size,
|
'pageSize' => $page_size,
|
|
@ -11,16 +11,9 @@
|
||||||
array_push($params, $hint);
|
array_push($params, $hint);
|
||||||
}
|
}
|
||||||
|
|
||||||
$lang_msg = lang($msg, FALSE, sub: $params);
|
$msg = lang($msg, sub: $params);
|
||||||
|
|
||||||
if(!$lang_msg) {
|
|
||||||
$lang_msg = $msg;
|
|
||||||
} else {
|
|
||||||
$lang_msg = ucfirst($lang_msg);
|
|
||||||
}
|
|
||||||
|
|
||||||
?>
|
?>
|
||||||
<div class="toast error">
|
<div class="toast error">
|
||||||
<?=$lang_msg?>
|
<?=ucfirst($msg)?>
|
||||||
<?=ilang('action_close', class: 'action-close-toast')?>
|
<?=ilang('action_close', class: 'action-close-toast')?>
|
||||||
</div>
|
</div>
|
|
@ -30,16 +30,6 @@ class Aesthetic {
|
||||||
'css/post.css'
|
'css/post.css'
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
'auth' => array(
|
|
||||||
'css' => [
|
|
||||||
'css/auth.css'
|
|
||||||
],
|
|
||||||
),
|
|
||||||
'people' => array(
|
|
||||||
'css' => [
|
|
||||||
'css/people.css'
|
|
||||||
],
|
|
||||||
),
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
/**
|
/**
|
|
@ -3,7 +3,5 @@
|
||||||
$routes = array();
|
$routes = array();
|
||||||
$routes['home'] = 'apps/home';
|
$routes['home'] = 'apps/home';
|
||||||
$routes['error'] = 'apps/error';
|
$routes['error'] = 'apps/error';
|
||||||
$routes['auth'] = 'apps/auth';
|
|
||||||
$routes['people'] = 'apps/people';
|
|
||||||
|
|
||||||
$routes[''] = '_index';
|
$routes[''] = '_index';
|
|
@ -45,20 +45,5 @@ abstract class Controller {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
protected function is_ajax(): bool {
|
|
||||||
$_POST = json_decode(
|
|
||||||
file_get_contents("php://input"), true
|
|
||||||
);
|
|
||||||
return isset($_SERVER['HTTP_X_REQUESTED_WITH']) && $_SERVER['HTTP_X_REQUESTED_WITH'] === 'XMLHttpRequest';
|
|
||||||
}
|
|
||||||
|
|
||||||
protected function error($code): void {
|
|
||||||
$_GET['code'] = $code;
|
|
||||||
$this->main->info['app'] = 'error';
|
|
||||||
$error_controller = $this->load->controller('apps/error');
|
|
||||||
$error_controller->index();
|
|
||||||
die();
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
?>
|
?>
|
|
@ -46,8 +46,6 @@ class DatabaseQuery {
|
||||||
if (!$this->where) {
|
if (!$this->where) {
|
||||||
$this->where = TRUE;
|
$this->where = TRUE;
|
||||||
$this->query .= "WHERE ";
|
$this->query .= "WHERE ";
|
||||||
} else {
|
|
||||||
$this->query .= "AND ";
|
|
||||||
}
|
}
|
||||||
$this->query .= "$cond ";
|
$this->query .= "$cond ";
|
||||||
return $this;
|
return $this;
|
||||||
|
@ -87,8 +85,6 @@ class DatabaseQuery {
|
||||||
if (!$this->where) {
|
if (!$this->where) {
|
||||||
$this->where = TRUE;
|
$this->where = TRUE;
|
||||||
$this->query .= "WHERE ";
|
$this->query .= "WHERE ";
|
||||||
} else {
|
|
||||||
$this->query .= "AND ";
|
|
||||||
}
|
}
|
||||||
if (empty($array)) {
|
if (empty($array)) {
|
||||||
$this->query .= "FALSE\n";
|
$this->query .= "FALSE\n";
|
||||||
|
@ -99,6 +95,16 @@ class DatabaseQuery {
|
||||||
return $this;
|
return $this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function and() {
|
||||||
|
$this->query .= "AND ";
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function or() {
|
||||||
|
$this->query .= "OR ";
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
public function join($table, $on, $type = 'LEFT') {
|
public function join($table, $on, $type = 'LEFT') {
|
||||||
$this->query .= "$type JOIN $table ON $on\n";
|
$this->query .= "$type JOIN $table ON $on\n";
|
||||||
return $this;
|
return $this;
|
||||||
|
@ -116,11 +122,6 @@ class DatabaseQuery {
|
||||||
return $this;
|
return $this;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function order_by($column, $order = 'ASC') {
|
|
||||||
$this->query .= "ORDER BY " . $column . ' ' . $order . ' ';
|
|
||||||
return $this;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function rows() {
|
public function rows() {
|
||||||
$stmt = $this->conn->prepare($this->query);
|
$stmt = $this->conn->prepare($this->query);
|
||||||
try {
|
try {
|
|
@ -69,7 +69,7 @@ function ilang($key,
|
||||||
}
|
}
|
||||||
echo '>' . $text . '</span>';
|
echo '>' . $text . '</span>';
|
||||||
}
|
}
|
||||||
if ($click || $button) {
|
if ($click) {
|
||||||
echo '</button>';
|
echo '</button>';
|
||||||
} else {
|
} else {
|
||||||
echo '</a>';
|
echo '</a>';
|
|
@ -1,6 +1,5 @@
|
||||||
<?php /* Copyright (c) 2024 Freya Murphy */
|
<?php /* Copyright (c) 2024 Freya Murphy */
|
||||||
|
|
||||||
session_save_path('/var/lib/php/session');
|
|
||||||
session_start();
|
session_start();
|
||||||
|
|
||||||
$webroot = dirname(__FILE__);
|
$webroot = dirname(__FILE__);
|
|
@ -6,7 +6,6 @@ $lang['api_column_first_name'] = 'first name';
|
||||||
$lang['api_column_last_name'] = 'last name';
|
$lang['api_column_last_name'] = 'last name';
|
||||||
$lang['api_column_middle_name'] = 'middle name';
|
$lang['api_column_middle_name'] = 'middle name';
|
||||||
$lang['api_column_email'] = 'email';
|
$lang['api_column_email'] = 'email';
|
||||||
$lang['api_column_password'] = 'password';
|
|
||||||
$lang['api_column_gender'] = 'gender';
|
$lang['api_column_gender'] = 'gender';
|
||||||
$lang['api_column_join_date'] = 'join date';
|
$lang['api_column_join_date'] = 'join date';
|
||||||
$lang['api_column_birth_date'] = 'birth date';
|
$lang['api_column_birth_date'] = 'birth date';
|
||||||
|
@ -23,10 +22,5 @@ $lang['api_null_value'] = '%s cannot be empty';
|
||||||
$lang['api_unique_value'] = '%s is not available (not unique)';
|
$lang['api_unique_value'] = '%s is not available (not unique)';
|
||||||
$lang['api_min_value'] = '%s length cannot be less than %s';
|
$lang['api_min_value'] = '%s length cannot be less than %s';
|
||||||
$lang['api_max_value'] = '%s length cannot exceed %s';
|
$lang['api_max_value'] = '%s length cannot exceed %s';
|
||||||
$lang['api_invalid_login'] = 'Invalid username or password';
|
|
||||||
$lang['api_unknown'] = 'An unknown error as occurred';
|
|
||||||
|
|
||||||
// toast messages
|
|
||||||
$lang['toast_date_empty'] = 'Birthday cannot be empty';
|
|
||||||
|
|
||||||
?>
|
?>
|
|
@ -1,44 +1,17 @@
|
||||||
:root {
|
:root {
|
||||||
--white: #E4E6EB;
|
--primary: #242424 !important;
|
||||||
--blue: #1778f2;
|
--secondary: #181818 !important;
|
||||||
--red: #f02849;
|
--hover: #1b1b1b !important;
|
||||||
--green: #30ab5a;
|
--light: #3e4042 !important;
|
||||||
|
--mild: #1b1b1b !important;
|
||||||
--blue-alt: #1D85FC;
|
--medium: #e2ded6 !important;
|
||||||
--green-alt: #39B463;
|
--extreme: #e2ded6 !important;
|
||||||
|
--logo: #1778f2 !important;
|
||||||
--font: Helvetica;
|
--error: #f02849 !important;
|
||||||
}
|
--success: #30ab5a !important;
|
||||||
|
--text: #ffffff !important;
|
||||||
:root {
|
--banner: #6b6b6b !important;
|
||||||
--base :#18191A;
|
--popup: #242424cc !important;
|
||||||
--surface0: #242526;
|
|
||||||
--surface1: #3A3B3C;
|
|
||||||
--surface2: #4E4F50;
|
|
||||||
|
|
||||||
--text: #E4E6EB;
|
|
||||||
--subtext: #B0B3B8;
|
|
||||||
--btntext: #E4E6EB;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
:root {
|
|
||||||
--base: #f0f2f5;
|
|
||||||
--surface0: #ffffff;
|
|
||||||
--surface1: #f0f2f5;
|
|
||||||
--surface2: #dadde1;
|
|
||||||
|
|
||||||
--text: #000000;
|
|
||||||
--subtext: #1d2129;
|
|
||||||
--btntext: #606770;
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
|
|
||||||
@font-face {
|
|
||||||
font-family: 'Helvetica Neue';
|
|
||||||
font-style: normal;
|
|
||||||
src: url("/public/font/helvetica-neue.otf") format("opentype");
|
|
||||||
font-display: swap;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@font-face {
|
@font-face {
|
||||||
|
@ -60,8 +33,14 @@
|
||||||
font-display: swap;
|
font-display: swap;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@font-face {
|
||||||
|
font-family: sfprobold;
|
||||||
|
src: url("/public/font/sfprobold.otf") format("opentype");
|
||||||
|
font-display: swap;
|
||||||
|
}
|
||||||
|
|
||||||
body {
|
body {
|
||||||
background-color: var(--surface0);
|
background-color: var(--secondary);
|
||||||
width: 100%;
|
width: 100%;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
margin: 0;
|
margin: 0;
|
||||||
|
@ -69,132 +48,93 @@ body {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
color: var(--text);
|
color: var(--text);
|
||||||
font-family: var(--font);
|
font-family: sfpro;
|
||||||
}
|
|
||||||
|
|
||||||
#main-content {
|
|
||||||
background-color: var(--base);
|
|
||||||
padding-top: 1rem;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
header {
|
header {
|
||||||
top: 0;
|
top: 0;
|
||||||
position: sticky;
|
position: sticky;
|
||||||
height: 3.5rem;
|
height: 3.5rem;
|
||||||
background-color: var(--surface0);
|
background-color: var(--primary);
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: row;
|
flex-direction: row;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
padding: 0 1rem;
|
padding: 0 1rem;
|
||||||
border-bottom: 1px solid var(--surface1);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
header .logo {
|
header .logo {
|
||||||
font-family: facebook;
|
font-family: facebook;
|
||||||
color: var(--blue);
|
color: var(--logo);
|
||||||
font-size: 2.25rem;
|
font-size: 2.25rem;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
line-height: 2rem;
|
line-height: 2rem;
|
||||||
margin-top: .75rem;
|
margin-top: .75rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
footer {
|
|
||||||
text-align: center;
|
|
||||||
padding: 1rem;
|
|
||||||
color: var(--subtext);
|
|
||||||
font-size: .75rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
hr {
|
|
||||||
color: var(--surface2);
|
|
||||||
background-color: var(--surface2);
|
|
||||||
width: 100%;
|
|
||||||
height: 1px;
|
|
||||||
border: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
a, button, input, div {
|
|
||||||
box-sizing: border-box;
|
|
||||||
}
|
|
||||||
|
|
||||||
a, button, input {
|
a, button, input {
|
||||||
background: none;
|
background: none;
|
||||||
border: none;
|
border: none;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
align-items: center;
|
||||||
|
font-family: sfprobold;
|
||||||
color: inherit;
|
color: inherit;
|
||||||
|
text-decoration: none;
|
||||||
|
font-size: 1rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
a, button {
|
a, button {
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
}
|
}
|
||||||
|
|
||||||
.btn {
|
form button {
|
||||||
color: var(--btntext);
|
padding: .5rem;
|
||||||
display: flex;
|
border-radius: .5rem;
|
||||||
align-items: center;
|
|
||||||
align-content: center;
|
|
||||||
flex-direction: row;
|
|
||||||
font-weight: bold;
|
|
||||||
font-size: 1rem;
|
|
||||||
text-decoration: none;
|
|
||||||
|
|
||||||
padding: .4rem .6rem;
|
|
||||||
border-radius: .25rem;
|
|
||||||
background-color: transparent;
|
|
||||||
width: fit-content;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.btn:hover {
|
input:focus {
|
||||||
background-color: var(--surface1);
|
|
||||||
}
|
|
||||||
|
|
||||||
.btn-alt {
|
|
||||||
background-color: var(--surface1);
|
|
||||||
}
|
|
||||||
|
|
||||||
.btn-alt:hover {
|
|
||||||
background-color: var(--surface2);
|
|
||||||
}
|
|
||||||
|
|
||||||
.btn-wide {
|
|
||||||
width: auto;
|
|
||||||
flex-grow: 1;
|
|
||||||
justify-content: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
.btn-line:hover {
|
|
||||||
background-color: inherit;
|
|
||||||
text-decoration: underline;
|
|
||||||
}
|
|
||||||
|
|
||||||
.btn-blue {
|
|
||||||
color: var(--blue-alt);
|
|
||||||
}
|
|
||||||
|
|
||||||
input.btn:focus {
|
|
||||||
border: none;
|
border: none;
|
||||||
outline: none;
|
outline: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
.btn-submit {
|
.header-entry {
|
||||||
color: var(--white);
|
display: flex;
|
||||||
background-color: var(--blue);
|
flex-direction: row;
|
||||||
flex-grow: 1;
|
text-decoration: none;
|
||||||
padding: .5rem;
|
align-items: center;
|
||||||
|
color: var(--text);
|
||||||
}
|
}
|
||||||
|
|
||||||
.btn-submit:hover {
|
.nav .header-entry {
|
||||||
background-color: var(--blue-alt);
|
height: 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
.btn-success {
|
.nav-center .header-entry:hover {
|
||||||
color: var(--white);
|
background-color: var(--hover);
|
||||||
background-color: var(--green);
|
|
||||||
flex-grow: 1;
|
|
||||||
padding: .5rem;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.btn-success:hover {
|
.btn-action {
|
||||||
background-color: var(--green-alt);
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
padding: .35rem;
|
||||||
|
margin: .25rem;
|
||||||
|
border-radius: .25rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-action:hover {
|
||||||
|
background-color: var(--hover);
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-blue:hover {
|
||||||
|
color: var(--logo);
|
||||||
|
}
|
||||||
|
|
||||||
|
.header .btn-blue {
|
||||||
|
border-bottom: 1px solid var(--logo);
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-line:hover {
|
||||||
|
text-decoration: underline;
|
||||||
}
|
}
|
||||||
|
|
||||||
.nav,
|
.nav,
|
||||||
|
@ -227,13 +167,12 @@ input.btn:focus {
|
||||||
}
|
}
|
||||||
|
|
||||||
@media (min-width: 800px) {
|
@media (min-width: 800px) {
|
||||||
.nav-center .btn > span {
|
.header-entry > span {
|
||||||
display: none;
|
display: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
.nav-center .btn {
|
.nav-center .header-entry {
|
||||||
padding: 0 3rem;
|
padding: 0 3rem;
|
||||||
height: 100%;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#action-hamburger {
|
#action-hamburger {
|
||||||
|
@ -248,7 +187,7 @@ input.btn:focus {
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
top: 100%;
|
top: 100%;
|
||||||
height: fit-content;
|
height: fit-content;
|
||||||
background-color: var(--surface0);
|
background-color: var(--primary);
|
||||||
width: 100%;
|
width: 100%;
|
||||||
left: 0;
|
left: 0;
|
||||||
transform: translateX(0%);
|
transform: translateX(0%);
|
||||||
|
@ -259,18 +198,18 @@ input.btn:focus {
|
||||||
display: inherit !important;
|
display: inherit !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
.nav-center .btn {
|
.nav-center .header-entry {
|
||||||
width: calc(100% - 3rem);
|
width: calc(100% - 3rem);
|
||||||
padding: .75rem 0rem !important;
|
padding: .75rem 0rem !important;
|
||||||
padding-left: 3rem !important;
|
padding-left: 3rem !important;
|
||||||
justify-content: flex-start;
|
justify-content: flex-start;
|
||||||
}
|
}
|
||||||
|
|
||||||
.nav-center .btn > span {
|
.nav-center .header-entry > span {
|
||||||
margin-left: 1rem;
|
margin-left: 1rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
.nav-center .btn.active {
|
.nav-center .header-entry.active {
|
||||||
border-bottom: none;
|
border-bottom: none;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -279,6 +218,11 @@ input.btn:focus {
|
||||||
display: block;
|
display: block;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.nav-right .header-entry {
|
||||||
|
padding: 0;
|
||||||
|
padding-left: 1.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
@keyframes shimmer {
|
@keyframes shimmer {
|
||||||
to {
|
to {
|
||||||
background-position-x: 0%;
|
background-position-x: 0%;
|
||||||
|
@ -298,32 +242,40 @@ input.btn:focus {
|
||||||
}
|
}
|
||||||
|
|
||||||
.image-loading {
|
.image-loading {
|
||||||
background: linear-gradient(-45deg, var(--surface0) 0%, var(--base) 25%, var(--surface0) 50%);
|
background: linear-gradient(-45deg, var(--secondary) 0%, var(--primary) 25%, var(--secondary) 50%);
|
||||||
background-size: 500%;
|
background-size: 500%;
|
||||||
background-position-x: 150%;
|
background-position-x: 150%;
|
||||||
animation: shimmer 1s linear infinite;
|
animation: shimmer 1s linear infinite;
|
||||||
}
|
}
|
||||||
|
|
||||||
.card {
|
.card {
|
||||||
background-color: var(--surface0);
|
background-color: var(--primary);
|
||||||
border-radius: .5rem;
|
border-radius: .5rem;
|
||||||
padding: 1rem;
|
padding: 1rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
.card p {
|
|
||||||
margin-bottom: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.card form {
|
.card form {
|
||||||
flex-grow: 1;
|
flex-grow: 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
.card .sub-card {
|
.card .sub-card {
|
||||||
background-color: var(--surface1);
|
background-color: var(--secondary);
|
||||||
border-radius: .5rem;
|
border-radius: .5rem;
|
||||||
padding: .75rem;
|
padding: .75rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.input {
|
||||||
|
padding: 10px;
|
||||||
|
border-radius: 10px;
|
||||||
|
width: calc(100% - 20px);
|
||||||
|
background-color: var(--secondary);
|
||||||
|
font-family: sfpro;
|
||||||
|
}
|
||||||
|
|
||||||
|
.input:hover {
|
||||||
|
background-color: var(--hover);
|
||||||
|
}
|
||||||
|
|
||||||
.row {
|
.row {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: row;
|
flex-direction: row;
|
||||||
|
@ -362,12 +314,8 @@ input.btn:focus {
|
||||||
margin-bottom: .75rem;
|
margin-bottom: .75rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
.pb {
|
|
||||||
padding-bottom: 1rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
.dim {
|
.dim {
|
||||||
color: var(--subtext);
|
color: var(--medium);
|
||||||
}
|
}
|
||||||
|
|
||||||
.modal-container {
|
.modal-container {
|
||||||
|
@ -381,7 +329,7 @@ input.btn:focus {
|
||||||
}
|
}
|
||||||
|
|
||||||
.modal {
|
.modal {
|
||||||
background-color: var(--surface0);
|
background-color: var(--primary);
|
||||||
position: absolute;
|
position: absolute;
|
||||||
top: 50%;
|
top: 50%;
|
||||||
left: 50%;
|
left: 50%;
|
||||||
|
@ -424,14 +372,18 @@ input.btn:focus {
|
||||||
}
|
}
|
||||||
|
|
||||||
.modal-header {
|
.modal-header {
|
||||||
font-weight: bold;
|
font-family: sfprobold;
|
||||||
position: relative;
|
position: relative;
|
||||||
border-bottom: 1px solid var(--surface1);
|
border-bottom: 1px solid var(--light);
|
||||||
|
text-align: center;
|
||||||
|
margin: 0 1rem;
|
||||||
|
border-radius: .5rem .5rem 0 0;
|
||||||
display: flex;
|
display: flex;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
|
padding-left: 1rem;
|
||||||
cursor: grab;
|
cursor: grab;
|
||||||
padding: 1rem 0;
|
padding: 1rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
.modal-content {
|
.modal-content {
|
||||||
|
@ -450,9 +402,9 @@ input.btn:focus {
|
||||||
|
|
||||||
.float-right {
|
.float-right {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
top: 50%;
|
transform: translate(0%, -50%);
|
||||||
left: 100%;
|
top: 45%;
|
||||||
transform: translate(-125%, -50%);
|
right: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
.mi {
|
.mi {
|
||||||
|
@ -469,6 +421,17 @@ input.btn:focus {
|
||||||
font-size: 2rem;
|
font-size: 2rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
button[type="submit"] {
|
||||||
|
text-align: center;
|
||||||
|
background-color: var(--logo);
|
||||||
|
flex-grow: 1;
|
||||||
|
padding: .5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
button[type="submit"]:hover {
|
||||||
|
background-color: var(--logo);
|
||||||
|
}
|
||||||
|
|
||||||
#toast-container {
|
#toast-container {
|
||||||
position: fixed;
|
position: fixed;
|
||||||
top: 4rem;
|
top: 4rem;
|
||||||
|
@ -479,92 +442,20 @@ input.btn:focus {
|
||||||
}
|
}
|
||||||
|
|
||||||
.toast {
|
.toast {
|
||||||
color: var(--white);
|
|
||||||
padding: .75rem;
|
padding: .75rem;
|
||||||
margin: .5rem;
|
margin: .5rem;
|
||||||
border-radius: .5rem;
|
border-radius: .5rem;
|
||||||
min-width: 15rem;
|
min-width: 15rem;
|
||||||
|
font-family: sfpro;
|
||||||
animation: fadeIn .1s, slideIn .25s linear;
|
animation: fadeIn .1s, slideIn .25s linear;
|
||||||
display: flex;
|
display: flex;
|
||||||
justify-content: space-between;
|
justify-content: space-between;
|
||||||
}
|
}
|
||||||
|
|
||||||
.toast.error {
|
.toast.error {
|
||||||
background-color: var(--red);
|
background-color: var(--error);
|
||||||
}
|
}
|
||||||
|
|
||||||
.toast.success {
|
.toast.success {
|
||||||
background-color: var(--green);
|
background-color: var(--success);
|
||||||
}
|
|
||||||
|
|
||||||
form input:not(.btn) {
|
|
||||||
display: block;
|
|
||||||
font-size: 1.1rem;
|
|
||||||
outline: 2px solid var(--surface2);
|
|
||||||
border-radius: .25rem;
|
|
||||||
padding: .75rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
form input:not(.btn):focus {
|
|
||||||
outline-color: var(--blue);
|
|
||||||
}
|
|
||||||
|
|
||||||
form .rel label:not(.static) {
|
|
||||||
position: absolute;
|
|
||||||
top: 50%;
|
|
||||||
transform: translate(.5rem, -40%);
|
|
||||||
color: var(--subtext);
|
|
||||||
transition: all 0.2s ease-out;
|
|
||||||
pointer-events: none;
|
|
||||||
width: fit-content;
|
|
||||||
font-size: 1.1rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
input:focus + label:not(.static),
|
|
||||||
input:not(:placeholder-shown) + label:not(.static) {
|
|
||||||
color: var(--text);
|
|
||||||
top: 0;
|
|
||||||
padding: .5rem;
|
|
||||||
padding-top: 0;
|
|
||||||
font-size: .75rem;
|
|
||||||
transform: translate(.5rem, -25%);
|
|
||||||
background-color: var(--surface0);
|
|
||||||
}
|
|
||||||
|
|
||||||
.rel {
|
|
||||||
position: relative;
|
|
||||||
}
|
|
||||||
|
|
||||||
.rel input {
|
|
||||||
width: 100%;
|
|
||||||
flex-grow: 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
input[type=radio] {
|
|
||||||
padding: 3rem !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
.radio {
|
|
||||||
display: flex;
|
|
||||||
flex-direction: row;
|
|
||||||
width: auto;
|
|
||||||
flex-grow: 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
.radio label {
|
|
||||||
border: 1px solid var(--surface2);
|
|
||||||
height: fit-content;
|
|
||||||
width: 100%;
|
|
||||||
padding: .75rem;
|
|
||||||
border-radius: .25rem;
|
|
||||||
cursor: pointer;
|
|
||||||
}
|
|
||||||
|
|
||||||
.radio input {
|
|
||||||
position: absolute;
|
|
||||||
top: 50%;
|
|
||||||
left: 100%;
|
|
||||||
transform: translate(-250%, -70%);
|
|
||||||
width: fit-content;
|
|
||||||
outline: none !important;
|
|
||||||
}
|
}
|
|
@ -1,16 +1,16 @@
|
||||||
#main-content {
|
#error {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
padding: 10rem 0;
|
margin-top: 10rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
#main-content h1 {
|
#error h1 {
|
||||||
color: var(--blue);
|
color: var(--logo);
|
||||||
font-family: Facebook;
|
font-family: Facebook;
|
||||||
font-size: 5rem;
|
font-size: 5rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
#main-content span {
|
#error span {
|
||||||
font-size: 2rem;
|
font-size: 2rem;
|
||||||
}
|
}
|
|
@ -1,7 +1,9 @@
|
||||||
#main-content {
|
#main-content {
|
||||||
|
width: 100%;
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
|
margin-top: 1rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
.card {
|
.card {
|
||||||
|
@ -13,6 +15,7 @@
|
||||||
border: none;
|
border: none;
|
||||||
resize: none;
|
resize: none;
|
||||||
outline: none;
|
outline: none;
|
||||||
|
font-family: sfpro;
|
||||||
font-size: 1.5rem;
|
font-size: 1.5rem;
|
||||||
margin: 1rem 0;
|
margin: 1rem 0;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
|
@ -20,5 +23,4 @@
|
||||||
flex-grow: 1;
|
flex-grow: 1;
|
||||||
background-color: transparent;
|
background-color: transparent;
|
||||||
color: var(--text);
|
color: var(--text);
|
||||||
font-family: var(--font);
|
|
||||||
}
|
}
|
|
@ -1,13 +1,16 @@
|
||||||
|
.post hr {
|
||||||
|
color: var(--light);
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.post hr:nth-of-type(1) {
|
||||||
|
margin-top: .5rem;
|
||||||
|
}
|
||||||
|
|
||||||
.action-load-comments {
|
.action-load-comments {
|
||||||
margin-left: 4rem;
|
margin-left: 4rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
#action-load-posts {
|
#action-load-posts {
|
||||||
width: 100%;
|
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
}
|
}
|
||||||
|
|
||||||
.post {
|
|
||||||
padding-bottom: 0;
|
|
||||||
}
|
|
Before Width: | Height: | Size: 37 KiB After Width: | Height: | Size: 37 KiB |
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue