您好,欢迎来到三六零分类信息网!老站,搜索引擎当天收录,欢迎发信息

创建内容管理系统:nodePress

2025/8/13 18:03:20发布25次查看
您已使用 go 成功创建了平面文件系统内容管理系统 (cms)。下一步是采用同样的理念,使用 node.js 制作一个 web 服务器。我将向您展示如何加载库、创建服务器和运行服务器。
此 cms 将使用第一个教程“构建 cms:结构和样式”中介绍的站点数据结构。因此,请下载此基本结构并将其安装在新目录中。
获取节点和节点库在 mac 上安装 node.js 最简单的方法是使用 homebrew。如果您尚未安装 homebrew,教程 homebrew 揭秘:os x 的终极包管理器将向您展示如何安装。
要使用 homebrew 安装 node.js,请在终端中输入以下指令:
brew install node
完成后,您的 mac 上将完全安装了 node 和 npm 命令。对于所有其他平台,请按照 node.js 网站上的说明进行操作。
请注意:许多包管理器当前正在安装 node.js 版本 0.10。本教程假设您拥有 5.3 或更高版本。您可以通过键入以下内容来检查您的版本:
node --version
node 命令运行 javascript 解释器。 npm 命令是 node.js 的包管理器,用于安装新库、创建新项目以及运行项目脚本。 envato tuts+ 上有许多关于 node.js 和 npm 的精彩教程和课程。
要安装 web 服务器的库,您必须在 terminal.app 或 iterm.app 程序中运行以下命令:
npm install express --savenpm install handlebars --savenpm install moment --savenpm install marked --savenpm install jade --savenpm install morgan --save
express 是一个 web 应用程序开发平台。它类似于go中的goweb库。 handlebars 是用于创建页面的模板引擎。 moment 是一个用于处理日期的库。 marked 是一个很棒的 javascript 中 markdown 到 html 转换器。 jade 是一种 html 速记语言,可轻松创建 html。 morgan 是 express 的中间件库,可生成 apache 标准日志文件。
安装库的另一种方法是下载本教程的源文件。下载并解压后,在主目录中输入:
npm --install
这将安装创建该项目所需的一切。
nodepress.js现在您可以开始创建服务器了。在项目的顶层目录中,创建一个名为 nodepress.js 的文件,在您选择的编辑器中打开它,然后开始添加以下代码。我将解释放入文件中的代码。
//// load the libraries used.//var fs = require('fs');var path = require(path);var child_process = require('child_process');var process = require('process');var express = require('express'); // http://expressjs.com/en/var morgan = require('morgan'); // https://github.com/expressjs/morganvar handlebars = require(handlebars); // http://handlebarsjs.com/var moment = require(moment); // http://momentjs.com/var marked = require('marked'); // https://github.com/chjj/markedvar jade = require('jade'); // http://jade-lang.com/
服务器代码从初始化用于创建服务器的所有库开始。没有带有网址的注释的库是内部 node.js 库。
//// setup global variables.//var parts = json.parse(fs.readfilesync('./server.json', 'utf8'));var styledir = process.cwd() + '/themes/styling/' + parts['currentstyling'];var layoutdir = process.cwd() + '/themes/layouts/' + parts['currentlayout'];var sitecss = null;var sitescripts = null;var mainpage = null;
接下来,我设置所有全局变量和库配置。使用全局变量并不是最好的软件设计实践,但它确实有效并且有助于快速开发。
parts 变量是一个包含网页所有部分的哈希数组。每个页面都引用该变量的内容。它从服务器目录顶部的 server.json 文件的内容开始。
然后,我使用 server.json 文件中的信息创建用于此站点的 styles 和 layouts 目录的完整路径。
然后将三个变量设置为空值:sitecss、sitescripts 和 mainpage。这些全局变量将包含所有 css、javascript 和主索引页内容。这三个项目是任何 web 服务器上请求最多的项目。因此,将它们保留在内存中可以节省时间。如果 server.json 文件中的 cache 变量为 false,则每个请求都会重新读取这些项目。
marked.setoptions({ renderer: new marked.renderer(), gfm: true, tables: true, breaks: false, pedantic: false, sanitize: false, smartlists: true, smartypants: false});
此代码块用于配置 marked 库以从 markdown 生成 html。大多数情况下,我会打开表格和 smartlists 支持。
parts[layout] = fs.readfilesync(layoutdir + '/template.html', 'utf8');parts[404] = fs.readfilesync(styledir + '/404.html', 'utf8');parts[footer] = fs.readfilesync(styledir + '/footer.html', 'utf8');parts[header] = fs.readfilesync(styledir + '/header.html', 'utf8');parts[sidebar] = fs.readfilesync(styledir + '/sidebar.html', 'utf8');//// read in the page parts.//var partfiles = fs.readdirsync(parts['sitebase'] + parts/);partfiles.foreach(function(ele, index, array) { parts[path.basename(ele, path.extname(ele))] = figurepage(parts['sitebase'] + parts/ + path.basename(ele, path.extname(ele)));});
parts 变量进一步加载 styles 和 layout 目录中的部分。 site 目录内的 parts 目录中的每个文件也被加载到 parts 全局变量中。不带扩展名的文件名是用于存储文件内容的名称。这些名称在 handlebars 宏中得到扩展。
//// setup handlebar's helpers.////// handlebars helper: save//// description: this helper expects a// <name> <value> where the name// is saved with the value for future// expansions. it also returns the// value directly.//handlebars.registerhelper(save, function(name, text) { // // local variables. // var newname = , newtext = ; // // see if the name and text is in the first argument // with a |. if so, extract them properly. otherwise, // use the name and text arguments as given. // if(name.indexof(|) > 0) { var parts = name.split(|); newname = parts[0]; newtext = parts[1]; } else { newname = name; newtext = text; } // // register the new helper. // handlebars.registerhelper(newname, function() { return newtext; }); // // return the text. // return newtext;});//// handlebars helper: date//// description: this helper returns the date// based on the format given.//handlebars.registerhelper(date, function(dformat) { return moment().format(dformat);});//// handlebars helper: cdate//// description: this helper returns the date given// in to a format based on the format// given.//handlebars.registerhelper(cdate, function(ctime, dformat) { return moment(ctime).format(dformat);});
下一段代码定义了我定义的在 web 服务器中使用的 handlebars 帮助程序:save、date 和 cdate。保存助手允许在页面内创建变量。此版本支持 gopress 版本,其中参数的名称和值一起用“|”分隔。您还可以使用两个参数指定保存。例如:
{{save name|richard guay}}{{save newname richard guay}}name is: {{name}}newname is: {{newname}}
这将产生相同的结果。我更喜欢第二种方法,但 go 中的 handlebars 库不允许使用多个参数。
date 和 cdate 帮助程序格式化当前日期 (date) 或给定日期 (cdate)根据 moment.js 库格式化规则。 cdate 帮助程序期望渲染的日期是第一个参数并且具有 iso 8601 格式。
//// create and configure the server.//var nodepress = express();//// configure middleware.//nodepress.use(morgan('combined'))
现在,代码创建一个 express 实例来配置实际的服务器引擎。 nodepress.use() 函数设置中间件软件。中间件是在每次调用服务器时提供服务的任何代码。在这里,我设置了 morgan.js 库来创建正确的服务器日志输出。
//// define the routes.//nodepress.get('/', function(request, response) { setbasicheader(response); if((parts[cache] == true) && (mainpage != null)) { response.send(mainpage); } else { mainpage = page(main); response.send(mainpage); }});nodepress.get('/favicon.ico', function(request, response) { var options = { root: parts['sitebase'] + 'images/', dotfiles: 'deny', headers: { 'x-timestamp': date.now(), 'x-sent': true } }; response.set(content-type, image/ico); setbasicheader(response); response.sendfile('favicon.ico', options, function(err) { if (err) { console.log(err); response.status(err.status).end(); } else { console.log('favicon was sent:', 'favicon.ico'); } });});nodepress.get('/stylesheets.css', function(request, response) { response.set(content-type, text/css); setbasicheader(response); response.type(css); if((parts[cache] == true) && (sitecss != null)) { response.send(sitecss); } else { sitecss = fs.readfilesync(parts['sitebase'] + 'css/final/final.css'); response.send(sitecss); }});nodepress.get('/scripts.js', function(request, response) { response.set(content-type, text/javascript); setbasicheader(response); if((parts[cache] == true) && (sitescripts != null)) { response.send(sitescripts); } else { sitescripts = fs.readfilesync(parts['sitebase'] + 'js/final/final.js', 'utf8'); response.send(sitescripts); }});nodepress.get('/images/:image', function(request, response) { var options = { root: parts['sitebase'] + 'images/', dotfiles: 'deny', headers: { 'x-timestamp': date.now(), 'x-sent': true } }; response.set(content-type, image/ + path.extname(request.params.image).substr(1)); setbasicheader(response); response.sendfile(request.params.image, options, function(err) { if (err) { console.log(err); response.status(err.status).end(); } else { console.log('image was sent:', request.params.image); } });});nodepress.get('/posts/blogs/:blog', function(request, response) { setbasicheader(response); response.send(post(blogs, request.params.blog, index));});nodepress.get('/posts/blogs/:blog/:post', function(request, response) { setbasicheader(response); response.send(post(blogs, request.params.blog, request.params.post));});nodepress.get('/posts/news/:news', function(request, response) { setbasicheader(response); response.send(post(news, request.params.news, index));});nodepress.get('/posts/news/:news/:post', function(request, response) { setbasicheader(response); response.send(post(news, request.params.news, request.params.post));});nodepress.get('/:page', function(request, response) { setbasicheader(response); response.send(page(request.params.page));});
这部分代码定义了实现 web 服务器所需的所有路由。所有路由都运行 setbasicheader() 函数来设置正确的标头值。所有针对页面类型的请求都会调用 page() 函数,而所有针对 post 类型页面的请求都会调用 posts() 函数。
content-type 的默认值为 html。因此,对于 css、javascript 和图像,content-type 显式设置为其适当的值。
您还可以使用 put、delete 和 post rest 动词定义路由。这个简单的服务器仅使用 get 动词。
//// start the server.//var addressitems = parts['serveraddress'].split(':');var server = nodepress.listen(addressitems[2], function() { var host = server.address().address; var port = server.address().port; console.log('nodepress is listening at http://%s:%s', host, port);});
在定义所使用的不同函数之前要做的最后一件事是启动服务器。 server.json 文件包含 dns 名称(此处为 localhost)和服务器的端口。解析后,服务器的 listen() 函数使用端口号来启动服务器。服务器端口打开后,脚本会记录服务器的地址和端口。
//// function: setbasicheader//// description: this function will set the basic header information// needed.//// inputs:// response the response object//function setbasicheader(response) { response.append(cache-control, max-age=2592000, cache); response.append(server, nodepress - a cms written in node from custom computer tools: http://customct.com.);}
定义的第一个函数是 setbasicheader() 函数。该函数设置响应头,告诉浏览器将页面缓存一个月。它还告诉浏览器该服务器是nodepress服务器。如果您需要任何其他标准标头值,您可以使用 response.append() 函数在此处添加它们。
//// function: page//// description: this function processes a page request//// inputs:// page the requested page//function page(page) { // // process the given page using the standard layout. // return (processpage(parts[layout], parts['sitebase'] + pages/ + page));}
page() 函数将页面的布局模板以及页面在服务器上的位置发送到 processpage() 函数。
//// function: post//// description: this function processes a post request//// inputs:// type the type of post.// cat the category of the post.// post the requested post//function post(type, cat, post) { // // process the post given the type and the post name. // return (processpage(parts[layout], parts['sitebase'] + posts/ + type + / + cat + / + post));}
post() 函数就像 page() 函数,不同之处在于帖子有更多项目来定义每个帖子。在这个系列的服务器中,一个post包含一个type、category,以及实际的post。类型为 blogs 或 news。类别是 flatcms。由于这些代表目录名称,因此您可以将它们设为您想要的任何名称。只需将命名与文件系统中的名称相匹配即可。
//// function: processpage//// description: this function processes a page for the cms.//// inputs:// layout the layout to use for the page.// page path to the page to render.//function processpage(layout, page) { // // get the pages contents and add to the layout. // var context = {}; context = mergerecursive(context, parts); context['content'] = figurepage(page); context['pagename'] = path.basename(page, path.extname(page)); // // load page data. // if(fileexists(page + .json)) { // // load the page's data file and add it to the data structure. // context = mergerecursive(context, json.parse(fs.readfilesync(page + '.json', 'utf8'))); } // // process handlebars codes. // var template = handlebars.compile(layout); var html = template(context); // // process all shortcodes. // html = processshortcodes(html); // // run through handlebars again. // template = handlebars.compile(html); html = template(context); // // return results. // return (html);}
processpage() 函数获取要呈现的页面内容的布局和路径。该函数首先创建 parts 全局变量的本地副本,并添加“contents”主题标签以及调用 figurepage() 函数的结果。然后,它将 pagename 哈希值设置为页面名称。
然后,该函数使用 handlebars 将页面内容编译到布局模板。之后, processshortcodes() 函数将展开页面上定义的所有短代码。然后,handlebars 模板引擎再次检查代码。然后浏览器接收结果。
//// function: processshortcodes//// description: this function takes a string and// processes all of the shortcodes in // the string.//// inputs:// content string to process//function processshortcodes(content) { // // create the results variable. // var results = ; // // find the first match. // var scregfind = /\-\[([^\]]*)\]\-/i; var match = scregfind.exec(content); if (match != null) { results += content.substr(0,match.index); var scregnamearg = /(\w+)(.*)*/i; var parts = scregnamearg.exec(match[1]); if (parts != null) { // // find the closing tag. // var scregclose = new regexp(\\-\\[\\/ + parts[1] + \\]\\-); var left = content.substr(match.index + 4 + parts[1].length); var match2 = scregclose.exec(left); if (match2 != null) { // // process the enclosed shortcode text. // var enclosed = processshortcodes(content.substr(match.index + 4 + parts[1].length, match2.index)); // // figure out if there were any arguments. // var args = ; if (parts.length == 2) { args = parts[2]; } // // execute the shortcode. // results += shortcodes[parts[1]](args, enclosed); // // process the rest of the code for shortcodes. // results += processshortcodes(left.substr(match2.index + 5 + parts[1].length)); } else { // // invalid shortcode. return full string. // results = content; } } else { // // invalid shortcode. return full string. // results = content; } } else { // // no shortcodes found. return the string. // results = content; } return (results);}
processshortcodes() 函数将网页内容作为字符串并搜索所有短代码。短代码是类似于 html 标签的代码块。一个例子是:
-[box]- <p>this is inside a box</p>-[/box]-
此代码在 html 段落周围有一个 box 的简码。其中 html 使用 >,短代码使用 -[ 和 >]-。在名称后面,可以包含或不可以包含包含短代码参数的字符串。
processshortcodes() 函数查找短代码,获取其名称和参数,找到末尾以获取内容,处理短代码的内容,使用参数和内容执行短代码,将结果添加到完成中页面,并在页面的其余部分搜索下一个短代码。循环是通过递归调用函数来执行的。
//// define the shortcodes function array.//var shortcodes = { 'box': function(args, inside) { return (<div class='box'> + inside + </div>); }, 'column1': function(args, inside) { return (<div class='col1'> + inside + </div>); }, 'column2': function(args, inside) { return (<div class='col2'> + inside + </div>); }, 'column1of3': function(args, inside) { return (<div class='col1of3'> + inside + </div>); }, 'column2of3': function(args, inside) { return (<div class='col2of3'> + inside + </div>); }, 'column3of3': function(args, inside) { return (<div class='col3of3'> + inside + </div>); }, 'php': function(args, inside) { return (<div class='showcode'><pre type='syntaxhighlighter' class='brush: php'> + inside + </pre></div>); }, 'js': function(args, inside) { return (<div class='showcode'><pre type='syntaxhighlighter' class='brush: javascript'> + inside + </pre></div>); }, 'html': function(args, inside) { return (<div class='showcode'><pre type='syntaxhighlighter' class='brush: html'> + inside + </pre></div>); }, 'css': function(args, inside) { return (<div class='showcode'><pre type='syntaxhighlighter' class='brush: css'> + inside + </pre></div>); }};
下一节定义 shortcodes json 结构,该结构定义与其函数关联的短代码的名称。所有短代码函数都接受两个参数:args 和 inside。 args 是名称和空格之后、标签结束之前的所有内容。 inside 是开始和结束短代码标记包含的所有内容。这些功能是基本功能,但您可以创建一个短代码来执行您能在 javascript 中想到的任何功能。
//// function: figurepage//// description: this function figures the page type// and loads the contents appropriately// returning the html contents for the page.//// inputs:// page the page to load contents.//function figurepage(page) { var result = ; if (fileexists(page + .html)) { // // it's an html file. read it in and send it on. // result = fs.readfilesync(page + .html); } else if (fileexists(page + .amber)) { // // it's a jade file. convert to html and send it on. i // am still using the amber extension for compatibility // to gopress. // var jadefun = jade.compilefile(page + .amber, {}); // render the function var result = jadefun({}); } else if (fileexists(page + .md)) { // // it's a markdown file. convert to html and send // it on. // result = marked(fs.readfilesync(page + .md).tostring()); // // this undo marked's uri encoding of quote marks. // result = result.replace(/\&quot\;/g,\); } return (result);}
figurepage() 函数接收服务器上页面的完整路径。然后,此函数根据扩展名测试它是否为 html、markdown 或 jade 页面。我仍然在 jade 中使用 .amber,因为那是我在 gopress 服务器上使用的库。所有 markdown 和 jade 内容都会先转换为 html,然后再传递给调用例程。由于 markdown 处理器将所有引号翻译为 ",因此我在传回之前将它们翻译回来。
//// function: fileexists//// description: this function returns a boolean true if // the file exists. otherwise, false.//// inputs:// filepath path to a file in a string.//function fileexists(filepath) { try { return fs.statsync(filepath).isfile(); } catch (err) { return false; }}
fileexists() 函数是 fs.exists() 函数的替代品,该函数曾经是 node.js 的 fs 库的一部分。它使用 fs.statsync() 函数来尝试获取文件的状态。如果发生错误,则会返回 false。否则,返回 true。
//// function: mergerecursive//// description: recursively merge properties of two objects//// inputs:// obj1 the first object to merge// obj2 the second object to merge//function mergerecursive(obj1, obj2) { for (var p in obj2) { try { // property in destination object set; update its value. if (obj2[p].constructor == object) { obj1[p] = mergerecursive(obj1[p], obj2[p]); } else { obj1[p] = obj2[p]; } } catch (e) { // property in destination object not set; create it and set its value. obj1[p] = obj2[p]; } } return obj1;}
最后一个函数是 mergerecursive() 函数。它将第二个传递对象复制到第一个传递对象中。在添加特定于页面的部分之前,我利用它将主 parts 全局变量复制到本地副本中。
本地运行保存文件后,您可以使用以下命令运行服务器:
node nodepress.js
或者,您可以使用 package.json 文件中的 npm 脚本。您可以像这样运行 npm 脚本:
npm start
这将运行 package.json 文件内的 start 脚本。
将您的网络浏览器指向 http://localhost:8080,您将看到上面的页面。您可能已经注意到我在主页上添加了更多测试代码。对页面的所有更改都包含在本教程的下载中。它们大多只是一些小的调整,以更全面地测试功能并适应使用不同库的任何差异。最显着的区别是 jade 库不使用 $ 来命名变量,而 amber 则使用。
结论现在,您在 go 和 node.js 中拥有完全相同的平面文件系统 cms。这只是您可以使用此平台构建的内容的表面。尝试并尝试新事物。这是创建您自己的网络服务器的最佳部分。
以上就是创建内容管理系统:nodepress的详细内容。
该用户其它信息

VIP推荐

免费发布信息,免费发布B2B信息网站平台 - 三六零分类信息网 沪ICP备09012988号-2
企业名录 Product