{"id":22835262,"url":"https://github.com/gueff/mymvc_module_email","last_synced_at":"2025-03-31T02:44:12.892Z","repository":{"id":96371228,"uuid":"226177842","full_name":"gueff/myMVC_module_Email","owner":"gueff","description":"E-Mail Spooler Module for myMVC","archived":false,"fork":false,"pushed_at":"2023-05-01T16:35:50.000Z","size":88,"stargazers_count":2,"open_issues_count":0,"forks_count":1,"subscribers_count":2,"default_branch":"1.3.x","last_synced_at":"2025-02-06T07:46:22.799Z","etag":null,"topics":["email","mymvc","mymvc-module","php"],"latest_commit_sha":null,"homepage":"","language":"PHP","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"gpl-3.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/gueff.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE.md","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2019-12-05T19:52:08.000Z","updated_at":"2023-05-29T14:15:31.000Z","dependencies_parsed_at":null,"dependency_job_id":"1323a96e-5d0d-4a58-b297-be0b764d6bd6","html_url":"https://github.com/gueff/myMVC_module_Email","commit_stats":null,"previous_names":[],"tags_count":4,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/gueff%2FmyMVC_module_Email","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/gueff%2FmyMVC_module_Email/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/gueff%2FmyMVC_module_Email/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/gueff%2FmyMVC_module_Email/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/gueff","download_url":"https://codeload.github.com/gueff/myMVC_module_Email/tar.gz/refs/heads/1.3.x","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":246408099,"owners_count":20772228,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["email","mymvc","mymvc-module","php"],"created_at":"2024-12-12T22:08:51.275Z","updated_at":"2025-03-31T02:44:12.881Z","avatar_url":"https://github.com/gueff.png","language":"PHP","funding_links":[],"categories":[],"sub_categories":[],"readme":"\nEmails to be sent are processed via a spooler. \nFor each of the different states (new, done, retry, fail) there are separate folders into which the emails are moved. \nData type classes are available for composing emails and attachments, which simplify the declaration. \nEmails are saved as JSON files after delivery.\n\n---\n\n# Requirements\n\n- Linux\n- php \u003e=7.4\n- myMVC\n  - myMVC 3.2.x: https://github.com/gueff/myMVC/tree/3.2.x\n  - ZIP: https://github.com/gueff/myMVC/archive/refs/heads/3.2.x.zip\n  - Doku: https://mymvc.ueffing.net/\n\n---\n\n## Installation\n\n_cd into the modules folder of your `myMVC3.2.x` copy; e.g.:_\n~~~bash\ncd /var/www/myMVC/modules/;\n~~~\n\n_clone `myMVC_module_Email` as `Email`_\n~~~bash\ngit clone https://github.com/gueff/myMVC_module_Email.git Email;\n~~~\n\n_run install shell script_  \n~~~bash\n./_install.sh\n~~~\n\n\n## Config\n\nadd this config to the config of your primary working module.\n\n~~~php\n//-------------------------------------------------------------------------------------\n// Module Email\n\n$aConfig['MODULE']['Email'] = array(\n\n    // Spooler Folder\n    'sAbsolutePathToFolderSpooler' =\u003e $aConfig['MVC_MODULES_DIR'] . '/Email/etc/data/spooler/',\n\n    // Attachment Folder\n    'sAbsolutePathToFolderAttachment' =\u003e $aConfig['MVC_MODULES_DIR'] . '/Email/etc/data/attachment/',\n\n    // Number of e-mails to be processed simultaneously\n    'iAmountToSpool' =\u003e 50,\n\n    // max. time span for new delivery attempts (from \"retry\")\n    'iMaxSecondsOfRetry' =\u003e (60 * 60 * 24 * 1), // 24h\n\n    // max. time AFTER RETRY (`iMaxSecondsOfRetry`)\n    // files in spooler will finally be deleted\n    // so in fact calculation is: (iMaxSecondsOfRetry + iMaxSecondsOfDeletion)\n    'iMaxSecondsOfDeletionAfterRetry' =\u003e (60 * 60 * 12 * 1), // x after iMaxSecondsOfRetry\n\n    // callback function\n    'oCallback' =\u003e function($oEmail) {\n\n        // E-Mail Versand via SMTP\n        return \\Email\\Model\\Smtp::sendViaPhpMailer($oEmail);\n\n        $oResponse = \\MVC\\DataType\\DTArrayObject::create()\n            -\u003eadd_aKeyValue(\\MVC\\DataType\\DTKeyValue::create()-\u003eset_sKey('bSuccess')-\u003eset_sValue(true))\n            -\u003eadd_aKeyValue(\\MVC\\DataType\\DTKeyValue::create()-\u003eset_sKey('sMessage')-\u003eset_sValue(\"SUCCESS\\t\" . ' *** Closure *** '))\n            -\u003eadd_aKeyValue(\\MVC\\DataType\\DTKeyValue::create()-\u003eset_sKey('oException')-\u003eset_sValue(null));\n\n        return $oResponse;\n    },\n    \n    /**\n     * SMTP account settings\n     */\n    'sHost' =\u003e '',\n    'iPort' =\u003e 465, # ssl=465 | tls=587\n    'sSecure' =\u003e 'ssl', # ssl | tls\n    'bAuth' =\u003e true,\n    'sUsername' =\u003e '',\n    'sPassword' =\u003e '',\n);\n~~~\n\n---\n\n## Usage\n\n..in your primary working Module:\n\n_init_  \n~~~php\n$oEmailController = new \\Email\\Controller\\Index(\n  Config::MODULE('Email')\n);\n~~~\n\n_create and save Email_  \n~~~php\n// create email\n$oEmail = Email::create()\n    -\u003eset_subject('Example Subject')\n    -\u003eset_recipientMailAdresses(array('bar@example.com',))\n    -\u003eset_senderMail('foo@example.com')\n    -\u003eset_senderName('foo')\n    -\u003eset_text(\"Foo\\nbar\\n\")\n    -\u003eset_html('\u003ch1\u003eFoo\u003c/h1\u003e\u003cp\u003ebar\u003c/p\u003e')\n    -\u003eset_oAttachment(DTArrayObject::create()\n        // 1. attachment\n        -\u003eadd_aKeyValue(DTKeyValue::create()-\u003eset_sKey('oEmailAttachment')-\u003eset_sValue(EmailAttachment::create()\n            -\u003eset_file('/tmp/foo.txt')\n            -\u003eset_name('foo.txt')       // \u003c== optional; to overwrite original filename\n        ))\n    );\n// save email\n$oEmailController-\u003eoModelEmail-\u003esaveToSpooler(\n    $oEmail\n);\n~~~\n\n_Processes the mails to be sent in the spooler folder_ \n~~~php\n$oEmailController-\u003espool();\n~~~\n\n_Escalation to failed mails_  \n~~~php\n$oEmailController-\u003eescalate();\n~~~\n\n_Deletes older emails and attachments from spooler_    \n~~~php\n$oEmailController-\u003ecleanup();\n~~~\n\n---\n\n## Explaining Spool\n\n- E-mail files from the `retry` folder are read and moved to either `new` or `fail`, depending on the \nwhether the maximum time for retry attempts ($iMaxSecondsOfRetry) for retry mails has been reached or not.\n- There is still time for new delivery attempts_: E-Mail files are moved to the folder `new`.\n- There is **no** time left for new delivery attempts_: Email files are moved to the `fail` folder\n- E-mail files from the `new` folder are read and sent.\n- _successful_: E-mail files are moved to the `done` folder.\n- _failed_: Email files are moved to the `retry` folder\n\nThe maximum time period for new delivery attempts is defined in the config (see above) with the key `iMaxSecondsOfRetry`.\n\n---\n\n## Pro Tip for your main module\n\n### adding routes and calling via cronjob\n\ninstead of running `spool` and `escalate` during runtime, consider calling them \nseparately via cronjob.\n\n_add routes to your main module_  \n~~~php\n// spool mails\n\\MVC\\Route::GET(\n    '/mail/spool/',\n    'module=Email\u0026c=Index\u0026m=spool'\n);\n// escalate mails\n\\MVC\\Route::GET(\n    '/mail/escalate/',\n    'module=Email\u0026c=Index\u0026m=escalate'\n);\n// cleanup spooler\n\\MVC\\Route::GET(\n    '/mail/cleanup/',\n    'module=Email\u0026c=Index\u0026m=cleanup'\n);\n~~~\n\n_then add cronjobs which call those routes_ \n~~~shell\n# send emails\n* * * * * cd /var/www/BLG/public; /usr/bin/php index.php \"/mail/spool/\" \u003e /dev/null 2\u003e/dev/null;\n# escalate mails\n* * * * * cd /var/www/BLG/public; /usr/bin/php index.php \"/mail/escalate/\" \u003e /dev/null 2\u003e/dev/null;\n# cleanup spooler\n* * * * * cd /var/www/BLG/public; /usr/bin/php index.php \"/mail/cleanup/\" \u003e /dev/null 2\u003e/dev/null;\n~~~\n\n### Logging\n\nif you want to log operations in the module, you should add these event listeners to your module\n\n~~~php\n$aEvent = [\n  \n  // email module\n  'email.model.index.saveToSpooler.done' =\u003e array(\n      function(\\MVC\\DataType\\DTArrayObject $oDTArrayObject) {\n          $sFilename = $oDTArrayObject-\u003egetDTKeyValueByKey('sFilename')-\u003eget_sValue();\n          $sData = $oDTArrayObject-\u003egetDTKeyValueByKey('sData')-\u003eget_sValue();\n          $sSuccess = (true === $oDTArrayObject-\u003egetDTKeyValueByKey('bSuccess')-\u003eget_sValue()) ? 'true' : 'false';\n          $sMessage = 'save: ' . $sData . ' =\u003e ' . $sFilename . ' (success: ' . $sSuccess . ')';\n          \\MVC\\Log::write($sMessage, 'mail.log');\n      }\n  ),\n  'email.model.index.spool' =\u003e array(\n      function(\\MVC\\DataType\\DTArrayObject $oDTArrayObject) {\n          $oSendResponse = $oDTArrayObject-\u003egetDTKeyValueByKey('oSendResponse')-\u003eget_sValue();\n          $oSpoolResponse = $oDTArrayObject-\u003egetDTKeyValueByKey('oSpoolResponse')-\u003eget_sValue();\n            \\MVC\\Log::write(json_encode(\\MVC\\Convert::objectToArray($oSendResponse)), 'mail.log');\n            \\MVC\\Log::write(json_encode(\\MVC\\Convert::objectToArray($oSpoolResponse)), 'mail.log');\n      }\n  ),\n  'email.model.index._handleRetries' =\u003e array(\n      function(\\MVC\\DataType\\DTArrayObject $oDTArrayObject) {\n          $sOldname = $oDTArrayObject-\u003egetDTKeyValueByKey('sOldname')-\u003eget_sValue();\n          $sNewname = $oDTArrayObject-\u003egetDTKeyValueByKey('sNewname')-\u003eget_sValue();\n          $sMoveSuccess = (true === $oDTArrayObject-\u003egetDTKeyValueByKey('bMoveSuccess')-\u003eget_sValue()) ? 'true' : 'false';\n          $aMessage = $oDTArrayObject-\u003egetDTKeyValueByKey('aMessage')-\u003eget_sValue();\n          \\MVC\\Log::write('email (retry: ' . $sMoveSuccess . '),  ' . $sOldname . ' =\u003e ' . $sMoveSuccess, 'mail.log');\n  \n          foreach ($aMessage as $sMessage)\n          {\n              \\MVC\\Log::write('email (retry: ' . $sMoveSuccess . '),  ' . $sMessage, 'mail.log');\n          }\n      }\n  ),\n  'email.model.index.escalate' =\u003e array(\n      function(\\MVC\\DataType\\DTArrayObject $oDTArrayObject) {\n          $sMailFileName = $oDTArrayObject-\u003egetDTKeyValueByKey('sMailFileName')-\u003eget_sValue();\n          $sEscalatedFileName = $oDTArrayObject-\u003egetDTKeyValueByKey('sEscalatedFileName')-\u003eget_sValue();\n          $sMessage = 'escalate: ' . $sMailFileName . ' =\u003e ' . $sEscalatedFileName;\n          \\MVC\\Log::write($sMessage, 'mail.log');\n      }\n  ),\n  'email.model.index.deleteEmailFile' =\u003e array(\n      function(\\MVC\\DataType\\DTArrayObject $oDTArrayObject) {\n          $sUnlink = (true === $oDTArrayObject-\u003egetDTKeyValueByKey('bUnlink')-\u003eget_sValue()) ? 'true' : 'false';\n          $sAbsoluteFilePath = $oDTArrayObject-\u003egetDTKeyValueByKey('sFile')-\u003eget_sValue();\n          $sMessage = 'email (del: ' . $sUnlink . '), ' . $sAbsoluteFilePath;\n          \\MVC\\Log::write($sMessage, 'mail.log');\n      }\n  ),\n  'email.model.index.deleteEmailAttachment' =\u003e array(\n      function(\\MVC\\DataType\\DTArrayObject $oDTArrayObject) {\n          $sUnlink = (true === $oDTArrayObject-\u003egetDTKeyValueByKey('bUnlink')-\u003eget_sValue()) ? 'true' : 'false';\n          $sAbsoluteFilePath = $oDTArrayObject-\u003egetDTKeyValueByKey('sFile')-\u003eget_sValue();\n          $sMessage = 'attachment (del: ' . $sUnlink . '), ' . $sAbsoluteFilePath;\n          \\MVC\\Log::write($sMessage, 'mail.log');\n      }\n  ),\n];\n\n#-------------------------------------------------------------\n# process: bind the declared ones\n\nif ('develop' === \\MVC\\Config::get_MVC_ENV())\n{\n    \\MVC\\Event::processBindConfigStack($aEvent);\n}\n~~~\n\n---\n\n## Module Events\n\n`email.model.index.saveToSpooler.done`\n~~~\nEvent::RUN('email.model.index.saveToSpooler.done',\n    DTArrayObject::create()\n        -\u003eadd_aKeyValue(DTKeyValue::create()-\u003eset_sKey('sFilename')-\u003eset_sValue($sFilename))\n        -\u003eadd_aKeyValue(DTKeyValue::create()-\u003eset_sKey('sData')-\u003eset_sValue($sData))\n        -\u003eadd_aKeyValue(DTKeyValue::create()-\u003eset_sKey('bSuccess')-\u003eset_sValue($bSuccess))\n);\n~~~\n\n`email.model.index.spool`\n~~~\nEvent::RUN('email.model.index.spool',  \n    DTArrayObject::create()\n    -\u003eadd_aKeyValue(DTKeyValue::create()-\u003eset_sKey('oSendResponse')-\u003eset_sValue($oSendResponse)) // bSuccess, sMessage, oException\n    -\u003eadd_aKeyValue(DTKeyValue::create()-\u003eset_sKey('oSpoolResponse')-\u003eset_sValue($oSpoolResponse))    \n);\n~~~\n\n`email.model.index._handleRetries`\n~~~\nEvent::RUN('email.model.index._handleRetries',\n    DTArrayObject::create()\n        -\u003eadd_aKeyValue(DTKeyValue::create()-\u003eset_sKey('sOldname')-\u003eset_sValue($sOldName))\n        -\u003eadd_aKeyValue(DTKeyValue::create()-\u003eset_sKey('sNewname')-\u003eset_sValue($sNewName))\n        -\u003eadd_aKeyValue(DTKeyValue::create()-\u003eset_sKey('bMoveSuccess')-\u003eset_sValue($bRename))\n        -\u003eadd_aKeyValue(DTKeyValue::create()-\u003eset_sKey('aMessage')-\u003eset_sValue($aMsg))\n);\n~~~\n\n`email.model.index.escalate`\n~~~\n\\MVC\\Event::RUN('email.model.index.escalate',\n    DTArrayObject::create()\n        -\u003eadd_aKeyValue(\n            DTKeyValue::create()-\u003eset_sKey('sMailFileName')-\u003eset_sValue($sMailFileName)\n        )\n        -\u003eadd_aKeyValue(\n            DTKeyValue::create()-\u003eset_sKey('sEscalatedFileName')-\u003eset_sValue($sEscalatedFileName)\n        )\t\t\t\t\t\n);\n~~~\n\n`email.model.index.deleteEmailAttachment`\n~~~\nEvent::RUN(\n    'email.model.index.deleteEmailAttachment',\n    DTArrayObject::create()\n        -\u003eadd_aKeyValue(\n            DTKeyValue::create()\n                -\u003eset_sKey('bUnlink')\n                -\u003eset_sValue($bUnlink)\n        )\n        -\u003eadd_aKeyValue(\n            DTKeyValue::create()\n                -\u003eset_sKey('sFile')\n                -\u003eset_sValue($sAbsoluteFilePath)\n        )\n);\n~~~\n\n`email.model.index.deleteEmailFile`\n~~~\nEvent::RUN(\n    'email.model.index.deleteEmailFile',\n    DTArrayObject::create()\n        -\u003eadd_aKeyValue(\n            DTKeyValue::create()\n                -\u003eset_sKey('bUnlink')\n                -\u003eset_sValue($bUnlink)\n        )\n        -\u003eadd_aKeyValue(\n            DTKeyValue::create()\n                -\u003eset_sKey('sFile')\n                -\u003eset_sValue($sAbsoluteFilePath)\n        )\n);\n~~~\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fgueff%2Fmymvc_module_email","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fgueff%2Fmymvc_module_email","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fgueff%2Fmymvc_module_email/lists"}