-
Notifications
You must be signed in to change notification settings - Fork 11
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Allow lists (core, custom, search results, articles, filtered or not) to be exported to a CSV file that is sent to e-mail. - [x] Add an `exportList` GraphQL mutation - [x] Implement a generic export class that supports media, articles and feeds - [x] Validate maximum number of results (which is a global configuration key) - [x] Validate permission - [x] Create Sidekiq job to export results - [x] Create a CSV for the export - [x] Save CSV in S3 using a pre-signed URL that expires after X days ("X" is a global configuration key) - [x] Add support to MailCatcher - [x] Send CSV by e-mail - [x] Automated tests - [x] Make sure it works for articles as well - [x] Make sure it works for shared feeds as well References: CV2-5067 and CV2-4979.
- Loading branch information
Showing
22 changed files
with
643 additions
and
8 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,24 @@ | ||
module ExportMutations | ||
class ExportList < Mutations::BaseMutation | ||
argument :query, GraphQL::Types::String, required: true # JSON | ||
argument :type, GraphQL::Types::String, required: true # 'media', 'feed', 'fact-check' or 'explainer' | ||
|
||
field :success, GraphQL::Types::Boolean, null: true | ||
|
||
def resolve(query:, type:) | ||
ability = context[:ability] | ||
team = Team.find_if_can(Team.current.id, ability) | ||
if ability.cannot?(:export_list, team) | ||
{ success: false } | ||
else | ||
export = ListExport.new(type.to_sym, query, team.id) | ||
if export.number_of_rows > CheckConfig.get(:export_csv_maximum_number_of_results, 10000, :integer) | ||
{ success: false } | ||
else | ||
export.generate_csv_and_send_email_in_background(User.current) | ||
{ success: true } | ||
end | ||
end | ||
end | ||
end | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,13 @@ | ||
class ExportListMailer < ApplicationMailer | ||
layout nil | ||
|
||
def send_csv(csv_file_url, user) | ||
@csv_file_url = csv_file_url | ||
@user = user | ||
expire_in = Time.now.to_i + CheckConfig.get('export_csv_expire', 7.days.to_i, :integer) | ||
@expire_in = I18n.l(Time.at(expire_in), format: :email) | ||
subject = I18n.t('mails_notifications.export_list.subject') | ||
Rails.logger.info "Sending export e-mail to #{@user.email}" | ||
mail(to: @user.email, email_type: 'export_list', subject: subject) | ||
end | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,130 @@ | ||
<%= render "shared/header" %> | ||
|
||
<style> | ||
@media only screen and (max-width: 481px) { | ||
.notify-title a { | ||
display: block !important; | ||
width: auto !important; | ||
} | ||
} | ||
|
||
|
||
@media only screen and (max-width: 601px) { | ||
.notify-title__content { | ||
padding: 0 30px; | ||
} | ||
} | ||
|
||
|
||
@media only screen and (max-width: 481px) { | ||
.notify-title__content { | ||
padding: 0 !important; | ||
} | ||
} | ||
</style> | ||
|
||
<!--~~// Notify-title module start \\~~--> | ||
<!--————————————————————————————--> | ||
<div class="notify-title" style="text-align: <%= @direction[:align] %> !important; direction: <%= @direction[:dir] %>"> | ||
<table cellpadding="0" cellspacing="0" border="0" width="100%" style="border-collapse: collapse; mso-table-lspace: 0pt; mso-table-rspace: 0pt;"> | ||
<tr> | ||
<td style="border-collapse: collapse; font-size: 44px; line-height: 1; text-decoration: none !important;"> </td> | ||
</tr> | ||
</table> | ||
<div class="container " width="600" style="margin: 0 auto; text-align: <%= @direction[:align] %>; width: 600px;"> | ||
<table class="container__table" cellpadding="0" cellspacing="0" border="0" width="100%" style="border-collapse: collapse; margin: 0 auto; mso-table-lspace: 0pt; mso-table-rspace: 0pt; table-layout: fixed;"> | ||
<tr> | ||
<th class="col-1" style="font-weight: normal; mso-line-height-rule: exactly; padding: 0;" width="600px" valign="top" align="<%= @direction[:align] %>"> | ||
<div class="notify-title__content" style="text-align:<%= @direction[:align] %>;"> | ||
<div class="h3 notify-title__proj" style="font-size: 21px; letter-spacing: -0.2px; line-height: 29px;"> | ||
<%= I18n.t(:"mails_notifications.export_list.hello", name: @user.name) %> | ||
</div> | ||
<table cellpadding="0" cellspacing="0" border="0" width="100%" style="border-collapse: collapse; mso-table-lspace: 0pt; mso-table-rspace: 0pt;"> | ||
<tr> | ||
<td style="border-collapse: collapse; font-size: 13px; line-height: 1; text-decoration: none !important;"> </td> | ||
</tr> | ||
</table> | ||
<div class="h1 notify-title__header" style="font-size: 40px; font-weight: bold; letter-spacing: -0.8px; line-height: 40px;"> | ||
<%= I18n.t("mails_notifications.export_list.subject") %> | ||
</div> | ||
<table cellpadding="0" cellspacing="0" border="0" width="100%" style="border-collapse: collapse; mso-table-lspace: 0pt; mso-table-rspace: 0pt;"> | ||
<tr> | ||
<td style="border-collapse: collapse; font-size: 12px; line-height: 1; text-decoration: none !important;"> </td> | ||
</tr> | ||
</table> | ||
<div class="text-gray" style="color: #757575 !important;"> | ||
<div class="h3" style="font-size: 21px; letter-spacing: -0.2px; line-height: 30px;"> | ||
<%= I18n.t(:"mails_notifications.export_list.body") %> | ||
</div> | ||
</div> | ||
</div> | ||
|
||
<table cellpadding="0" cellspacing="0" border="0" width="100%" style="border-collapse: collapse; mso-table-lspace: 0pt; mso-table-rspace: 0pt;"> | ||
<tr> | ||
<td style="border-collapse: collapse; font-size: 28px; line-height: 1; text-decoration: none !important;"> </td> | ||
</tr> | ||
</table> | ||
|
||
<!--————————————————————————————--> | ||
<!--~~\\ Notify-title module end //~~--> | ||
|
||
<div class="container wide" width="600" style="margin: 0 auto; text-align: <%= @direction[:align] %>; width: 600px;"> | ||
|
||
<table cellpadding="0" cellspacing="0" border="0" width="100%" style="border-collapse: collapse; mso-table-lspace: 0pt; mso-table-rspace: 0pt;"> | ||
<tr> | ||
<td class="notify-title__button text-white" style="background: #2E77FC; border-collapse: collapse; border-radius: 4px; color: #f1f1f1 !important; display: inline-block; padding-bottom: 15px; padding-left: 25px; padding-right: 23px; padding-top: 15px;" | ||
width="auto"> | ||
<table cellpadding="0" cellspacing="0" border="0" width="100%" style="border-collapse: collapse; mso-table-lspace: 0pt; mso-table-rspace: 0pt;"> | ||
<tr> | ||
<td style="border-collapse: collapse; padding-right: 14px;"> | ||
<span class="span" style="font-size: 17px; font-weight: bold; line-height: 20px;"> | ||
<%= | ||
link_to(I18n.t('mails_notifications.export_list.button_label'), | ||
@csv_file_url, | ||
:style => "text-decoration: none !important;color: #fff !important;" | ||
) | ||
%> | ||
</span> | ||
</td> | ||
<td style="border-collapse: collapse;" align="right"> | ||
<%= image_tag("https://images.ctfassets.net/g118h5yoccvd/#{@direction[:arrow]}", width: "7", alt: "arrow-icon", style: "-ms-interpolation-mode: bicubic; border: 0 none; height: auto; line-height: 100%; outline: none; text-decoration: none;") %> | ||
</td> | ||
</tr> | ||
</table> | ||
</td> | ||
</tr> | ||
</table> | ||
|
||
<table cellpadding="0" cellspacing="0" border="0" width="100%" style="border-collapse: collapse; mso-table-lspace: 0pt; mso-table-rspace: 0pt;"> | ||
<tr> | ||
<td style="border-collapse: collapse; font-size: 23px; line-height: 1; text-decoration: none !important;"> </td> | ||
</tr> | ||
</table> | ||
|
||
<div class="text-gray" style="color: #757575 !important;"> | ||
<div class="h3" style="font-size: 21px; letter-spacing: -0.2px; line-height: 30px;"> | ||
<%= I18n.t(:"mails_notifications.export_list.footer", date: @expire_in) %> | ||
</div> | ||
</div> | ||
</div> | ||
<table cellpadding="0" cellspacing="0" border="0" width="100%" style="border-collapse: collapse; mso-table-lspace: 0pt; mso-table-rspace: 0pt;"> | ||
<tr> | ||
<td style="border-collapse: collapse; font-size: 23px; line-height: 1; text-decoration: none !important;"> </td> | ||
</tr> | ||
</table> | ||
<style> | ||
@media only screen and (max-width: 601px) { | ||
th.footer { | ||
padding: 0 30px !important; | ||
} | ||
} | ||
|
||
|
||
@media only screen and (max-width: 481px) { | ||
th.footer { | ||
padding: 0 !important; | ||
} | ||
} | ||
</style> | ||
|
||
<%= render "shared/footer" %> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,14 @@ | ||
<%= I18n.t('mails_notifications.export_list.hello', name: @user.name) %> | ||
|
||
<%= I18n.t('mails_notifications.export_list.subject') %> | ||
|
||
<%= I18n.t('mails_notifications.export_list.body') %> | ||
|
||
<%= I18n.t('mails_notifications.export_list.button_label') %>: <%= @csv_file_url %> | ||
|
||
<%= I18n.t('mails_notifications.export_list.footer', date: @expire_in ) %> | ||
|
||
... | ||
|
||
<%= strip_tags I18n.t("mails_notifications.copyright_html", app_name: CheckConfig.get('app_name')) %> | ||
https://meedan.com |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,2 +1,2 @@ | ||
# Load classes on boot, in production, that otherwise wouldn't be auto-loaded by default | ||
CcDeville && Bot::Keep && Workflow::Workflow.workflows && CheckS3 && Bot::Tagger && Bot::Fetch && Bot::Smooch && Bot::Slack && Bot::Alegre && CheckChannels && RssFeed && UrlRewriter && ClusterTeam | ||
CcDeville && Bot::Keep && Workflow::Workflow.workflows && CheckS3 && Bot::Tagger && Bot::Fetch && Bot::Smooch && Bot::Slack && Bot::Alegre && CheckChannels && RssFeed && UrlRewriter && ClusterTeam && ListExport |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.