feat: History page - List previous commits
This commit is contained in:
		@@ -1,5 +1,4 @@
 | 
			
		||||
client/js
 | 
			
		||||
client/scss
 | 
			
		||||
client
 | 
			
		||||
coverage
 | 
			
		||||
data
 | 
			
		||||
logs
 | 
			
		||||
 
 | 
			
		||||
@@ -19,7 +19,7 @@ $(() => {
 | 
			
		||||
    offset: -50
 | 
			
		||||
  })
 | 
			
		||||
 | 
			
		||||
  $('.stickyscroll').sticky({ topSpacing: 15 })
 | 
			
		||||
  $('.stickyscroll').sticky({ topSpacing: 15, bottomSpacing: 75 })
 | 
			
		||||
 | 
			
		||||
  // ====================================
 | 
			
		||||
  // Notifications
 | 
			
		||||
@@ -58,5 +58,6 @@ $(() => {
 | 
			
		||||
  require('./pages/create.js')(alerts, socket)
 | 
			
		||||
  require('./pages/edit.js')(alerts, socket)
 | 
			
		||||
  require('./pages/source.js')(alerts)
 | 
			
		||||
  require('./pages/history.js')(alerts)
 | 
			
		||||
  require('./pages/admin.js')(alerts)
 | 
			
		||||
})
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										11
									
								
								client/js/pages/history.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										11
									
								
								client/js/pages/history.js
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,11 @@
 | 
			
		||||
'use strict'
 | 
			
		||||
 | 
			
		||||
import $ from 'jquery'
 | 
			
		||||
 | 
			
		||||
module.exports = (alerts) => {
 | 
			
		||||
  if ($('#page-type-history').length) {
 | 
			
		||||
    let currentBasePath = ($('#page-type-history').data('entrypath') !== 'home') ? $('#page-type-history').data('entrypath') : ''
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
@@ -45,6 +45,21 @@
 | 
			
		||||
					transition: all .4s ease;
 | 
			
		||||
					line-height: 14px;
 | 
			
		||||
 | 
			
		||||
          &.is-multiline {
 | 
			
		||||
            flex-wrap: wrap;
 | 
			
		||||
          }
 | 
			
		||||
 | 
			
		||||
          &.is-active {
 | 
			
		||||
            border-left: 5px solid mc('blue', '500');
 | 
			
		||||
            color: mc('blue', '300');
 | 
			
		||||
            padding-left: 15px;
 | 
			
		||||
 | 
			
		||||
            .is-small {
 | 
			
		||||
              color: mc('blue', '500');
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
          }
 | 
			
		||||
 | 
			
		||||
					i {
 | 
			
		||||
						margin-right: 7px;
 | 
			
		||||
						color: mc('blue-grey', '300');
 | 
			
		||||
@@ -55,6 +70,13 @@
 | 
			
		||||
						text-decoration: none;
 | 
			
		||||
					}
 | 
			
		||||
 | 
			
		||||
          .is-small {
 | 
			
		||||
            flex: 1 0 100%;
 | 
			
		||||
            display: block;
 | 
			
		||||
            font-size: 11px;
 | 
			
		||||
            color: rgba(255,255,255,.5)
 | 
			
		||||
          }
 | 
			
		||||
 | 
			
		||||
				}
 | 
			
		||||
 | 
			
		||||
				> ul {
 | 
			
		||||
 
 | 
			
		||||
@@ -179,6 +179,27 @@ router.get('/source/*', (req, res, next) => {
 | 
			
		||||
  })
 | 
			
		||||
})
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * View history of a document
 | 
			
		||||
 */
 | 
			
		||||
router.get('/hist/*', (req, res, next) => {
 | 
			
		||||
  let safePath = entryHelper.parsePath(_.replace(req.path, '/hist', ''))
 | 
			
		||||
 | 
			
		||||
  entries.getHistory(safePath).then((pageData) => {
 | 
			
		||||
    if (pageData) {
 | 
			
		||||
      res.render('pages/history', { pageData })
 | 
			
		||||
    } else {
 | 
			
		||||
      throw new Error('Invalid page path.')
 | 
			
		||||
    }
 | 
			
		||||
    return true
 | 
			
		||||
  }).catch((err) => {
 | 
			
		||||
    res.render('error', {
 | 
			
		||||
      message: err.message,
 | 
			
		||||
      error: {}
 | 
			
		||||
    })
 | 
			
		||||
  })
 | 
			
		||||
})
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * View document
 | 
			
		||||
 */
 | 
			
		||||
 
 | 
			
		||||
@@ -402,5 +402,17 @@ module.exports = {
 | 
			
		||||
        return rights.checkRole('/' + r._id, usr.rights, 'read')
 | 
			
		||||
      })
 | 
			
		||||
    })
 | 
			
		||||
  },
 | 
			
		||||
 | 
			
		||||
  getHistory (entryPath) {
 | 
			
		||||
    return db.Entry.findOne({ _id: entryPath, isEntry: true }).then(entry => {
 | 
			
		||||
      if (!entry) { return false }
 | 
			
		||||
      return git.getHistory(entryPath).then(history => {
 | 
			
		||||
        return {
 | 
			
		||||
          meta: entry,
 | 
			
		||||
          history
 | 
			
		||||
        }
 | 
			
		||||
      })
 | 
			
		||||
    })
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -253,6 +253,30 @@ module.exports = {
 | 
			
		||||
        if (_.includes(err.stdout, 'nothing to commit')) { return true }
 | 
			
		||||
      })
 | 
			
		||||
    })
 | 
			
		||||
  },
 | 
			
		||||
 | 
			
		||||
  getHistory (entryPath) {
 | 
			
		||||
    let self = this
 | 
			
		||||
    let gitFilePath = entryPath + '.md'
 | 
			
		||||
 | 
			
		||||
    return self._git.exec('log', ['-n', '25', '--format=format:%H %h %cI %cE %cN', '--', gitFilePath]).then((cProc) => {
 | 
			
		||||
      let out = cProc.stdout.toString()
 | 
			
		||||
      if (_.includes(out, 'fatal')) {
 | 
			
		||||
        let errorMsg = _.capitalize(_.head(_.split(_.replace(out, 'fatal: ', ''), ',')))
 | 
			
		||||
        throw new Error(errorMsg)
 | 
			
		||||
      }
 | 
			
		||||
      let hist = _.chain(out).split('\n').map(h => {
 | 
			
		||||
        let hParts = h.split(' ', 4)
 | 
			
		||||
        return {
 | 
			
		||||
          commit: hParts[0],
 | 
			
		||||
          commitAbbr: hParts[1],
 | 
			
		||||
          date: hParts[2],
 | 
			
		||||
          email: hParts[3],
 | 
			
		||||
          name: hParts[4]
 | 
			
		||||
        }
 | 
			
		||||
      }).value()
 | 
			
		||||
      return hist
 | 
			
		||||
    })
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -7,6 +7,7 @@ block adminContent
 | 
			
		||||
      h2.subtitle Manage site configuration
 | 
			
		||||
    .form-sections
 | 
			
		||||
      section
 | 
			
		||||
        img(src='/images/logo.png', style={width:'200px', float:'right'})
 | 
			
		||||
        label.label System Version
 | 
			
		||||
        .section-block
 | 
			
		||||
          p Current Version: #[strong= sysversion.current]
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										35
									
								
								server/views/pages/history.pug
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										35
									
								
								server/views/pages/history.pug
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,35 @@
 | 
			
		||||
extends ../layout.pug
 | 
			
		||||
 | 
			
		||||
block rootNavRight
 | 
			
		||||
  i.nav-item#notifload
 | 
			
		||||
  .nav-item
 | 
			
		||||
    a.button(href='/' + pageData.meta._id)
 | 
			
		||||
      i.icon-circle-check
 | 
			
		||||
      span View Latest
 | 
			
		||||
 | 
			
		||||
block content
 | 
			
		||||
 | 
			
		||||
  #page-type-history.page-type-container(data-entrypath=pageData.meta._id)
 | 
			
		||||
    .container.is-fluid.has-mkcontent
 | 
			
		||||
      .columns.is-gapless
 | 
			
		||||
 | 
			
		||||
        .column.is-narrow.is-hidden-touch.sidebar
 | 
			
		||||
 | 
			
		||||
          aside.stickyscroll
 | 
			
		||||
            .sidebar-label
 | 
			
		||||
              span Past versions
 | 
			
		||||
            ul.sidebar-menu
 | 
			
		||||
              each item, index in pageData.history
 | 
			
		||||
                - var itemDate = moment(item.date)
 | 
			
		||||
                li: a.is-multiline(class={ 'is-active': index < 1 }, href='', title=itemDate.format('LLLL'))
 | 
			
		||||
                  span= itemDate.calendar(null, { sameElse: 'llll'})
 | 
			
		||||
                  span.is-small= item.commitAbbr
 | 
			
		||||
 | 
			
		||||
        .column
 | 
			
		||||
 | 
			
		||||
          .hero
 | 
			
		||||
            h1.title#title= pageData.meta.title
 | 
			
		||||
            if pageData.meta.subtitle
 | 
			
		||||
              h2.subtitle= pageData.meta.subtitle
 | 
			
		||||
          .content.mkcontent
 | 
			
		||||
            != pageData.html
 | 
			
		||||
@@ -18,9 +18,9 @@ block rootNavRight
 | 
			
		||||
    a.button.is-outlined(href='/source/' + pageData.meta.path)
 | 
			
		||||
      i.icon-loader
 | 
			
		||||
      span Source
 | 
			
		||||
    a.button.is-outlined(href='/diff/' + pageData.meta.path)
 | 
			
		||||
      i.icon-flow-merge
 | 
			
		||||
      span Diff
 | 
			
		||||
    a.button.is-outlined(href='/hist/' + pageData.meta.path)
 | 
			
		||||
      i.icon-clock
 | 
			
		||||
      span History
 | 
			
		||||
    if rights.write
 | 
			
		||||
      a.button(href='/edit/' + pageData.meta.path)
 | 
			
		||||
        i.icon-document-text
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user