From c811244c6424dedd15ad0ef2d2cfd4752f374ff6 Mon Sep 17 00:00:00 2001 From: Rodrigo Nascimento Date: Thu, 25 Jun 2015 18:28:40 -0300 Subject: [PATCH 01/33] Create method to load url and extract headers and meta tags --- packages/rocketchat-oembed/package.js | 1 + packages/rocketchat-oembed/server.coffee | 82 ++++++++++++++++++++++++ 2 files changed, 83 insertions(+) create mode 100644 packages/rocketchat-oembed/server.coffee diff --git a/packages/rocketchat-oembed/package.js b/packages/rocketchat-oembed/package.js index 1c3cbaf36269..ada2123d07b5 100644 --- a/packages/rocketchat-oembed/package.js +++ b/packages/rocketchat-oembed/package.js @@ -15,6 +15,7 @@ Package.onUse(function(api) { ]); api.addFiles('oembed.coffee', ['server','client']); + api.addFiles('server.coffee', ['server']); }); Package.onTest(function(api) { diff --git a/packages/rocketchat-oembed/server.coffee b/packages/rocketchat-oembed/server.coffee new file mode 100644 index 000000000000..253b95d194c5 --- /dev/null +++ b/packages/rocketchat-oembed/server.coffee @@ -0,0 +1,82 @@ +URL = Npm.require('url') +http = Npm.require('http') +https = Npm.require('https') +querystring = Npm.require('querystring') + +getUrlContent = (urlObj, redirectCount = 5, callback) -> + opts = + method: 'GET' + port: urlObj.port + hostname: urlObj.hostname + path: urlObj.path + + httpOrHttps = if urlObj.protocol is 'https:' then https else http + + request = httpOrHttps.request opts, (response) -> + if response.statusCode is 301 and response.headers.location? + request.abort() + console.log response.headers.location + + if redirectCount <= 0 + return callback null, null + + getUrlContent response.headers.location, --redirectCount, callback + return + + if response.statusCode isnt 200 + return callback null, null + + str = '' + response.on 'data', (chunk) -> + str += chunk + if str.length > 250000 + request.abort() + + response.on 'end', -> + callback null, { + headers: response.headers + body: str + } + + request.end() + +getUrlMeta = (url, withFragment) -> + getUrlContentSync = Meteor.wrapAsync getUrlContent + + urlObj = URL.parse url + + if withFragment? + queryStringObj = querystring.parse urlObj.query + queryStringObj._escaped_fragment_ = '' + urlObj.query = querystring.stringify queryStringObj + + path = urlObj.pathname + if urlObj.query? + path += '?' + urlObj.query + + urlObj.path = path + + content = getUrlContentSync urlObj, 5 + + metas = {} + + content?.body.replace //gmi, (meta, name, value) -> + metas[name] = value + + return { + meta: metas + headers: content?.headers + } + + +Meteor.methods + getUrlMeta: (url) -> + data = getUrlMeta url + + if data.meta.fragment is '!' + data = getUrlMeta url, true + + console.log data.meta + console.log data.headers + + return data \ No newline at end of file From 545698f6078bc2f3ab29267ad49df5b740e0e438 Mon Sep 17 00:00:00 2001 From: Rodrigo Nascimento Date: Thu, 25 Jun 2015 19:51:53 -0300 Subject: [PATCH 02/33] Process message and update url with more info --- client/lib/collections.coffee | 5 +- packages/rocketchat-oembed/package.js | 2 +- packages/rocketchat-oembed/server.coffee | 63 ++++++++++++++++++++---- 3 files changed, 59 insertions(+), 11 deletions(-) diff --git a/client/lib/collections.coffee b/client/lib/collections.coffee index 1b5c116c1a2e..1b7196da9e85 100644 --- a/client/lib/collections.coffee +++ b/client/lib/collections.coffee @@ -12,4 +12,7 @@ Meteor.startup -> ChatMessageHistory.insert record changed: (record) -> - ChatMessageHistory.update {_id: record._id, msg: {$ne: record.msg}}, record + _id = record._id + delete record._id + + ChatMessageHistory.update { _id: _id }, { $set: record } diff --git a/packages/rocketchat-oembed/package.js b/packages/rocketchat-oembed/package.js index ada2123d07b5..ce5fa7329852 100644 --- a/packages/rocketchat-oembed/package.js +++ b/packages/rocketchat-oembed/package.js @@ -14,8 +14,8 @@ Package.onUse(function(api) { 'rocketchat:lib@0.0.1' ]); - api.addFiles('oembed.coffee', ['server','client']); api.addFiles('server.coffee', ['server']); + api.export('OEmbed', ['server']); }); Package.onTest(function(api) { diff --git a/packages/rocketchat-oembed/server.coffee b/packages/rocketchat-oembed/server.coffee index 253b95d194c5..ae794fc37d27 100644 --- a/packages/rocketchat-oembed/server.coffee +++ b/packages/rocketchat-oembed/server.coffee @@ -3,7 +3,12 @@ http = Npm.require('http') https = Npm.require('https') querystring = Npm.require('querystring') +OEmbed = {} + getUrlContent = (urlObj, redirectCount = 5, callback) -> + if _.isString(urlObj) + urlObj = URL.parse urlObj + opts = method: 'GET' port: urlObj.port @@ -36,11 +41,12 @@ getUrlContent = (urlObj, redirectCount = 5, callback) -> callback null, { headers: response.headers body: str + parsedUrl: _.pick urlObj, ['host', 'hash', 'pathname', 'protocol', 'port', 'query'] } request.end() -getUrlMeta = (url, withFragment) -> +OEmbed.getUrlMeta = (url, withFragment) -> getUrlContentSync = Meteor.wrapAsync getUrlContent urlObj = URL.parse url @@ -63,20 +69,59 @@ getUrlMeta = (url, withFragment) -> content?.body.replace //gmi, (meta, name, value) -> metas[name] = value + if metas.fragment is '!' and not withFragment? + return OEmbed.getUrlMeta url, true + return { meta: metas headers: content?.headers + parsedUrl: content.parsedUrl } +getRelevantHeaders = (headersObj) -> + headers = {} + for key, value of headersObj + key = key.toLowerCase() + if key in ['content-type', 'content-length'] and value?.trim() isnt '' + headers[key.replace(/\./g, '-')] = value + + if Object.keys(headers).length > 0 + return headers + return + +getRelevantMetaTags = (metaObj) -> + tags = {} + for key, value of metaObj + key = key.toLowerCase() + if /^(og|fb|twitter):.+|description$/.test(key) and value?.trim() isnt '' + tags[key.replace(/\./g, '-')] = value + + if Object.keys(tags).length > 0 + return tags + return + +RocketUrlParser = (message) -> + if Array.isArray message.urls + for item in message.urls + data = OEmbed.getUrlMeta item.url + + if data?.meta? + item.meta = getRelevantMetaTags data.meta + + if data?.headers? + item.headers = getRelevantHeaders data.headers + + item.parsedUrl = data.parsedUrl + + ChatMessage.update {_id: message._id}, { $set: { urls: message.urls } } -Meteor.methods - getUrlMeta: (url) -> - data = getUrlMeta url +RocketChat.callbacks.add 'afterSaveMessage', RocketUrlParser, RocketChat.callbacks.priority.LOW - if data.meta.fragment is '!' - data = getUrlMeta url, true +# Meteor.methods +# getUrlMeta: (url) -> +# data = OEmbed.getUrlMeta url - console.log data.meta - console.log data.headers +# console.log data.meta +# console.log data.headers - return data \ No newline at end of file +# return data \ No newline at end of file From e85f46236d075bd1ea95664ee222d5a4fc6e5fcf Mon Sep 17 00:00:00 2001 From: Rodrigo Nascimento Date: Thu, 25 Jun 2015 19:52:23 -0300 Subject: [PATCH 03/33] Change urls of messages from Array of String to Array of Objects --- server/methods/sendMessage.coffee | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/server/methods/sendMessage.coffee b/server/methods/sendMessage.coffee index 74de6bb8eddd..0def48e734e4 100644 --- a/server/methods/sendMessage.coffee +++ b/server/methods/sendMessage.coffee @@ -15,7 +15,7 @@ Meteor.methods message.u = Meteor.users.findOne Meteor.userId(), fields: username: 1 if urls = message.msg.match /([A-Za-z]{3,9}):\/\/([-;:&=\+\$,\w]+@{1})?([-A-Za-z0-9\.]+)+:?(\d+)?((\/[-\+=!:~%\/\.@\,\w]+)?\??([-\+=&!:;%@\/\.\,\w]+)?#?([\w]+)?)?/g - message.urls = urls + message.urls = urls.map (url) -> url: url message = RocketChat.callbacks.run 'beforeSaveMessage', message From 12aff6327ecc77bb019b0d8208413cf6cb914644 Mon Sep 17 00:00:00 2001 From: Rodrigo Nascimento Date: Thu, 25 Jun 2015 19:52:52 -0300 Subject: [PATCH 04/33] Change sendMessage to user insert and delete instead upsert to pass correct _id to aferSaveMessage --- server/methods/sendMessage.coffee | 16 ++++++---------- 1 file changed, 6 insertions(+), 10 deletions(-) diff --git a/server/methods/sendMessage.coffee b/server/methods/sendMessage.coffee index 0def48e734e4..2e3f304c4a0c 100644 --- a/server/methods/sendMessage.coffee +++ b/server/methods/sendMessage.coffee @@ -107,19 +107,15 @@ Meteor.methods ### Save the message. If there was already a typing record, update it. ### - ChatMessage.upsert - rid: message.rid - t: 't' - $and: [{ 'u._id': message.u._id }] - , - $set: message - $unset: - t: 1 - expireAt: 1 + message._id = ChatMessage.insert message Meteor.defer -> + ChatMessage.remove + rid: message.rid + t: 't' + 'u._id': message.u._id - message._id = Random.id() + Meteor.defer -> RocketChat.callbacks.run 'afterSaveMessage', message From bac59beeffccc1655a1344b339af44ff45f54662 Mon Sep 17 00:00:00 2001 From: Rodrigo Nascimento Date: Thu, 25 Jun 2015 21:10:32 -0300 Subject: [PATCH 05/33] Remove iframely --- .meteor/versions | 1 - packages/rocketchat-oembed/oembed.coffee | 41 ------------------------ 2 files changed, 42 deletions(-) delete mode 100644 packages/rocketchat-oembed/oembed.coffee diff --git a/.meteor/versions b/.meteor/versions index 9da48a853e8b..a5bbd3186447 100644 --- a/.meteor/versions +++ b/.meteor/versions @@ -32,7 +32,6 @@ html-tools@1.0.4 htmljs@1.0.4 http@1.1.0 id-map@1.0.3 -iframely:oembed@0.0.2 iron:controller@1.0.8 iron:core@1.0.8 iron:dynamic-template@1.0.8 diff --git a/packages/rocketchat-oembed/oembed.coffee b/packages/rocketchat-oembed/oembed.coffee deleted file mode 100644 index 010fbeb94ffc..000000000000 --- a/packages/rocketchat-oembed/oembed.coffee +++ /dev/null @@ -1,41 +0,0 @@ -### -# OEmbed is a temporary image and map embedder for bots development -# @param {Object} msg - The message object -# to be replaced by proper implementation in 1.0 -### - -# IframelyOembed.setEndpoint 'http://open.iframe.ly/api/oembed?api_key=' + Meteor.settings.public?.iframelyApiKey? -IframelyOembed.setCacheOptions - cacheTTL: 1000 * 60 * 60, # Hour. - cacheErrorTTL: 1000 * 60, # Minute. - cacheEnabled: true - -class OEmbed - constructor: (message) -> - console.log "OEmbed constructor" if window.rocketDebug - - message.urls?.forEach (url) -> - console.log "OEmbed iframely.oembed", url if window.rocketDebug - Meteor.call 'iframely.oembed', url, (error, data) => - console.log "OEmbed iframely.oembed callback", error, data if window.rocketDebug - if _.trim data.html - message.html = message.html + data.html - - # if _.trim message.msg - - # picmatch = message.msg.match(/^https?:\/\/(?:[a-z0-9\-]+\.)+[a-z0-9]{2,6}(?:\/[^\/#?]+)+\.(?:jpe?g|gif|png)$/i) - # if picmatch? - # # inline style to limit code pollution - # console.log "OEmbed picmatch" if window.rocketDebug - # message.html = "" - - # else - # mapmatch = message.msg.match(/^https?\:\/\/maps\.(google|googleapis)\.[a-z]+\/maps\/api.*format=png$/i) - # if mapmatch? - # console.log "OEmbed mapmatch" if window.rocketDebug - # message.html = "" - - return message - - -# RocketChat.callbacks.add 'renderMessage', OEmbed, RocketChat.callbacks.priority.HIGH From a93968e0b2af95eead6b13a75cc2a3e613a31b00 Mon Sep 17 00:00:00 2001 From: Rodrigo Nascimento Date: Thu, 25 Jun 2015 21:11:04 -0300 Subject: [PATCH 06/33] Move oembed server file --- packages/rocketchat-oembed/{ => server}/server.coffee | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename packages/rocketchat-oembed/{ => server}/server.coffee (100%) diff --git a/packages/rocketchat-oembed/server.coffee b/packages/rocketchat-oembed/server/server.coffee similarity index 100% rename from packages/rocketchat-oembed/server.coffee rename to packages/rocketchat-oembed/server/server.coffee From d9b937f2fc780ae04f4c9bc0e4d45a20b0321ca5 Mon Sep 17 00:00:00 2001 From: Rodrigo Nascimento Date: Thu, 25 Jun 2015 21:11:47 -0300 Subject: [PATCH 07/33] Implement oembed renders and improve quotes --- client/stylesheets/base.less | 7 ++++--- client/views/app/message.coffee | 12 +++++------- packages/rocketchat-markdown/markdown.coffee | 3 ++- .../rocketchat-oembed/client/baseWidget.coffee | 10 ++++++++++ .../rocketchat-oembed/client/baseWidget.html | 3 +++ .../client/oembedImageWidget.html | 9 +++++++++ .../client/oembedUrlWidget.coffee | 7 +++++++ .../client/oembedUrlWidget.html | 11 +++++++++++ .../client/oembedYoutubeWidget.coffee | 3 +++ .../client/oembedYoutubeWidget.html | 9 +++++++++ packages/rocketchat-oembed/package.js | 18 +++++++++++++++--- 11 files changed, 78 insertions(+), 14 deletions(-) create mode 100644 packages/rocketchat-oembed/client/baseWidget.coffee create mode 100644 packages/rocketchat-oembed/client/baseWidget.html create mode 100644 packages/rocketchat-oembed/client/oembedImageWidget.html create mode 100644 packages/rocketchat-oembed/client/oembedUrlWidget.coffee create mode 100644 packages/rocketchat-oembed/client/oembedUrlWidget.html create mode 100644 packages/rocketchat-oembed/client/oembedYoutubeWidget.coffee create mode 100644 packages/rocketchat-oembed/client/oembedYoutubeWidget.html diff --git a/client/stylesheets/base.less b/client/stylesheets/base.less index fe290e2c6ec6..cb855fc0606b 100644 --- a/client/stylesheets/base.less +++ b/client/stylesheets/base.less @@ -66,15 +66,16 @@ pre { width: 100%; } -q { +blockquote { padding-left: 10px; + position: relative; &:before { content: ' '; background-color: #ccc; - height: 22px; + height: 100%; width: 3px; position: absolute; - left: 50px; + left: 0px; } } diff --git a/client/views/app/message.coffee b/client/views/app/message.coffee index 1f9ccc26c13a..cc380773772c 100644 --- a/client/views/app/message.coffee +++ b/client/views/app/message.coffee @@ -54,13 +54,11 @@ Template.message.onViewRendered = (context) -> wrapper = ul.parentElement if context.urls?.length > 0 - for url in context.urls - do (url) -> - Meteor.call 'iframely.oembed', url, (error, data) -> - height = lastNode.clientHeight - - urlNode = lastNode.querySelector('.body a[href="'+url+'"]') - urlNode?.innerHTML = Blaze.toHTMLWithData Template.iframelyBaseWidget, data + for item in context.urls + do (item) -> + urlNode = lastNode.querySelector('.body a[href="'+item.url+'"]') + if urlNode? + $(urlNode).replaceWith Blaze.toHTMLWithData Template.oembedBaseWidget, item if not lastNode.nextElementSibling? if lastNode.classList.contains('own') is true diff --git a/packages/rocketchat-markdown/markdown.coffee b/packages/rocketchat-markdown/markdown.coffee index 5694fd2ab2de..76ae02cb0f72 100644 --- a/packages/rocketchat-markdown/markdown.coffee +++ b/packages/rocketchat-markdown/markdown.coffee @@ -15,7 +15,8 @@ class Markdown msg = msg.replace(/(\ |^)\_([^_]+)\_(\ |$)/gm, '$1_$2_$3') msg = msg.replace(/(\ |^)\`([^`]+)\`(\ |$)/gm, '$1`$2`$3') msg = msg.replace(/(\ |^)\~{1,2}([^~]+)\~{1,2}(\ |$)/gm, '$1~$2~$3') - msg = msg.replace(/^>(.*)$/gm, '>$1') + msg = msg.replace(/^>(.*)$/gm, '
>$1
') + msg = msg.replace(/<\/blockquote>\n
/gm, '
') message.html = msg diff --git a/packages/rocketchat-oembed/client/baseWidget.coffee b/packages/rocketchat-oembed/client/baseWidget.coffee new file mode 100644 index 000000000000..eaceb2244fc4 --- /dev/null +++ b/packages/rocketchat-oembed/client/baseWidget.coffee @@ -0,0 +1,10 @@ +Template.oembedBaseWidget.helpers + template: -> + console.log this + if this.headers?['content-type']?.match(/image\/.*/)? + return 'oembedImageWidget' + + if this.parsedUrl?.host is 'www.youtube.com' + return 'oembedYoutubeWidget' + + return 'oembedUrlWidget' \ No newline at end of file diff --git a/packages/rocketchat-oembed/client/baseWidget.html b/packages/rocketchat-oembed/client/baseWidget.html new file mode 100644 index 000000000000..13a68dabe38a --- /dev/null +++ b/packages/rocketchat-oembed/client/baseWidget.html @@ -0,0 +1,3 @@ + \ No newline at end of file diff --git a/packages/rocketchat-oembed/client/oembedImageWidget.html b/packages/rocketchat-oembed/client/oembedImageWidget.html new file mode 100644 index 000000000000..8e6a1c100e51 --- /dev/null +++ b/packages/rocketchat-oembed/client/oembedImageWidget.html @@ -0,0 +1,9 @@ + \ No newline at end of file diff --git a/packages/rocketchat-oembed/client/oembedUrlWidget.coffee b/packages/rocketchat-oembed/client/oembedUrlWidget.coffee new file mode 100644 index 000000000000..9759c8ca23fc --- /dev/null +++ b/packages/rocketchat-oembed/client/oembedUrlWidget.coffee @@ -0,0 +1,7 @@ +Template.oembedUrlWidget.helpers + description: -> + if not this.meta? + return + + + return this.meta['og:description'] or this.meta['description'] \ No newline at end of file diff --git a/packages/rocketchat-oembed/client/oembedUrlWidget.html b/packages/rocketchat-oembed/client/oembedUrlWidget.html new file mode 100644 index 000000000000..eff848616ecb --- /dev/null +++ b/packages/rocketchat-oembed/client/oembedUrlWidget.html @@ -0,0 +1,11 @@ + \ No newline at end of file diff --git a/packages/rocketchat-oembed/client/oembedYoutubeWidget.coffee b/packages/rocketchat-oembed/client/oembedYoutubeWidget.coffee new file mode 100644 index 000000000000..0fb8466eeaf9 --- /dev/null +++ b/packages/rocketchat-oembed/client/oembedYoutubeWidget.coffee @@ -0,0 +1,3 @@ +Template.oembedYoutubeWidget.helpers + player: -> + return this.meta?['twitter:player'] \ No newline at end of file diff --git a/packages/rocketchat-oembed/client/oembedYoutubeWidget.html b/packages/rocketchat-oembed/client/oembedYoutubeWidget.html new file mode 100644 index 000000000000..4d26ae157d00 --- /dev/null +++ b/packages/rocketchat-oembed/client/oembedYoutubeWidget.html @@ -0,0 +1,9 @@ + \ No newline at end of file diff --git a/packages/rocketchat-oembed/package.js b/packages/rocketchat-oembed/package.js index ce5fa7329852..ceb74779e000 100644 --- a/packages/rocketchat-oembed/package.js +++ b/packages/rocketchat-oembed/package.js @@ -9,13 +9,25 @@ Package.onUse(function(api) { api.versionsFrom('1.0'); api.use([ + 'templating', 'coffeescript', - 'iframely:oembed', 'rocketchat:lib@0.0.1' ]); - api.addFiles('server.coffee', ['server']); - api.export('OEmbed', ['server']); + api.addFiles('client/baseWidget.html', 'client'); + api.addFiles('client/baseWidget.coffee', 'client'); + + api.addFiles('client/oembedImageWidget.html', 'client'); + + api.addFiles('client/oembedYoutubeWidget.html', 'client'); + api.addFiles('client/oembedYoutubeWidget.coffee', 'client'); + + api.addFiles('client/oembedUrlWidget.html', 'client'); + api.addFiles('client/oembedUrlWidget.coffee', 'client'); + + api.addFiles('server/server.coffee', 'server'); + + api.export('OEmbed', 'server'); }); Package.onTest(function(api) { From 103a80a811d26f17625e069846d05fcf2f08186b Mon Sep 17 00:00:00 2001 From: Rodrigo Nascimento Date: Thu, 25 Jun 2015 21:16:19 -0300 Subject: [PATCH 08/33] Readd url in oembedUrlWidget --- packages/rocketchat-oembed/client/oembedUrlWidget.html | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/rocketchat-oembed/client/oembedUrlWidget.html b/packages/rocketchat-oembed/client/oembedUrlWidget.html index eff848616ecb..0c62bdd4fc3c 100644 --- a/packages/rocketchat-oembed/client/oembedUrlWidget.html +++ b/packages/rocketchat-oembed/client/oembedUrlWidget.html @@ -1,4 +1,5 @@