Issues (791)

src/cli/cms/media/image.js (9 issues)

1
import execPromise from 'child-process-promise'
2
import mkdirp from 'mkdirp'
3
import fse from 'fs-extra'
4
import limax from 'limax'
5
import Jimp from 'jimp'
6
import path from 'path'
7
import {Promise} from 'bluebird'
8
9
import {
10
  abeExtend,
11
  coreUtils,
12
  cmsData,
13
  config
14
} from '../../'
15
16
export function cropAndSaveFile(imageSize, file, newFile) {
17
  var p = new Promise((resolve) => {
18
    Jimp.read(file).then(function (lenna) {
19
      lenna.crop(0, 0, parseInt(imageSize[0]), parseInt(imageSize[1])).write(newFile)
20
    }).catch(function (err) {
21
      console.error(err)
22
    })
23
    resolve()
24
  })
25
  return p
26
}
27
28
export function smartCropAndSaveFile(imageSize, file, newFile) {
29
  var cmd = `node node_modules/smartcrop-cli/smartcrop-cli.js --width ${parseInt(imageSize[0])} --height ${parseInt(imageSize[1])} ${file} ${newFile}`
30
  var p = execPromise.exec(cmd)
31
  return p
32
}
33
34
export function cropAndSaveFiles(images, file, resp) {
35
  var length = images.length
36
  var cropedImage = 0
37
  resp.thumbs = []
38
  var p = new Promise((resolve) => {
39
    for (var i = 0; i < length; i++) {
40
      var image = images[i]
41
      var ext = path.extname(file)
42
      var newFile = file.replace(ext, `_${images[i]}${ext}`)
43
      resp.thumbs.push({
44
        name: newFile.replace(path.join(config.root, config.publish.url), ''),
45
        size: image
46
      })
47
      smartCropAndSaveFile(image.split('x'), file, newFile)
48
        .then(function (result) {
49
          if(result.stderr) {
50
            cropAndSaveFile(image.split('x'), file, newFile).then(function () {
0 ignored issues
show
The variable image is changed as part of the for loop for example by images.i on line 40. Only the value of the last iteration will be visible in this function if it is called after the loop.
Loading history...
The variable newFile is changed as part of the for loop for example by file.replace(ext, `_${images.i}${ext}`) on line 42. Only the value of the last iteration will be visible in this function if it is called after the loop.
Loading history...
51
              if(++cropedImage === length) {
52
                resolve(resp)
53
              }
54
            })
55
          }
56
          else if(++cropedImage === length) {
57
            resolve(resp)
58
          }
59
        })
60
        .catch(function (err) {
61
          console.log(err)
0 ignored issues
show
console.log looks like debug code. Are you sure you do not want to remove it?
Loading history...
62
        })
63
    }
64
  })
65
66
  return p
67
}
68
69
export function generateThumbnail(file) {
70
  var ext = path.extname(file).toLowerCase()
71
  var thumbFileName = file.replace(ext, `_thumb${ext}`)
72
  var thumbFileNameRelative = thumbFileName.replace(path.join(config.root, config.publish.url), '')
73
  var p = new Promise((resolve) => {
74
    var cropThumb = smartCropAndSaveFile([250, 250], file, thumbFileName)
75
    cropThumb.then(function (result) {
76
      var stderr = result.stderr
77
      if(stderr) {
78
        cropAndSaveFile([250, 250], file, thumbFileName).then(function () {
79
          resolve({thumb: thumbFileNameRelative})
80
        })
81
      }
82
      else resolve({thumb: thumbFileNameRelative})
0 ignored issues
show
Coding Style Best Practice introduced by
Curly braces around statements make for more readable code and help prevent bugs when you add further statements.

Consider adding curly braces around all statements when they are executed conditionally. This is optional if there is only one statement, but leaving them out can lead to unexpected behaviour if another statement is added later.

Consider:

if (a > 0)
    b = 42;

If you or someone else later decides to put another statement in, only the first statement will be executed.

if (a > 0)
    console.log("a > 0");
    b = 42;

In this case the statement b = 42 will always be executed, while the logging statement will be executed conditionally.

if (a > 0) {
    console.log("a > 0");
    b = 42;
}

ensures that the proper code will be executed conditionally no matter how many statements are added or removed.

Loading history...
83
    })
84
  })
85
86
  return p
87
}
88
89
export function saveFile(req) {
90
  var p = new Promise((resolve) => {
91
    var resp = {success: 1}
92
    var filePath
93
    req.pipe(req.busboy)
94
    req.busboy.on('file', function (fieldname, file, filename, encoding, mimetype) {
95
      var ext = path.extname(filename).toLowerCase()
96
      var slug = createMediaSlug(filename, ext)
97
      var mediaType = getMediaType(ext)
98
99
      var folderFilePath = createMediaFolder(mediaType)
100
      var hasSentHeader = false
101
      var folderWebPath = '/' + config.upload.image
102
      if(config.upload[mediaType] != null){
103
        folderWebPath = '/' + config.upload[mediaType]  
104
      }
105
      
106
      filePath = path.join(folderFilePath, slug)
107
      resp['filePath'] = path.join('/' + folderWebPath, slug)
108
109
      file.on('limit', function() {
110
        hasSentHeader = true
111
        file.resume()
112
        resolve({error: 1, response: 'file is too big'})
113
        return
0 ignored issues
show
This return has no effect and can be removed.
Loading history...
114
      })
115
116
      var isValid = isValidMedia(mimetype, ext)
117
      if(isValid.error){
118
        hasSentHeader = true
119
        file.resume()
120
        resolve({error: 1, response: isValid.error})
121
        return
122
      }
123
124
      var fstream = fse.createWriteStream(filePath)
125
      fstream.on('finish', function() {
126
        if(hasSentHeader) return
0 ignored issues
show
Coding Style Best Practice introduced by
Curly braces around statements make for more readable code and help prevent bugs when you add further statements.

Consider adding curly braces around all statements when they are executed conditionally. This is optional if there is only one statement, but leaving them out can lead to unexpected behaviour if another statement is added later.

Consider:

if (a > 0)
    b = 42;

If you or someone else later decides to put another statement in, only the first statement will be executed.

if (a > 0)
    console.log("a > 0");
    b = 42;

In this case the statement b = 42 will always be executed, while the logging statement will be executed conditionally.

if (a > 0) {
    console.log("a > 0");
    b = 42;
}

ensures that the proper code will be executed conditionally no matter how many statements are added or removed.

Loading history...
127
128
        resp = abeExtend.hooks.instance.trigger('afterSaveImage', resp, req)
129
130
        if(mediaType === 'image') {
131
          var thumbPromise = generateThumbnail(filePath)
132
          thumbPromise.then(function (thumbResp) {
133
            resp.thumbnail = thumbResp.thumb
134
            if(req.query.input.indexOf('data-size') > -1){
135
              var thumbsSizes = cmsData.regex.getAttr(req.query.input, 'data-size').split(',')
136
              cropAndSaveFiles(thumbsSizes, filePath, resp).then(function (resp) {
137
                resolve(resp)
138
              })
139
            }
140
            else resolve(resp)
0 ignored issues
show
Coding Style Best Practice introduced by
Curly braces around statements make for more readable code and help prevent bugs when you add further statements.

Consider adding curly braces around all statements when they are executed conditionally. This is optional if there is only one statement, but leaving them out can lead to unexpected behaviour if another statement is added later.

Consider:

if (a > 0)
    b = 42;

If you or someone else later decides to put another statement in, only the first statement will be executed.

if (a > 0)
    console.log("a > 0");
    b = 42;

In this case the statement b = 42 will always be executed, while the logging statement will be executed conditionally.

if (a > 0) {
    console.log("a > 0");
    b = 42;
}

ensures that the proper code will be executed conditionally no matter how many statements are added or removed.

Loading history...
141
          })
142
        } else {
143
          resolve(resp)
144
        }
145
      })
146
      file.pipe(fstream)
147
    })
148
  })
149
150
  return p
151
}
152
153
export function isValidMedia(mimetype, ext) {
154
  var allowedExtensions = config.upload.extensions
155
  var allowedMimetypes = config.upload.mimetypes
156
157
  var error = false
158
  if (allowedMimetypes.indexOf(mimetype) < 0) error = 'unauthorized file'
0 ignored issues
show
Coding Style Best Practice introduced by
Curly braces around statements make for more readable code and help prevent bugs when you add further statements.

Consider adding curly braces around all statements when they are executed conditionally. This is optional if there is only one statement, but leaving them out can lead to unexpected behaviour if another statement is added later.

Consider:

if (a > 0)
    b = 42;

If you or someone else later decides to put another statement in, only the first statement will be executed.

if (a > 0)
    console.log("a > 0");
    b = 42;

In this case the statement b = 42 will always be executed, while the logging statement will be executed conditionally.

if (a > 0) {
    console.log("a > 0");
    b = 42;
}

ensures that the proper code will be executed conditionally no matter how many statements are added or removed.

Loading history...
159
  else if (allowedExtensions.indexOf(ext) < 0) error = 'not a valid asset'
0 ignored issues
show
Coding Style Best Practice introduced by
Curly braces around statements make for more readable code and help prevent bugs when you add further statements.

Consider adding curly braces around all statements when they are executed conditionally. This is optional if there is only one statement, but leaving them out can lead to unexpected behaviour if another statement is added later.

Consider:

if (a > 0)
    b = 42;

If you or someone else later decides to put another statement in, only the first statement will be executed.

if (a > 0)
    console.log("a > 0");
    b = 42;

In this case the statement b = 42 will always be executed, while the logging statement will be executed conditionally.

if (a > 0) {
    console.log("a > 0");
    b = 42;
}

ensures that the proper code will be executed conditionally no matter how many statements are added or removed.

Loading history...
160
161
  return {error: error}
162
}
163
164
export function getMediaType(ext) {
165
  let type = 'document'
166
167
  if(/\.(jpg|jpeg|png|gif|svg)/.test(ext)){
168
    type = 'image'
169
  } else if(/\.(mov|avi|mp4)/.test(ext)) {
170
    type = 'video'
171
  } else if(/\.(mp3|wav)/.test(ext)){
172
    type = 'sound'
173
  }
174
175
  return type
176
}
177
178
export function createMediaSlug(filename, ext) {
179
  var filenameNoExt = path.basename(filename, ext)
180
  return limax(filenameNoExt, {separateNumbers: false}) + '-' + coreUtils.random.generateUniqueIdentifier(2) + ext
181
}
182
183
export function createMediaFolder(mediaType) {
184
  var folderWebPath = '/' + config.upload.image
185
  if(config.upload[mediaType] != null){
186
    folderWebPath = '/' + config.upload[mediaType]  
187
  }
188
  folderWebPath = abeExtend.hooks.instance.trigger('beforeSaveImage', folderWebPath)
189
  var folderFilePath = path.join(config.root, config.publish.url, folderWebPath)
190
  mkdirp.sync(folderFilePath)
191
192
  return folderFilePath
193
}
194
195
export function getThumbsList() {
196
  var thumbsList = []
197
  var pathToThumbs = path.join(config.root, config.publish.url, config.upload.image)
198
  var files = coreUtils.file.getFilesSync(pathToThumbs, true)
199
  Array.prototype.forEach.call(files, (pathFile) => {
200
    pathFile = pathFile.replace(path.join(config.root, config.publish.url), '')
201
    if(pathFile.indexOf('_thumb.') > -1){
202
      thumbsList.push({
203
        originalFile: pathFile.replace('_thumb.', '.'),
204
        thumbFile: pathFile
205
      })
206
    }
207
  })
208
209
  return thumbsList
210
}
211
212
export function getAssociatedImageFileFromThumb(name) {
213
  var rexMatchImageName = /_(thumb|\d+x\d+)\./
214
  name = path.join(path.sep, name)
215
  var originalName = path.join(path.sep, name.replace(rexMatchImageName, '.'))
216
  var imageList = {
217
    thumbFile: name,
218
    originalFile: originalName,
219
    thumbs: []
220
  }
221
  var pathThumb = name.split('/')
222
  pathThumb.pop()
223
  pathThumb = path.join(config.root, config.publish.url, pathThumb.join('/'))
224
225
  var files = coreUtils.file.getFilesSync(pathThumb, true)
226
  Array.prototype.forEach.call(files, (pathFile) => {
227
    pathFile = pathFile.replace(path.join(config.root, config.publish.url), '')
228
    if(pathFile !== originalName && pathFile !== name && pathFile.replace(rexMatchImageName, '.') === originalName){
229
      imageList.thumbs.push(pathFile)
230
    }
231
  })
232
233
  return imageList
234
}
235