Merge branch 'master' into production

This commit is contained in:
Khaled Garbaya
2017-11-08 17:19:25 +01:00
committed by GitHub
17 changed files with 107 additions and 187 deletions

View File

@@ -1,35 +1,40 @@
# The Node.js example app ## The node.js Example App
The Contentful example app, written in node.js.
The node.js Example App aims at getting across the very basics of how to work with our headless content management system and how to build apps using our officially supported JavaScript SDK. Youll learn best practices for using the SDK to deliver content to your app and additionally learn some techniques for modelling your content in Contentful. We hope this app will give you a better understanding of how decoupling content from its presentation enables greater flexibility and facilitates shipping higher quality software more quickly.
Contentful is a content management platform for web applications, mobile apps and connected devices. It allows you to create, edit and manage content in the cloud and publish it anywhere via powerful API. Contentful offers tools for managing editorial teams and enabling cooperation between organizations.
<a href="https://the-example-app-nodejs.herokuapp.com/" target="_blank"><img src="https://images.contentful.com/ft4tkuv7nwl0/31mTpJSgBigeueQyoSGKSg/be3b56dca256d20f5ee530d58bb6d90e/Screen_Shot_2017-11-08_at_15.32.36.png" alt="Screenshot of the example app"/></a>
## Requirements ## Requirements
* Node 8 * Node 8
* Git * Git
Without any changes, this app is connected to a Contentful space that is not publicly accessible. The full end-to-end Contentful experience requires you to clone this space to your own Contentful account, and enables you to see how content editing in the Contentful web app and see those changes propagate to this running application. Signing up and getting started with our free plan is... free!
## Installation You can clone the space for this example app to your own Contentful account by following the instructions [here](https://github.com/contentful/content-models/tree/master/the-example-app/README.md). Once youve created a space, you can change the credentials in the variables.env. If you dont feel like changing code immediately, you can also inject credentials via url parameters like so:
```
https://localhost:3000?space_id=<YOUR_CLONED_SPACE_ID>&delivery_token=<YOUR_DELIVERY_TOKEN>&preview_token=<YOUR_PREVIEW_TOKEN>
```
## Installing the Node.js app
```bash ```bash
git clone https://github.com/contentful/example-contentful-university-js.git git clone https://github.com/contentful/the-example-app.nodejs.git
``` ```
```bash ```bash
npm install npm install
``` ```
## Usage ## Running the Node.js app
``` To start the server, run the following
npm start
```bash
npm run start:dev
``` ```
Open http://localhost:3000/?enable_editorial_features in your browser. Open [https://localhost:3000](https://localhost:3000) and take a look around. If you have configured the app to connect to a space that you own, use [https://localhost:3000?enable_editorial_features](https://localhost:3000?enable_editorial_features). This URL flag adds an “Edit” button in the app on every editable piece of content which will take you back to Contentful web app where you can make changes. It also adds “Draft” and “Pending Changes” status indicators to all content if relevant.
## Deep links
The following deep links are supported:
* `?enable_editorial_features` - Shows `Edit in web app` button on every content type plus `Draft` and `Pending Changes` status pills
* `?space_id=xxx&delivery_token=xxx&preview_token=xxx` - Configure the connected space

View File

@@ -44,3 +44,14 @@ module.exports.translate = (symbol, locale = 'en-US') => {
return translatedValue return translatedValue
} }
/**
* Checks if string is translatable
* @param symbol string Identifier for static text
* @param locale string Locale code
*
* @returns boolean
*/
module.exports.translationAvaliable = (symbol, locale = 'en-US') => {
return !!(translations[locale] || {})[symbol]
}

View File

@@ -73,5 +73,8 @@
"deliveryKeyInvalidLabel": "Ihr Delivery API Zugangsschlüssel ist ungültig.", "deliveryKeyInvalidLabel": "Ihr Delivery API Zugangsschlüssel ist ungültig.",
"spaceOrTokenInvalid": "Dieser Space existiert nicht, oder Ihr Access Token kommt nicht von diesem Space.", "spaceOrTokenInvalid": "Dieser Space existiert nicht, oder Ihr Access Token kommt nicht von diesem Space.",
"somethingWentWrongLabel": "Irgendetwas lief falsch.", "somethingWentWrongLabel": "Irgendetwas lief falsch.",
"previewKeyInvalidLabel": "Ihr Preview API Zugangsschlüssel ist ungültig." "previewKeyInvalidLabel": "Ihr Preview API Zugangsschlüssel ist ungültig.",
"beginnerLabel": "Anfänger",
"intermediateLabel": "Fortgeschrittener",
"advancedLabel": "Profi"
} }

View File

@@ -15,6 +15,7 @@
"logoAlt": "Contentful Example App", "logoAlt": "Contentful Example App",
"homeLabel": "Home", "homeLabel": "Home",
"coursesLabel": "Courses", "coursesLabel": "Courses",
"lessonsLabel": "Lessons",
"footerDisclaimer": "Powered by Contentful. This website and the materials found on it are for demo purposes. You can use this to preview the content created on your Contentful account.", "footerDisclaimer": "Powered by Contentful. This website and the materials found on it are for demo purposes. You can use this to preview the content created on your Contentful account.",
"imprintLabel": "Imprint", "imprintLabel": "Imprint",
"contactUsLabel": "Contact us", "contactUsLabel": "Contact us",
@@ -27,7 +28,7 @@
"pendingChangesLabel": "pending changes", "pendingChangesLabel": "pending changes",
"lessonModuleErrorTitle": "⚠️ Invalid lesson module", "lessonModuleErrorTitle": "⚠️ Invalid lesson module",
"lessonModuleErrorBody": "Could not determine type of", "lessonModuleErrorBody": "Could not determine type of",
"nextLessonLabel": "View next lesson", "nextLessonLabel": "Go to next lesson",
"imageErrorTitle": "⚠️ Image missing", "imageErrorTitle": "⚠️ Image missing",
"viewCourseLabel": "view course", "viewCourseLabel": "view course",
"categoriesWelcomeLabel": "Welcome to", "categoriesWelcomeLabel": "Welcome to",
@@ -73,5 +74,8 @@
"deliveryKeyInvalidLabel": "Your Delivery API key is invalid.", "deliveryKeyInvalidLabel": "Your Delivery API key is invalid.",
"spaceOrTokenInvalid": "This space does not exist or your access token is not associated with your space.", "spaceOrTokenInvalid": "This space does not exist or your access token is not associated with your space.",
"somethingWentWrongLabel": "Something went wrong", "somethingWentWrongLabel": "Something went wrong",
"previewKeyInvalidLabel": "Your Preview API key is invalid." "previewKeyInvalidLabel": "Your Preview API key is invalid.",
"beginnerLabel": "Beginner",
"intermediateLabel": "Intermediate",
"advancedLabel": "Advanced"
} }

View File

@@ -1,5 +1,5 @@
const url = require('url') const url = require('url')
const { translate } = require('../i18n/i18n') const { translate, translationAvaliable } = require('../i18n/i18n')
module.exports = (modifier) => { module.exports = (modifier) => {
return (request, response, next) => { return (request, response, next) => {
@@ -13,17 +13,26 @@ module.exports = (modifier) => {
}) })
// Map components of the path to breadcrumbs with resolvable URLs // Map components of the path to breadcrumbs with resolvable URLs
let mappedComponents = urlComponents.map((component, i, array) => { let mappedComponents = urlComponents.map((component, i, array) => {
const currentLocale = response.locals.currentLocale
const path = array.slice(0, i + 1).join('/') const path = array.slice(0, i + 1).join('/')
let label = component.replace(/-/g, ' ')
if (translationAvaliable(`${label}Label`, currentLocale.code)) {
label = translate(`${label}Label`, currentLocale.code)
}
return { return {
label: component.replace(/-/g, ' '), label: label,
url: url.resolve(baseUrl, path), url: url.resolve(baseUrl, path),
path: path path: path
} }
}) })
breadcrumbs = breadcrumbs.concat(mappedComponents) breadcrumbs = breadcrumbs.concat(mappedComponents)
if (modifier) { if (modifier) {
breadcrumbs = breadcrumbs.map(modifier) breadcrumbs = breadcrumbs.map(modifier)
} }
// Make it global // Make it global
request.app.locals.breadcrumb = breadcrumbs request.app.locals.breadcrumb = breadcrumbs
next() next()

13
lib/enhance-breadcrumb.js Normal file
View File

@@ -0,0 +1,13 @@
module.exports = (request, resource) => {
const breadcrumbs = request.app.locals.breadcrumb
let enhancedBreadcrumbs = breadcrumbs.map((breadcrumb) => {
if (breadcrumb.label.replace(/ /g, '-') == resource.fields.slug) {
breadcrumb.label = resource.fields.title
}
return breadcrumb
})
// We replace the breadcrumbs with the enhanced version
request.app.locals.breadcrumb = enhancedBreadcrumbs
}

View File

@@ -5,6 +5,7 @@
"scripts": { "scripts": {
"start:watch": "nodemon ./bin/www --ignore public/", "start:watch": "nodemon ./bin/www --ignore public/",
"start:dev": "node ./bin/www", "start:dev": "node ./bin/www",
"debug": "node debug ./bin/www",
"start": "NODE_ENV=production node ./bin/www", "start": "NODE_ENV=production node ./bin/www",
"start:production": "NODE_ENV=production node ./bin/www", "start:production": "NODE_ENV=production node ./bin/www",
"lint": "eslint ./app.js routes", "lint": "eslint ./app.js routes",

View File

@@ -1,77 +0,0 @@
{
"defaultTitle": "Die Beispielanwendung",
"whatIsThisApp": "Was ist die Beispielanwendung?",
"metaDescription": "Dies ist die Beispielanwendung, eine Anwendung die Ihnen hilft Ihre eigene Anwendung mit Contentful zu bauen.",
"metaTwitterCard": "Dies ist die Beispielanwendung, eine Anwendung die Ihnen hilft Ihre eigene Anwendung mit Contentful zu bauen.",
"metaImageAlt": "Dies ist die Beispielanwendung, eine Anwendung die Ihnen hilft Ihre eigene Anwendung mit Contentful zu bauen.",
"metaImageDescription": "Dies ist die Beispielanwendung, eine Anwendung die Ihnen hilft Ihre eigene Anwendung mit Contentful zu bauen.",
"viewOnGithub": "Auf GitHub ansehen",
"apiSwitcherHelp": "Ansehen des veröffentlichten und unveröffentlichten Inhalts durch Wechsel von Delivery und Preview APIs.",
"apiLabelHelpcda": "(veröffentlichter Inhalt)",
"apiLabelHelpcpa": "(unveröffentlichter Inhalt)",
"locale": "Sprache",
"localeQuestion": "Sie arbeiten mit verschiedenen Sprachen? Dann können Sie die Sprache für Anfragen an die Content Delivery API definieren.",
"settingsLabel": "Einstellungen",
"logoAlt": "Die Beispielanwendung für Contentful",
"homeLabel": "Home",
"coursesLabel": "Kurse",
"footerDisclaimer": "Powered by Contentful. Diese Website und deren Materialien existieren nur für Demonstrationszwecken. Sie können diese benutzen, um den Inhalt ihres Contentful Kontos anzusehen.",
"imprintLabel": "Impressum",
"contactUsLabel": "Uns Kontaktieren",
"modalTitle": "Ein referenzierbares Beispiel für Entwickler, die Contentful benutzen.",
"modalIntro": "Dies ist die Beispielanwendung, eine Anwendung die Ihnen hilft Ihre eigene Anwendung mit Contentful zu bauen. Sie stellt eine Plattform fürs online Lernen dar, die mithilfe von Contentful gebaut wurde. (sehr meta!)",
"modalCodeIntro": "Wenn Sie es bevorzugen, sich die Hände schmutzig zu machen, sehen sie sich die Anwendung hier an",
"modalCTALabel": "Gut, verstanden.",
"editorialFeaturesHint": "Bearbeiten Sie diesen Eintrag in unserer Web App. Sie müssen sich eingelogged haben und Zugang zum Space haben, um diese Funktion nutzen zu können.",
"draftLabel": "Entwurf",
"pendingChangesLabel": "Änderungen vorbehalten",
"lessonModuleErrorTitle": "⚠️ Ungültiges Lektionsmodul",
"lessonModuleErrorBody": "Konnte den Typ nicht erkennen: ",
"nextLessonLabel": "Nächste Lektion ansehen",
"imageErrorTitle": "⚠️ Bild fehlt",
"viewCourseLabel": "Kurs ansehen",
"categoriesWelcomeLabel": "Willkommen zur folgenden Kategorie: ",
"sitemapWelcomeLabel": "Willkommen zur folgenden Kategorie: ",
"tableOfContentsLabel": "Inhalt",
"courseOverviewLabel": "Kurs Übersicht",
"overviewLabel": "Übersicht",
"durationLabel": "Dauer",
"minutesLabel": "min",
"skillLevelLabel": "Komplexität",
"startCourseLabel": "Kurs beginnen",
"categoriesLabel": "Kategorien",
"allCoursesLabel": "Alle Kurse",
"somethingWentWrongLabel": "Hmm, etwas ging schief.",
"tryLabel": "Versuchen Sie",
"contentModelChangedErrorLabel": "Überprüfen Sie, ob das Content Model verändert wurde.",
"draftOrPublishedErrorLabel": "Überprüfen Sie, ob es nicht veröffentlichte Änderungen gibt.",
"localeContentErrorLabel": "Überprüfen Sie, ob es Inhalt für diese Sprache gibt.",
"verifyCredentialsErrorLabel": "Überprüfen Sie, ob die Zugangsdaten richtig und nicht abgelaufen sind.",
"stackTraceErrorLabel": "Schauen Sie sich den folgenden Stack Trace an",
"errorLabel": "Fehler",
"stackTraceLabel": "Stack Trace",
"companyLabel": "Firma",
"officeLabel": "Büro in Berlin",
"germanyLabel": "Deutschland",
"registrationCourtLabel": "Amtsgericht",
"managingDirectorLabel": "Verwalter",
"vatNumberLabel": "Steuernummer",
"settingsIntroLabel": "Um Inhalt von unseren APIs zu bekommen, müssen Anwendungen von Kunden sich authentifizieren, sowohl mit der Space ID als auch dem Access Token.",
"changesSavedLabel": "Änderungen erfolgreich gespeichert!",
"errorOcurredTitleLabel": "Fehler aufgetreten",
"errorOcurredMessageLabel": "Einige Fehler sind aufgetreten. Bitte schauen Sie sich die Fehlermeldungen neben den Feldern an.",
"connectedToSpaceLabel": "Mit einem Space verbinden.",
"spaceIdLabel": "Space ID",
"spaceIdHelpText": "Die Space Id ist eine eindeutige Identifizierung für Ihren Space.",
"accessTokenLabel": "Access Token",
"contentDeliveryApiHelpText": "Schauen Sie sich veröffentlichten Inhalt mit dieser API an.",
"contentPreviewApiHelpText": "Schauen Sie sich unveröffentlichten Inhalt mit dieser API an. (z.B. Inhalt im Zustand “Entwurf”).",
"enableEditorialFeaturesLabel": "Editoriale Funktionen aktivieren.",
"enableEditorialFeaturesHelpText": "Aktivieren, um Bearbeitung und weitere kontextabhängige Helferlein zu aktivieren. Damit dies funktioniert, müssen sie Zugang zu dem Space haben.",
"saveSettingsButtonLabel": "Einstellungen Speichern",
"fieldIsRequiredLabel": "Diese Feld ist notwendig.",
"deliveryKeyInvalidLabel": "Ihr Delivery API Zugangsschlüssel ist ungültig.",
"spaceOrTokenInvalid": "Dieser Space existiert nicht, oder Ihr Access Token kommt nicht von diesem Space.",
"somethingWentWrongLabel": "Irgendetwas lief falsch.",
"previewKeyInvalidLabel": "Ihr Preview API Zugangsschlüssel ist ungültig."
}

View File

@@ -1,77 +0,0 @@
{
"defaultTitle": "The Example App",
"whatIsThisApp": "What is this example app?",
"metaDescription": "This is The Example App, an application built to serve you as a reference while building your own applications using Contentful.",
"metaTwitterCard": "This is The Example App, an application built to serve you as a reference while building your own applications using Contentful.",
"metaImageAlt": "This is The Example App, an application built to serve you as a reference while building your own applications using Contentful.",
"metaImageDescription": "This is The Example App, an application built to serve you as a reference while building your own applications using Contentful.",
"viewOnGithub": "View on Github",
"apiSwitcherHelp": "View the published or draft content by simply switching between the Deliver and Preview APIs.",
"apiLabelHelpcda": "(published content)",
"apiLabelHelpcpa": "(draft content)",
"locale": "Locale",
"localeQuestion": "Working with multiple languages? You can query the Content Delivery API for a specific locale.",
"settingsLabel": "Settings",
"logoAlt": "Contentful Example App",
"homeLabel": "Home",
"coursesLabel": "Courses",
"footerDisclaimer": "Powered by Contentful. This website and the materials found on it are for demo purposes. You can use this to preview the content created on your Contentful account.",
"imprintLabel": "Imprint",
"contactUsLabel": "Contact us",
"modalTitle": "A referenceable example for developers using Contentful",
"modalIntro": "This is The Example App, an application built to serve you as a reference while building your own applications using Contentful. This app is an online learning platform which teaches you how Contentful was used to build this app (so meta)!",
"modalCodeIntro": "If you prefer to start by getting your hands dirty with code, check out this app on",
"modalCTALabel": "Ok, got it.",
"editorialFeaturesHint": "Edit this entry in our web app. You have to be logged in and have access to the connected space to use this feature.",
"draftLabel": "draft",
"pendingChangesLabel": "pending changes",
"lessonModuleErrorTitle": "⚠️ Invalid lesson module",
"lessonModuleErrorBody": "Could not determine type of",
"nextLessonLabel": "View next lesson",
"imageErrorTitle": "⚠️ Image missing",
"viewCourseLabel": "view course",
"categoriesWelcomeLabel": "Welcome to",
"sitemapWelcomeLabel": "Welcome to",
"tableOfContentsLabel": "Table of contents",
"courseOverviewLabel": "Cource overview",
"overviewLabel": "Overview",
"durationLabel": "Duration",
"minutesLabel": "min",
"skillLevelLabel": "Skill level",
"startCourseLabel": "Start course",
"categoriesLabel": "Categories",
"allCoursesLabel": "All courses",
"somethingWentWrongLabel": "Oops Something went wrong",
"tryLabel": "Try",
"contentModelChangedErrorLabel": "Check if the content model has changed",
"draftOrPublishedErrorLabel": "Check the selection has content in draft or published state (for Preview or Delivery)",
"localeContentErrorLabel": "Check if there's any content for this locale",
"verifyCredentialsErrorLabel": "Verify credentials are correct and up to date",
"stackTraceErrorLabel": "Check the stack trace below",
"errorLabel": "Error",
"stackTraceLabel": "Stack trace",
"companyLabel": "Company",
"officeLabel": "Office Berlin",
"germanyLabel": "Germany",
"registrationCourtLabel": "Registration Court",
"managingDirectorLabel": "Managing Director",
"vatNumberLabel": "VAT Number",
"settingsIntroLabel": "To query and get content using the APIs, client applications need to authenticate with both the Space ID and an access token.",
"changesSavedLabel": "Changes saved successfully!",
"errorOcurredTitleLabel": "Error occurred",
"errorOcurredMessageLabel": "Some errors occurred. Please check the error messages next to the fields.",
"connectedToSpaceLabel": "Connected to space",
"spaceIdLabel": "Space ID",
"spaceIdHelpText": "The Space ID is a unique identifier for your space.",
"accessTokenLabel": "access token",
"contentDeliveryApiHelpText": "View published content using this API.",
"contentPreviewApiHelpText": "Preview unpublished content using this API (i.e. content with “Draft” status).",
"enableEditorialFeaturesLabel": "Enable editorial features",
"enableEditorialFeaturesHelpText": "Enable to display an edit link and other contextual helpers for authors. You need to have access to the connected space to make this work.",
"saveSettingsButtonLabel": "Save settings",
"fieldIsRequiredLabel": "This field is required",
"deliveryKeyInvalidLabel": "Your Delivery API key is invalid.",
"spaceOrTokenInvalid": "This space does not exist or your access token is not associated with your space.",
"somethingWentWrongLabel": "Something went wrong",
"previewKeyInvalidLabel": "Your Preview API key is invalid."
}

View File

@@ -10,6 +10,7 @@ const {
} = require('./../services/contentful') } = require('./../services/contentful')
const attachEntryState = require('../lib/entry-state') const attachEntryState = require('../lib/entry-state')
const enhanceBreadcrumb = require('../lib/enhance-breadcrumb')
const { updateCookie } = require('../lib/cookies') const { updateCookie } = require('../lib/cookies')
const { translate } = require('../i18n/i18n') const { translate } = require('../i18n/i18n')
@@ -69,6 +70,9 @@ module.exports.getCourse = async (request, response, next) => {
course = await attachEntryState(course) course = await attachEntryState(course)
} }
// Enhance the breadcrumbs with the course
enhanceBreadcrumb(request, course)
response.render('course', {title: course.fields.title, course, lesson, lessons, lessonIndex, visitedLessons}) response.render('course', {title: course.fields.title, course, lesson, lessons, lessonIndex, visitedLessons})
} }
@@ -93,6 +97,10 @@ module.exports.getCoursesByCategory = async (request, response, next) => {
} catch (e) { } catch (e) {
console.log('Error ', e) console.log('Error ', e)
} }
// Enhance the breadcrumbs with the active category
enhanceBreadcrumb(request, activeCategory)
response.render('courses', { title: `${activeCategory.fields.title} (${courses.length})`, categories, courses }) response.render('courses', { title: `${activeCategory.fields.title} (${courses.length})`, categories, courses })
} }
@@ -123,6 +131,10 @@ module.exports.getLesson = async (request, response, next) => {
lesson = await attachEntryState(lesson) lesson = await attachEntryState(lesson)
} }
// Enhance the breadcrumbs with the course and active lesson
enhanceBreadcrumb(request, course)
enhanceBreadcrumb(request, lesson)
response.render('course', { response.render('course', {
title: `${course.fields.title} | ${lesson.fields.title}`, title: `${course.fields.title} | ${lesson.fields.title}`,
course, course,

View File

@@ -66,7 +66,7 @@ module.exports.getCourses = assert((locale = 'en-US', api = `cda`) => {
return getClient(api).getEntries({ return getClient(api).getEntries({
content_type: 'course', content_type: 'course',
locale, locale,
order: 'sys.createdAt', // Ordering the entries by creation date order: '-sys.createdAt', // Ordering the entries by creation date
include: 6 // We use include param to increase the link level, the include value goes from 1 to 6 include: 6 // We use include param to increase the link level, the include value goes from 1 to 6
}) })
.then((response) => response.items) .then((response) => response.items)

View File

@@ -2,12 +2,18 @@
const { getCourses, getCourse, getCoursesByCategory, getLesson } = require('../../routes/courses') const { getCourses, getCourse, getCoursesByCategory, getLesson } = require('../../routes/courses')
const { getSettings } = require('../../routes/settings') const { getSettings } = require('../../routes/settings')
const { mockCourse, mockCategory } = require('./mocks/index') const { mockCourse, mockCategory } = require('./mocks/index')
const { translate, initializeTranslations } = require('../../i18n/i18n') const { translate, translationAvaliable, initializeTranslations } = require('../../i18n/i18n')
jest.mock('../../services/contentful') jest.mock('../../services/contentful')
const contentful = require('../../services/contentful') const contentful = require('../../services/contentful')
const request = {} const request = {
app: {
locals: {
breadcrumb: []
}
}
}
const response = { const response = {
locals: { locals: {
settings: { settings: {
@@ -104,4 +110,13 @@ describe('i18n', () => {
test('It returns the translated string when symbol is found on locale file', () => { test('It returns the translated string when symbol is found on locale file', () => {
expect(translate('coursesLabel', 'en-US')).toBe('Courses') expect(translate('coursesLabel', 'en-US')).toBe('Courses')
}) })
test('It returns true if string is found for locale', () => {
expect(translationAvaliable('coursesLabel', 'en-US')).toBe(true)
})
test('It returns false if string is not found for locale', () => {
expect(translationAvaliable('foo-symbol', 'en-US')).toBe(false)
})
test('It returns false if locale is not found', () => {
expect(translationAvaliable('coursesLabel', 'foo-locale')).toBe(false)
})
}) })

View File

@@ -1,6 +1,7 @@
const mockCourse = { const mockCourse = {
sys: { id: 'courseId' }, sys: { id: 'courseId' },
fields: { fields: {
slug: 'courseSlug',
title: 'Course title', title: 'Course title',
lessons: [ lessons: [
{ sys: {id: 'lessonId'}, fields: { slug: 'lessonSlug', title: 'Lesson title' } } { sys: {id: 'lessonId'}, fields: { slug: 'lessonSlug', title: 'Lesson title' } }

View File

@@ -1,5 +1,5 @@
NODE_ENV=development NODE_ENV=development
CONTENTFUL_SPACE_ID=ft4tkuv7nwl0 CONTENTFUL_SPACE_ID=qz0n5cdakyl9
CONTENTFUL_DELIVERY_TOKEN=57459fe48bd2b1bef4855294455af52562dbc0c7f0eb84f8b2cd68692c186417 CONTENTFUL_DELIVERY_TOKEN=580d5944194846b690dd89b630a1cb98a0eef6a19b860ef71efc37ee8076ddb8
CONTENTFUL_PREVIEW_TOKEN=a9972e3cd83528def2fc9d3428c67cd622eb26d0a24239718c6ac61fe0288f2f CONTENTFUL_PREVIEW_TOKEN=e8fc39d9661c7468d0285a7ff949f7a23539dd2e686fcb7bd84dc01b392d698b
PORT=3000 PORT=3000

View File

@@ -38,7 +38,7 @@ block content
.course__overview-item .course__overview-item
svg.course__overview-icon svg.course__overview-icon
use(xlink:href='/icons/icons.svg#skill-level') use(xlink:href='/icons/icons.svg#skill-level')
.course__overview-value #{translate('skillLevelLabel', currentLocale.code)}: #{course.fields.skillLevel} .course__overview-value #{translate('skillLevelLabel', currentLocale.code)}: #{translate(`${course.fields.skillLevel}Label`, currentLocale.code)}
.course__overview-cta-wrapper .course__overview-cta-wrapper
a.course__overview-cta.cta(href=`/courses/${course.fields.slug}/lessons/${course.fields.lessons[0].fields.slug}${queryString}`) #{translate('startCourseLabel', currentLocale.code)} a.course__overview-cta.cta(href=`/courses/${course.fields.slug}/lessons/${course.fields.lessons[0].fields.slug}${queryString}`) #{translate('startCourseLabel', currentLocale.code)}
.course__description !{helpers.markdown(course.fields.description)} .course__description !{helpers.markdown(course.fields.description)}

View File

@@ -108,7 +108,7 @@ html
| .&nbsp; | .&nbsp;
a(href=`/imprint${queryString}` ) #{translate('imprintLabel', currentLocale.code)} a(href=`/imprint${queryString}` ) #{translate('imprintLabel', currentLocale.code)}
| .&nbsp; | .&nbsp;
a(href=`https://www.contentful.com/contact/` ) #{translate('contactUsLabel', currentLocale.code)} a(href=`https://www.contentful.com/contact/` target='_blank') #{translate('contactUsLabel', currentLocale.code)}
| . | .
.footer__social .footer__social
p p
@@ -118,7 +118,7 @@ html
a(href='https://twitter.com/contentful' target='_blank' rel='noopener') a(href='https://twitter.com/contentful' target='_blank' rel='noopener')
svg svg
use(xlink:href='/icons/icons.svg#twitter') use(xlink:href='/icons/icons.svg#twitter')
a(href='https://images.contentful.com/82t39nctsu20/1JOkYZnY8YG0w88ImweME2/c8aef71dfe1ea633e16e17d99379416c/Github-repo_2x__1_.png' target='_blank' rel='noopener') a(href='https://github.com/contentful' target='_blank' rel='noopener')
svg svg
use(xlink:href='/icons/icons.svg#github') use(xlink:href='/icons/icons.svg#github')
section.modal#about-this-modal section.modal#about-this-modal
@@ -129,7 +129,7 @@ html
p #{translate('modalIntro', currentLocale.code)} p #{translate('modalIntro', currentLocale.code)}
p p
| #{translate('modalCodeIntro', currentLocale.code)}&nbsp; | #{translate('modalCodeIntro', currentLocale.code)}&nbsp;
a(href='https://images.contentful.com/82t39nctsu20/1JOkYZnY8YG0w88ImweME2/c8aef71dfe1ea633e16e17d99379416c/Github-repo_2x__1_.png' target='_blank' rel='noopener') Github a(href='https://github.com/contentful/the-example-app.nodejs' target='_blank' rel='noopener') Github
| . | .
.modal__cta-wrapper .modal__cta-wrapper
a(href='#').modal__cta.close #{translate('modalCTALabel', currentLocale.code)} a(href='#').modal__cta.close #{translate('modalCTALabel', currentLocale.code)}

View File

@@ -2,11 +2,11 @@ mixin lessonModuleCodeSnippet(module)
.lesson-module.lesson-module-code .lesson-module.lesson-module-code
.lesson-module-code__header .lesson-module-code__header
if module.fields.curl if module.fields.curl
a.lesson-module-code__trigger(data-target=`${module.sys.id}-curl`) curl a.lesson-module-code__trigger(data-target=`${module.sys.id}-curl`) cURL
if module.fields.dotNet if module.fields.dotNet
a.lesson-module-code__trigger(data-target=`${module.sys.id}-dotnet`) .NET a.lesson-module-code__trigger(data-target=`${module.sys.id}-dotnet`) .NET
if module.fields.javascript if module.fields.javascript
a.lesson-module-code__trigger.lesson-module-code__trigger--active(data-target=`${module.sys.id}-javascript`) Javascript a.lesson-module-code__trigger.lesson-module-code__trigger--active(data-target=`${module.sys.id}-javascript`) JavaScript
if module.fields.java if module.fields.java
a.lesson-module-code__trigger(data-target=`${module.sys.id}-java`) Java a.lesson-module-code__trigger(data-target=`${module.sys.id}-java`) Java
if module.fields.javaAndroid if module.fields.javaAndroid