feat: Views localization
This commit is contained in:
		| @@ -155,7 +155,7 @@ module.exports = { | ||||
|         return false | ||||
|       } | ||||
|     }).catch((err) => { // eslint-disable-line handle-callback-err | ||||
|       throw new Promise.OperationalError('Entry ' + entryPath + ' does not exist!') | ||||
|       throw new Promise.OperationalError(lang.t('errors:notexist', { path: entryPath })) | ||||
|     }) | ||||
|   }, | ||||
|  | ||||
| @@ -184,11 +184,11 @@ module.exports = { | ||||
|             } | ||||
|           }) | ||||
|         } else { | ||||
|           return Promise.reject(new Error('Parent entry is not a valid file.')) | ||||
|           return Promise.reject(new Error(lang.t('errors:parentinvalid'))) | ||||
|         } | ||||
|       }) | ||||
|     } else { | ||||
|       return Promise.reject(new Error('Parent entry is root.')) | ||||
|       return Promise.reject(new Error(lang.t('errors:parentisroot'))) | ||||
|     } | ||||
|   }, | ||||
|  | ||||
| @@ -212,11 +212,11 @@ module.exports = { | ||||
|           }) | ||||
|         }) | ||||
|       } else { | ||||
|         return Promise.reject(new Error('Entry does not exist!')) | ||||
|         return Promise.reject(new Error(lang.t('errors:notexist', { path: entryPath })) | ||||
|       } | ||||
|     }).catch((err) => { | ||||
|       winston.error(err) | ||||
|       return Promise.reject(new Error('Failed to save document.')) | ||||
|       return Promise.reject(new Error(lang.t('errors:savefailed'))) | ||||
|     }) | ||||
|   }, | ||||
|  | ||||
| @@ -316,11 +316,11 @@ module.exports = { | ||||
|           }) | ||||
|         }) | ||||
|       } else { | ||||
|         return Promise.reject(new Error('Entry already exists!')) | ||||
|         return Promise.reject(new Error(lang.t('errors:alreadyexists'))) | ||||
|       } | ||||
|     }).catch((err) => { | ||||
|       winston.error(err) | ||||
|       return Promise.reject(new Error('Something went wrong.')) | ||||
|       return Promise.reject(new Error(lang.t('errors:generic'))) | ||||
|     }) | ||||
|   }, | ||||
|  | ||||
| @@ -352,7 +352,7 @@ module.exports = { | ||||
|     let self = this | ||||
|  | ||||
|     if (_.isEmpty(entryPath) || entryPath === 'home') { | ||||
|       return Promise.reject(new Error('Invalid path!')) | ||||
|       return Promise.reject(new Error(lang.t('errors:invalidpath'))) | ||||
|     } | ||||
|  | ||||
|     return git.moveDocument(entryPath, newEntryPath).then(() => { | ||||
|   | ||||
| @@ -206,7 +206,7 @@ module.exports = { | ||||
|       let out = cProc.stdout.toString() | ||||
|       return _.includes(out, gitFilePath) | ||||
|     }).then((isTracked) => { | ||||
|       commitMsg = (isTracked) ? 'Updated ' + gitFilePath : 'Added ' + gitFilePath | ||||
|       commitMsg = (isTracked) ? lang.t('git:updated', { path: gitFilePath }) : lang.t('git:added', { path: gitFilePath }) | ||||
|       return self._git.add(gitFilePath) | ||||
|     }).then(() => { | ||||
|       let commitUsr = securityHelper.sanitizeCommitUser(author) | ||||
|   | ||||
| @@ -163,7 +163,7 @@ module.exports = { | ||||
|     let fpath = path.resolve(this._uploadsPath, fld, f) | ||||
|  | ||||
|     return fs.statAsync(fpath).then((s) => { | ||||
|       throw new Error('File ' + f + ' already exists.') | ||||
|       throw new Error(lang.t('errors:fileexists', { path: f })) | ||||
|     }).catch((err) => { | ||||
|       if (err.code === 'ENOENT') { | ||||
|         return f | ||||
|   | ||||
| @@ -59,14 +59,14 @@ module.exports = { | ||||
|       return self.processFile(pInfo.folder, pInfo.filename).then((mData) => { | ||||
|         return db.UplFile.findByIdAndUpdate(mData._id, mData, { upsert: true }) | ||||
|       }).then(() => { | ||||
|         return git.commitUploads('Uploaded ' + p) | ||||
|         return git.commitUploads(lang.t('git:uploaded', { path: p })) | ||||
|       }) | ||||
|     }) | ||||
|  | ||||
|     // -> Remove upload file | ||||
|  | ||||
|     self._watcher.on('unlink', (p) => { | ||||
|       return git.commitUploads('Deleted/Renamed ' + p) | ||||
|       return git.commitUploads(lang.t('git:deleted', { path: p })) | ||||
|     }) | ||||
|   }, | ||||
|  | ||||
|   | ||||
| @@ -168,7 +168,7 @@ module.exports = { | ||||
|  | ||||
|     return upl.validateUploadsFolder(destFolder).then((destFolderPath) => { | ||||
|       if (!destFolderPath) { | ||||
|         return Promise.reject(new Error('Invalid Folder')) | ||||
|         return Promise.reject(new Error(lang.t('errors:invalidfolder'))) | ||||
|       } | ||||
|  | ||||
|       return lcdata.validateUploadsFilename(fUrlFilename, destFolder).then((destFilename) => { | ||||
| @@ -192,7 +192,7 @@ module.exports = { | ||||
|               rq.abort() | ||||
|               destFileStream.destroy() | ||||
|               fs.remove(destFilePath) | ||||
|               reject(new Error('Remote file is too large!')) | ||||
|               reject(new Error(lang.t('errors:remotetoolarge'))) | ||||
|             } | ||||
|           }).on('error', (err) => { | ||||
|             destFileStream.destroy() | ||||
| @@ -243,7 +243,7 @@ module.exports = { | ||||
|             // -> Check for invalid operations | ||||
|  | ||||
|             if (sourceFilePath === destFilePath) { | ||||
|               return Promise.reject(new Error('Invalid Operation!')) | ||||
|               return Promise.reject(new Error(lang.t('errors:invalidoperation'))) | ||||
|             } | ||||
|  | ||||
|             // -> Delete DB entry | ||||
| @@ -271,7 +271,7 @@ module.exports = { | ||||
|           }) | ||||
|         }) | ||||
|       } else { | ||||
|         return Promise.reject(new Error('Invalid Destination Folder')) | ||||
|         return Promise.reject(new Error(lang.t('errors:invaliddestfolder'))) | ||||
|       } | ||||
|     }) | ||||
|   } | ||||
|   | ||||
| @@ -12,7 +12,9 @@ | ||||
|   "errors": { | ||||
|     "invalidlogin": "Invalid Login", | ||||
|     "invalidloginmsg": "The email or password is invalid.", | ||||
|     "invaliduseremail": "Invalid User Email", | ||||
|     "loginerror": "Login error", | ||||
|     "notyetauthorized": "You have not been authorized to login to this site yet.", | ||||
|     "toomanyattempts": "Too many attempts!", | ||||
|     "toomanyattemptsmsg": "You've made too many failed attempts in a short period of time, please try again {{time}}.", | ||||
|     "usernotfound": "User not found" | ||||
|   | ||||
| @@ -1,11 +1,54 @@ | ||||
| { | ||||
|   "wiki": "Wiki", | ||||
|   "headers": { | ||||
|     "overview": "Overview" | ||||
|   "header": { | ||||
|     "overview": "Overview", | ||||
|     "createdoc": "Create New Document" | ||||
|   }, | ||||
|   "footer": { | ||||
|     "poweredby": "Powered by", | ||||
|     "home": "Home", | ||||
|     "top": "Return to top" | ||||
|   }, | ||||
|   "search": { | ||||
|     "placeholder": "Search...", | ||||
|     "results": "Search Results", | ||||
|     "nomatch": "No results matching your query", | ||||
|     "didyoumean": "Did you mean...?" | ||||
|   }, | ||||
|   "sidebar": { | ||||
|     "nav": "NAV", | ||||
|     "navigation": "Navigation", | ||||
|     "pagecontents": "Page Contents", | ||||
|     "pastversions": "Past Versions" | ||||
|   }, | ||||
|   "nav": { | ||||
|     "home": "Home", | ||||
|     "account": "Account", | ||||
|     "myprofile": "My Profile", | ||||
|     "stats": "Stats", | ||||
|     "syssettings": "System Settings", | ||||
|     "users": "Users", | ||||
|     "logout": "Logout", | ||||
|     "create": "Create", | ||||
|     "edit": "Edit", | ||||
|     "history": "History", | ||||
|     "source": "Source", | ||||
|     "move": "Move", | ||||
|     "allpages": "All Pages", | ||||
|     "login": "Login", | ||||
|     "normalview": "Normal View", | ||||
|     "viewlatest": "View Latest", | ||||
|     "discard": "Discard", | ||||
|     "savechanges": "Save Changes", | ||||
|     "savedocument": "Save Document" | ||||
|   }, | ||||
|   "welcome": { | ||||
|     "title": "Welcome to your wiki!", | ||||
|     "subtitle": "Let's get started and create the home page.", | ||||
|     "createhome": "Create Home Page" | ||||
|   }, | ||||
|   "loading": { | ||||
|     "source": "Loading source...", | ||||
|     "editor": "Loading editor..." | ||||
|   } | ||||
| } | ||||
| @@ -1,17 +1,25 @@ | ||||
| { | ||||
|   "alreadyexists": "This entry already exists!", | ||||
|   "debugmsg": "Detailed debug trail", | ||||
|   "fileexists": "File {{path}} already exists.", | ||||
|   "forbidden": "Forbidden", | ||||
|   "forbiddendetail": "Sorry, you don't have the necessary permissions to access this page.", | ||||
|   "generic": "Oops, something went wrong", | ||||
|   "invalidaction": "Invalid Action.", | ||||
|   "invaliddestfolder": "Invalid Destination Folder!", | ||||
|   "invalidfiletype": "Invalid File Type.", | ||||
|   "invalidfolder": "Invalid Folder.", | ||||
|   "invalidoperation": "Invalid Operation!", | ||||
|   "invalidpath": "Invalid page path.", | ||||
|   "invaliduserid": "Invalid User Id", | ||||
|   "newpasswordtooshort": "New password is too short!", | ||||
|   "notexist": "Entry {{path}} does not exist!", | ||||
|   "notexistdetail": "Would you like to create this entry?", | ||||
|   "parentinvalid": "Parent entry is not a valid file.", | ||||
|   "parentisroot": "Parent entry is root.", | ||||
|   "remotetoolarge": "Remote file is too large!", | ||||
|   "reservedname": "You cannot create a document with this name as it is reserved by the system.", | ||||
|   "savefailed": "Failed to save document", | ||||
|   "starterfailed": "Could not load starter content!", | ||||
|   "unauthorized": "Unauthorized", | ||||
|   "actions": { | ||||
|   | ||||
							
								
								
									
										6
									
								
								server/locales/en/git.json
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										6
									
								
								server/locales/en/git.json
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,6 @@ | ||||
| { | ||||
|   "added": "Added {{path}}", | ||||
|   "deleted": "Deleted/Renamed {{path}}", | ||||
|   "updated": "Updated {{path}}", | ||||
|   "uploaded": "Uplodated {{path}}" | ||||
| } | ||||
| @@ -55,7 +55,7 @@ userSchema.statics.processProfile = (profile) => { | ||||
|   } else if (profile.user && profile.user.email && profile.user.email.length > 5) { | ||||
|     primaryEmail = profile.user.email | ||||
|   } else { | ||||
|     return Promise.reject(new Error('Invalid User Email')) | ||||
|     return Promise.reject(new Error(lang.t('auth:errors.invaliduseremail'))) | ||||
|   } | ||||
|  | ||||
|   profile.provider = _.lowerCase(profile.provider) | ||||
| @@ -89,7 +89,7 @@ userSchema.statics.processProfile = (profile) => { | ||||
|       } | ||||
|       return db.User.create(nUsr) | ||||
|     } | ||||
|     return user || Promise.reject(new Error('You have not been authorized to login to this site yet.')) | ||||
|     return user || Promise.reject(new Error(lang.t('auth:errors:notyetauthorized'))) | ||||
|   }) | ||||
| } | ||||
|  | ||||
| @@ -99,7 +99,7 @@ userSchema.statics.hashPassword = (rawPwd) => { | ||||
|  | ||||
| userSchema.methods.validatePassword = function (rawPwd) { | ||||
|   return bcrypt.compare(rawPwd, this.password).then((isValid) => { | ||||
|     return (isValid) ? true : Promise.reject(new Error('Invalid Login')) | ||||
|     return (isValid) ? true : Promise.reject(new Error(lang.t('auth:errors:invalidlogin'))) | ||||
|   }) | ||||
| } | ||||
|  | ||||
|   | ||||
| @@ -11,7 +11,7 @@ | ||||
|       block rootNavCenter | ||||
|         .nav-item | ||||
|           p.control(v-bind:class='{ "is-loading": searchload > 0 }') | ||||
|             input.input#search-input(type='text', v-model='searchq', @keyup.esc='closeSearch', @keyup.down='moveDownSearch', @keyup.up='moveUpSearch', @keyup.enter='moveSelectSearch', debounce='400', placeholder='Search...') | ||||
|             input.input#search-input(type='text', v-model='searchq', @keyup.esc='closeSearch', @keyup.down='moveDownSearch', @keyup.up='moveUpSearch', @keyup.enter='moveSelectSearch', debounce='400', placeholder=t('search.placeholder')) | ||||
|     span.nav-toggle | ||||
|       span | ||||
|       span | ||||
| @@ -22,13 +22,13 @@ | ||||
|  | ||||
|   transition(name='searchresults-anim', enter-active-class='slideInDown', leave-active-class='fadeOutUp') | ||||
|     .searchresults.animated(v-show='searchactive', v-cloak, style={'display':'none'}) | ||||
|       p.searchresults-label Search Results | ||||
|       p.searchresults-label= t('search.results') | ||||
|       ul.searchresults-list | ||||
|         li(v-if='searchres.length === 0') | ||||
|           a: em No results matching your query | ||||
|           a: em= t('search.nomatch') | ||||
|         li(v-for='sres in searchres', v-bind:class='{ "is-active": searchmovekey === "res." + sres.entryPath }') | ||||
|           a(v-bind:href='"/" + sres.entryPath') {{ sres.title }} | ||||
|       p.searchresults-label(v-if='searchsuggest.length > 0') Did you mean...? | ||||
|       p.searchresults-label(v-if='searchsuggest.length > 0')= t('search.didyoumean') | ||||
|       ul.searchresults-list(v-if='searchsuggest.length > 0') | ||||
|         li(v-for='sug in searchsuggest', v-bind:class='{ "is-active": searchmovekey === "sug." + sug }') | ||||
|           a(v-on:click='useSuggestion(sug)') {{ sug }} | ||||
|   | ||||
| @@ -1,14 +1,14 @@ | ||||
| extends ../../layout.pug | ||||
|  | ||||
| block rootNavCenter | ||||
|   h2.nav-item Account | ||||
|   h2.nav-item= t('nav.account') | ||||
|  | ||||
| block rootNavRight | ||||
|   i.nav-item#notifload | ||||
|   .nav-item | ||||
|     a.button.btn-edit-discard(href='/') | ||||
|       i.icon-home | ||||
|       span Home | ||||
|       span= t('nav.home') | ||||
|  | ||||
| block content | ||||
|  | ||||
| @@ -20,38 +20,38 @@ block content | ||||
|  | ||||
|           aside | ||||
|             .sidebar-label | ||||
|               span Navigation | ||||
|               span= t('sidebar.navigation') | ||||
|             ul.sidebar-menu | ||||
|               li | ||||
|                 a(href='/') | ||||
|                   i.icon-home | ||||
|                   span Home | ||||
|                   span= t('nav.home') | ||||
|  | ||||
|           aside | ||||
|             .sidebar-label | ||||
|               span Account | ||||
|               span= t('nav.account') | ||||
|             ul.sidebar-menu | ||||
|               li | ||||
|                 a(href='/admin/profile') | ||||
|                   i.icon-user | ||||
|                   span My Profile | ||||
|                   span= t('nav.myprofile') | ||||
|               li | ||||
|                 a(href='/admin/stats') | ||||
|                   i.icon-bar-graph-2 | ||||
|                   span Stats | ||||
|                   span= t('nav.stats') | ||||
|               if rights.manage | ||||
|                 li | ||||
|                   a(href='/admin/users') | ||||
|                     i.icon-users | ||||
|                     span Users | ||||
|                     span= t('nav.users') | ||||
|                 li | ||||
|                   a(href='/admin/settings') | ||||
|                     i.icon-cog | ||||
|                     span System Settings | ||||
|                     span= t('nav.syssettings') | ||||
|               li | ||||
|                 a(href='/logout') | ||||
|                   i.icon-delete2 | ||||
|                   span Logout | ||||
|                   span= t('nav.logout') | ||||
|  | ||||
|         .column | ||||
|           block adminContent | ||||
|   | ||||
| @@ -10,22 +10,22 @@ block content | ||||
|       .sidebar.is-collapsed | ||||
|         aside | ||||
|           .sidebar-label | ||||
|             span NAV | ||||
|             span= t('sidebar.nav') | ||||
|           ul.sidebar-menu | ||||
|             li | ||||
|               a(href='/') | ||||
|                 i.icon-home | ||||
|                 span Home | ||||
|                 span= t('nav.home') | ||||
|             if !isGuest | ||||
|               li | ||||
|                 a(href='/admin') | ||||
|                   i.icon-head | ||||
|                   span Account | ||||
|                   span= t('nav.account') | ||||
|             else | ||||
|               li | ||||
|                 a(href='/login') | ||||
|                   i.icon-unlock | ||||
|                   span Login | ||||
|                   span= t('nav.login') | ||||
|       ul.collapsable-nav(v-for='treeItem in tree', :class='{ "has-children": treeItem.hasChildren }', v-cloak) | ||||
|         li(v-for='page in treeItem.pages', :class='{ "is-active": page.isActive }') | ||||
|           a(v-on:click='mainAction(page)') | ||||
| @@ -34,7 +34,7 @@ block content | ||||
|               span {{ page.title }} | ||||
|             template(v-else) | ||||
|               i.icon-home | ||||
|               span Home | ||||
|               span= t('nav.home') | ||||
|           a.is-pagelink(v-if='page.isDirectory && page.isEntry', v-on:click='goto(page._id)') | ||||
|             i.icon-file-text-o | ||||
|             i.icon-arrow-right2 | ||||
|   | ||||
| @@ -1,17 +1,17 @@ | ||||
| extends ../layout.pug | ||||
|  | ||||
| block rootNavCenter | ||||
|   h2.nav-item Create New Document | ||||
|   h2.nav-item= t('header.createdoc') | ||||
|  | ||||
| block rootNavRight | ||||
|   i.nav-item#notifload | ||||
|   span.nav-item | ||||
|     a.button.is-outlined.btn-create-discard | ||||
|       i.icon-cross | ||||
|       span Discard | ||||
|       span= t('nav.discard') | ||||
|     a.button.btn-create-save | ||||
|       i.icon-check | ||||
|       span Save Document | ||||
|       span= t('nav.savedocument') | ||||
|  | ||||
| block content | ||||
|  | ||||
| @@ -29,4 +29,4 @@ block content | ||||
| block outside | ||||
|   #page-loader | ||||
|     i | ||||
|     span Loading editor... | ||||
|     span= t('loading.editor') | ||||
|   | ||||
| @@ -8,10 +8,10 @@ block rootNavRight | ||||
|   span.nav-item | ||||
|     a.button.is-outlined.btn-edit-discard | ||||
|       i.icon-cross | ||||
|       span Discard | ||||
|       span= t('nav.discard') | ||||
|     a.button.btn-edit-save | ||||
|       i.icon-check | ||||
|       span Save Changes | ||||
|       span= t('nav.savechanges') | ||||
|  | ||||
| block content | ||||
|  | ||||
| @@ -29,4 +29,4 @@ block content | ||||
| block outside | ||||
|   #page-loader | ||||
|     i | ||||
|     span Loading editor... | ||||
|     span= t('loading.editor') | ||||
|   | ||||
| @@ -5,7 +5,7 @@ block rootNavRight | ||||
|   .nav-item | ||||
|     a.button(href='/' + pageData.meta._id) | ||||
|       i.icon-circle-check | ||||
|       span View Latest | ||||
|       span= t('nav.viewlatest') | ||||
|  | ||||
| block content | ||||
|  | ||||
| @@ -17,7 +17,7 @@ block content | ||||
|  | ||||
|           aside.stickyscroll | ||||
|             .sidebar-label | ||||
|               span Past versions | ||||
|               span= t('sidebar.pastversions') | ||||
|             ul.sidebar-menu | ||||
|               each item, index in pageData.history | ||||
|                 - var itemDate = moment(item.date) | ||||
|   | ||||
| @@ -9,17 +9,17 @@ block rootNavRight | ||||
|     if rights.write | ||||
|       a.button.is-outlined.btn-move-prompt.is-hidden | ||||
|         i.icon-shuffle | ||||
|         span Move | ||||
|         span= t('nav.move') | ||||
|     a.button.is-outlined(href='/' + pageData.meta.path) | ||||
|       i.icon-loader | ||||
|       span Normal View | ||||
|       span= t('nav.normalview') | ||||
|     if rights.write | ||||
|       a.button.is-orange(href='/edit/' + pageData.meta.path) | ||||
|         i.fa.fa-edit | ||||
|         span Edit | ||||
|         span= t('nav.edit') | ||||
|       a.button.is-blue.btn-create-prompt | ||||
|         i.fa.fa-plus | ||||
|         span Create | ||||
|         span= t('nav.create') | ||||
|  | ||||
| block content | ||||
|  | ||||
| @@ -33,4 +33,4 @@ block content | ||||
| block outside | ||||
|   #page-loader | ||||
|     i | ||||
|     span Loading source... | ||||
|     span= t('loading.source') | ||||
|   | ||||
| @@ -14,20 +14,20 @@ block rootNavRight | ||||
|     if rights.write | ||||
|       a.button.is-outlined.btn-move-prompt.is-hidden | ||||
|         i.icon-shuffle | ||||
|         span Move | ||||
|         span= t('nav.move') | ||||
|     a.button.is-outlined(href='/source/' + pageData.meta.path) | ||||
|       i.icon-loader | ||||
|       span Source | ||||
|       span= t('nav.source') | ||||
|     a.button.is-outlined(href='/hist/' + pageData.meta.path) | ||||
|       i.icon-clock | ||||
|       span History | ||||
|       span= t('nav.history') | ||||
|     if rights.write | ||||
|       a.button(href='/edit/' + pageData.meta.path) | ||||
|         i.icon-document-text | ||||
|         span Edit | ||||
|         span= t('nav.edit') | ||||
|       a.button.btn-create-prompt | ||||
|         i.icon-plus | ||||
|         span Create | ||||
|         span= t('nav.create') | ||||
|  | ||||
| block content | ||||
|  | ||||
| @@ -39,16 +39,16 @@ block content | ||||
|  | ||||
|           aside | ||||
|             .sidebar-label | ||||
|               span Navigation | ||||
|               span= t('sidebar.navigation') | ||||
|             ul.sidebar-menu | ||||
|               li | ||||
|                 a(href='/') | ||||
|                   i.icon-home | ||||
|                   span Home | ||||
|                   span= t('nav.home') | ||||
|               li | ||||
|                 a(href='/all') | ||||
|                   i.icon-paper | ||||
|                   span All Pages | ||||
|                   span= t('nav.allpages') | ||||
|               if pageData.parent | ||||
|                 li | ||||
|                   a(href='/' + pageData.parent.path) | ||||
| @@ -58,15 +58,15 @@ block content | ||||
|                 li | ||||
|                   a(href='/admin') | ||||
|                     i.icon-head | ||||
|                     span Account | ||||
|                     span= t('nav.account') | ||||
|               else | ||||
|                 li | ||||
|                   a(href='/login') | ||||
|                     i.icon-unlock | ||||
|                     span Login | ||||
|                     span= t('nav.login') | ||||
|           aside.stickyscroll | ||||
|             .sidebar-label | ||||
|               span Page Contents | ||||
|               span= t('sidebar.pagecontents') | ||||
|             ul.sidebar-menu | ||||
|               li.is-hidden-until-scroll: a(href='#root', title='Top of Page') | ||||
|                 i.icon-arrow-up2 | ||||
|   | ||||
| @@ -11,6 +11,6 @@ block content | ||||
|     .container | ||||
|       .welcome | ||||
|         img(src='/images/logo.png', alt='Wiki.js') | ||||
|         h1 Welcome to your wiki! | ||||
|         h2 Let's get started and create the home page. | ||||
|         a.button.is-indigo(href='/create/home') Create Home Page | ||||
|         h1= t('welcome.title') | ||||
|         h2= t('welcome.subtitle') | ||||
|         a.button.is-indigo(href='/create/home')= t('welcome.createhome') | ||||
|   | ||||
		Reference in New Issue
	
	Block a user