diff --git a/assets/typescript/Comments.ts b/assets/typescript/Comments.ts index db08dfe6..936e5c2d 100644 --- a/assets/typescript/Comments.ts +++ b/assets/typescript/Comments.ts @@ -76,7 +76,7 @@ function commentDelete(commentId: number, onSuccess: (info: CommentDeletionInfo) else if (!message && onSuccess) onSuccess(json); }); - xhr.open('GET', `/comments.php?m=delete&c=${commentId}&csrf=${getCSRFToken('comments')}`); + xhr.open('GET', urlFormat('comments-delete', [{name:'comment',value:commentId}])); xhr.setRequestHeader('X-Misuzu-XHR', 'comments'); xhr.send(); } @@ -122,7 +122,7 @@ function commentPost(formData: FormData, onSuccess: (comment: CommentPostInfo) = onSuccess(json); }); - xhr.open('POST', '/comments.php?m=create'); + xhr.open('POST', urlFormat('comment-create')); xhr.setRequestHeader('X-Misuzu-XHR', 'comments'); xhr.send(formData); } @@ -202,8 +202,8 @@ function commentConstruct(comment: CommentPostInfo, layer: number = 0): HTMLElem // container const commentAvatar: HTMLAnchorElement = commentContainer.appendChild(document.createElement('a')); commentAvatar.className = 'avatar comment__avatar'; - commentAvatar.href = '/profile.php?u=' + comment.user_id; - commentAvatar.style.backgroundImage = `url('/user-assets.php?m=avatar&u=${comment.user_id}')`; + commentAvatar.href = urlFormat('user-profile', [{name:'user',value:comment.user_id}]); + commentAvatar.style.backgroundImage = "url('{0}')".replace('{0}', urlFormat('user-avatar', [{name:'user',value:comment.user_id}])); const commentContent: HTMLDivElement = commentContainer.appendChild(document.createElement('div')); commentContent.className = 'comment__content'; @@ -303,7 +303,7 @@ function commentConstruct(comment: CommentPostInfo, layer: number = 0): HTMLElem // reply container const replyAvatar: HTMLDivElement = replyContainer.appendChild(document.createElement('div')); replyAvatar.className = 'avatar comment__avatar'; - replyAvatar.style.backgroundImage = `url('/user-assets.php?m=avatar&u=${comment.user_id}')`; + replyAvatar.style.backgroundImage = "url('{0}')".replace('{0}', urlFormat('user-avatar', [{name:'user',value:comment.user_id}])); const replyContent: HTMLDivElement = replyContainer.appendChild(document.createElement('div')); replyContent.className = 'comment__content'; @@ -424,7 +424,7 @@ function commentVote( else if (!message && onSuccess) onSuccess(json); }; - xhr.open('GET', `/comments.php?m=vote&c=${commentId}&v=${vote}&csrf=${getCSRFToken('comments')}`); + xhr.open('GET', urlFormat('comment-vote', [{name: 'comment', value: commentId}, {name: 'vote', value: vote}])); xhr.setRequestHeader('X-Misuzu-XHR', 'comments'); xhr.send(); } @@ -500,7 +500,7 @@ function commentPin( else if (!message && onSuccess) onSuccess(json); }; - xhr.open('GET', `/comments.php?m=${mode}&c=${commentId}&csrf=${getCSRFToken('comments')}`); + xhr.open('GET', urlFormat(`comment-${mode}`, [{name: 'comment', value: commentId}])); xhr.setRequestHeader('X-Misuzu-XHR', 'comments'); xhr.send(); } diff --git a/assets/typescript/FormUtilities.ts b/assets/typescript/FormUtilities.ts index 9d3d5675..6532624c 100644 --- a/assets/typescript/FormUtilities.ts +++ b/assets/typescript/FormUtilities.ts @@ -73,7 +73,8 @@ function getCSRF(realm: string): CSRFToken { } function getCSRFToken(realm: string): string { - return getCSRF(realm).token || ''; + const token: CSRFToken = getCSRF(realm); + return token ? token.token : ''; } function setCSRF(realm: string, token: string): void { diff --git a/assets/typescript/UrlRegistry.ts b/assets/typescript/UrlRegistry.ts new file mode 100644 index 00000000..95696c06 --- /dev/null +++ b/assets/typescript/UrlRegistry.ts @@ -0,0 +1,91 @@ +interface UrlRegistryVariable { + name: string; + value: string; +} + +interface UrlRegistryEntryQuery { + name: string; + value: string; +} + +interface UrlRegistryEntry { + name: string; + path: string; + query: UrlRegistryEntryQuery[]; + fragment: string; +} + +let urlRegistryTable: UrlRegistryEntry[] = []; + +function getRawUrlRegistry(): UrlRegistryEntry[] +{ + const urlListElement: HTMLElement = document.getElementById('js-urls-list') as HTMLElement; + + if (!urlListElement) + return null; + + return JSON.parse(urlListElement.textContent) as UrlRegistryEntry[]; +} + +function urlRegistryInit(): void +{ + urlRegistryTable = getRawUrlRegistry(); +} + +function urlFormat(name: string, vars: UrlRegistryVariable[] = []): string +{ + const entry: UrlRegistryEntry = urlRegistryTable.find(x => x.name == name); + + if (!entry || !entry.path) { + return ''; + } + + const splitUrl: string[] = entry.path.split('/'); + + for (let i = 0; i < splitUrl.length; i++) { + splitUrl[i] = urlVariable(splitUrl[i], vars); + } + + let url: string = splitUrl.join('/'); + + if (entry.query) { + url += '?'; + + for (let i = 0; i < entry.query.length; i++) { + const query: UrlRegistryEntryQuery = entry.query[i], + value: string = urlVariable(query.value, vars); + + if (!value || (query.name === 'page' && parseInt(value) < 2)) { + continue; + } + + url += `${query.name}=${value}&`; + } + + url = url.replace(/^[\?\&]+|[\?\&]+$/g, ''); + } + + if (entry.fragment) { + url += ('#' + urlVariable(entry.fragment, vars)).replace(/[\#]+$/g, ''); + } + + return url; +} + +function urlVariable(value: string, vars: UrlRegistryVariable[]): string +{ + if (value[0] === '<' && value.slice(-1) === '>') { + const urvar: UrlRegistryVariable = vars.find(x => x.name == value.slice(1, -1)); + return urvar ? urvar.value : ''; + } + + if (value[0] === '[' && value.slice(-1) === ']') { + return ''; // not sure if there's a proper substitute for this, should probably resolve these in url_list + } + + if (value[0] === '{' && value.slice(-1) === '}') { + return getCSRFToken(value.slice(1, -1)); + } + + return value; +} diff --git a/assets/typescript/UserRelations.ts b/assets/typescript/UserRelations.ts index e600672f..942a5da0 100644 --- a/assets/typescript/UserRelations.ts +++ b/assets/typescript/UserRelations.ts @@ -121,7 +121,7 @@ function userRelationSet( else if (!message && onSuccess) onSuccess(json); }); - xhr.open('GET', `/relations.php?u=${userId}&m=${relationType}`); + xhr.open('GET', urlFormat('user-relation-create', [{name: 'user', value: userId}, {name: 'type', value: relationType.toString()}])); xhr.setRequestHeader('X-Misuzu-XHR', 'user_relation'); xhr.setRequestHeader('X-Misuzu-CSRF', getCSRFToken('user_relation')); xhr.send(); diff --git a/assets/typescript/misuzu.ts b/assets/typescript/misuzu.ts index b4aaee7a..c1848447 100644 --- a/assets/typescript/misuzu.ts +++ b/assets/typescript/misuzu.ts @@ -7,6 +7,7 @@ /// /// /// +/// declare const timeago: any; declare const hljs: any; @@ -15,10 +16,13 @@ let loginFormAvatarTimeout: number = 0; // Initialisation process. window.addEventListener('load', () => { + console.log("%c __ ____\n / |/ (_)______ ______ __ __\n / /|_/ / / ___/ / / /_ / / / / /\n / / / / (__ ) /_/ / / /_/ /_/ /\n/_/ /_/_/____/\\__,_/ /___/\\__,_/\nhttps://github.com/flashwave/misuzu", 'color: #8559a5'); + timeago().render(document.querySelectorAll('time')); hljs.initHighlighting(); initCSRF(); + urlRegistryInit(); userInit(); userRelationsInit(); @@ -74,9 +78,9 @@ function loginFormUpdateAvatar(avatarElement: HTMLElement, usernameElement: HTML if (xhr.readyState !== 4) return; - avatarElement.style.backgroundImage = `url('/user-assets.php?m=avatar&u=${xhr.responseText}')`; + avatarElement.style.backgroundImage = "url('{0}')".replace('{0}', urlFormat('user-avatar', [{name: 'user', value: xhr.responseText}])); }); - xhr.open('GET', `/auth.php?m=get_user&u=${encodeURI(usernameElement.value)}`); + xhr.open('GET', urlFormat('auth-resolve-user', [{name: 'username', value: encodeURI(usernameElement.value)}])); xhr.send(); } @@ -150,7 +154,7 @@ function loginModal(): boolean { const container: HTMLFormElement = element.appendChild(document.createElement('form')); container.className = 'container messagebox__container auth js-login-form'; container.method = 'post'; - container.action = '/auth.php'; + container.action = urlFormat('auth-login'); const titleElement = container.appendChild(document.createElement('div')), titleBackground = titleElement.appendChild(document.createElement('div')), @@ -162,7 +166,7 @@ function loginModal(): boolean { const authAvatar: HTMLDivElement = titleHeader.appendChild(document.createElement('div')); authAvatar.className = 'avatar auth__avatar'; - authAvatar.style.backgroundImage = "url('/user-assets.php?u=0&m=avatar')"; + authAvatar.style.backgroundImage = "url('{0}')".replace('{0}', urlFormat('user-avatar')); const hiddenMode: HTMLInputElement = container.appendChild(document.createElement('input')); hiddenMode.type = 'hidden'; diff --git a/src/url.php b/src/url.php index 67b45458..332f0508 100644 --- a/src/url.php +++ b/src/url.php @@ -17,6 +17,7 @@ define('MSZ_URLS', [ 'auth-forgot' => ['/auth.php', ['m' => 'forgot']], 'auth-reset' => ['/auth.php', ['m' => 'reset', 'u' => '']], 'auth-logout' => ['/auth.php', ['m' => 'logout', 's' => '{logout}']], + 'auth-resolve-user' => ['/auth.php', ['m' => 'get_user', 'u' => '']], 'changelog-index' => ['/changelog.php'], 'changelog-change' => ['/changelog.php', ['c' => '']], @@ -61,6 +62,7 @@ define('MSZ_URLS', [ 'user-avatar' => ['/user-assets.php', ['u' => '', 'm' => 'avatar']], 'user-background' => ['/user-assets.php', ['u' => '', 'm' => 'background']], + 'user-relation-create' => ['/relations.php', ['u' => '', 'm' => '', 'c' => '{user_relation}']], 'user-relation-none' => ['/relations.php', ['u' => '', 'm' => '[MSZ_USER_RELATION_NONE]', 'c' => '{user_relation}']], 'user-relation-follow' => ['/relations.php', ['u' => '', 'm' => '[MSZ_USER_RELATION_FOLLOW]', 'c' => '{user_relation}']], @@ -105,6 +107,11 @@ function url(string $name, array $variables = []): string } $info = MSZ_URLS[$name]; + + if (!is_string($info[0] ?? null)) { + return ''; + } + $splitUrl = explode('/', $info[0]); for ($i = 0; $i < count($splitUrl); $i++) { @@ -113,10 +120,6 @@ function url(string $name, array $variables = []): string $url = implode('/', $splitUrl); - if (!is_string($url)) { - return ''; - } - if (!empty($info[1]) && is_array($info[1])) { $url .= '?'; diff --git a/templates/_layout/header.twig b/templates/_layout/header.twig index 429d50cc..ae98d01f 100644 --- a/templates/_layout/header.twig +++ b/templates/_layout/header.twig @@ -7,7 +7,7 @@ 'menu': [ { 'title': 'Members', - 'url': url('members-index'), + 'url': url('user-list'), }, { 'title': 'Changelog',