Revision 743

Date:
2018/11/14 22:54:18
Author:
ahitrov
Revision Log:
Email-sender system

Files:

Legend:

 
Added
 
Removed
 
Modified
  • utf8/core/lib/Contenido/Accessor.pm

     
    1 package Contenido::Accessor;
    2
    3 sub mk_accessors {
    4 my ($class, @names) = @_;
    5
    6 foreach my $name (@names) {
    7 no strict 'refs';
    8 *{"${class}::$name"} = sub {
    9 my $self = shift;
    10 if ( @_ ) {
    11 return $self->{$name} = shift;
    12 } else {
    13 return $self->{$name};
    14 }
    15 };
    16 }
    17 }
    18
    19 1;
  • utf8/core/lib/Contenido/Email.pm

     
    3 3 use strict;
    4 4 use base 'Contenido::Document';
    5 5 use Contenido::Globals;
    6 use SQL::MailQueueTable;
    6 7
    7 8 sub extra_properties
    8 9 {
    9
    10 10 return (
    11 11 { 'attr' => 'status',
    12 12 'cases' => [
     
    20 20 { 'attr' => 'cc', 'type' => 'text', 'rusname' => 'Дополнительные получатели (по одному на строку)', rows => 10 },
    21 21 { 'attr' => 'body_text', 'type' => 'text', 'rusname' => 'Текст письма (txt)', 'rows' => 40 },
    22 22 { 'attr' => 'body_html', 'type' => 'wysiwyg', 'rusname' => 'Текст письма (html)', 'rows' => 40 },
    23 { 'attr' => 'error', 'type' => 'text', 'rusname' => 'Сообщение об ошибке', 'rows' => 5 },
    23 24 { 'attr' => 'pictures', 'type' => 'images', 'rusname' => 'Список иллюстраций', preview => ['400x400'], crop => ['870x450','85x85'] },
    24 25 )
    25 26 }
  • utf8/core/lib/Contenido/Init.pm

     
    33 33 use Contenido::Section;
    34 34 use Contenido::Keeper;
    35 35 use Contenido::Image;
    36 use Contenido::Mail;
    37 use Contenido::Email;
    36 38 # Типы данных
    37 39 use Contenido::Type::File;
    38 40
     
    239 241 push (@{ $state->{'available_sections'} }, 'Contenido::Section');
    240 242 push (@{ $state->{'available_users'} }, 'Contenido::User');
    241 243 push (@{ $state->{'available_links'} }, 'Contenido::Link');
    244 if ( $state->email_enable ) {
    245 push (@{ $state->{'available_documents'} }, 'Contenido::Email');
    246 }
    242 247
    243 248 return $LR;
    244 249 }
  • utf8/core/lib/Contenido/Keeper.pm

     
    1
    2 1 package Contenido::Keeper;
    3 2
    4 3 # ----------------------------------------------------------------------------
  • utf8/core/lib/Contenido/Mail.pm

     
    1 1 package Contenido::Mail;
    2 2
    3 use strict;
    4 use warnings;
    5
    3 6 use Net::SMTP;
    4 7 use MIME::Lite;
    5 8 use MIME::Base64;
    6 9 use Data::Dumper;
    10
    11 use parent 'Contenido::Accessor';
    12 __PACKAGE__->mk_accessors(qw(enable mailer from login password server hello timeout ssl port));
    13
    7 14 use Contenido::Globals;
    15 use Contenido::Email;
    8 16
    9 17 sub new {
    10 my ($proto) = @_;
    18 my ($proto, $args) = @_;
    11 19 my $class = ref($proto) || $proto;
    20 $args //= {};
    12 21 my $self = {};
    22 bless $self, $class;
    13 23
    14 $self->{enable} = $state->{email_enable};
    15 if ( $self->{enable} ) {
    16 $self->{mailer} = $state->{email_mailer};
    17 $self->{from} = $state->{email_from};
    18 $self->{login} = $state->{email_auth_login};
    19 $self->{password} = $state->{email_auth_password};
    20 if ( $self->{mailer} eq 'smtp' ) {
    21 $self->{server} = $state->{email_smtp_server};
    22 $self->{hello} = $state->{email_smtp_hello};
    23 $self->{timeout} = $state->{email_smtp_timeout};
    24 $self->{ssl} = $state->{email_smtp_ssl};
    25 $self->{port} = $state->{email_smtp_port};
    24 $self->enable( $state->{email_enable} );
    25 if ( $self->enable ) {
    26 $self->mailer( $state->{email_mailer} );
    27 $self->from( $state->{email_from} );
    28 $self->login( delete $args->{login} || $state->{email_auth_login} );
    29 $self->password( delete $args->{password} || $state->{email_auth_password} );
    30 if ( $self->mailer eq 'smtp' ) {
    31 $self->server( $state->{email_smtp_server} );
    32 $self->hello( $state->{email_smtp_hello} );
    33 $self->timeout( $state->{email_smtp_timeout} );
    34 $self->ssl( $state->{email_smtp_ssl} );
    35 $self->port( $state->{email_smtp_port} );
    26 36 }
    27 37 }
    28 38
    29 bless $self, $class;
    39 warn Dumper $self;
    30 40 return $self;
    31 41 }
    32 42
     
    35 45 my $self;
    36 46 if ( ref $_[0] eq 'Contenido::Mail' ) {
    37 47 $self = shift;
    48 } elsif ( !ref $_[0] && $_[0] eq 'Contenido::Mail' ) {
    49 my $class = shift;
    50 $self = $class->new;
    38 51 } else {
    39 52 $self = Contenido::Mail->new;
    40 53 }
     
    52 65 $subject =~ s/\s//sgi;
    53 66 $subject = '=?utf-8?B?'.$subject.'?=';
    54 67
    68 my $error;
    55 69 my $emailfrom;
    56 70 if ( $email->{from} ) {
    57 71 my ($from, $efrom) = $email->{from} =~ /^(.*?)<(.*?)>/ ? ($1, $2) : $email->{from} =~ /<(.*?)>/ ? ('',$1) : ('',$email->{from});
     
    148 162 $mailer->dataend;
    149 163 $mailer->quit;
    150 164 } else {
    151 warn "MAIL ERROR! Can't connect to Yandex SMTP\n";
    165 $error = "MAIL ERROR! Can't connect to SMTP";
    166 warn "$error\n";
    152 167 }
    153 168 }
    169 return $error;
    154 170 }
    155 171
    156 172
    173 sub add {
    174 return unless @_;
    175 my $self;
    176 if ( ref $_[0] eq 'Contenido::Mail' ) {
    177 $self = shift;
    178 } elsif ( !ref $_[0] && $_[0] eq 'Contenido::Mail' ) {
    179 my $class = shift;
    180 $self = $class->new;
    181 } else {
    182 $self = Contenido::Mail->new;
    183 }
    184
    185 my $opts = shift // {};
    186
    187 my $email = delete $opts->{email} // return undef;
    188 return unless ref $email && exists $email->{to} && $email->{subject} && $email->{body};
    189
    190 my $que = Contenido::Email->new( $keeper );
    191 $que->status( 0 );
    192 $que->name( $email->{to} );
    193 $que->subject( $email->{subject} );
    194 $que->body_html( $email->{body} );
    195 if ( exists $email->{text} ) {
    196 $que->body_text( $email->{text} );
    197 }
    198 if ( exists $email->{cc} && ref $email->{cc} eq 'ARRAY' ) {
    199 $que->cc( join("\n", @{$email->{cc}}) );
    200 } elsif ( exists $email->{cc} && $email->{cc} ) {
    201 $que->cc( $email->{cc} );
    202 }
    203 if ( exists $email->{date} && ref $email->{date} eq 'DateTime' ) {
    204 $que->dtime( $email->{date}->ymd('-').' '.$email->{date}->hms );
    205 } elsif ( exists $email->{date} && $email->{date} ) {
    206 my $dt;
    207 eval{ $dt = Contenido::DateTime->new( postgres => $email->{date} ) };
    208 if ( ref $dt ) {
    209 $que->dtime( $dt->ymd('-').' '.$dt->hms );
    210 }
    211 } else {
    212 my $now = Contenido::DateTime->new;
    213 $que->dtime( $now->ymd('-').' '.$now->hms );
    214 }
    215 if ( $que->store ) {
    216 return $que;
    217 }
    218 return undef;
    219 }
    220
    157 221 1;
  • utf8/core/lib/Contenido/State.pm.proto

     
    150 150 $self->{email_from} = '@EMAIL_FROM@';
    151 151 $self->{email_auth_login} = '@EMAIL_LOGIN@';
    152 152 $self->{email_auth_password} = '@EMAIL_PASSWORD@';
    153 warn "Password: ".$self->{email_auth_password}."\n";
    153 154 if ( $self->{email_mailer} eq 'smtp' ) {
    154 155 $self->{email_smtp_server} = '@EMAIL_SMTP_SERVER@';
    155 156 $self->{email_smtp_hello} = '@EMAIL_SMTP_HELLO@';
  • utf8/core/services/mail_process.pl

     
    1 #!/usr/bin/perl
    2
    3 use strict;
    4 use warnings "all";
    5 use locale;
    6
    7 BEGIN { require 'inc.pl' };
    8
    9 use Contenido::Globals;
    10 use Contenido::Init;
    11 use ErrorTee;
    12 use PidFile;
    13 use Data::Dumper;
    14
    15 # begin
    16 Contenido::Init->init();
    17
    18 my $keeper_module = $state->project.'::Keeper';
    19 $keeper = $keeper_module->new($state);
    20
    21 #PidFile->new($keeper, compat=>1); # db-based locking (run only on one host)
    22 #PidFile->new($keeper, compat=>1, per_host=>1); # db-based locking (run on whole cluster)
    23
    24 ############################################
    25 # please use:
    26 # $state->{log_dir} for logging
    27 # $state->{tmp_dir} for temporary files
    28 ###########################################
    29 $keeper->t_connect() || die $keeper->error();
    30 my $ids = $keeper->TSQL->selectall_arrayref("SELECT id FROM mailqueue WHERE status = 0 and dtime <= CURRENT_TIMESTAMP ORDER BY ctime LIMIT 5");
    31 if ( ref $ids eq 'ARRAY' && @$ids ) {
    32 $keeper->TSQL->do("UPDATE mailqueue SET status = 2 WHERE ID IN (".join(',', '?' x scalar @$ids).")", {}, map { $_->[0] } @$ids);
    33 }
    34 $keeper->t_finish();
    35
    36 if ( @$ids ) {
    37 my $sendmail = Contenido::Mail->new({password => 'g8$feds1'});
    38 foreach my $row ( @$ids ) {
    39 my $mail = $keeper->get_document_by_id($row->[0], class => 'Contenido::Email');
    40 if ( ref $mail ) {
    41 my $data = {
    42 to => $mail->name,
    43 subject => $mail->subject,
    44 body => $mail->body_html,
    45 };
    46 if ( $mail->cc ) {
    47 $data->{cc} = [split /\n/, $mail->cc];
    48 }
    49 if ( $mail->body_text ) {
    50 $data->{text} = $mail->body_text;
    51 }
    52 if ( my $err = $sendmail->send({ email => $data }) ) {
    53 $mail->status(3);
    54 $mail->error( $err );
    55 } else {
    56 $mail->status(2);
    57 }
    58 $mail->store;
    59 }
    60 }
    61 }
  • utf8/core/sql/TOAST/mailqueue.sql

     
    15 15 subject text,
    16 16 data text
    17 17 );
    18 create index mailqueue_sections on mailqueue (sections);
    18 create index mailqueue_actual on mailqueue (status, dtime) WHERE status = 0;
    19 19 create index mailqueue_dtime on mailqueue (dtime);

Небольшая справка по веткам

cnddist – контейнер, в котором хранятся все дистрибутивы всех библиотек и программных пакетов, которые использовались при построении различных версий Contenido. Если какой-то библиотеки в данном хранилище нет, инсталлятор сделает попытку "подтянуть" ее с веба (например, с CPAN). Если библиотека слишком старая, есть очень большая вероятность, что ее там уже нет. Поэтому мы храним весь хлам от всех сборок. Если какой-то дистрибутив вдруг отсутствует в cnddist - напишите нам, мы положим его туда.

koi8 – отмирающая ветка, чей код, выдача и все внутренние библиотеки заточены на кодировку KOI8-R. Вносятся только те дополнения, которые касаются внешнего вида и функционала админки, баги ядра, обязательные обновления портов и мелочи, которые легко скопипастить. В дальнейшем планируется полная остановка поддержки по данной ветке.

utf8 – актуальная ветка, заточенная под UTF-8.

Внутри каждой ветки: core – исходники ядра; install – скрипт установки инсталляции; plugins – плагины; samples – "готовые к употреблению" проекты, которые можно поставить, запустить и посмотреть, как они работают.