diff --git a/services/catarse/Gemfile b/services/catarse/Gemfile index a2f839f21f..b9411cb4b5 100644 --- a/services/catarse/Gemfile +++ b/services/catarse/Gemfile @@ -76,11 +76,16 @@ gem 'video_info', '3.0.1' gem 'webpacker', '5.4.3', require: false gem 'whenever', '1.0.0' gem 'zendesk_api', '1.33.0' +gem "recaptcha" group :production do gem 'fog-aws', '3.12.0' end +group :production, :sandbox do + gem 'cloudflare-rails' +end + group :development do gem 'brakeman', '5.1.2' gem 'letter_opener', '1.7.0' diff --git a/services/catarse/Gemfile.lock b/services/catarse/Gemfile.lock index a59177719c..d516d2c4d3 100644 --- a/services/catarse/Gemfile.lock +++ b/services/catarse/Gemfile.lock @@ -177,6 +177,10 @@ GEM chronic_duration (0.10.6) numerizer (~> 0.1.1) chunky_png (1.4.0) + cloudflare-rails (3.0.0) + actionpack (>= 6.0, < 7.1.0) + activesupport (>= 6.0, < 7.1.0) + railties (>= 6.0, < 7.1.0) concurrent-ruby (1.1.9) connection_pool (2.2.5) countries (4.0.1) @@ -473,6 +477,7 @@ GEM ffi (~> 1.0) rdstation-ruby-client (0.0.5) httparty (~> 0.12) + recaptcha (5.14.0) redcarpet (3.5.1) redis (4.5.1) regexp_parser (2.2.0) @@ -674,6 +679,7 @@ DEPENDENCIES catarse_pagarme! catarse_scripts! catarse_settings_db (= 0.2.0) + cloudflare-rails concurrent-ruby (= 1.1.9) countries (= 4.0.1) cpf_cnpj (= 0.5.0) @@ -718,6 +724,7 @@ DEPENDENCIES rails-observers (= 0.1.5) ranked-model (= 0.4.7) rdstation-ruby-client (= 0.0.5) + recaptcha redactor-rails (= 0.7.0)! redis (= 4.5.1) responders (= 3.0.1) diff --git a/services/catarse/app/assets/javascripts/analytics.js b/services/catarse/app/assets/javascripts/analytics.js index e980056f31..fad28adb87 100644 --- a/services/catarse/app/assets/javascripts/analytics.js +++ b/services/catarse/app/assets/javascripts/analytics.js @@ -1,21 +1,21 @@ //evitamos criar duas vezes... //tem q ser não dependente de outros codigos -window.CatarseAnalytics = window.CatarseAnalytics || (function(){ - /*! - * cookie-monster - a simple cookie library - * v0.3.0 - * https://github.com/jgallen23/cookie-monster - * copyright Greg Allen 2014 - * MIT License - */ - var monster={set:function(name,value,days,path,secure,domain){var date=new Date(),expires='',type=typeof(value),valueToUse='',secureFlag='';path=path||"/";if(days){date.setTime(date.getTime()+(days*24*60*60*1000));expires="; expires="+date.toUTCString();}if(type==="object"&&type!=="undefined"){if(!("JSON"in window))throw"Bummer, your browser doesn't support JSON parsing.";valueToUse=encodeURIComponent(JSON.stringify({v:value}));}else{valueToUse=encodeURIComponent(value);}if(secure){secureFlag="; secure";}domain=domain?"; domain="+encodeURIComponent(domain):"";document.cookie=name+"="+valueToUse+expires+"; path="+path+secureFlag+domain;},get:function(name){var nameEQ=name+"=",ca=document.cookie.split(';'),value='',firstChar='',parsed={};for(var i=0;i=i;i++)dec2hex[i]=i.toString(16);return function(){for(var uuid="",i=1;36>=i;i++)uuid+=9===i||14===i||19===i||24===i?"-":15===i?4:20===i?dec2hex[4*Math.random()|8]:dec2hex[15*Math.random()|0];return uuid}}(); - sid=UUID(); + var UUID = function () { for (var dec2hex = [], i = 0; 15 >= i; i++)dec2hex[i] = i.toString(16); return function () { for (var uuid = "", i = 1; 36 >= i; i++)uuid += 9 === i || 14 === i || 19 === i || 24 === i ? "-" : 15 === i ? 4 : 20 === i ? dec2hex[4 * Math.random() | 8] : dec2hex[15 * Math.random() | 0]; return uuid } }(); + sid = UUID(); } - cookie.set('ctrse_sid',sid,180,'/',true,cookie_url); + cookie.set('ctrse_sid', sid, 180, '/', true, cookie_url); return sid; })(monster); - var _apiHost,_user,_project; - var _analyticsOneTimeEventFired={}; + var _apiHost, _user, _project; + var _analyticsOneTimeEventFired = {}; function _actualRequest() { var location = window.location; var domain = location.origin || (location.protocol + '//' + location.hostname); return { - referrer: document.referrer||undefined, + referrer: document.referrer || undefined, url: location.href, - protocol: location.protocol.substr(0,location.protocol.length-1), + protocol: location.protocol.substr(0, location.protocol.length - 1), hostname: location.hostname, domain: domain, - pathname: location.pathname || location.href.substr(domain.length).replace(/[\?\#].*$/,''), - userAgent: typeof navigator!=='undefined' ? navigator.userAgent : undefined, - hash: location.hash.replace(/^\#/,''), + pathname: location.pathname || location.href.substr(domain.length).replace(/[\?\#].*$/, ''), + userAgent: typeof navigator !== 'undefined' ? navigator.userAgent : undefined, + hash: location.hash.replace(/^\#/, ''), query: (function parseParams() { - if(location.search) { - try { - return location.search.replace(/^\?/,'').split('&').reduce(function (params, param) { - var paramSplit = param.split('=').map(function (value) { - return decodeURIComponent(value.replace('+', ' ')); - }); - params[paramSplit[0]] = paramSplit[1]; - return params; - }, {}); - } catch(e) { - return location.search; - } + if (location.search) { + try { + return location.search.replace(/^\?/, '').split('&').reduce(function (params, param) { + var paramSplit = param.split('=').map(function (value) { + return decodeURIComponent(value.replace('+', ' ')); + }); + params[paramSplit[0]] = paramSplit[1]; + return params; + }, {}); + } catch (e) { + return location.search; } + } })() }; }; - var _firstPageView=true; + var _firstPageView = true; function _origin(_request) { - var _date=new Date(); + var _date = new Date(); try { - var o = JSON.parse(monster.get('ctrse_origin')||null) || {createdAt: _date,updatedAt: _date}; - } catch(e) { - o = {createdAt: _date,updatedAt: _date}; + var o = JSON.parse(monster.get('ctrse_origin') || null) || { createdAt: _date, updatedAt: _date }; + } catch (e) { + o = { createdAt: _date, updatedAt: _date }; } - var query=_request.query; - var origin_has_utm=['domain','campaign','source','medium','content','term'].some(function(p){return !!o[p]}); - var query_has_utm=query && ['utm_campaign','utm_source','utm_medium','utm_content','utm_term'].some(function(p){return !!query[p]}); - var expired = !o._time || (_date.getTime()-o._time)>10*60*1000/*10 min*/; + var query = _request.query; + var origin_has_utm = ['domain', 'campaign', 'source', 'medium', 'content', 'term'].some(function (p) { return !!o[p] }); + var query_has_utm = query && ['utm_campaign', 'utm_source', 'utm_medium', 'utm_content', 'utm_term'].some(function (p) { return !!query[p] }); + var expired = !o._time || (_date.getTime() - o._time) > 10 * 60 * 1000/*10 min*/; - var fromSite= (!_firstPageView)//se não é a primeira vez q passa aqui, só pode ser fromSite. - || (_request.referrer && new RegExp(base_url).test(_request.referrer)); - if(fromSite) { + var fromSite = (!_firstPageView)//se não é a primeira vez q passa aqui, só pode ser fromSite. + || (_request.referrer && new RegExp(base_url).test(_request.referrer)); + if (fromSite) { //Só pega o ultimo ref. Não atualiza utms... - if(query && query.ref && o.ref!=query.ref) { - if(!origin_has_utm) o.domain=base_url; + if (query && query.ref && o.ref != query.ref) { + if (!origin_has_utm) o.domain = base_url; o.ref = query.ref; o.updatedAt = _date; } - } else if(/*!fromSite && */ _request.referrer || expired) { - var m=_request.referrer && _request.referrer.match(/https?:\/\/([^\/\?#]+)/); - var refDomain=(m && m[1]) || undefined; + } else if (/*!fromSite && */ _request.referrer || expired) { + var m = _request.referrer && _request.referrer.match(/https?:\/\/([^\/\?#]+)/); + var refDomain = (m && m[1]) || undefined; //se, e somente se, tem algum utm na query... - if(query_has_utm) {//então, substitui todos, mesmo os q nao tem, pois são um conjunto de informações... - o.domain =refDomain; - o.campaign=query.utm_campaign; - o.source =query.utm_source; - o.medium =query.utm_medium; - o.content =query.utm_content; - o.term =query.utm_term; - o.updatedAt=_date; + if (query_has_utm) {//então, substitui todos, mesmo os q nao tem, pois são um conjunto de informações... + o.domain = refDomain; + o.campaign = query.utm_campaign; + o.source = query.utm_source; + o.medium = query.utm_medium; + o.content = query.utm_content; + o.term = query.utm_term; + o.updatedAt = _date; } else if (refDomain && !origin_has_utm) {//se tem refDomain e não tem no origin algum utm ou domain anterior... - o.domain = refDomain; - o.updatedAt= _date; + o.domain = refDomain; + o.updatedAt = _date; } - if(!o.campaign && query && query.ref) { + if (!o.campaign && query && query.ref) { //nesse caso, como veio de outro dominio, sem utm params, mas com ref, assumimos q esse ref é um campaign. o.campaign = query.ref; - o.updatedAt= _date; + o.updatedAt = _date; } } //fazemos o _time aqui por causa da verificação acima !o._time, indicando q foi criado agora. - o._time=_date.getTime(); - monster.set('ctrse_origin',JSON.stringify(o),180,'/',true,cookie_url); + o._time = _date.getTime(); + monster.set('ctrse_origin', JSON.stringify(o), 180, '/', true, cookie_url); return o; }; //Metodos semelhantes ao modulo "h" function _getApiHost() { - if(window.CatarseAnalyticsURL) + if (window.CatarseAnalyticsURL) return window.CatarseAnalyticsURL; - if(_apiHost) + if (_apiHost) return _apiHost; - var el=document.getElementById('api-moments-host'); + var el = document.getElementById('api-moments-host'); _apiHost = (el && el.getAttribute('content')); - if(_apiHost) - _apiHost=_apiHost+'/rpc/track'; + if (_apiHost) + _apiHost = _apiHost + '/rpc/track'; return _apiHost; } function _getUser() { - if(_user) + if (_user) return _user; var body = document.getElementsByTagName('body'), - data = body && body[0] && body[0].getAttribute('data-user'); - if(data) { + data = body && body[0] && body[0].getAttribute('data-user'); + if (data) { try { - return _user=JSON.parse(data); - } catch(e) { - console.error('[CatarseAnalytics._getUser] error parsing data '+JSON.stringify(data), e); + return _user = JSON.parse(data); + } catch (e) { + console.error('[CatarseAnalytics._getUser] error parsing data ' + JSON.stringify(data), e); } } } function _getProject() { - if(_project) + if (_project) return _project; - var el = document.getElementById('project-show-root')||document.getElementById('project-header'),//pode não existir - data = el && (el.getAttribute('data-parameters')||el.getAttribute('data-stats')); - if(data) { + var el = document.getElementById('project-show-root') || document.getElementById('project-header'),//pode não existir + data = el && (el.getAttribute('data-parameters') || el.getAttribute('data-stats')); + if (data) { try { - return _project=JSON.parse(data); - } catch(e) { - console.error('[CatarseAnalytics._getProject] error parsing data '+JSON.stringify(data), e); + return _project = JSON.parse(data); + } catch (e) { + console.error('[CatarseAnalytics._getProject] error parsing data ' + JSON.stringify(data), e); } }//else return undefined } @@ -197,73 +197,79 @@ window.CatarseAnalytics = window.CatarseAnalytics || (function(){ function _event(eventObj, fn, ignoreGA) { if (eventObj) { try { - var project = eventObj.project||_getProject(), - user = eventObj.user||_getUser(); - var ga = window.ga;//o ga tem q ser verificado aqui pq pode não existir na criaçaõ do DOM - var gaTracker = (typeof ga==='function' && ga.getAll && ga.getAll() && ga.getAll()[0]) || null; - ignoreGA = ignoreGA || typeof ga!=='function'; + var project = eventObj.project || _getProject(), + user = eventObj.user || _getUser(); + //var ga = window.ga;//o ga tem q ser verificado aqui pq pode não existir na criaçaõ do DOM + var gtag = window.gtag; + //var gaTracker = (typeof ga==='function' && ga.getAll && ga.getAll() && ga.getAll()[0]) || null; + var gaTracker = null; + ignoreGA = ignoreGA || typeof gtag !== 'function'; var _request = _actualRequest(); - var data = eventObj.extraData&&typeof eventObj.extraData==='object' ? JSON.parse(JSON.stringify(eventObj.extraData)) : {}; - data.ctrse_sid=ctrse_sid; - data.origin=_origin(_request); - data.category=eventObj.cat; - data.action=eventObj.act; - data.label=eventObj.lbl; - data.value=eventObj.val; - data.request=_request; - if(user&&user.user_id) { - data.user={ + var data = eventObj.extraData && typeof eventObj.extraData === 'object' ? JSON.parse(JSON.stringify(eventObj.extraData)) : {}; + data.ctrse_sid = ctrse_sid; + data.origin = _origin(_request); + data.category = eventObj.cat; + data.action = eventObj.act; + data.label = eventObj.lbl; + data.value = eventObj.val; + data.request = _request; + if (user && user.user_id) { + data.user = { id: user.user_id, contributions: user.contributions, published_projects: user.published_projects }; } - if(project&&(project.id||project.project_id)) { - data.project={ - id: project.id||project.project_id, - user_id: project.user_id||project.project_user_id, + if (project && (project.id || project.project_id)) { + data.project = { + id: project.id || project.project_id, + user_id: project.user_id || project.project_user_id, category_id: project.category_id, state: project.address && project.address.state_acronym, city: project.address && project.address.city }; } - if(gaTracker) { - data.ga={clientId: gaTracker.get('clientId')}; - } + // if(gaTracker) { + // data.ga={clientId: gaTracker.get('clientId')}; + // } try { - var apiUrl=_getApiHost(); - if(apiUrl) { + var apiUrl = _getApiHost(); + if (apiUrl) { var sendData = { event: data }; ajax({ - url: apiUrl, - // The key needs to match your method's input parameter (case-sensitive). - body: JSON.stringify(sendData), - headers: { - 'Content-Type': "application/json; charset=utf-8" - } - }, function(status, responseText, req){ - if(status!==200) - console.error(status,responseText,req); + url: apiUrl, + // The key needs to match your method's input parameter (case-sensitive). + body: JSON.stringify(sendData), + headers: { + 'Content-Type': "application/json; charset=utf-8" + } + }, function (status, responseText, req) { + if (status !== 200) + console.error(status, responseText, req); }); } - } catch(e) { + } catch (e) { console.error('[CatarseAnalytics.event] error:', e); } - if(!ignoreGA && typeof ga!='undefined') { + if (!ignoreGA && typeof gtag != 'undefined') { //https://developers.google.com/analytics/devguides/collection/analyticsjs/sending-hits#the_send_method - ga('send', 'event', eventObj.cat, eventObj.act, eventObj.lbl, eventObj.val, { - nonInteraction: eventObj.nonInteraction!==false,//default é true,e só será false se, e somente se, esse parametro for definido como false - transport: 'beacon' + gtag('event', eventObj.act, { + 'event_category': eventObj.cat, + 'event_label': eventObj.lbl }); + // ga('send', 'event', eventObj.cat, eventObj.act, eventObj.lbl, eventObj.val, { + // nonInteraction: eventObj.nonInteraction!==false,//default é true,e só será false se, e somente se, esse parametro for definido como false + // transport: 'beacon' + // }); } - } catch(e) { - console.error('[CatarseAnalytics.event] error:',e); + } catch (e) { + console.error('[CatarseAnalytics.event] error:', e); } } fn && fn(); @@ -277,16 +283,18 @@ window.CatarseAnalytics = window.CatarseAnalytics || (function(){ }*/ var pvto; function _pageView(firstPageView) { - if(!firstPageView) - _firstPageView=false; + if (!firstPageView) + _firstPageView = false; - pvto&&clearTimeout(pvto); - pvto=setTimeout(function() { - _event({cat:'navigation',act:'pageview',lbl:location.pathname}, null, true);//ignoraGa pq vai mandar abaixo + pvto && clearTimeout(pvto); + pvto = setTimeout(function () { + _event({ cat: 'navigation', act: 'pageview', lbl: location.pathname }, null, true);//ignoraGa pq vai mandar abaixo //on first page view, GA will to this for us - if(!firstPageView && typeof ga!='undefined') { - ga('set','page',location.pathname); - ga('send', 'pageview', location.pathname); + if (!firstPageView && typeof gtag != 'undefined') { + gtag('event', 'pageview', { + 'event_category': 'navigation', + 'event_label': location.pathname + }); } }); } @@ -294,54 +302,54 @@ window.CatarseAnalytics = window.CatarseAnalytics || (function(){ function _checkout(transactionId, prodName, sku, category, price, fee) { try { - if(typeof ga==='function') { - ga('ecommerce:addTransaction', { - 'id': transactionId, // Transaction ID. Required. - //'affiliation': 'Acme Clothing', // Affiliation or store name. - 'revenue': price, // Grand Total. - //'shipping': , // Shipping. - 'tax': fee, // Tax. Nossa porcentagem - 'current': 'BRL' - }); - ga('ecommerce:addItem', { - 'id': transactionId, // Transaction ID. Required. - 'name': prodName, // Product name. Required. - 'sku': sku, // SKU/code. - 'category': category, // Category or variation. - 'price': price, // Unit price. - 'quantity': '1' // Quantity. + if (typeof gtag === 'function') { + gtag('event', 'purchase', { + currency: 'BRL', + transaction_id: transactionId, + value: price, + // affiliation: 'Google Store', + // shipping: 5.25, + tax: fee, + items: [ + { + item_id: sku, + item_name: prodName, + item_category: category, + price: price, + quantity: 1 + }, + ] }); - ga('ecommerce:send'); } - } catch(e) { - console.error('[CatarseAnalytics.checkout]',e); + } catch (e) { + console.error('[CatarseAnalytics.checkout]', e); } } return { - origin: function(){ + origin: function () { //Evita de passar outro request return _origin(_actualRequest()); }, event: _event, pageView: _pageView, - oneTimeEvent: function(eventObj, fn) { - if (!eventObj) { - return fn; + oneTimeEvent: function (eventObj, fn) { + if (!eventObj) { + return fn; + } + try { + if (!eventObj.cat && !eventObj.act) { + throw new Error('Should inform cat or act'); } - try { - if (!eventObj.cat && !eventObj.act) { - throw new Error('Should inform cat or act'); - } - var eventKey = eventObj.cat && eventObj.act ? eventObj.cat+'_'+eventObj.act : (eventObj.cat || eventObj.act); - if (!_analyticsOneTimeEventFired[eventKey]) { - //console.log('oneTimeEvent',eventKey); - _analyticsOneTimeEventFired[eventKey] = true; - _event(eventObj, fn); - } - } catch(e) { - console.error('[CatarseAnalytics.oneTimeEvent] error:',e); + var eventKey = eventObj.cat && eventObj.act ? eventObj.cat + '_' + eventObj.act : (eventObj.cat || eventObj.act); + if (!_analyticsOneTimeEventFired[eventKey]) { + //console.log('oneTimeEvent',eventKey); + _analyticsOneTimeEventFired[eventKey] = true; + _event(eventObj, fn); } + } catch (e) { + console.error('[CatarseAnalytics.oneTimeEvent] error:', e); + } }, checkout: _checkout }; diff --git a/services/catarse/app/assets/stylesheets/catarse_bootstrap/_main.scss b/services/catarse/app/assets/stylesheets/catarse_bootstrap/_main.scss index d3c1b9e6ac..0358328a70 100644 --- a/services/catarse/app/assets/stylesheets/catarse_bootstrap/_main.scss +++ b/services/catarse/app/assets/stylesheets/catarse_bootstrap/_main.scss @@ -737,9 +737,10 @@ a:hover { } .project-video { - height: 488px; + height: auto; margin-bottom: 30px; background-color: hsla(0, 0%, 87.1%, 0.57); + aspect-ratio: 16 / 9; } .fontsize-smaller { @@ -5106,7 +5107,7 @@ html.w-mod-js *[data-ix="display-0-on-load"] { margin-top: 8px; } .project-video { - height: 358px; + height: auto; } .fontsize-smaller { font-size: 13px; diff --git a/services/catarse/app/controllers/registrations_controller.rb b/services/catarse/app/controllers/registrations_controller.rb new file mode 100644 index 0000000000..edc29fd626 --- /dev/null +++ b/services/catarse/app/controllers/registrations_controller.rb @@ -0,0 +1,19 @@ +# frozen_string_literal: true + +class RegistrationsController < Devise::RegistrationsController + prepend_before_action :check_captcha, only: [:create] + private + + def check_captcha + return if verify_recaptcha # verify_recaptcha(action: 'signup') for v3 + + self.resource = resource_class.new sign_up_params + resource.validate + set_minimum_password_length + + respond_with_navigational(resource) do + flash.discard(:recaptcha_error) + render :new + end + end +end diff --git a/services/catarse/app/models/user.rb b/services/catarse/app/models/user.rb index 786fbc9cf1..4bd1270767 100644 --- a/services/catarse/app/models/user.rb +++ b/services/catarse/app/models/user.rb @@ -413,7 +413,7 @@ def generate_reset_password_token def update_tracked_fields(request) super - login_activities.build(ip_address: current_sign_in_ip) + login_activities.build(ip_address: (request.headers['CF-Connecting-IP'].presence || current_sign_in_ip)) end def account_active? @@ -521,4 +521,10 @@ def cancel_all_subscriptions common_wrapper.cancel_subscription(_sub) end end + + protected + + def extract_ip_from(request) + request.headers['CF-Connecting-IP'].presence || request.remote_ip + end end diff --git a/services/catarse/app/views/catarse_bootstrap/devise/registrations/new.html.slim b/services/catarse/app/views/catarse_bootstrap/devise/registrations/new.html.slim index 31557f7a39..c935f15b77 100644 --- a/services/catarse/app/views/catarse_bootstrap/devise/registrations/new.html.slim +++ b/services/catarse/app/views/catarse_bootstrap/devise/registrations/new.html.slim @@ -28,6 +28,8 @@ .u-marginbottom-20 = form.input_field :newsletter, as: :boolean, boolean_style: :inline, class: 'checkbox-fix', checked: false label.w-form-label.fontsize-smallest= t('.form.labels.newsletter') + .u-marginbottom-20 + = recaptcha_tags .w-row.u-marginbottom-20 .w-col.w-col-6.w-col-small-6.w-col-tiny-6 = form.button :submit, t('.form.inputs.submit'), class:'btn btn-large' diff --git a/services/catarse/app/views/catarse_bootstrap/layouts/_analytics.html.slim b/services/catarse/app/views/catarse_bootstrap/layouts/_analytics.html.slim index 20568f62c4..43df77cc77 100644 --- a/services/catarse/app/views/catarse_bootstrap/layouts/_analytics.html.slim +++ b/services/catarse/app/views/catarse_bootstrap/layouts/_analytics.html.slim @@ -23,24 +23,23 @@ javascript: (a[n]=a[n]||[]).hide=h;setTimeout(function(){i();h.end=null},c);h.timeout=c; })(window,document.documentElement,'async-hide','dataLayer',4000, {'#{CatarseSettings['google_optimize_id']}':true}); - javascript: - (function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){ - (i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o), - m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m) - })(window,document,'script','//www.google-analytics.com/analytics.js','ga'); - - window.trackingFunctions = window.trackingFunctions || {}; - - window.trackingFunctions['GA'] = function(google_analytics_id) { - var analyticsId = 'UA-' + google_analytics_id; - ga('create', analyticsId, 'auto', { name: 'project' }) - ga('require', 'ecommerce'); - ga('project.send', 'pageview'); - }; - if CatarseSettings['google_analytics_id'].present? + javascript: - ga('create', '#{CatarseSettings['google_analytics_id']}', '#{CatarseSettings[:host]}'); // analytics id, key - ga('require', 'ecommerce'); - ga('require', '#{CatarseSettings['google_optimize_id']}'); // optimize id, key - ga('send', 'pageview'); + window.dataLayer = window.dataLayer || []; + function gtag(){dataLayer.push(arguments);} + gtag('js', new Date()); + gtag('config', '#{CatarseSettings['google_analytics_id']}'); + + window.trackingFunctions = window.trackingFunctions || {}; + + window.trackingFunctions['GA'] = function(google_analytics_id) { + var analyticsId = 'G-' + google_analytics_id; + gtag('config', analyticsId); + gtag('event', 'pageview', { + 'send_to': analyticsId, + 'event_category': 'navigation', + 'event_label': location.pathname + }); + }; diff --git a/services/catarse/catarse.js/legacy/spec/lib/analytics.js b/services/catarse/catarse.js/legacy/spec/lib/analytics.js index ca52b5f26f..f6f7ac30c9 100644 --- a/services/catarse/catarse.js/legacy/spec/lib/analytics.js +++ b/services/catarse/catarse.js/legacy/spec/lib/analytics.js @@ -1,21 +1,21 @@ //evitamos criar duas vezes... //tem q ser não dependente de outros codigos -window.CatarseAnalytics = window.CatarseAnalytics || (function(){ - /*! - * cookie-monster - a simple cookie library - * v0.3.0 - * https://github.com/jgallen23/cookie-monster - * copyright Greg Allen 2014 - * MIT License - */ - var monster={set:function(name,value,days,path,secure,domain){var date=new Date(),expires='',type=typeof(value),valueToUse='',secureFlag='';path=path||"/";if(days){date.setTime(date.getTime()+(days*24*60*60*1000));expires="; expires="+date.toUTCString();}if(type==="object"&&type!=="undefined"){if(!("JSON"in window))throw"Bummer, your browser doesn't support JSON parsing.";valueToUse=encodeURIComponent(JSON.stringify({v:value}));}else{valueToUse=encodeURIComponent(value);}if(secure){secureFlag="; secure";}domain=domain?"; domain="+encodeURIComponent(domain):"";document.cookie=name+"="+valueToUse+expires+"; path="+path+secureFlag+domain;},get:function(name){var nameEQ=name+"=",ca=document.cookie.split(';'),value='',firstChar='',parsed={};for(var i=0;i=i;i++)dec2hex[i]=i.toString(16);return function(){for(var uuid="",i=1;36>=i;i++)uuid+=9===i||14===i||19===i||24===i?"-":15===i?4:20===i?dec2hex[4*Math.random()|8]:dec2hex[15*Math.random()|0];return uuid}}(); - sid=UUID(); + var UUID = function () { for (var dec2hex = [], i = 0; 15 >= i; i++)dec2hex[i] = i.toString(16); return function () { for (var uuid = "", i = 1; 36 >= i; i++)uuid += 9 === i || 14 === i || 19 === i || 24 === i ? "-" : 15 === i ? 4 : 20 === i ? dec2hex[4 * Math.random() | 8] : dec2hex[15 * Math.random() | 0]; return uuid } }(); + sid = UUID(); } - cookie.set('ctrse_sid',sid,180,'/',false,'.catarse.me'); + cookie.set('ctrse_sid', sid, 180, '/', false, '.catarse.me'); return sid; })(monster); - var _apiHost,_user,_project; - var _analyticsOneTimeEventFired={}; + var _apiHost, _user, _project; + var _analyticsOneTimeEventFired = {}; try { var location = window.location; var domain = location.origin || (location.protocol + '//' + location.hostname); - var _actualRequest={ - referrer: document.referrer||undefined, + var _actualRequest = { + referrer: document.referrer || undefined, url: location.href, - protocol: location.protocol.substr(0,location.protocol.length-1), + protocol: location.protocol.substr(0, location.protocol.length - 1), hostname: location.hostname, domain: domain, - pathname: location.pathname || location.href.substr(domain.length).replace(/[\?\#].*$/,''), - userAgent: typeof navigator!=='undefined' ? navigator.userAget : undefined, - hash: location.hash.replace(/^\#/,''), + pathname: location.pathname || location.href.substr(domain.length).replace(/[\?\#].*$/, ''), + userAgent: typeof navigator !== 'undefined' ? navigator.userAget : undefined, + hash: location.hash.replace(/^\#/, ''), query: (function parseParams() { - if(location.search) { - try { - return location.search.replace(/^\?/,'').split('&').reduce(function (params, param) { - var paramSplit = param.split('=').map(function (value) { - return decodeURIComponent(value.replace('+', ' ')); - }); - params[paramSplit[0]] = paramSplit[1]; - return params; - }, {}); - } catch(e) { - return location.search; - } + if (location.search) { + try { + return location.search.replace(/^\?/, '').split('&').reduce(function (params, param) { + var paramSplit = param.split('=').map(function (value) { + return decodeURIComponent(value.replace('+', ' ')); + }); + params[paramSplit[0]] = paramSplit[1]; + return params; + }, {}); + } catch (e) { + return location.search; } + } })() }; - var origin = (function(request,cookie) { + var origin = (function (request, cookie) { try { - var o = JSON.parse(cookie.get('ctrse_origin')||null) || {createdAt: new Date()}; - } catch(e) { - o = {createdAt: new Date()}; + var o = JSON.parse(cookie.get('ctrse_origin') || null) || { createdAt: new Date() }; + } catch (e) { + o = { createdAt: new Date() }; } - var fromCatarse=request.referrer && /^https?:\/\/([^\\/]+\.)?catarse\.me/.test(request.referrer); - if(fromCatarse) { + var fromCatarse = request.referrer && /^https?:\/\/([^\\/]+\.)?catarse\.me/.test(request.referrer); + if (fromCatarse) { //Só pega o ultimo ref. Não atualiza utms... - o.ref = (request.query&&request.query.ref) || o.ref; //preferencia para a query. - } else if(/*!fromCatarse && */ request.referrer || (!o._time || new Date().getTime()-o._time>10*60*1000/*10min*/)) { - var m=request.referrer && request.referrer.match(/https?:\/\/([^\/\?#]+)/); - var refDomain=(m && m[1]) || undefined; - var query=request.query; + o.ref = (request.query && request.query.ref) || o.ref; //preferencia para a query. + } else if (/*!fromCatarse && */ request.referrer || (!o._time || new Date().getTime() - o._time > 10 * 60 * 1000/*10min*/)) { + var m = request.referrer && request.referrer.match(/https?:\/\/([^\/\?#]+)/); + var refDomain = (m && m[1]) || undefined; + var query = request.query; //se, e somente se, tem algum utm na query... - if(query && ['utm_campaign','utm_source','utm_medium','utm_content','utm_term'].some(function(p){ + if (query && ['utm_campaign', 'utm_source', 'utm_medium', 'utm_content', 'utm_term'].some(function (p) { return !!query[p]; })) {//então, substitui todos, mesmo os q nao tem, pois são um conjunto de informações... - o.domain = refDomain; - o.campaign=query.utm_campaign; - o.source= query.utm_source; - o.medium= query.utm_medium; - o.content= query.utm_content; - o.term= query.utm_term; - } else if (refDomain && !['domain','utm_campaign','utm_source','utm_medium','utm_content','utm_term'].some(function(p){ + o.domain = refDomain; + o.campaign = query.utm_campaign; + o.source = query.utm_source; + o.medium = query.utm_medium; + o.content = query.utm_content; + o.term = query.utm_term; + } else if (refDomain && !['domain', 'utm_campaign', 'utm_source', 'utm_medium', 'utm_content', 'utm_term'].some(function (p) { return !!o[p]; })) {//se tem refDomain e não tem no origin algum utm ou domain anterior... - o.domain = refDomain; + o.domain = refDomain; } - if(!o.campaign && query && query.ref) { + if (!o.campaign && query && query.ref) { //nesse caso, como veio de outro dominio, sem utm params, mas com ref, assumimos q esse ref é um campaign. o.campaign = query.ref; } } //fazemos o _time aqui por causa da verificação acima !o._time, indicando q foi criado agora. - o._time=new Date().getTime(); - cookie.set('ctrse_origin',JSON.stringify(o),180,'/',false,'.catarse.me'); + o._time = new Date().getTime(); + cookie.set('ctrse_origin', JSON.stringify(o), 180, '/', false, '.catarse.me'); return o; - })(_actualRequest,monster); - } catch(e) { - console.error('[CatarseAnalytics] error',e); + })(_actualRequest, monster); + } catch (e) { + console.error('[CatarseAnalytics] error', e); } //Metodos semelhantes ao modulo "h" function _getApiHost() { - if(window.CatarseAnalyticsURL) + if (window.CatarseAnalyticsURL) return window.CatarseAnalyticsURL; - if(_apiHost) + if (_apiHost) return _apiHost; - var el=document.getElementById('api-host-moments'); + var el = document.getElementById('api-host-moments'); _apiHost = (el && el.getAttribute('content')); - if(_apiHost) - _apiHost=_apiHost+'/rpc/track'; + if (_apiHost) + _apiHost = _apiHost + '/rpc/track'; return _apiHost; } function _getUser() { - if(_user) + if (_user) return _user; var body = document.getElementsByTagName('body'), - data = body && body[0] && body[0].getAttribute('data-user'); - if(data) { + data = body && body[0] && body[0].getAttribute('data-user'); + if (data) { try { - return _user=JSON.parse(data); - } catch(e) { - console.error('[CatarseAnalytics._getUser] error parsing data '+JSON.stringify(data), e); + return _user = JSON.parse(data); + } catch (e) { + console.error('[CatarseAnalytics._getUser] error parsing data ' + JSON.stringify(data), e); } } } function _getProject() { - if(_project) + if (_project) return _project; - var el = document.getElementById('project-show-root')||document.getElementById('project-header'),//pode não existir - data = el && (el.getAttribute('data-parameters')||el.getAttribute('data-stats')); - if(data) { + var el = document.getElementById('project-show-root') || document.getElementById('project-header'),//pode não existir + data = el && (el.getAttribute('data-parameters') || el.getAttribute('data-stats')); + if (data) { try { - return _project=JSON.parse(data); - } catch(e) { - console.error('[CatarseAnalytics._getProject] error parsing data '+JSON.stringify(data), e); + return _project = JSON.parse(data); + } catch (e) { + console.error('[CatarseAnalytics._getProject] error parsing data ' + JSON.stringify(data), e); } }//else return undefined } @@ -185,72 +185,79 @@ window.CatarseAnalytics = window.CatarseAnalytics || (function(){ function _event(eventObj, fn, ignoreGA) { if (eventObj) { try { - var project = eventObj.project||_getProject(), - user = eventObj.user||_getUser(); - var ga = window.ga;//o ga tem q ser verificado aqui pq pode não existir na criaçaõ do DOM - var gaTracker = (typeof ga==='function' && ga.getAll && ga.getAll() && ga.getAll()[0]) || null; - ignoreGA = ignoreGA || typeof ga!=='function'; + var project = eventObj.project || _getProject(), + user = eventObj.user || _getUser(); + //var ga = window.ga;//o ga tem q ser verificado aqui pq pode não existir na criaçaõ do DOM + var gtag = window.gtag; + //var gaTracker = (typeof ga==='function' && ga.getAll && ga.getAll() && ga.getAll()[0]) || null; + var gaTracker = null; + ignoreGA = ignoreGA || typeof gtag !== 'function'; - var data = eventObj.extraData&&typeof eventObj.extraData==='object' ? JSON.parse(JSON.stringify(eventObj.extraData)) : {}; - data.ctrse_sid=ctrse_sid; - data.origin=origin; - data.category=eventObj.cat; - data.action=eventObj.act; - data.label=eventObj.lbl; - data.value=eventObj.val; - data.request=_actualRequest; - if(user&&user.user_id) { - data.user={ + var _request = _actualRequest(); + var data = eventObj.extraData && typeof eventObj.extraData === 'object' ? JSON.parse(JSON.stringify(eventObj.extraData)) : {}; + data.ctrse_sid = ctrse_sid; + data.origin = _origin(_request); + data.category = eventObj.cat; + data.action = eventObj.act; + data.label = eventObj.lbl; + data.value = eventObj.val; + data.request = _request; + if (user && user.user_id) { + data.user = { id: user.user_id, contributions: user.contributions, published_projects: user.published_projects }; } - if(project&&(project.id||project.project_id)) { - data.project={ - id: project.id||project.project_id, - user_id: project.user_id||project.project_user_id, + if (project && (project.id || project.project_id)) { + data.project = { + id: project.id || project.project_id, + user_id: project.user_id || project.project_user_id, category_id: project.category_id, state: project.address && project.address.state_acronym, city: project.address && project.address.city }; } - if(gaTracker) { - data.ga={clientId: gaTracker.get('clientId')}; - } + // if(gaTracker) { + // data.ga={clientId: gaTracker.get('clientId')}; + // } try { - var apiUrl=_getApiHost(); - if(apiUrl) { + var apiUrl = _getApiHost(); + if (apiUrl) { var sendData = { event: data }; ajax({ - url: apiUrl, - // The key needs to match your method's input parameter (case-sensitive). - body: JSON.stringify(sendData), - headers: { - 'content-type': "application/json; charset=utf-8" - } - }, function(status, responseText, req){ - if(status!==200) - console.error(status,responseText,req); + url: apiUrl, + // The key needs to match your method's input parameter (case-sensitive). + body: JSON.stringify(sendData), + headers: { + 'Content-Type': "application/json; charset=utf-8" + } + }, function (status, responseText, req) { + if (status !== 200) + console.error(status, responseText, req); }); } - } catch(e) { + } catch (e) { console.error('[CatarseAnalytics.event] error:', e); } - if(!ignoreGA) { + if (!ignoreGA && typeof gtag != 'undefined') { //https://developers.google.com/analytics/devguides/collection/analyticsjs/sending-hits#the_send_method - ga('send', 'event', eventObj.cat, eventObj.act, eventObj.lbl, eventObj.val, { - nonInteraction: eventObj.nonInteraction!==false,//default é true,e só será false se, e somente se, esse parametro for definido como false - transport: 'beacon' + gtag('event', eventObj.act, { + 'event_category': eventObj.cat, + 'event_label': eventObj.lbl }); + // ga('send', 'event', eventObj.cat, eventObj.act, eventObj.lbl, eventObj.val, { + // nonInteraction: eventObj.nonInteraction!==false,//default é true,e só será false se, e somente se, esse parametro for definido como false + // transport: 'beacon' + // }); } - } catch(e) { - console.error('[CatarseAnalytics.event] error:',e); + } catch (e) { + console.error('[CatarseAnalytics.event] error:', e); } } fn && fn(); @@ -263,55 +270,55 @@ window.CatarseAnalytics = window.CatarseAnalytics || (function(){ } }*/ function _pageView() { - _event({cat:'navigation',act:'pageview',lbl:location.pathname}, null, true) + _event({ cat: 'navigation', act: 'pageview', lbl: location.pathname }, null, true) } _pageView(); function _checkout(transactionId, prodName, sku, category, price, fee) { try { - if(typeof ga==='function') { - ga('ecommerce:addTransaction', { - 'id': transactionId, // Transaction ID. Required. - //'affiliation': 'Acme Clothing', // Affiliation or store name. - 'revenue': price, // Grand Total. - //'shipping': , // Shipping. - 'tax': fee, // Tax. Nossa porcentagem - 'current': 'BRL' - }); - ga('ecommerce:addItem', { - 'id': transactionId, // Transaction ID. Required. - 'name': prodName, // Product name. Required. - 'sku': sku, // SKU/code. - 'category': category, // Category or variation. - 'price': price, // Unit price. - 'quantity': '1' // Quantity. + if (typeof gtag === 'function') { + gtag('event', 'purchase', { + currency: 'BRL', + transaction_id: transactionId, + value: price, + // affiliation: 'Google Store', + // shipping: 5.25, + tax: fee, + items: [ + { + item_id: sku, + item_name: prodName, + item_category: category, + price: price, + quantity: 1 + }, + ] }); - ga('ecommerce:send'); } - } catch(e) { - console.error('[CatarseAnalytics.checkout]',e); + } catch (e) { + console.error('[CatarseAnalytics.checkout]', e); } } return { origin: origin, event: _event, - oneTimeEvent: function(eventObj, fn) { - if (!eventObj) { - return fn; + oneTimeEvent: function (eventObj, fn) { + if (!eventObj) { + return fn; + } + try { + if (!eventObj.cat && !eventObj.act) { + throw new Error('Should inform cat or act'); } - try { - if (!eventObj.cat && !eventObj.act) { - throw new Error('Should inform cat or act'); - } - var eventKey = eventObj.cat && eventObj.act ? eventObj.cat+'_'+eventObj.act : (eventObj.cat || eventObj.act); - if (!_analyticsOneTimeEventFired[eventKey]) { - _analyticsOneTimeEventFired[eventKey] = true; - _event(eventObj, fn); - } - } catch(e) { - console.error('[CatarseAnalytics.oneTimeEvent] error:',e); + var eventKey = eventObj.cat && eventObj.act ? eventObj.cat + '_' + eventObj.act : (eventObj.cat || eventObj.act); + if (!_analyticsOneTimeEventFired[eventKey]) { + _analyticsOneTimeEventFired[eventKey] = true; + _event(eventObj, fn); } + } catch (e) { + console.error('[CatarseAnalytics.oneTimeEvent] error:', e); + } }, checkout: _checkout }; diff --git a/services/catarse/catarse.js/legacy/src/root/project-edit-integrations.js b/services/catarse/catarse.js/legacy/src/root/project-edit-integrations.js index 43aec15ced..736f7ac154 100644 --- a/services/catarse/catarse.js/legacy/src/root/project-edit-integrations.js +++ b/services/catarse/catarse.js/legacy/src/root/project-edit-integrations.js @@ -58,7 +58,7 @@ export class ProjectEditIntegrations { const response = shouldCreate ? await projectVM.updateIntegration(projectId, integrationData) - : + : await projectVM.createIntegration(projectId, integrationData); integrationData.id = response.integration_id; @@ -75,7 +75,7 @@ export class ProjectEditIntegrations { await requestForIntegration(FBPixelTracking); showSuccess(true); - } catch(e) { + } catch (e) { error(true); showError(true); } @@ -137,7 +137,7 @@ export class ProjectEditIntegrations { m('div.w-col.w-col-7', m('div.w-row', [ m('div.text-field.prefix.no-hover.medium.prefix-permalink.w-col.w-col-2.w-col-tiny-2', - m('div.fontcolor-secondary.u-text-center.fontsize-smallest', 'UA-') + m('div.fontcolor-secondary.u-text-center.fontsize-smallest', 'G-') ), m('div.w-col.w-col-10.w-col-tiny-10', m(`input${error() ? '.error' : ''}.text-field.postfix.positive.medium.w-input[type="text"][placeholder="1234567-1"][id="google-analytics-id"]`, { @@ -151,11 +151,11 @@ export class ProjectEditIntegrations { m('div.u-marginbottom-20.card.card-terciary.medium.w-row', [ m('div.w-col.w-col-5', [ m('label.fontweight-semibold.fontsize-base', 'Facebook Pixel'), - m('label.field-label.fontsize-smallest.fontcolor-secondary', [ - 'Envia informações dos visitantes de sua página para o seu Facebook Pixel ', - m('a.alt-link[target="_blank"][href="https://suporte.catarse.me/hc/pt-br/articles/360040152071-Como-integrar-a-campanha-no-Catarse-com-os-an%C3%BAncios-do-Facebook-por-meio-do-Facebook-Pixel-"]', 'Saiba mais') - ]), - m('img[src="/assets/facebook-pixel-logotyp.png"][width="146"][alt=""]') + m('label.field-label.fontsize-smallest.fontcolor-secondary', [ + 'Envia informações dos visitantes de sua página para o seu Facebook Pixel ', + m('a.alt-link[target="_blank"][href="https://suporte.catarse.me/hc/pt-br/articles/360040152071-Como-integrar-a-campanha-no-Catarse-com-os-an%C3%BAncios-do-Facebook-por-meio-do-Facebook-Pixel-"]', 'Saiba mais') + ]), + m('img[src="/assets/facebook-pixel-logotyp.png"][width="146"][alt=""]') ]), m('div.w-col.w-col-7', [ m(`input${error() ? '.error' : ''}.text-field.medium.positive.w-input[type="text"][placeholder="123456789123456"][id="fb-pixel-id"]`, { diff --git a/services/catarse/config/routes.rb b/services/catarse/config/routes.rb index 51a74990a1..38c598e98d 100644 --- a/services/catarse/config/routes.rb +++ b/services/catarse/config/routes.rb @@ -23,12 +23,12 @@ def matches?(request) devise_for(:users, path: '', path_names: { sign_in: :login, sign_out: :logout, sign_up: :sign_up }, - controllers: { passwords: :passwords, sessions: :sessions }, + controllers: { passwords: :passwords, sessions: :sessions, registrations: :registrations }, skip: :omniauth_callbacks ) devise_scope :user do - post '/sign_up', { to: 'devise/registrations#create', as: :sign_up } + post '/sign_up', { to: 'registrations#create', as: :sign_up } get '/not-my-account', to: 'sessions#destroy_and_redirect', as: :not_my_account end