Construire un CMS nodePress

Vous avez créé avec succès un système de fichiers à plat système de gestion de contenu (CMS) avec Go. L'étape suivante consiste à adopter le même idéal et à créer un serveur Web à l'aide de Node.js. Je vais vous montrer comment charger les bibliothèques, créer le serveur et exécuter le serveur..

Ce système de gestion de contenu utilisera la structure de données du site décrite dans le premier didacticiel, Création d’un système de gestion de contenu: structure et style. Par conséquent, téléchargez et installez cette structure de base dans un nouveau répertoire..

Obtenir un nœud et les bibliothèques de nœuds

Le moyen le plus simple d'installer Node.js sur un Mac consiste à utiliser Homebrew. Si vous n'avez pas encore installé Homebrew, le tutoriel Homebrew Demystified: le gestionnaire de paquets ultime d'OS X vous montrera comment.

Pour installer Node.js avec Homebrew, tapez cette instruction dans un terminal:

brassez le noeud d'installation

Ceci fait, les commandes node et npm seront entièrement installées sur votre Mac. Pour toutes les autres plates-formes, suivez les instructions sur le site Web de Node.js..

Attention: de nombreux gestionnaires de paquets sont en train d'installer Node.js version 0.10. Ce tutoriel suppose que vous avez la version 5.3 ou plus récente. Vous pouvez vérifier votre version en tapant:

noeud --version

le nœud La commande exécute l'interpréteur JavaScript. le npm commande est un gestionnaire de paquets pour Node.js qui permet d'installer de nouvelles bibliothèques, de créer de nouveaux projets et d'exécuter des scripts pour un projet. Il existe de nombreux tutoriels et cours sur Node.js et NPM chez Envato Tuts+.

Pour installer les bibliothèques du serveur Web, vous devez exécuter ces commandes dans le programme Terminal.app ou iTerm.app:

npm install express --save guidon d’installation npm --save moment d’installation npm --save installation npm marqué --save npm install jade --save npm install morgan --save

Express est une plateforme de développement d'applications Web. C'est semblable à la bibliothèque goWeb dans Go. Handlebars est le moteur de gabarit permettant de créer les pages. Moment est une bibliothèque pour travailler avec des dates. Marked est un excellent convertisseur Markdown to HTML en JavaScript. Jade est un langage abrégé HTML permettant de créer facilement du code HTML. Morgan est une bibliothèque de middleware pour Express qui génère les fichiers journaux Apache Standard..

Une autre méthode pour installer les bibliothèques consiste à télécharger les fichiers source de ce didacticiel. Une fois téléchargé et décompressé, tapez ceci dans le répertoire principal:

npm --install

Cela installera tout le nécessaire pour créer ce projet.

nodePress.js

Vous pouvez maintenant commencer à créer le serveur. Dans le répertoire supérieur du projet, créez un fichier nommé nodePress.js, ouvrez-le dans l'éditeur de votre choix et commencez à ajouter le code suivant. Je vais expliquer le code tel qu'il est placé dans le fichier.

// // Charge les bibliothèques utilisées. // var fs = require ('fs'); var path = require ("chemin"); var child_process = require ('child_process'); var process = require ('process'); var express = require ('express'); // http://expressjs.com/fr/ var morgan = require ('morgan'); // https://github.com/expressjs/morgan var Handlebars = require ("handlebars"); // http://handlebarsjs.com/ var moment = require ("moment"); // http://momentjs.com/ var marquée = require ('marquée'); // https://github.com/chjj/marked var jade = require ('jade'); // http://jade-lang.com/

Le code serveur commence par l'initialisation de toutes les bibliothèques utilisées pour créer le serveur. Les bibliothèques qui n'ont pas de commentaire avec une adresse Web sont des bibliothèques internes Node.js.

// // Configuration des variables globales. // 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;

Ensuite, je configure toutes les variables globales et les configurations de bibliothèque. L'utilisation de variables globales n'est pas la meilleure pratique de conception logicielle, mais cela fonctionne et permet un développement rapide.

le les pièces variable est un tableau de hachage contenant toutes les parties d'une page Web. Chaque page fait référence au contenu de cette variable. Il commence par le contenu du fichier server.json situé en haut du répertoire du serveur..

J'utilise ensuite les informations du fichier server.json pour créer les chemins complets du modes et mises en page annuaires utilisés pour ce site.

Trois variables sont ensuite définies sur des valeurs nulles: siteCSS, scripts de site, et page d'accueil. Ces variables globales contiendront tout le contenu CSS, JavaScripts et le contenu principal de la page d'index. Ces trois éléments sont les éléments les plus demandés sur n’importe quel serveur Web. Par conséquent, les garder en mémoire vous fait gagner du temps. Si la Cache variable dans le fichier server.json est false, ces éléments sont relus à chaque requête.

marquée.setOptions (rendu: nouvelle marquée.Rendueur (), gfm: vrai, tables: vrai, sauts: faux, pédant: faux, assaini: faux, smartListes: vrai, smartypants: faux);

Ce bloc de code sert à configurer la bibliothèque Marked pour générer du HTML à partir de Markdown. La plupart du temps, j'active le support de table et 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'); // // Lire dans les parties de page. // var partFiles = fs.readdirSync (parts ['Sitebase'] + "parts /"); partFiles.forEach (function (ele, index, array) parts [path.basename (ele, path.extname (ele))]] = figurePage (parts ['Sitebase'] + "parts /" + chemin.basename (ele, path.extname (ele))););

le les pièces variable est en outre chargé avec les pièces de la modes et disposition des répertoires. Chaque fichier dans le les pièces répertoire à l'intérieur du site répertoire est également chargé dans le les pièces variable globale. Le nom du fichier sans extension est le nom utilisé pour stocker le contenu du fichier. Ces noms sont développés dans la macro Guidons..

// // Aide du guidon. // // // HandleBars Helper: save // ​​// Description: Cet utilitaire attend un // """"où le nom // est enregistré avec la valeur pour les // extensions futures. Il renvoie également la valeur // directement. // Handlebars.registerHelper (" save ", fonction (nom, texte) // // Variables locales. // var newName = "", newText = ""; // // Vérifiez si le nom et le texte figurent dans le premier argument // avec un |. Si c'est le cas, extrayez-les correctement. Sinon, // utilisez le nom et le texte arguments tels que donnés. // if (name.indexOf ("|")> 0) var parts = name.split ("|"); newName = parts [0]; newText = parts [1]; else newName = name; newText = text; // // Enregistrez le nouvel assistant. // Handlebars.registerHelper (newName, function () return newText;); // // renvoie le texte. // return newText;) ; // // HandleBars Helper: date // // Description: Cet utilitaire renvoie la date // en fonction du format fourni. // Handlebars.registerHelper ("date", fonction (dFormat) return moment (). Format ( dFormat);); // // HandleBars Helper: cdate // // Description: Cet assistant retourne la date donnée // dans un format basé sur le format // donné. // Handlebars.registerHelper ("cdate", fonction (cTime, dFormat) return moment (cTime) .format (dFormat););

La section de code suivante définit les helpers Handlebars que j'ai définis pour une utilisation sur le serveur Web: enregistrer, rendez-vous amoureux, et cdate. L’aide à la sauvegarde permet la création de variables dans une page. Cette version prend en charge la version de goPress où le paramètre a le nom et la valeur séparés par un “|”. Vous pouvez également spécifier une sauvegarde en utilisant deux paramètres. Par exemple:

save "name | Richard Guay" save "newName" "Richard Guay" Le nom est: name newName est: newName

Cela produira les mêmes résultats. Je préfère la seconde approche, mais la bibliothèque Handlebars de Go ne permet pas plus d’un paramètre..

le rendez-vous amoureux et cdate les aides formater la date actuelle (rendez-vous amoureux) ou une date donnée (cdate) selon le moment.js règles de formatage de la bibliothèque. le cdate helper s'attend à ce que la date à rendre soit le premier paramètre et ait le format ISO 8601.

// // Crée et configure le serveur. // var nodePress = express (); // // Configure le middleware. // nodePress.use (morgan ('combiné'))

Désormais, le code crée une instance Express pour configurer le moteur de serveur réel. le nodePress.use () fonction configure le logiciel middleware. Un middleware est un code qui est servi à chaque appel sur le serveur. Ici, j'ai configuré la bibliothèque Morgan.js pour créer la sortie de journal du serveur appropriée.

// // Définit les itinéraires. // nodePress.get ('/', fonction (demande, réponse) setBasicHeader (réponse); if ((parts ["Cache"] == true) && (mainPage! = null)) response.send (mainPage) ; else mainPage = page ("main"); response.send (mainPage);); nodePress.get ('/ favicon.ico', fonction (demande, réponse) var options = racine: parties ['Sitebase']] + 'images /', fichiers dot: 'deny', en-têtes: 'x-timestamp' : Date.now (), 'x-senti': true, response.set ("Content-Type", "image / ico"); setBasicHeader (réponse); response.sendFile ('favicon.ico', options , function (err) if (err) console.log (err); response.status (err.status) .end (); else console.log ('Favicon a été envoyé:', 'favicon.ico' ););); nodePress.get ('/ stylesheets.css', fonction (demande, réponse) response.set ("Content-Type", "text / css"); setBasicHeader (réponse); response.type ("css"); if ((parts ["Cache"] == true) && (siteCSS! = null)) response.send (siteCSS); else siteCSS = fs.readFileSync (parts ['Sitebase'] + 'css / final / final .css '); response.send (site CSS);); nodePress.get ('/ scripts.js', fonction (demande, réponse) response.set ("Content-Type", "text / javascript"); setBasicHeader (réponse); 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', fonction (demande, réponse) var options = racine: parties ['Sitebase']] + 'images /', fichiers dot: 'deny', en-têtes: 'x-timestamp ': Date.now (),' x-sent ': true; response.set ("Content-Type", "image /" + chemin.extname (request.params.image) .substr (1)); setBasicHeader (response); response.sendFile (request.params.image, options, fonction (err) if (err) console.log (err); response.status (err.status) .end (); else  console.log ('L'image a été envoyée:', request.params.image););); nodePress.get ('/ posts / blogs /: blog', fonction (demande, réponse) setBasicHeader (réponse); response.send (post ("blogs", request.params.blog, "index"));) ; nodePress.get ('/ posts / blogs /: blog /: post', fonction (requête, réponse) setBasicHeader (réponse); response.send (post ("blogs", request.params.blog, request.params.post ));); nodePress.get ('/ posts / news /: news', fonction (requête, réponse) setBasicHeader (response); response.send (post ("news", request.params.news, "index")) ; nodePress.get ('/ posts / news /: news /: post', fonction (requête, réponse) setBasicHeader (réponse); response.send (post ("news", request.params.news, request.params.post ));); nodePress.get ('/: page', fonction (demande, réponse) setBasicHeader (réponse); response.send (page (request.params.page)););

Cette section de code définit toutes les routes nécessaires à la mise en œuvre du serveur Web. Tous les itinéraires passent par setBasicHeader () fonction pour définir les valeurs d'en-tête appropriées. Toutes les demandes pour un type de page évoquent les page() fonction, alors que toutes les demandes de type de poste évoquent le des postes() une fonction.

La valeur par défaut pour Type de contenu est HTML. Par conséquent, pour CSS, JavaScript et les images, la Type de contenu est explicitement mis à sa valeur appropriée.

Vous pouvez également définir des itinéraires avec le mettre, effacer, et poster REST verbes. Ce serveur simple utilise uniquement le obtenir verbe.

// // Démarrer le serveur. // var addressItems = parts ['ServerAddress']. split (':'); var server = nodePress.listen (addressItems [2], function () hôte var = server.address (). address; var port = server.address (). port; console.log ('nodePress écoute à l'adresse http: / /% s:% s ', hôte, port););

La dernière chose à faire avant de définir les différentes fonctions utilisées est de démarrer le serveur. Le fichier server.json contient le nom DNS (ici, c’est localhost) et le port du serveur. Une fois analysé, le serveur écouter () fonction utilise le numéro de port pour démarrer le serveur. Une fois le port du serveur ouvert, le script enregistre l'adresse et le port du serveur..

// // Fonction: setBasicHeader // // Description: cette fonction définira les informations de base de l'en-tête // nécessaires. // // Entrées: // réponse Objet de réponse // fonction setBasicHeader (réponse) response.append ("Cache-Control", "max-age = 2592000, cache"); response.append ("Serveur", "nodePress - un CMS écrit dans un nœud à partir de Custom Computer Tools: http://customct.com."); 

La première fonction définie est la setBasicHeader () une fonction. Cette fonction définit l'en-tête de réponse pour indiquer au navigateur de mettre la page en cache pendant un mois. Il indique également au navigateur que le serveur est un serveur nodePress. Si vous souhaitez utiliser d'autres valeurs d'en-tête standard, vous devez les ajouter ici avec response.append () une fonction.

// // Fonction: page // // Description: Cette fonction traite une requête de page // // Entrées: // page La page demandée // fonction page (page) // // Traite la page donnée à l'aide de la norme disposition. // return (processPage (parts ["layout"], parts ['Sitebase']] + "pages /" + page))); 

le page() fonction envoie le gabarit de présentation d’une page et l’emplacement de la page sur le serveur au processPage () une fonction.

// // Fonction: post // // Description: cette fonction traite une demande de publication // // Entrées: // type Le type de publication. // chat La catégorie de la publication. // post Le post demandé // fonction post (type, cat, post) // // Traite le post en fonction du type et du nom du post. // return (processPage (parts ["layout"], parts ['Sitebase']] + "posts /" + type + "/" + cat + "/" + post)); 

le poster() la fonction est comme le page() fonction, sauf que les articles ont plus d'éléments pour définir chaque article. Dans cette série de serveurs, un message contient un type, catégorie, et le réel poster. Le type est soit blogs ou nouvelles. La catégorie est flatcms. Comme ils représentent des noms de répertoire, vous pouvez les créer comme vous le souhaitez. Faites juste correspondre le nom à ce qui est dans votre système de fichiers.

// // Fonction: processPage // // Description: Cette fonction traite une page pour le CMS. // // Entrées: // layout La disposition à utiliser pour la page. // page Chemin d'accès à la page à rendre. // function processPage (layout, page) // // Récupère le contenu de la page et l'ajoute à la disposition. // var context = ; context = MergeRecursive (contexte, parties); context ['content'] = figurePage (page); context ['Nom de la page'] = chemin.nom_basé (page, chemin.nom_extrait (page)); // // Charge les données de la page. // if (fileExists (page + ".json")) // // Chargez le fichier de données de la page et ajoutez-le à la structure de données. // context = MergeRecursive (context, JSON.parse (fs.readFileSync (page + '.json', 'utf8')));  // // Traiter les codes du guidon. // var template = Handlebars.compile (layout); var html = template (context); // // Traite tous les codes courts. // html = processShortCodes (html); // // Répète le guidon. // template = Handlebars.compile (html); html = template (contexte); // // Retourne les résultats. // return (html); 

le processPage () function récupère la mise en page et le chemin d'accès au contenu de la page à rendre. La fonction commence par créer une copie locale de la les pièces variable globale et en ajoutant le hashtag «contenu» avec les résultats de l'appel figurePage () une fonction. Il définit ensuite le Nom de la page valeur de hachage au nom de la page.

Cette fonction compile ensuite le contenu de la page dans le modèle de présentation à l’aide des Guidons. Après cela, le processShortCodes () La fonction étendra tous les codes courts définis sur la page. Ensuite, le moteur de gabarit Handlebars parcourt à nouveau le code. Le navigateur reçoit alors les résultats.

// // Fonction: processShortCodes // // Description: cette fonction prend une chaîne et // traite tous les codes abrégés de la chaîne //. // // Entrées: // contenu Chaîne à traiter // fonction processShortCodes (content) // // Crée la variable de résultat. // var results = ""; // // trouve la première correspondance. // var scregFind = / \ - \ [([^ \]] *) \] \ - / i; var match = scregFind.exec (contenu); if (match! = null) résultats + = content.substr (0, match.index); var scregNameArg = /(\w+)(.*)*/i; var parts = scregNameArg.exec (match [1]); if (parts! = null) // // Trouve la balise de fermeture. // var scregClose = new RegExp ("\\ - \\ [\\ /" + parts [1] + "\\] \\ -"); var left = content.substr (match.index + 4 + parts [1] .length); var match2 = scregClose.exec (à gauche); if (match2! = null) // // Traite le texte du shortcode inclus. // var inclus = processShortCodes (content.substr (match.index + 4 + parties [1] .length, match2.index)); // // Déterminez s'il y a des arguments. // var args = ""; if (parts.length == 2) args = parts [2];  // // Exécuter le shortcode. // résultats + = shortcodes [parts [1]] (arguments, inclus); // // Traite le reste du code pour les codes courts. // résultats + = processShortCodes (left.substr (match2.index + 5 + parties [1] .length));  else // // shortcode invalide. Retourne la chaîne complète. // résultats = contenu;  else // // shortcode invalide. Retourne la chaîne complète. // résultats = contenu;  else // // Aucun code court trouvé. Renvoie la chaîne. // résultats = contenu;  return (résultats); 

le processShortCodes () function prend le contenu de la page Web sous forme de chaîne et recherche tous les codes courts. Un shortcode est un bloc de code similaire aux balises HTML. Un exemple serait:

-[boîte]- 

Ceci est à l'intérieur d'une boîte

-[/boîte]-

Ce code a un shortcode pour boîte autour d'un paragraphe HTML. Où HTML utilise < et >, utilisation des codes courts -[ et ]-. Après le nom, une chaîne contenant des arguments pour le shortcode peut ou ne peut pas être là.

le processShortCodes () function trouve un shortcode, obtient son nom et ses arguments, trouve la fin pour obtenir le contenu, traite le contenu des shortcodes, exécute le shortcode avec les arguments et le contenu, ajoute les résultats à la page finie et recherche le prochain shortcode dans le reste de la page. Le bouclage est effectué en appelant la fonction de manière récursive.

// // Définit le tableau de fonctions de codes courts. // var shortcodes = 'box': function (args, inside) return ("
"+ dedans +"
");, 'Column1': function (args, inside) return ("
"+ dedans +"
");, 'Column2': function (args, inside) return ("
"+ dedans +"
");, 'Column1of3': function (arguments, inside) return ("
"+ dedans +"
");, 'Column2of3': function (arguments, inside) return ("
"+ dedans +"
");, 'Column3of3': function (arguments, inside) return ("
"+ dedans +"
");, 'php': function (args, inside) return ("
"+ dedans +"
");, 'js': function (args, inside) return ("
"+ dedans +"
");, 'html': function (args, inside) return ("
"+ dedans +"
");, 'css': function (args, inside) return ("
"+ dedans +"
");;

Cette section suivante définit la codes courts Structure json qui définit le nom d'un shortcode associé à sa fonction. Toutes les fonctions de shortcode acceptent deux paramètres: args et à l'intérieur. le args est tout après le nom et l'espace et avant la fermeture de la balise. le à l'intérieur est tout ce qui est contenu dans les balises shortcode d’ouverture et de fermeture. Ces fonctions sont basiques, mais vous pouvez créer un shortcode pour exécuter tout ce que vous pouvez penser en JavaScript..

// // Fonction: figurePage // // Description: cette fonction définit le type de page // et charge le contenu de manière appropriée // en renvoyant le contenu HTML de la page. // // Entrées: // page La page pour charger le contenu. // fonction figurePage (page) var result = ""; if (fileExists (page + ".html")) // // C'est un fichier HTML. Lisez-le et envoyez-le. // resultat = fs.readFileSync (page + ".html");  else if (fileExists (page + ".amber")) // // C'est un fichier jade. Convertir en HTML et l'envoyer. J'utilise toujours l'extension orange pour la compatibilité // pour goPress. // var jadeFun = jade.compileFile (page + ".amber", ); // Rendre la fonction var result = jadeFun ();  else if (fileExists (page + ".md")) // // C'est un fichier de démarques. Convertissez au format HTML et envoyez-le //. // résultat = marqué (fs.readFileSync (page + ".md"). toString ()); // // Le codage URI des guillemets de cette annotation annotée. // resultat = result.replace (/ \ & quot \; / g, "\" "); return (result);

le figurePage () function reçoit le chemin complet d'une page sur le serveur. Cette fonction teste ensuite s'il s'agit d'une page HTML, Markdown ou Jade basée sur l'extension. J'utilise toujours .amber pour Jade car c'est la bibliothèque que j'ai utilisée avec le serveur goPress. Tous les contenus de Markdown et Jade sont traduits en HTML avant de les transmettre à la routine d'appel. Depuis que le processeur Markdown traduit toutes les citations en ", Je les traduis avant de le renvoyer.

// // Fonction: fileExists // // Description: Cette fonction renvoie un booléen true si // le fichier existe. Sinon, faux. // // Entrées: // chemin_fichier Chemin d'accès à un fichier dans une chaîne. // fonction fileExists (filePath) try return fs.statSync (filePath) .isFile ();  catch (err) return false; 

le le fichier existe() la fonction remplace le fs.exists () fonction qui faisait partie de la fs bibliothèque de Node.js. Il utilise le fs.statSync () fonction pour essayer d'obtenir le statut du fichier. Si une erreur survient, un faux est retourné. Sinon, ça retourne vrai.

// // Fonction: MergeRecursive // ​​// Description: Fusion récursive des propriétés de deux objets // // Entrées: // obj1 Premier objet à fusionner // obj2 Deuxième objet à fusionner // fonction MergeRecursive (obj1, obj2) for (var p dans obj2) try // Propriété dans le jeu d'objets de destination; mettre à jour sa valeur. if (obj2 [p] .constructor == Object) obj1 [p] = MergeRecursive (obj1 [p], obj2 [p]);  else obj1 [p] = obj2 [p];  catch (e) // La propriété dans l'objet de destination n'est pas définie; créez-le et définissez sa valeur. obj1 [p] = obj2 [p];  return obj1; 

La dernière fonction est la FusionnerRécursif () une fonction. Il copie le second objet de transfert dans le premier objet transmis. Je me sers de cela pour copier le texte principal les pièces variable globale dans une copie locale avant d'ajouter des pièces spécifiques à la page.

Courir localement

Après avoir enregistré le fichier, vous pouvez exécuter le serveur avec:

noeud nodePress.js

Alternativement, vous pouvez utiliser le npm script qui se trouve dans le fichier package.json. Vous exécutez les scripts npm comme ceci:

npm start

Cela lancera le début script qui se trouve dans le fichier package.json.

Page principale de nodePress Server

Pointez votre navigateur Web sur http: // localhost: 8080 et vous verrez la page ci-dessus. Vous avez peut-être remarqué que j'ai ajouté plus de code de test à la page principale. Toutes les modifications apportées aux pages sont dans le téléchargement pour ce tutoriel. Ce ne sont généralement que quelques modifications mineures permettant de tester plus complètement les fonctionnalités et d'adapter toute différence d'utilisation de bibliothèques différentes. La différence la plus notable est que la bibliothèque Jade n’utilise pas $ nommer des variables tandis qu'Ambre le fait.

Conclusion

Maintenant, vous avez exactement le même système de fichiers à plat CMS dans Go et Node.js. Cela ne fait qu'effleurer la surface de ce que vous pouvez construire avec cette plate-forme. Expérimentez et essayez quelque chose de nouveau. C’est la meilleure partie de la création de votre propre serveur Web..