Costruisci un CMS nodePress

È stato creato correttamente un sistema di gestione dei contenuti (CMS) a file system con Go. Il prossimo passo è prendere lo stesso ideale e creare un server web usando Node.js. Ti mostrerò come caricare le librerie, creare il server ed eseguire il server.

Questo CMS utilizzerà la struttura dei dati del sito come illustrato nel primo tutorial, Building a CMS: Structure and Styling. Pertanto, scarica e installa questa struttura di base in una nuova directory.

Ottenere il nodo e le librerie del nodo

Il modo più semplice per installare Node.js su un Mac è con Homebrew. Se non hai ancora installato Homebrew, il tutorial Homebrew Demystified: Ultimate Package Manager di OS X ti mostrerà come.

Per installare Node.js con Homebrew, digita questa istruzione in un terminale:

brew install node

Al termine, i comandi node e npm saranno completamente installati sul tuo Mac. Per tutte le altre piattaforme, seguire le istruzioni sul sito Web Node.js.

Fai attenzione: molti gestori di pacchetti stanno installando la versione 0.10 di Node.js. Questo tutorial presuppone che tu abbia la versione 5.3 o successiva. Puoi verificare la tua versione digitando:

nodo --versione

Il nodo comando esegue l'interprete JavaScript. Il npm comando è un gestore di pacchetti per Node.js per installare nuove librerie, creare nuovi progetti ed eseguire script per un progetto. Ci sono molti tutorial e corsi su Node.js e NPM su Envato Tuts+.

Per installare le librerie per il server web, devi eseguire questi comandi nel programma Terminal.app o iTerm.app:

npm install express --save npm installa manubri --save npm installa momento --save npm installa segnato --save npm installa jade --save npm installa morgan --save

Express è una piattaforma di sviluppo di applicazioni web. È simile alla libreria goWeb in Go. Handlebars è il motore dei template per la creazione delle pagine. Moment è una libreria per lavorare con le date. Marked è un ottimo convertitore Markdown to HTML in JavaScript. Jade è un linguaggio stenografico HTML per creare facilmente HTML. Morgan è una libreria middleware per Express che genera i file di log standard Apache.

Un modo alternativo per installare le librerie è scaricare i file sorgente per questo tutorial. Una volta scaricato e decompresso, digita questo nella directory principale:

npm --install

Questo installerà tutto il necessario per creare questo progetto.

nodePress.js

Ora puoi iniziare a creare il server. Nella top directory del progetto, crea un file chiamato nodePress.js, aprilo nell'editor che preferisci e inizia ad aggiungere il seguente codice. Ho intenzione di spiegare il codice mentre viene inserito nel file.

// // Carica le librerie utilizzate. // var fs = require ('fs'); var path = require ("percorso"); 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/morgan var Handlebars = require ("handlebars"); // http://handlebarsjs.com/ var momento = require ("momento"); // http://momentjs.com/ var marked = require ('marked'); // https://github.com/chjj/marked var jade = require ('jade'); // http://jade-lang.com/

Il codice server inizia con l'inizializzazione di tutte le librerie utilizzate per creare il server. Le librerie che non hanno un commento con un indirizzo web sono le librerie interne di Node.js.

// // Imposta variabili globali. // 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;

Successivamente, ho impostato tutte le variabili globali e le configurazioni della libreria. L'uso di variabili globali non è la migliore pratica di progettazione del software, ma funziona e rende rapido lo sviluppo.

Il parti variabile è un array di hash contenente tutte le parti di una pagina web. Ogni pagina fa riferimento al contenuto di questa variabile. Inizia con il contenuto del file server.json trovato nella parte superiore della directory del server.

Quindi utilizzo le informazioni dal file server.json per creare i percorsi completi per stili e layout directory utilizzate per questo sito.

Tre variabili vengono quindi impostate su valori nulli: siteCSS, siteScripts, e pagina principale. Queste variabili globali conterranno tutti i CSS, i JavaScript e i contenuti della pagina principale dell'indice. Questi tre elementi sono gli elementi più richiesti su qualsiasi server web. Pertanto, tenerli in memoria consente di risparmiare tempo. Se la nascondiglio variabile nel file server.json è falsa, questi elementi vengono riletti con ogni richiesta.

marked.setOptions (renderer: new marked.Renderer (), gfm: true, tables: true, breaks: false, pedantic: false, sanitize: false, smartLists: true, smartypants: false);

Questo blocco di codice serve a configurare la libreria Marked per generare HTML da Markdown. Principalmente, accendo il supporto di table e smartList.

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'); // // Leggi nella pagina parti. // var partFiles = fs.readdirSync (parts ['Sitebase'] + "parts /"); partFiles.forEach (function (ele, index, array) parts [path.basename (ele, path.extname (ele))] = figurePage (parti ['Sitebase'] + "parts /" + path.basename (ele, path.extname (ele))););

Il parti variabile viene ulteriormente caricata con le parti dal stili e disposizione le directory. Ogni file nel parti directory all'interno del posto la directory è anche caricata nel parti variabile globale. Il nome del file senza estensione è il nome utilizzato per memorizzare il contenuto del file. Questi nomi vengono espansi nella macro di Handlebars.

// // Imposta gli helper del manubrio. // // // HandleBars Helper: save // ​​// Descrizione: Questo helper si aspetta un // """"dove il nome // viene salvato con il valore per le espansioni // future. Inoltre restituisce direttamente il valore // // Handlebars.registerHelper (" salva ", funzione (nome, testo) // // Variabili locali. // var newName = "", newText = ""; // // Controlla se il nome e il testo si trovano nel primo argomento // con un |. In caso affermativo, estraili correttamente. Altrimenti, // usa il nome e il testo argomenti come dati. // if (name.indexOf ("|")> 0) var parts = name.split ("|"); newName = parts [0]; newText = parts [1]; else newName = nome; newText = testo; // // Registra il nuovo helper. // Handlebars.registerHelper (newName, function () return newText;); // // Restituisce il testo. // return newText;) ; // // HandleBars Helper: date // // Descrizione: Questo helper restituisce la data // in base al formato indicato. // Handlebars.registerHelper ("date", function (dFormat) return moment (). Format ( dFormat);); // // HandleBars Helper: cdate // // Descrizione: Questo helper restituisce la data data // in a un formato basato sul formato // data. // Handlebars.registerHelper ("cdate", function (cTime, dFormat) return moment (cTime) .format (dFormat););

La prossima sezione di codice definisce gli helper di Handlebars che ho definito per l'utilizzo nel web server: salvare, Data, e CDate. L'helper di salvataggio consente la creazione di variabili all'interno di una pagina. Questa versione supporta la versione goPress in cui il parametro ha il nome e il valore separati da un "|". Puoi anche specificare un salvataggio usando due parametri. Per esempio:

save "name | Richard Guay" save "newName" "Richard Guay" Name is: name newName è: newName

Questo produrrà gli stessi risultati. Preferisco il secondo approccio, ma la libreria di Handlebars in Go non consente più di un parametro.

Il Data e CDate aiutanti formattano la data corrente (Data) o una data specifica (CDate) secondo il moment.js regole di formattazione della libreria. Il CDate helper si aspetta che la data diventi il ​​primo parametro e abbia il formato ISO 8601.

// // Crea e configura il server. // var nodePress = express (); // // Configura il middleware. // nodePress.use (morgan ('combined'))

Ora, il codice crea un'istanza Express per la configurazione del motore server effettivo. Il nodePress.use () la funzione imposta il software middleware. Il middleware è qualsiasi codice che viene pubblicato su ogni chiamata al server. Qui, ho impostato la libreria Morgan.js per creare l'output del registro del server corretto.

// // Definisci i percorsi. // 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', intestazioni: 'x-timestamp' : Date.now (), 'x-sent:' true; response.set ("Content-Type", "image / ico"); setBasicHeader (risposta); response.sendFile ('favicon.ico', opzioni , function (err) if (err) console.log (err); response.status (err.status) .end (); else console.log ('Favicon è stato inviato:', 'favicon.ico' ););); nodePress.get ('/ stylesheets.css', function (request, response) response.set ("Content-Type", "text / css"); setBasicHeader (response); response.type ("css"); if ((parti ["Cache"] == true) && (siteCSS! = null)) response.send (siteCSS); else siteCSS = fs.readFileSync (parti ['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', intestazioni: '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 ('Immagine inviata:', request.params.image););); nodePress.get ('/ posts / blog /: blog', funzione (richiesta, risposta) setBasicHeader (risposta); response.send (post ("blog", request.params.blog, "index"));) ; nodePress.get ('/ posts / blog /: blog /: post', funzione (richiesta, risposta) setBasicHeader (risposta); response.send (post ("blog", request.params.blog, request.params.post ));); nodePress.get ('/ posts / news /: news', funzione (richiesta, risposta) setBasicHeader (risposta); response.send (post ("news", request.params.news, "index"));) ; nodePress.get ('/ posts / news /: news /: post', funzione (richiesta, risposta) setBasicHeader (risposta); response.send (post ("news", request.params.news, request.params.post ));); nodePress.get ('/: page', function (request, response) setBasicHeader (response); response.send (page (request.params.page)););

Questa sezione di codice definisce tutti i percorsi necessari per implementare il web server. Tutti i percorsi eseguono il setBasicHeader () funzione per impostare i valori di intestazione appropriati. Tutte le richieste per un tipo di pagina evocheranno il pagina() funzione, mentre tutte le richieste per la pagina del tipo di post evocheranno il messaggi () funzione.

L'impostazione predefinita per Tipo di contenuto è HTML. Pertanto, per CSS, JavaScript e immagini, il Tipo di contenuto è esplicitamente impostato sul valore appropriato.

Puoi anche definire percorsi con il mettere, Elimina, e inviare Verbi REST. Questo semplice server utilizza solo il ottenere verbo.

// // Avvia il server. // var addressItems = parts ['ServerAddress']. split (':'); var server = nodePress.listen (addressItems [2], function () var host = server.address (). indirizzo; var port = server.address (). port; console.log ('nodePress sta ascoltando all'indirizzo http: / /% s:% s ', host, porta););

L'ultima cosa da fare prima di definire le diverse funzioni utilizzate è avviare il server. Il file server.json contiene il nome DNS (qui, lo è localhost) e la porta per il server. Una volta analizzato, il server ascolta() la funzione utilizza il numero di porta per avviare il server. Una volta aperta la porta del server, lo script registra l'indirizzo e la porta per il server.

// // Funzione: setBasicHeader // // Descrizione: questa funzione imposterà le informazioni di intestazione di base // necessarie. // // Input: // response L'oggetto response // function setBasicHeader (response) response.append ("Cache-Control", "max-age = 2592000, cache"); response.append ("Server", "nodePress - un CMS scritto nel nodo da Custom Computer Tools: http://customct.com."); 

La prima funzione definita è il setBasicHeader () funzione. Questa funzione imposta l'intestazione della risposta per dire al browser di memorizzare nella cache la pagina per un mese. Indica anche al browser che il server è un server nodePress. Se ci sono altri valori di intestazione standard che desideri, li aggiungerei qui con response.append () funzione.

// // Funzione: pagina // // Descrizione: Questa funzione elabora una richiesta di pagina // // Input: // pagina La pagina richiesta // pagina di funzione (pagina) // // Elabora la pagina specificata utilizzando lo standard disposizione. // return (processPage (parts ["layout"], parts ['Sitebase'] + "pages /" + page)); 

Il pagina() funzione invia il modello di layout per una pagina e la posizione della pagina sul server al processPage () funzione.

// // Funzione: post // // Descrizione: questa funzione elabora una richiesta post // // Input: // type Il tipo di post. // cat La categoria del post. // post Il post richiesto // function post (type, cat, post) // // Elabora il post in base al tipo e al nome del post. // return (processPage (parts ["layout"], parts ['Sitebase'] + "posts /" + type + "/" + cat + "/" + post)); 

Il inviare() la funzione è proprio come la pagina() funzione, eccetto che i post hanno più elementi per definire ogni post. In questa serie di server, un post contiene un genere, categoria, e il reale inviare. Il tipo è o blog o notizia. La categoria è flatcms. Dal momento che questi rappresentano i nomi delle directory, puoi renderli come vuoi. Basta abbinare la denominazione a ciò che è nel tuo file system.

// // Funzione: processPage // // Descrizione: questa funzione elabora una pagina per il CMS. // // Input: // layout Il layout da utilizzare per la pagina. // pagina Percorso della pagina da rappresentare. // function processPage (layout, page) // // Ottieni il contenuto delle pagine e aggiungi al layout. // var context = ; context = MergeRecursive (context, parts); context ['content'] = figurePage (pagina); context ['PageName'] = path.basename (pagina, path.extname (pagina)); // // Carica i dati della pagina. // if (fileExists (pagina + ".json")) // // Carica il file di dati della pagina e lo aggiunge alla struttura dati. // context = MergeRecursive (context, JSON.parse (fs.readFileSync (pagina + '.json', 'utf8')));  // // Elabora i codici Handlebars. // var template = Handlebars.compile (layout); var html = template (context); // // Elabora tutti gli shortcode. // html = processShortCodes (html); // // Esegui nuovamente i manubri. // template = Handlebars.compile (html); html = template (context); // // Restituisce i risultati. // return (html); 

Il processPage () la funzione ottiene il layout e il percorso dei contenuti della pagina da rendere. La funzione inizia facendo una copia locale di parti variabile globale e aggiungendo l'hashtag "contenuti" con i risultati della chiamata figurePage () funzione. Quindi imposta il PageName valore hash al nome della pagina.

Questa funzione quindi compila i contenuti della pagina sul modello di layout usando Handlebars. Dopo quello, il processShortCodes () la funzione espande tutti gli shortcode definiti sulla pagina. Quindi, il motore di template di Handlebars ripassa il codice ancora una volta. Il browser riceve quindi i risultati.

// // Funzione: processShortCodes // // Descrizione: questa funzione accetta una stringa e // elabora tutti gli shortcode in // la stringa. // // Input: // content Stringa per elaborare // function processShortCodes (content) // // Crea la variabile dei risultati. // var results = ""; // // Trova la prima corrispondenza. // var scregFind = / \ - \ [([^ \]] *) \] \ - / i; var match = scregFind.exec (contenuto); if (match! = null) results + = content.substr (0, match.index); var scregNameArg = /(\w+)(.*)*/i; var parts = scregNameArg.exec (match [1]); if (parts! = null) // // Trova il tag di chiusura. // var scregClose = new RegExp ("\\ - \\ [\\ /" + parts [1] + "\\] \\ -"); var left = content.substr (match.index + 4 + parts [1] .length); var match2 = scregClose.exec (a sinistra); if (match2! = null) // // Elabora il testo shortcode allegato. // var enclosed = processShortCodes (content.substr (match.index + 4 + parts [1] .length, match2.index)); // // Capire se ci fossero argomenti. // var args = ""; if (parts.length == 2) args = parts [2];  // // Esegue lo shortcode. // results + = shortcodes [parts [1]] (args, enclosed); // // Elabora il resto del codice per gli shortcode. // results + = processShortCodes (left.substr (match2.index + 5 + parts [1] .length));  else // // Shortcode non valido. Restituisce la stringa completa. // results = content;  else // // Shortcode non valido. Restituisce la stringa completa. // results = content;  else // // Nessun codice corto trovato. Restituisce la stringa. // results = content;  return (risultati); 

Il processShortCodes () funzione prende il contenuto della pagina web come una stringa e cerca tutti gli shortcode. Uno shortcode è un blocco di codice simile ai tag HTML. Un esempio potrebbe essere:

-[scatola]- 

Questo è dentro una scatola

-[/scatola]-

Questo codice ha uno shortcode per scatola attorno a un paragrafo HTML. Dove HTML utilizza < e >, uso di shortcode -[ e ]-. Dopo il nome, una stringa contenente argomenti per lo shortcode può o non può essere lì.

Il processShortCodes () funzione trova uno shortcode, ottiene il suo nome e gli argomenti, trova la fine per ottenere i contenuti, elabora i contenuti per gli shortcode, esegue lo shortcode con gli argomenti e il contenuto, aggiunge i risultati alla pagina finita e cerca il prossimo shortcode nel resto della pagina. Il ciclo viene eseguito chiamando la funzione in modo ricorsivo.

// // Definisce la matrice di funzioni shortcode. // var shortcodes = 'box': function (args, inside) return ("
"+ dentro +"
");, 'Column1': function (args, inside) return ("
"+ dentro +"
");, 'Column2': function (args, inside) return ("
"+ dentro +"
");, 'Column1of3': function (args, inside) return ("
"+ dentro +"
");, 'Column2of3': function (args, inside) return ("
"+ dentro +"
");, 'Column3of3': function (args, inside) return ("
"+ dentro +"
");, 'php': function (args, inside) return ("
"+ dentro +"
");, 'js': function (args, inside) return ("
"+ dentro +"
");, 'html': function (args, inside) return ("
"+ dentro +"
");, 'css': function (args, inside) return ("
"+ dentro +"
");;

Questa prossima sezione definisce il shortcodes struttura json che definisce il nome di uno shortcode associato alla sua funzione. Tutte le funzioni di shortcode accettano due parametri: args e dentro. Il args è tutto dopo il nome e lo spazio e prima della chiusura del tag. Il dentro è tutto contenuto dai tag shortcode di apertura e chiusura. Queste funzioni sono di base, ma puoi creare uno shortcode per eseguire qualsiasi cosa tu possa pensare in JavaScript.

// // Funzione: figurePage // // Descrizione: Questa funzione illustra il tipo di pagina // e carica i contenuti in modo appropriato // restituendo i contenuti HTML per la pagina. // // Input: // page La pagina per caricare i contenuti. // function figurePage (page) var result = ""; if (fileExists (page + ".html")) // // È un file HTML. Leggi e invialo. // result = fs.readFileSync (page + ".html");  else if (fileExists (page + ".amber")) // // È un file jade. Converti in HTML e invialo. Io // sto ancora usando l'estensione ambra per compatibilità // per goPress. // var jadeFun = jade.compileFile (pagina + ".amber", ); // Rende la funzione var result = jadeFun ();  else if (fileExists (page + ".md")) // // È un file markdown. Converti in HTML e invia // su on. // result = marked (fs.readFileSync (page + ".md"). toString ()); // // Questo annullamento ha contrassegnato la codifica URI delle virgolette. // result = result.replace (/ \ & quot \; / g, "\" "); return (risultato);

Il figurePage () la funzione riceve il percorso completo di una pagina sul server. Questa funzione verifica quindi che sia una pagina HTML, Markdown o Jade basata sull'estensione. Sto ancora usando .amber per Jade dato che era la libreria che ho usato con il server goPress. Tutti i contenuti Markdown e Jade vengono tradotti in HTML prima di trasmetterli alla routine chiamante. Poiché il processore Markdown converte tutte le virgolette in ", Li traduco prima di passarli indietro.

// // Funzione: fileExists // // Descrizione: Questa funzione restituisce un valore booleano true se // il file esiste. Altrimenti, falso. // // Input: // filePath Percorso per un file in una stringa. // function fileExists (filePath) try return fs.statSync (filePath) .isFile ();  catch (err) return false; 

Il il file esiste() la funzione è una sostituzione per il fs.exists () funzione che era una parte del fs libreria di Node.js. Usa il fs.statSync () funzione per cercare di ottenere lo stato del file. Se si verifica un errore, a falso viene restituito. Altrimenti, ritorna vero.

// // Funzione: MergeRecursive // ​​// Descrizione: Unisce ricorsivamente le proprietà di due oggetti // // Input: // obj1 Il primo oggetto da unire // obj2 Il secondo oggetto da unire // function MergeRecursive (obj1, obj2) for (var p in obj2) try // Proprietà nel set di oggetti di destinazione; aggiorna il suo valore. if (obj2 [p] .constructor == Object) obj1 [p] = MergeRecursive (obj1 [p], obj2 [p]);  else obj1 [p] = obj2 [p];  catch (e) // Proprietà nell'oggetto di destinazione non impostato; creala e stabilisci il suo valore. obj1 [p] = obj2 [p];  return obj1; 

L'ultima funzione è il MergeRecursive () funzione. Copia l'oggetto secondo passaggio nel primo oggetto passato. Faccio uso di questo per copiare il principale parti variabile globale in una copia locale prima di aggiungere parti specifiche della pagina.

In esecuzione a livello locale

Dopo aver salvato il file, puoi eseguire il server con:

nodo nodePress.js

In alternativa, puoi usare il npm script che si trova nel file package.json. Si eseguono script npm come questo:

inizio di npm

Questo eseguirà il inizio script che si trova all'interno del file package.json.

Pagina principale di nodePress Server

Indirizza il tuo browser web a http: // localhost: 8080 e vedrai la pagina sopra. Avresti notato che ho aggiunto più codice di test alla pagina principale. Tutte le modifiche alle pagine sono nel download per questo tutorial. Sono per lo più solo alcune piccole modifiche per testare completamente la funzionalità e per adattarsi a qualsiasi differenza dall'utilizzo di librerie diverse. La differenza più notevole è che la libreria Jade non usa $ per nominare le variabili mentre Amber lo fa.

Conclusione

Ora hai esattamente lo stesso file system CMS flat in Go e Node.js. Questo graffia solo la superficie di ciò che puoi costruire con questa piattaforma. Sperimenta e prova qualcosa di nuovo. Questa è la parte migliore della creazione del tuo server web.