├── .gitignore ├── .travis.yml ├── CHANGELOG.md ├── CONTRIBUTING.md ├── LICENSE ├── composer.json ├── phpunit.xml ├── project.codestyle.json ├── readme.md ├── src ├── Exceptions │ └── AuthException.php ├── Facade │ └── LaravelGmail.php ├── GmailConnection.php ├── LaravelGmailClass.php ├── LaravelGmailServiceProvider.php ├── Services │ ├── Message.php │ ├── Message │ │ ├── Attachment.php │ │ └── Mail.php │ └── MessageCollection.php ├── Traits │ ├── Configurable.php │ ├── Filterable.php │ ├── HasDecodableBody.php │ ├── HasHeaders.php │ ├── HasLabels.php │ ├── HasParts.php │ ├── Modifiable.php │ ├── ModifiesLabels.php │ ├── Replyable.php │ └── SendsParameters.php └── config │ └── gmail.php ├── stale.yml └── tests ├── LaravelGmailTest.php └── TestCase.php /.gitignore: -------------------------------------------------------------------------------- 1 | /vendor 2 | composer.phar 3 | composer.lock 4 | .DS_Store 5 | Thumbs.db 6 | .idea -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: php 2 | 3 | php: 4 | - 5.6 5 | - 7.0 6 | - 7.2 7 | - 7.3 8 | - 7.4 9 | - 8.0.1 10 | 11 | install: travis_retry composer install --no-interaction --prefer-source 12 | 13 | script: 14 | # - mkdir -p build/logs 15 | # - php vendor/bin/phpunit -c phpunit.xml.dist` 16 | # - phpunit --coverage-text --coverage-clover=coverage.clover 17 | # - phpunit --coverage-clover build/logs/clover.xml 18 | 19 | after_script: 20 | # - wget https://scrutinizer-ci.com/ocular.phar 21 | # - php ocular.phar code-coverage:upload --format=php-clover coverage.clover 22 | # - travis_retry php vendor/bin/coveralls -v 23 | 24 | notifications: 25 | # slack: red-creek:5lI8ybvl6YTcCNPosh4TE13h 26 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # CHANGELOG 2 | 3 | ## 0.1 4 | 5 | First -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # CONTRIBUTING 2 | 3 | Contributions are welcome, and are accepted via pull requests. Please review these guidelines before submitting any pull requests. 4 | 5 | ## Guidelines 6 | 7 | - Please follow the [PSR-2 Coding Style Guide](http://www.php-fig.org/psr/psr-2), enforced by [StyleCI](https://styleci.io). 8 | - Ensure that the current tests pass, and if you've added something new, add the tests where relevant. 9 | - Send a coherent commit history, making sure each individual commit in your pull request is meaningful. 10 | - You may need to [rebase](https://git-scm.com/book/en/v2/Git-Branching-Rebasing) to avoid merge conflicts. 11 | - If you are changing the behavior, or the public api, you may need to update the docs. 12 | - Please remember that we follow [SemVer](http://semver.org). 13 | 14 | ## Running Tests 15 | 16 | You will need an install of [Composer](https://getcomposer.org) before continuing. 17 | 18 | First, install the dependencies: 19 | 20 | ```sh 21 | $ composer install 22 | ``` 23 | 24 | Then run [PHPUnit](https://phpunit.de): 25 | 26 | ```sh 27 | $ vendor/bin/phpunit 28 | ``` 29 | 30 | If the test suite passes on your local machine you should be good to go. 31 | 32 | When you make a pull request, the tests will automatically be run again by [Travis CI](https://travis-ci.org). -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) Daniel Castro 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy of 6 | this software and associated documentation files (the "Software"), to deal in 7 | the Software without restriction, including without limitation the rights to 8 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 9 | the Software, and to permit persons to whom the Software is furnished to do so, 10 | subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 17 | FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 18 | COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 19 | IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 20 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "dacastro4/laravel-gmail", 3 | "description": "Gmail API package for Laravel", 4 | "keywords": [ 5 | "api", 6 | "laravel", 7 | "Gmail" 8 | ], 9 | "license": "MIT", 10 | "authors": [ 11 | { 12 | "name": "Daniel Castro", 13 | "email": "danielcastro04@gmail.com", 14 | "homepage": "https://danielcastro.dev" 15 | } 16 | ], 17 | "require": { 18 | "php": "^7.4|^8.0", 19 | "google/apiclient": "^v2.12.6", 20 | "illuminate/auth": "~5.8|^6.0|^7.0|^8.0|^9.0|^10.0", 21 | "illuminate/config": "~5.8|^6.0|^7.0|^8.0|^9.0|^10.0", 22 | "illuminate/database": "~5.8|^6.0|^7.0|^8.0|^9.0|^10.0", 23 | "illuminate/routing": "~5.8|^6.0|^7.0|^8.0|^9.0|^10.0", 24 | "illuminate/session": "~5.8|^6.0|^7.0|^8.0|^9.0|^10.0", 25 | "illuminate/support": "~5.8|^6.0|^7.0|^8.0|^9.0|^10.0", 26 | "symfony/mime": "^5.4|^6.0", 27 | "ext-json": "*" 28 | }, 29 | "require-dev": { 30 | "phpunit/phpunit": "^9.3", 31 | "mockery/mockery": "^1.0", 32 | "squizlabs/php_codesniffer": "~3.4", 33 | "orchestra/testbench": "^v4.18.0|^v5.20.0|^v6.25.0|^8.0" 34 | }, 35 | "autoload": { 36 | "psr-4": { 37 | "Dacastro4\\LaravelGmail\\": "src/" 38 | } 39 | }, 40 | "autoload-dev": { 41 | "psr-4": { 42 | "Tests\\": "tests/" 43 | } 44 | }, 45 | "extra": { 46 | "laravel": { 47 | "providers": [ 48 | "Dacastro4\\LaravelGmail\\LaravelGmailServiceProvider" 49 | ], 50 | "aliases": { 51 | "LaravelGmail": "Dacastro4\\LaravelGmail\\Facade\\LaravelGmail" 52 | } 53 | } 54 | }, 55 | "config": { 56 | "preferred-install": "dist", 57 | "sort-packages": true, 58 | "optimize-autoloader": true 59 | }, 60 | "minimum-stability": "dev", 61 | "prefer-stable": true 62 | } 63 | -------------------------------------------------------------------------------- /phpunit.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /project.codestyle.json: -------------------------------------------------------------------------------- 1 | { 2 | "schemeName": "Project", 3 | "version": "1.0", 4 | "codeStyle": { 5 | "all": { 6 | "formatter_off_tag": "@formatter:off", 7 | "formatter_on_tag": "@formatter:on", 8 | "formatter_tags_accept_regexp": false, 9 | "formatter_tags_enabled": false, 10 | "max_line_length": 120, 11 | "wrap_on_typing": false 12 | }, 13 | "php": { 14 | "align_assignments": false, 15 | "align_class_constants": false, 16 | "align_group_field_declarations": false, 17 | "align_key_value_pairs": false, 18 | "align_multiline_array_initializer_expression": false, 19 | "align_multiline_binary_operation": false, 20 | "align_multiline_chained_methods": false, 21 | "align_multiline_extends_list": false, 22 | "align_multiline_for": true, 23 | "align_multiline_parameters": false, 24 | "align_multiline_parameters_in_calls": false, 25 | "align_multiline_ternary_operation": false, 26 | "align_phpdoc_comments": false, 27 | "align_phpdoc_param_names": false, 28 | "api_weight": 28, 29 | "array_initializer_new_line_after_left_brace": true, 30 | "array_initializer_right_brace_on_new_line": true, 31 | "array_initializer_wrap": "normal", 32 | "assignment_wrap": "off", 33 | "author_weight": 28, 34 | "binary_operation_sign_on_next_line": false, 35 | "binary_operation_wrap": "off", 36 | "blank_lines_after_class_header": 0, 37 | "blank_lines_after_function": 1, 38 | "blank_lines_after_imports": 1, 39 | "blank_lines_after_opening_tag": 0, 40 | "blank_lines_after_package": 1, 41 | "blank_lines_around_class": 1, 42 | "blank_lines_around_constants": 0, 43 | "blank_lines_around_field": 0, 44 | "blank_lines_around_method": 1, 45 | "blank_lines_before_imports": 1, 46 | "blank_lines_before_method_body": 0, 47 | "blank_lines_before_package": 1, 48 | "blank_lines_before_return_statement": 0, 49 | "brace_style": "end_of_line", 50 | "call_parameters_new_line_after_left_paren": false, 51 | "call_parameters_right_paren_on_new_line": false, 52 | "call_parameters_wrap": "normal", 53 | "catch_on_new_line": false, 54 | "category_weight": 28, 55 | "class_brace_style": "next_line", 56 | "comma_after_last_array_element": false, 57 | "concat_spaces": false, 58 | "continuation_indent_size": 4, 59 | "copyright_weight": 28, 60 | "deprecated_weight": 28, 61 | "do_while_brace_force": "always", 62 | "else_if_style": "combine", 63 | "else_on_new_line": false, 64 | "example_weight": 28, 65 | "extends_keyword_wrap": "off", 66 | "extends_list_wrap": "off", 67 | "fields_default_visibility": "private", 68 | "filesource_weight": 28, 69 | "finally_on_new_line": false, 70 | "for_brace_force": "always", 71 | "for_statement_new_line_after_left_paren": false, 72 | "for_statement_right_paren_on_new_line": false, 73 | "for_statement_wrap": "off", 74 | "force_short_declaration_array_style": false, 75 | "global_weight": 28, 76 | "group_use_wrap": "on_every_item", 77 | "if_brace_force": "always", 78 | "if_lparen_on_next_line": false, 79 | "if_rparen_on_next_line": false, 80 | "ignore_weight": 28, 81 | "import_sorting": "alphabetic", 82 | "indent_break_from_case": true, 83 | "indent_case_from_switch": true, 84 | "indent_code_in_php_tags": false, 85 | "indent_size": 4, 86 | "indent_style": "space", 87 | "internal_weight": 28, 88 | "keep_blank_lines_after_lbrace": 2, 89 | "keep_blank_lines_before_right_brace": 2, 90 | "keep_blank_lines_in_code": 2, 91 | "keep_blank_lines_in_declarations": 2, 92 | "keep_control_statement_in_one_line": true, 93 | "keep_first_column_comment": true, 94 | "keep_indents_on_empty_lines": false, 95 | "keep_line_breaks": true, 96 | "keep_rparen_and_lbrace_on_one_line": true, 97 | "keep_simple_methods_in_one_line": false, 98 | "lambda_brace_style": "end_of_line", 99 | "license_weight": 28, 100 | "line_comment_add_space": false, 101 | "line_comment_at_first_column": true, 102 | "link_weight": 28, 103 | "lower_case_boolean_const": true, 104 | "lower_case_null_const": true, 105 | "method_brace_style": "next_line", 106 | "method_call_chain_wrap": "off", 107 | "method_parameters_new_line_after_left_paren": true, 108 | "method_parameters_right_paren_on_new_line": true, 109 | "method_parameters_wrap": "on_every_item", 110 | "method_weight": 28, 111 | "modifier_list_wrap": false, 112 | "multiline_chained_calls_semicolon_on_new_line": false, 113 | "namespace_brace_style": "end_of_line", 114 | "null_type_position": "in_the_end", 115 | "package_weight": 28, 116 | "param_weight": 0, 117 | "parentheses_expression_new_line_after_left_paren": false, 118 | "parentheses_expression_right_paren_on_new_line": false, 119 | "phpdoc_blank_line_before_tags": false, 120 | "phpdoc_blank_lines_around_parameters": false, 121 | "phpdoc_keep_blank_lines": true, 122 | "phpdoc_param_spaces_between_name_and_description": 2, 123 | "phpdoc_param_spaces_between_tag_and_type": 2, 124 | "phpdoc_param_spaces_between_type_and_name": 2, 125 | "phpdoc_use_fqcn": false, 126 | "phpdoc_wrap_long_lines": false, 127 | "place_assignment_sign_on_next_line": false, 128 | "place_parens_for_constructor": 0, 129 | "property_read_weight": 28, 130 | "property_weight": 28, 131 | "property_write_weight": 28, 132 | "return_type_on_new_line": false, 133 | "return_weight": 1, 134 | "see_weight": 28, 135 | "since_weight": 28, 136 | "smart_tabs": false, 137 | "sort_phpdoc_elements": true, 138 | "space_after_colon": true, 139 | "space_after_colon_in_return_type": true, 140 | "space_after_comma": true, 141 | "space_after_for_semicolon": true, 142 | "space_after_quest": true, 143 | "space_after_type_cast": true, 144 | "space_after_unary_not": false, 145 | "space_before_array_initializer_left_brace": false, 146 | "space_before_catch_keyword": true, 147 | "space_before_catch_left_brace": true, 148 | "space_before_catch_parentheses": true, 149 | "space_before_class_left_brace": true, 150 | "space_before_closure_left_parenthesis": true, 151 | "space_before_colon": true, 152 | "space_before_colon_in_return_type": false, 153 | "space_before_comma": false, 154 | "space_before_do_left_brace": true, 155 | "space_before_else_keyword": true, 156 | "space_before_else_left_brace": true, 157 | "space_before_finally_keyword": true, 158 | "space_before_finally_left_brace": true, 159 | "space_before_for_left_brace": true, 160 | "space_before_for_parentheses": true, 161 | "space_before_for_semicolon": false, 162 | "space_before_if_left_brace": true, 163 | "space_before_if_parentheses": true, 164 | "space_before_method_call_parentheses": false, 165 | "space_before_method_left_brace": true, 166 | "space_before_method_parentheses": false, 167 | "space_before_quest": true, 168 | "space_before_switch_left_brace": true, 169 | "space_before_switch_parentheses": true, 170 | "space_before_try_left_brace": true, 171 | "space_before_unary_not": false, 172 | "space_before_while_keyword": true, 173 | "space_before_while_left_brace": true, 174 | "space_before_while_parentheses": true, 175 | "space_between_ternary_quest_and_colon": false, 176 | "spaces_around_additive_operators": true, 177 | "spaces_around_arrow": false, 178 | "spaces_around_assignment_in_declare": false, 179 | "spaces_around_assignment_operators": true, 180 | "spaces_around_bitwise_operators": true, 181 | "spaces_around_equality_operators": true, 182 | "spaces_around_logical_operators": true, 183 | "spaces_around_multiplicative_operators": true, 184 | "spaces_around_null_coalesce_operator": true, 185 | "spaces_around_relational_operators": true, 186 | "spaces_around_shift_operators": true, 187 | "spaces_around_unary_operator": false, 188 | "spaces_around_var_within_brackets": false, 189 | "spaces_within_array_initializer_braces": false, 190 | "spaces_within_brackets": false, 191 | "spaces_within_catch_parentheses": false, 192 | "spaces_within_for_parentheses": false, 193 | "spaces_within_if_parentheses": false, 194 | "spaces_within_method_call_parentheses": false, 195 | "spaces_within_method_parentheses": false, 196 | "spaces_within_parentheses": false, 197 | "spaces_within_short_echo_tags": true, 198 | "spaces_within_switch_parentheses": false, 199 | "spaces_within_while_parentheses": false, 200 | "special_else_if_treatment": false, 201 | "subpackage_weight": 28, 202 | "tab_width": 4, 203 | "ternary_operation_signs_on_next_line": false, 204 | "ternary_operation_wrap": "off", 205 | "throws_weight": 2, 206 | "todo_weight": 28, 207 | "unknown_tag_weight": 28, 208 | "upper_case_boolean_const": false, 209 | "upper_case_null_const": false, 210 | "uses_weight": 28, 211 | "var_weight": 28, 212 | "variable_naming_style": "mixed", 213 | "version_weight": 28, 214 | "while_brace_force": "always", 215 | "while_on_new_line": false 216 | }, 217 | "blade": { 218 | "continuation_indent_size": 8, 219 | "indent_size": 4, 220 | "indent_style": "space", 221 | "keep_indents_on_empty_lines": false, 222 | "smart_tabs": false, 223 | "tab_width": 4 224 | }, 225 | "coffeescript": { 226 | "align_function_body": false, 227 | "align_imports": false, 228 | "align_multiline_array_initializer_expression": true, 229 | "align_multiline_parameters": true, 230 | "align_multiline_parameters_in_calls": false, 231 | "align_object_properties": 0, 232 | "align_union_types": false, 233 | "align_var_statements": 0, 234 | "array_initializer_new_line_after_left_brace": false, 235 | "array_initializer_right_brace_on_new_line": false, 236 | "array_initializer_wrap": "normal", 237 | "blacklist_imports": "rxjs/Rx,node_modules/**/*", 238 | "blank_lines_around_function": 1, 239 | "call_parameters_new_line_after_left_paren": false, 240 | "call_parameters_right_paren_on_new_line": false, 241 | "call_parameters_wrap": "normal", 242 | "chained_call_dot_on_new_line": true, 243 | "comma_on_new_line": false, 244 | "continuation_indent_size": 2, 245 | "enforce_trailing_comma": "keep", 246 | "field_prefix": "_", 247 | "file_name_style": "relaxed", 248 | "force_quote_style": false, 249 | "force_semicolon_style": false, 250 | "function_expression_brace_style": "end_of_line", 251 | "import_merge_members": "global", 252 | "import_prefer_absolute_path": "global", 253 | "import_sort_members": true, 254 | "import_sort_module_name": false, 255 | "import_use_node_resolution": "true", 256 | "imports_wrap": "on_every_item", 257 | "indent_chained_calls": true, 258 | "indent_package_children": 0, 259 | "indent_size": 2, 260 | "indent_style": "space", 261 | "jsx_attribute_value": "braces", 262 | "keep_blank_lines_in_code": 2, 263 | "keep_first_column_comment": true, 264 | "keep_indents_on_empty_lines": false, 265 | "keep_line_breaks": true, 266 | "keep_simple_methods_in_one_line": false, 267 | "method_parameters_new_line_after_left_paren": false, 268 | "method_parameters_right_paren_on_new_line": false, 269 | "method_parameters_wrap": "off", 270 | "object_literal_wrap": "on_every_item", 271 | "prefer_as_type_cast": false, 272 | "reformat_c_style_comments": false, 273 | "smart_tabs": false, 274 | "space_after_comma": true, 275 | "space_after_dots_in_rest_parameter": false, 276 | "space_after_generator_mult": true, 277 | "space_after_property_colon": true, 278 | "space_after_type_colon": true, 279 | "space_after_unary_not": false, 280 | "space_before_async_arrow_lparen": true, 281 | "space_before_class_lbrace": true, 282 | "space_before_comma": false, 283 | "space_before_function_left_parenth": true, 284 | "space_before_generator_mult": false, 285 | "space_before_property_colon": false, 286 | "space_before_type_colon": false, 287 | "space_before_unary_not": false, 288 | "spaces_around_additive_operators": true, 289 | "spaces_around_arrow_function_operator": true, 290 | "spaces_around_assignment_operators": true, 291 | "spaces_around_bitwise_operators": true, 292 | "spaces_around_equality_operators": true, 293 | "spaces_around_logical_operators": true, 294 | "spaces_around_multiplicative_operators": true, 295 | "spaces_around_relational_operators": true, 296 | "spaces_around_shift_operators": true, 297 | "spaces_around_unary_operator": false, 298 | "spaces_within_array_initializer_braces": false, 299 | "spaces_within_array_initializer_brackets": false, 300 | "spaces_within_imports": false, 301 | "spaces_within_index_brackets": false, 302 | "spaces_within_interpolation_expressions": false, 303 | "spaces_within_method_call_parentheses": false, 304 | "spaces_within_method_parentheses": false, 305 | "spaces_within_object_braces": false, 306 | "spaces_within_object_literal_braces": false, 307 | "spaces_within_object_type_braces": true, 308 | "spaces_within_range_brackets": false, 309 | "spaces_within_type_assertion": false, 310 | "spaces_within_union_types": true, 311 | "tab_width": 2, 312 | "union_types_wrap": "on_every_item", 313 | "use_chained_calls_group_indents": false, 314 | "use_double_quotes": true, 315 | "use_explicit_js_extension": "global", 316 | "use_path_mapping": "always", 317 | "use_public_modifier": false, 318 | "use_semicolon_after_statement": false, 319 | "var_declaration_wrap": "normal" 320 | }, 321 | "css": { 322 | "align_closing_brace_with_properties": false, 323 | "blank_lines_around_nested_selector": 1, 324 | "blank_lines_between_blocks": 1, 325 | "brace_placement": 0, 326 | "continuation_indent_size": 8, 327 | "hex_color_long_format": false, 328 | "hex_color_lower_case": false, 329 | "hex_color_short_format": false, 330 | "hex_color_upper_case": false, 331 | "indent_size": 4, 332 | "indent_style": "space", 333 | "keep_blank_lines_in_code": 2, 334 | "keep_indents_on_empty_lines": false, 335 | "keep_single_line_blocks": false, 336 | "properties_order": [ 337 | "font", 338 | "font-family", 339 | "font-size", 340 | "font-weight", 341 | "font-style", 342 | "font-variant", 343 | "font-size-adjust", 344 | "font-stretch", 345 | "line-height", 346 | "position", 347 | "z-index", 348 | "top", 349 | "right", 350 | "bottom", 351 | "left", 352 | "display", 353 | "visibility", 354 | "float", 355 | "clear", 356 | "overflow", 357 | "overflow-x", 358 | "overflow-y", 359 | "clip", 360 | "zoom", 361 | "align-content", 362 | "align-items", 363 | "align-self", 364 | "flex", 365 | "flex-flow", 366 | "flex-basis", 367 | "flex-direction", 368 | "flex-grow", 369 | "flex-shrink", 370 | "flex-wrap", 371 | "justify-content", 372 | "order", 373 | "box-sizing", 374 | "width", 375 | "min-width", 376 | "max-width", 377 | "height", 378 | "min-height", 379 | "max-height", 380 | "margin", 381 | "margin-top", 382 | "margin-right", 383 | "margin-bottom", 384 | "margin-left", 385 | "padding", 386 | "padding-top", 387 | "padding-right", 388 | "padding-bottom", 389 | "padding-left", 390 | "table-layout", 391 | "empty-cells", 392 | "caption-side", 393 | "border-spacing", 394 | "border-collapse", 395 | "list-style", 396 | "list-style-position", 397 | "list-style-type", 398 | "list-style-image", 399 | "content", 400 | "quotes", 401 | "counter-reset", 402 | "counter-increment", 403 | "resize", 404 | "cursor", 405 | "user-select", 406 | "nav-index", 407 | "nav-up", 408 | "nav-right", 409 | "nav-down", 410 | "nav-left", 411 | "transition", 412 | "transition-delay", 413 | "transition-timing-function", 414 | "transition-duration", 415 | "transition-property", 416 | "transform", 417 | "transform-origin", 418 | "animation", 419 | "animation-name", 420 | "animation-duration", 421 | "animation-play-state", 422 | "animation-timing-function", 423 | "animation-delay", 424 | "animation-iteration-count", 425 | "animation-direction", 426 | "text-align", 427 | "text-align-last", 428 | "vertical-align", 429 | "white-space", 430 | "text-decoration", 431 | "text-emphasis", 432 | "text-emphasis-color", 433 | "text-emphasis-style", 434 | "text-emphasis-position", 435 | "text-indent", 436 | "text-justify", 437 | "letter-spacing", 438 | "word-spacing", 439 | "text-outline", 440 | "text-transform", 441 | "text-wrap", 442 | "text-overflow", 443 | "text-overflow-ellipsis", 444 | "text-overflow-mode", 445 | "word-wrap", 446 | "word-break", 447 | "tab-size", 448 | "hyphens", 449 | "pointer-events", 450 | "opacity", 451 | "color", 452 | "border", 453 | "border-width", 454 | "border-style", 455 | "border-color", 456 | "border-top", 457 | "border-top-width", 458 | "border-top-style", 459 | "border-top-color", 460 | "border-right", 461 | "border-right-width", 462 | "border-right-style", 463 | "border-right-color", 464 | "border-bottom", 465 | "border-bottom-width", 466 | "border-bottom-style", 467 | "border-bottom-color", 468 | "border-left", 469 | "border-left-width", 470 | "border-left-style", 471 | "border-left-color", 472 | "border-radius", 473 | "border-top-left-radius", 474 | "border-top-right-radius", 475 | "border-bottom-right-radius", 476 | "border-bottom-left-radius", 477 | "border-image", 478 | "border-image-source", 479 | "border-image-slice", 480 | "border-image-width", 481 | "border-image-outset", 482 | "border-image-repeat", 483 | "outline", 484 | "outline-width", 485 | "outline-style", 486 | "outline-color", 487 | "outline-offset", 488 | "background", 489 | "background-color", 490 | "background-image", 491 | "background-repeat", 492 | "background-attachment", 493 | "background-position", 494 | "background-position-x", 495 | "background-position-y", 496 | "background-clip", 497 | "background-origin", 498 | "background-size", 499 | "box-decoration-break", 500 | "box-shadow", 501 | "text-shadow" 502 | ], 503 | "smart_tabs": false, 504 | "space_after_colon": true, 505 | "space_before_opening_brace": true, 506 | "tab_width": 4, 507 | "value_alignment": 0 508 | }, 509 | "gherkin": { 510 | "continuation_indent_size": 8, 511 | "indent_size": 2, 512 | "indent_style": "space", 513 | "keep_indents_on_empty_lines": false, 514 | "smart_tabs": false, 515 | "tab_width": 4 516 | }, 517 | "haml": { 518 | "continuation_indent_size": 8, 519 | "indent_size": 2, 520 | "indent_style": "space", 521 | "keep_indents_on_empty_lines": false, 522 | "smart_tabs": false, 523 | "tab_width": 4 524 | }, 525 | "html": { 526 | "add_new_line_before_tags": [ 527 | "body", 528 | "div", 529 | "p", 530 | "form", 531 | "h1", 532 | "h2", 533 | "h3" 534 | ], 535 | "align_attributes": true, 536 | "align_text": false, 537 | "attribute_wrap": "normal", 538 | "block_comment_at_first_column": true, 539 | "continuation_indent_size": 8, 540 | "do_not_align_children_of_min_lines": 0, 541 | "do_not_break_if_inline_tags": [ 542 | "title", 543 | "h1", 544 | "h2", 545 | "h3", 546 | "h4", 547 | "h5", 548 | "h6", 549 | "p" 550 | ], 551 | "do_not_indent_children_of_tags": [ 552 | "html", 553 | "body", 554 | "thead", 555 | "tbody", 556 | "tfoot" 557 | ], 558 | "enforce_quotes": false, 559 | "indent_size": 4, 560 | "indent_style": "space", 561 | "inline_tags": [ 562 | "a", 563 | "abbr", 564 | "acronym", 565 | "b", 566 | "basefont", 567 | "bdo", 568 | "big", 569 | "br", 570 | "cite", 571 | "cite", 572 | "code", 573 | "dfn", 574 | "em", 575 | "font", 576 | "i", 577 | "img", 578 | "input", 579 | "kbd", 580 | "label", 581 | "q", 582 | "s", 583 | "samp", 584 | "select", 585 | "span", 586 | "strike", 587 | "strong", 588 | "sub", 589 | "sup", 590 | "textarea", 591 | "tt", 592 | "u", 593 | "var" 594 | ], 595 | "keep_blank_lines": 2, 596 | "keep_indents_on_empty_lines": false, 597 | "keep_line_breaks": true, 598 | "keep_line_breaks_in_text": true, 599 | "keep_whitespaces": false, 600 | "keep_whitespaces_inside": [ 601 | "span", 602 | "pre", 603 | "textarea" 604 | ], 605 | "line_comment_at_first_column": true, 606 | "new_line_after_last_attribute": "never", 607 | "new_line_before_first_attribute": "never", 608 | "quote_style": "double", 609 | "remove_new_line_before_tags": [ 610 | "br" 611 | ], 612 | "smart_tabs": false, 613 | "space_after_tag_name": false, 614 | "space_around_equality_in_attribute": false, 615 | "space_inside_empty_tag": false, 616 | "tab_width": 4, 617 | "text_wrap": "normal" 618 | }, 619 | "javascript": { 620 | "align_imports": false, 621 | "align_multiline_array_initializer_expression": false, 622 | "align_multiline_binary_operation": false, 623 | "align_multiline_chained_methods": false, 624 | "align_multiline_extends_list": false, 625 | "align_multiline_for": true, 626 | "align_multiline_parameters": true, 627 | "align_multiline_parameters_in_calls": false, 628 | "align_multiline_ternary_operation": false, 629 | "align_object_properties": 0, 630 | "align_union_types": false, 631 | "align_var_statements": 0, 632 | "array_initializer_new_line_after_left_brace": false, 633 | "array_initializer_right_brace_on_new_line": false, 634 | "array_initializer_wrap": "off", 635 | "assignment_wrap": "off", 636 | "binary_operation_sign_on_next_line": false, 637 | "binary_operation_wrap": "off", 638 | "blacklist_imports": "rxjs/Rx,node_modules/**/*", 639 | "blank_lines_after_imports": 1, 640 | "blank_lines_around_class": 1, 641 | "blank_lines_around_field": 0, 642 | "blank_lines_around_function": 1, 643 | "blank_lines_around_method": 1, 644 | "brace_style": "end_of_line", 645 | "call_parameters_new_line_after_left_paren": false, 646 | "call_parameters_right_paren_on_new_line": false, 647 | "call_parameters_wrap": "off", 648 | "catch_on_new_line": false, 649 | "chained_call_dot_on_new_line": true, 650 | "class_brace_style": "end_of_line", 651 | "comma_on_new_line": false, 652 | "continuation_indent_size": 4, 653 | "do_while_brace_force": "never", 654 | "else_on_new_line": false, 655 | "enforce_trailing_comma": "keep", 656 | "extends_keyword_wrap": "off", 657 | "extends_list_wrap": "off", 658 | "field_prefix": "_", 659 | "file_name_style": "relaxed", 660 | "finally_on_new_line": false, 661 | "for_brace_force": "never", 662 | "for_statement_new_line_after_left_paren": false, 663 | "for_statement_right_paren_on_new_line": false, 664 | "for_statement_wrap": "off", 665 | "force_quote_style": false, 666 | "force_semicolon_style": false, 667 | "function_expression_brace_style": "end_of_line", 668 | "if_brace_force": "never", 669 | "import_merge_members": "global", 670 | "import_prefer_absolute_path": "global", 671 | "import_sort_members": true, 672 | "import_sort_module_name": false, 673 | "import_use_node_resolution": "true", 674 | "imports_wrap": "on_every_item", 675 | "indent_case_from_switch": true, 676 | "indent_chained_calls": true, 677 | "indent_package_children": 0, 678 | "indent_size": 4, 679 | "indent_style": "space", 680 | "jsx_attribute_value": "braces", 681 | "keep_blank_lines_in_code": 2, 682 | "keep_first_column_comment": true, 683 | "keep_indents_on_empty_lines": false, 684 | "keep_line_breaks": true, 685 | "keep_simple_blocks_in_one_line": false, 686 | "keep_simple_methods_in_one_line": false, 687 | "line_comment_add_space": true, 688 | "line_comment_at_first_column": false, 689 | "method_brace_style": "end_of_line", 690 | "method_call_chain_wrap": "off", 691 | "method_parameters_new_line_after_left_paren": false, 692 | "method_parameters_right_paren_on_new_line": false, 693 | "method_parameters_wrap": "off", 694 | "object_literal_wrap": "on_every_item", 695 | "parentheses_expression_new_line_after_left_paren": false, 696 | "parentheses_expression_right_paren_on_new_line": false, 697 | "place_assignment_sign_on_next_line": false, 698 | "prefer_as_type_cast": false, 699 | "prefer_parameters_wrap": false, 700 | "reformat_c_style_comments": false, 701 | "smart_tabs": false, 702 | "space_after_colon": true, 703 | "space_after_comma": true, 704 | "space_after_dots_in_rest_parameter": false, 705 | "space_after_generator_mult": true, 706 | "space_after_property_colon": true, 707 | "space_after_quest": true, 708 | "space_after_type_colon": true, 709 | "space_after_unary_not": false, 710 | "space_before_async_arrow_lparen": true, 711 | "space_before_catch_keyword": true, 712 | "space_before_catch_left_brace": true, 713 | "space_before_catch_parentheses": true, 714 | "space_before_class_lbrace": true, 715 | "space_before_class_left_brace": true, 716 | "space_before_colon": true, 717 | "space_before_comma": false, 718 | "space_before_do_left_brace": true, 719 | "space_before_else_keyword": true, 720 | "space_before_else_left_brace": true, 721 | "space_before_finally_keyword": true, 722 | "space_before_finally_left_brace": true, 723 | "space_before_for_left_brace": true, 724 | "space_before_for_parentheses": true, 725 | "space_before_for_semicolon": false, 726 | "space_before_function_left_parenth": true, 727 | "space_before_generator_mult": false, 728 | "space_before_if_left_brace": true, 729 | "space_before_if_parentheses": true, 730 | "space_before_method_call_parentheses": false, 731 | "space_before_method_left_brace": true, 732 | "space_before_method_parentheses": false, 733 | "space_before_property_colon": false, 734 | "space_before_quest": true, 735 | "space_before_switch_left_brace": true, 736 | "space_before_switch_parentheses": true, 737 | "space_before_try_left_brace": true, 738 | "space_before_type_colon": false, 739 | "space_before_unary_not": false, 740 | "space_before_while_keyword": true, 741 | "space_before_while_left_brace": true, 742 | "space_before_while_parentheses": true, 743 | "spaces_around_additive_operators": true, 744 | "spaces_around_arrow_function_operator": true, 745 | "spaces_around_assignment_operators": true, 746 | "spaces_around_bitwise_operators": true, 747 | "spaces_around_equality_operators": true, 748 | "spaces_around_logical_operators": true, 749 | "spaces_around_multiplicative_operators": true, 750 | "spaces_around_relational_operators": true, 751 | "spaces_around_shift_operators": true, 752 | "spaces_around_unary_operator": false, 753 | "spaces_within_array_initializer_brackets": false, 754 | "spaces_within_brackets": false, 755 | "spaces_within_catch_parentheses": false, 756 | "spaces_within_for_parentheses": false, 757 | "spaces_within_if_parentheses": false, 758 | "spaces_within_imports": false, 759 | "spaces_within_interpolation_expressions": false, 760 | "spaces_within_method_call_parentheses": false, 761 | "spaces_within_method_parentheses": false, 762 | "spaces_within_object_literal_braces": false, 763 | "spaces_within_object_type_braces": true, 764 | "spaces_within_parentheses": false, 765 | "spaces_within_switch_parentheses": false, 766 | "spaces_within_type_assertion": false, 767 | "spaces_within_union_types": true, 768 | "spaces_within_while_parentheses": false, 769 | "special_else_if_treatment": true, 770 | "tab_width": 4, 771 | "ternary_operation_signs_on_next_line": false, 772 | "ternary_operation_wrap": "off", 773 | "union_types_wrap": "on_every_item", 774 | "use_chained_calls_group_indents": false, 775 | "use_double_quotes": true, 776 | "use_explicit_js_extension": "global", 777 | "use_path_mapping": "always", 778 | "use_public_modifier": false, 779 | "use_semicolon_after_statement": true, 780 | "var_declaration_wrap": "normal", 781 | "while_brace_force": "never", 782 | "while_on_new_line": false, 783 | "wrap_comments": false 784 | }, 785 | "json": { 786 | "continuation_indent_size": 8, 787 | "indent_size": 2, 788 | "indent_style": "space", 789 | "keep_blank_lines_in_code": 0, 790 | "keep_indents_on_empty_lines": false, 791 | "keep_line_breaks": true, 792 | "smart_tabs": false, 793 | "space_after_colon": true, 794 | "space_after_comma": true, 795 | "space_before_colon": true, 796 | "space_before_comma": false, 797 | "spaces_within_braces": false, 798 | "spaces_within_brackets": false, 799 | "tab_width": 4, 800 | "wrap_long_lines": false 801 | }, 802 | "less": { 803 | "align_closing_brace_with_properties": false, 804 | "blank_lines_around_nested_selector": 1, 805 | "blank_lines_between_blocks": 1, 806 | "brace_placement": 0, 807 | "continuation_indent_size": 8, 808 | "hex_color_long_format": false, 809 | "hex_color_lower_case": false, 810 | "hex_color_short_format": false, 811 | "hex_color_upper_case": false, 812 | "indent_size": 2, 813 | "indent_style": "space", 814 | "keep_blank_lines_in_code": 2, 815 | "keep_indents_on_empty_lines": false, 816 | "keep_single_line_blocks": false, 817 | "properties_order": [ 818 | "font", 819 | "font-family", 820 | "font-size", 821 | "font-weight", 822 | "font-style", 823 | "font-variant", 824 | "font-size-adjust", 825 | "font-stretch", 826 | "line-height", 827 | "position", 828 | "z-index", 829 | "top", 830 | "right", 831 | "bottom", 832 | "left", 833 | "display", 834 | "visibility", 835 | "float", 836 | "clear", 837 | "overflow", 838 | "overflow-x", 839 | "overflow-y", 840 | "clip", 841 | "zoom", 842 | "align-content", 843 | "align-items", 844 | "align-self", 845 | "flex", 846 | "flex-flow", 847 | "flex-basis", 848 | "flex-direction", 849 | "flex-grow", 850 | "flex-shrink", 851 | "flex-wrap", 852 | "justify-content", 853 | "order", 854 | "box-sizing", 855 | "width", 856 | "min-width", 857 | "max-width", 858 | "height", 859 | "min-height", 860 | "max-height", 861 | "margin", 862 | "margin-top", 863 | "margin-right", 864 | "margin-bottom", 865 | "margin-left", 866 | "padding", 867 | "padding-top", 868 | "padding-right", 869 | "padding-bottom", 870 | "padding-left", 871 | "table-layout", 872 | "empty-cells", 873 | "caption-side", 874 | "border-spacing", 875 | "border-collapse", 876 | "list-style", 877 | "list-style-position", 878 | "list-style-type", 879 | "list-style-image", 880 | "content", 881 | "quotes", 882 | "counter-reset", 883 | "counter-increment", 884 | "resize", 885 | "cursor", 886 | "user-select", 887 | "nav-index", 888 | "nav-up", 889 | "nav-right", 890 | "nav-down", 891 | "nav-left", 892 | "transition", 893 | "transition-delay", 894 | "transition-timing-function", 895 | "transition-duration", 896 | "transition-property", 897 | "transform", 898 | "transform-origin", 899 | "animation", 900 | "animation-name", 901 | "animation-duration", 902 | "animation-play-state", 903 | "animation-timing-function", 904 | "animation-delay", 905 | "animation-iteration-count", 906 | "animation-direction", 907 | "text-align", 908 | "text-align-last", 909 | "vertical-align", 910 | "white-space", 911 | "text-decoration", 912 | "text-emphasis", 913 | "text-emphasis-color", 914 | "text-emphasis-style", 915 | "text-emphasis-position", 916 | "text-indent", 917 | "text-justify", 918 | "letter-spacing", 919 | "word-spacing", 920 | "text-outline", 921 | "text-transform", 922 | "text-wrap", 923 | "text-overflow", 924 | "text-overflow-ellipsis", 925 | "text-overflow-mode", 926 | "word-wrap", 927 | "word-break", 928 | "tab-size", 929 | "hyphens", 930 | "pointer-events", 931 | "opacity", 932 | "color", 933 | "border", 934 | "border-width", 935 | "border-style", 936 | "border-color", 937 | "border-top", 938 | "border-top-width", 939 | "border-top-style", 940 | "border-top-color", 941 | "border-right", 942 | "border-right-width", 943 | "border-right-style", 944 | "border-right-color", 945 | "border-bottom", 946 | "border-bottom-width", 947 | "border-bottom-style", 948 | "border-bottom-color", 949 | "border-left", 950 | "border-left-width", 951 | "border-left-style", 952 | "border-left-color", 953 | "border-radius", 954 | "border-top-left-radius", 955 | "border-top-right-radius", 956 | "border-bottom-right-radius", 957 | "border-bottom-left-radius", 958 | "border-image", 959 | "border-image-source", 960 | "border-image-slice", 961 | "border-image-width", 962 | "border-image-outset", 963 | "border-image-repeat", 964 | "outline", 965 | "outline-width", 966 | "outline-style", 967 | "outline-color", 968 | "outline-offset", 969 | "background", 970 | "background-color", 971 | "background-image", 972 | "background-repeat", 973 | "background-attachment", 974 | "background-position", 975 | "background-position-x", 976 | "background-position-y", 977 | "background-clip", 978 | "background-origin", 979 | "background-size", 980 | "box-decoration-break", 981 | "box-shadow", 982 | "text-shadow" 983 | ], 984 | "smart_tabs": false, 985 | "space_after_colon": true, 986 | "space_before_opening_brace": true, 987 | "tab_width": 4, 988 | "value_alignment": 0 989 | }, 990 | "sass": { 991 | "align_closing_brace_with_properties": false, 992 | "blank_lines_around_nested_selector": 1, 993 | "blank_lines_between_blocks": 1, 994 | "brace_placement": 0, 995 | "continuation_indent_size": 8, 996 | "hex_color_long_format": false, 997 | "hex_color_lower_case": false, 998 | "hex_color_short_format": false, 999 | "hex_color_upper_case": false, 1000 | "indent_size": 2, 1001 | "indent_style": "space", 1002 | "keep_blank_lines_in_code": 2, 1003 | "keep_indents_on_empty_lines": false, 1004 | "keep_single_line_blocks": false, 1005 | "properties_order": [ 1006 | "font", 1007 | "font-family", 1008 | "font-size", 1009 | "font-weight", 1010 | "font-style", 1011 | "font-variant", 1012 | "font-size-adjust", 1013 | "font-stretch", 1014 | "line-height", 1015 | "position", 1016 | "z-index", 1017 | "top", 1018 | "right", 1019 | "bottom", 1020 | "left", 1021 | "display", 1022 | "visibility", 1023 | "float", 1024 | "clear", 1025 | "overflow", 1026 | "overflow-x", 1027 | "overflow-y", 1028 | "clip", 1029 | "zoom", 1030 | "align-content", 1031 | "align-items", 1032 | "align-self", 1033 | "flex", 1034 | "flex-flow", 1035 | "flex-basis", 1036 | "flex-direction", 1037 | "flex-grow", 1038 | "flex-shrink", 1039 | "flex-wrap", 1040 | "justify-content", 1041 | "order", 1042 | "box-sizing", 1043 | "width", 1044 | "min-width", 1045 | "max-width", 1046 | "height", 1047 | "min-height", 1048 | "max-height", 1049 | "margin", 1050 | "margin-top", 1051 | "margin-right", 1052 | "margin-bottom", 1053 | "margin-left", 1054 | "padding", 1055 | "padding-top", 1056 | "padding-right", 1057 | "padding-bottom", 1058 | "padding-left", 1059 | "table-layout", 1060 | "empty-cells", 1061 | "caption-side", 1062 | "border-spacing", 1063 | "border-collapse", 1064 | "list-style", 1065 | "list-style-position", 1066 | "list-style-type", 1067 | "list-style-image", 1068 | "content", 1069 | "quotes", 1070 | "counter-reset", 1071 | "counter-increment", 1072 | "resize", 1073 | "cursor", 1074 | "user-select", 1075 | "nav-index", 1076 | "nav-up", 1077 | "nav-right", 1078 | "nav-down", 1079 | "nav-left", 1080 | "transition", 1081 | "transition-delay", 1082 | "transition-timing-function", 1083 | "transition-duration", 1084 | "transition-property", 1085 | "transform", 1086 | "transform-origin", 1087 | "animation", 1088 | "animation-name", 1089 | "animation-duration", 1090 | "animation-play-state", 1091 | "animation-timing-function", 1092 | "animation-delay", 1093 | "animation-iteration-count", 1094 | "animation-direction", 1095 | "text-align", 1096 | "text-align-last", 1097 | "vertical-align", 1098 | "white-space", 1099 | "text-decoration", 1100 | "text-emphasis", 1101 | "text-emphasis-color", 1102 | "text-emphasis-style", 1103 | "text-emphasis-position", 1104 | "text-indent", 1105 | "text-justify", 1106 | "letter-spacing", 1107 | "word-spacing", 1108 | "text-outline", 1109 | "text-transform", 1110 | "text-wrap", 1111 | "text-overflow", 1112 | "text-overflow-ellipsis", 1113 | "text-overflow-mode", 1114 | "word-wrap", 1115 | "word-break", 1116 | "tab-size", 1117 | "hyphens", 1118 | "pointer-events", 1119 | "opacity", 1120 | "color", 1121 | "border", 1122 | "border-width", 1123 | "border-style", 1124 | "border-color", 1125 | "border-top", 1126 | "border-top-width", 1127 | "border-top-style", 1128 | "border-top-color", 1129 | "border-right", 1130 | "border-right-width", 1131 | "border-right-style", 1132 | "border-right-color", 1133 | "border-bottom", 1134 | "border-bottom-width", 1135 | "border-bottom-style", 1136 | "border-bottom-color", 1137 | "border-left", 1138 | "border-left-width", 1139 | "border-left-style", 1140 | "border-left-color", 1141 | "border-radius", 1142 | "border-top-left-radius", 1143 | "border-top-right-radius", 1144 | "border-bottom-right-radius", 1145 | "border-bottom-left-radius", 1146 | "border-image", 1147 | "border-image-source", 1148 | "border-image-slice", 1149 | "border-image-width", 1150 | "border-image-outset", 1151 | "border-image-repeat", 1152 | "outline", 1153 | "outline-width", 1154 | "outline-style", 1155 | "outline-color", 1156 | "outline-offset", 1157 | "background", 1158 | "background-color", 1159 | "background-image", 1160 | "background-repeat", 1161 | "background-attachment", 1162 | "background-position", 1163 | "background-position-x", 1164 | "background-position-y", 1165 | "background-clip", 1166 | "background-origin", 1167 | "background-size", 1168 | "box-decoration-break", 1169 | "box-shadow", 1170 | "text-shadow" 1171 | ], 1172 | "smart_tabs": false, 1173 | "space_after_colon": true, 1174 | "space_before_opening_brace": true, 1175 | "tab_width": 4, 1176 | "value_alignment": 0 1177 | }, 1178 | "scss": { 1179 | "align_closing_brace_with_properties": false, 1180 | "blank_lines_around_nested_selector": 1, 1181 | "blank_lines_between_blocks": 1, 1182 | "brace_placement": 0, 1183 | "continuation_indent_size": 8, 1184 | "hex_color_long_format": false, 1185 | "hex_color_lower_case": false, 1186 | "hex_color_short_format": false, 1187 | "hex_color_upper_case": false, 1188 | "indent_size": 2, 1189 | "indent_style": "space", 1190 | "keep_blank_lines_in_code": 2, 1191 | "keep_indents_on_empty_lines": false, 1192 | "keep_single_line_blocks": false, 1193 | "properties_order": [ 1194 | "font", 1195 | "font-family", 1196 | "font-size", 1197 | "font-weight", 1198 | "font-style", 1199 | "font-variant", 1200 | "font-size-adjust", 1201 | "font-stretch", 1202 | "line-height", 1203 | "position", 1204 | "z-index", 1205 | "top", 1206 | "right", 1207 | "bottom", 1208 | "left", 1209 | "display", 1210 | "visibility", 1211 | "float", 1212 | "clear", 1213 | "overflow", 1214 | "overflow-x", 1215 | "overflow-y", 1216 | "clip", 1217 | "zoom", 1218 | "align-content", 1219 | "align-items", 1220 | "align-self", 1221 | "flex", 1222 | "flex-flow", 1223 | "flex-basis", 1224 | "flex-direction", 1225 | "flex-grow", 1226 | "flex-shrink", 1227 | "flex-wrap", 1228 | "justify-content", 1229 | "order", 1230 | "box-sizing", 1231 | "width", 1232 | "min-width", 1233 | "max-width", 1234 | "height", 1235 | "min-height", 1236 | "max-height", 1237 | "margin", 1238 | "margin-top", 1239 | "margin-right", 1240 | "margin-bottom", 1241 | "margin-left", 1242 | "padding", 1243 | "padding-top", 1244 | "padding-right", 1245 | "padding-bottom", 1246 | "padding-left", 1247 | "table-layout", 1248 | "empty-cells", 1249 | "caption-side", 1250 | "border-spacing", 1251 | "border-collapse", 1252 | "list-style", 1253 | "list-style-position", 1254 | "list-style-type", 1255 | "list-style-image", 1256 | "content", 1257 | "quotes", 1258 | "counter-reset", 1259 | "counter-increment", 1260 | "resize", 1261 | "cursor", 1262 | "user-select", 1263 | "nav-index", 1264 | "nav-up", 1265 | "nav-right", 1266 | "nav-down", 1267 | "nav-left", 1268 | "transition", 1269 | "transition-delay", 1270 | "transition-timing-function", 1271 | "transition-duration", 1272 | "transition-property", 1273 | "transform", 1274 | "transform-origin", 1275 | "animation", 1276 | "animation-name", 1277 | "animation-duration", 1278 | "animation-play-state", 1279 | "animation-timing-function", 1280 | "animation-delay", 1281 | "animation-iteration-count", 1282 | "animation-direction", 1283 | "text-align", 1284 | "text-align-last", 1285 | "vertical-align", 1286 | "white-space", 1287 | "text-decoration", 1288 | "text-emphasis", 1289 | "text-emphasis-color", 1290 | "text-emphasis-style", 1291 | "text-emphasis-position", 1292 | "text-indent", 1293 | "text-justify", 1294 | "letter-spacing", 1295 | "word-spacing", 1296 | "text-outline", 1297 | "text-transform", 1298 | "text-wrap", 1299 | "text-overflow", 1300 | "text-overflow-ellipsis", 1301 | "text-overflow-mode", 1302 | "word-wrap", 1303 | "word-break", 1304 | "tab-size", 1305 | "hyphens", 1306 | "pointer-events", 1307 | "opacity", 1308 | "color", 1309 | "border", 1310 | "border-width", 1311 | "border-style", 1312 | "border-color", 1313 | "border-top", 1314 | "border-top-width", 1315 | "border-top-style", 1316 | "border-top-color", 1317 | "border-right", 1318 | "border-right-width", 1319 | "border-right-style", 1320 | "border-right-color", 1321 | "border-bottom", 1322 | "border-bottom-width", 1323 | "border-bottom-style", 1324 | "border-bottom-color", 1325 | "border-left", 1326 | "border-left-width", 1327 | "border-left-style", 1328 | "border-left-color", 1329 | "border-radius", 1330 | "border-top-left-radius", 1331 | "border-top-right-radius", 1332 | "border-bottom-right-radius", 1333 | "border-bottom-left-radius", 1334 | "border-image", 1335 | "border-image-source", 1336 | "border-image-slice", 1337 | "border-image-width", 1338 | "border-image-outset", 1339 | "border-image-repeat", 1340 | "outline", 1341 | "outline-width", 1342 | "outline-style", 1343 | "outline-color", 1344 | "outline-offset", 1345 | "background", 1346 | "background-color", 1347 | "background-image", 1348 | "background-repeat", 1349 | "background-attachment", 1350 | "background-position", 1351 | "background-position-x", 1352 | "background-position-y", 1353 | "background-clip", 1354 | "background-origin", 1355 | "background-size", 1356 | "box-decoration-break", 1357 | "box-shadow", 1358 | "text-shadow" 1359 | ], 1360 | "smart_tabs": false, 1361 | "space_after_colon": true, 1362 | "space_before_opening_brace": true, 1363 | "tab_width": 4, 1364 | "value_alignment": 0 1365 | }, 1366 | "sql": { 1367 | "continuation_indent_size": 8, 1368 | "indent_size": 4, 1369 | "indent_style": "space", 1370 | "keep_blank_lines_in_code": 2, 1371 | "keep_first_column_comment": true, 1372 | "keep_indents_on_empty_lines": false, 1373 | "keep_line_breaks": true, 1374 | "smart_tabs": false, 1375 | "space_after_comma": true, 1376 | "space_before_comma": false, 1377 | "space_before_method_call_parentheses": false, 1378 | "space_before_method_parentheses": false, 1379 | "spaces_within_method_call_parentheses": false, 1380 | "spaces_within_method_parentheses": false, 1381 | "spaces_within_parentheses": false, 1382 | "tab_width": 4 1383 | }, 1384 | "twig": { 1385 | "continuation_indent_size": 8, 1386 | "indent_size": 4, 1387 | "indent_style": "space", 1388 | "keep_indents_on_empty_lines": false, 1389 | "smart_tabs": false, 1390 | "spaces_inside_delimiters": true, 1391 | "spaces_inside_variable_delimiters": true, 1392 | "tab_width": 4 1393 | }, 1394 | "typescript": { 1395 | "align_imports": false, 1396 | "align_multiline_array_initializer_expression": false, 1397 | "align_multiline_binary_operation": false, 1398 | "align_multiline_chained_methods": false, 1399 | "align_multiline_extends_list": false, 1400 | "align_multiline_for": true, 1401 | "align_multiline_parameters": true, 1402 | "align_multiline_parameters_in_calls": false, 1403 | "align_multiline_ternary_operation": false, 1404 | "align_object_properties": 0, 1405 | "align_union_types": false, 1406 | "align_var_statements": 0, 1407 | "array_initializer_new_line_after_left_brace": false, 1408 | "array_initializer_right_brace_on_new_line": false, 1409 | "array_initializer_wrap": "off", 1410 | "assignment_wrap": "off", 1411 | "binary_operation_sign_on_next_line": false, 1412 | "binary_operation_wrap": "off", 1413 | "blacklist_imports": "rxjs/Rx,node_modules/**/*", 1414 | "blank_lines_after_imports": 1, 1415 | "blank_lines_around_class": 1, 1416 | "blank_lines_around_field": 0, 1417 | "blank_lines_around_field_in_interface": 0, 1418 | "blank_lines_around_function": 1, 1419 | "blank_lines_around_method": 1, 1420 | "blank_lines_around_method_in_interface": 1, 1421 | "brace_style": "end_of_line", 1422 | "call_parameters_new_line_after_left_paren": false, 1423 | "call_parameters_right_paren_on_new_line": false, 1424 | "call_parameters_wrap": "off", 1425 | "catch_on_new_line": false, 1426 | "chained_call_dot_on_new_line": true, 1427 | "class_brace_style": "end_of_line", 1428 | "comma_on_new_line": false, 1429 | "continuation_indent_size": 4, 1430 | "do_while_brace_force": "never", 1431 | "else_on_new_line": false, 1432 | "enforce_trailing_comma": "keep", 1433 | "extends_keyword_wrap": "off", 1434 | "extends_list_wrap": "off", 1435 | "field_prefix": "_", 1436 | "file_name_style": "relaxed", 1437 | "finally_on_new_line": false, 1438 | "for_brace_force": "never", 1439 | "for_statement_new_line_after_left_paren": false, 1440 | "for_statement_right_paren_on_new_line": false, 1441 | "for_statement_wrap": "off", 1442 | "force_quote_style": false, 1443 | "force_semicolon_style": false, 1444 | "function_expression_brace_style": "end_of_line", 1445 | "if_brace_force": "never", 1446 | "import_merge_members": "global", 1447 | "import_prefer_absolute_path": "global", 1448 | "import_sort_members": true, 1449 | "import_sort_module_name": false, 1450 | "import_use_node_resolution": "true", 1451 | "imports_wrap": "on_every_item", 1452 | "indent_case_from_switch": true, 1453 | "indent_chained_calls": true, 1454 | "indent_package_children": 0, 1455 | "indent_size": 4, 1456 | "indent_style": "space", 1457 | "jsdoc_include_types": false, 1458 | "jsx_attribute_value": "braces", 1459 | "keep_blank_lines_in_code": 2, 1460 | "keep_first_column_comment": true, 1461 | "keep_indents_on_empty_lines": false, 1462 | "keep_line_breaks": true, 1463 | "keep_simple_blocks_in_one_line": false, 1464 | "keep_simple_methods_in_one_line": false, 1465 | "line_comment_add_space": true, 1466 | "line_comment_at_first_column": false, 1467 | "method_brace_style": "end_of_line", 1468 | "method_call_chain_wrap": "off", 1469 | "method_parameters_new_line_after_left_paren": false, 1470 | "method_parameters_right_paren_on_new_line": false, 1471 | "method_parameters_wrap": "off", 1472 | "object_literal_wrap": "on_every_item", 1473 | "parentheses_expression_new_line_after_left_paren": false, 1474 | "parentheses_expression_right_paren_on_new_line": false, 1475 | "place_assignment_sign_on_next_line": false, 1476 | "prefer_as_type_cast": false, 1477 | "prefer_parameters_wrap": false, 1478 | "reformat_c_style_comments": false, 1479 | "smart_tabs": false, 1480 | "space_after_colon": true, 1481 | "space_after_comma": true, 1482 | "space_after_dots_in_rest_parameter": false, 1483 | "space_after_generator_mult": true, 1484 | "space_after_property_colon": true, 1485 | "space_after_quest": true, 1486 | "space_after_type_colon": true, 1487 | "space_after_unary_not": false, 1488 | "space_before_async_arrow_lparen": true, 1489 | "space_before_catch_keyword": true, 1490 | "space_before_catch_left_brace": true, 1491 | "space_before_catch_parentheses": true, 1492 | "space_before_class_lbrace": true, 1493 | "space_before_class_left_brace": true, 1494 | "space_before_colon": true, 1495 | "space_before_comma": false, 1496 | "space_before_do_left_brace": true, 1497 | "space_before_else_keyword": true, 1498 | "space_before_else_left_brace": true, 1499 | "space_before_finally_keyword": true, 1500 | "space_before_finally_left_brace": true, 1501 | "space_before_for_left_brace": true, 1502 | "space_before_for_parentheses": true, 1503 | "space_before_for_semicolon": false, 1504 | "space_before_function_left_parenth": true, 1505 | "space_before_generator_mult": false, 1506 | "space_before_if_left_brace": true, 1507 | "space_before_if_parentheses": true, 1508 | "space_before_method_call_parentheses": false, 1509 | "space_before_method_left_brace": true, 1510 | "space_before_method_parentheses": false, 1511 | "space_before_property_colon": false, 1512 | "space_before_quest": true, 1513 | "space_before_switch_left_brace": true, 1514 | "space_before_switch_parentheses": true, 1515 | "space_before_try_left_brace": true, 1516 | "space_before_type_colon": false, 1517 | "space_before_unary_not": false, 1518 | "space_before_while_keyword": true, 1519 | "space_before_while_left_brace": true, 1520 | "space_before_while_parentheses": true, 1521 | "spaces_around_additive_operators": true, 1522 | "spaces_around_arrow_function_operator": true, 1523 | "spaces_around_assignment_operators": true, 1524 | "spaces_around_bitwise_operators": true, 1525 | "spaces_around_equality_operators": true, 1526 | "spaces_around_logical_operators": true, 1527 | "spaces_around_multiplicative_operators": true, 1528 | "spaces_around_relational_operators": true, 1529 | "spaces_around_shift_operators": true, 1530 | "spaces_around_unary_operator": false, 1531 | "spaces_within_array_initializer_brackets": false, 1532 | "spaces_within_brackets": false, 1533 | "spaces_within_catch_parentheses": false, 1534 | "spaces_within_for_parentheses": false, 1535 | "spaces_within_if_parentheses": false, 1536 | "spaces_within_imports": false, 1537 | "spaces_within_interpolation_expressions": false, 1538 | "spaces_within_method_call_parentheses": false, 1539 | "spaces_within_method_parentheses": false, 1540 | "spaces_within_object_literal_braces": false, 1541 | "spaces_within_object_type_braces": true, 1542 | "spaces_within_parentheses": false, 1543 | "spaces_within_switch_parentheses": false, 1544 | "spaces_within_type_assertion": false, 1545 | "spaces_within_union_types": true, 1546 | "spaces_within_while_parentheses": false, 1547 | "special_else_if_treatment": true, 1548 | "tab_width": 4, 1549 | "ternary_operation_signs_on_next_line": false, 1550 | "ternary_operation_wrap": "off", 1551 | "union_types_wrap": "on_every_item", 1552 | "use_chained_calls_group_indents": false, 1553 | "use_double_quotes": true, 1554 | "use_explicit_js_extension": "global", 1555 | "use_path_mapping": "always", 1556 | "use_public_modifier": false, 1557 | "use_semicolon_after_statement": true, 1558 | "var_declaration_wrap": "normal", 1559 | "while_brace_force": "never", 1560 | "while_on_new_line": false, 1561 | "wrap_comments": false 1562 | }, 1563 | "xml": { 1564 | "block_comment_at_first_column": true, 1565 | "continuation_indent_size": 8, 1566 | "indent_size": 4, 1567 | "indent_style": "space", 1568 | "keep_indents_on_empty_lines": false, 1569 | "line_comment_at_first_column": true, 1570 | "smart_tabs": false, 1571 | "tab_width": 4 1572 | }, 1573 | "yaml": { 1574 | "continuation_indent_size": 2, 1575 | "indent_size": 2, 1576 | "indent_style": "space", 1577 | "keep_indents_on_empty_lines": false, 1578 | "keep_line_breaks": true, 1579 | "smart_tabs": false, 1580 | "tab_width": 4 1581 | } 1582 | } 1583 | } -------------------------------------------------------------------------------- /readme.md: -------------------------------------------------------------------------------- 1 | # Laravel Gmail 2 | 3 | [![Build Status](https://scrutinizer-ci.com/g/dacastro4/laravel-gmail/badges/build.png?b=master)](https://scrutinizer-ci.com/g/dacastro4/laravel-gmail/build-status/master) 4 | [![Scrutinizer Code Quality](https://scrutinizer-ci.com/g/dacastro4/laravel-gmail/badges/quality-score.png?b=master)](https://scrutinizer-ci.com/g/dacastro4/laravel-gmail/?branch=master) 5 | [![GitHub issues](https://img.shields.io/github/issues/dacastro4/laravel-gmail.svg)](https://github.com/dacastro4/laravel-gmail/issues) 6 | [![Total Downloads](https://poser.pugx.org/dacastro4/laravel-gmail/downloads)](https://packagist.org/packages/dacastro4/laravel-gmail) 7 | [![Monthly Downloads](https://poser.pugx.org/dacastro4/laravel-gmail/d/monthly)](https://packagist.org/packages/dacastro4/laravel-gmail) 8 | [![GitHub license](https://img.shields.io/github/license/dacastro4/laravel-gmail.svg)](https://github.com/dacastro4/laravel-gmail/blob/master/LICENSE) 9 | 10 | 11 | # Gmail 12 | Gmail API for Laravel 9 13 | 14 | You need to create an application in the [Google Console](https://console.developers.google.com/apis/credentials). Guidance [here](https://developers.google.com/gmail/api/quickstart/php#step_1_turn_on_the_api_name). 15 | 16 | if you need **Laravel 5** compatibility please use version `2.0.x`. 17 | if you need **Laravel 6** compatibility please use version `3.0.x`. 18 | if you need **Laravel 7** compatibility please use version `4.0.x`. 19 | if you need **Laravel 8** compatibility please use version `5.0.x`. 20 | 21 | # Requirements 22 | 23 | * PHP ^8.0 24 | * Laravel 9 25 | 26 | # Installation 27 | 28 | Add dacastro4/laravel-gmail to composer.json. 29 | 30 | `"dacastro4/laravel-gmail": "^6.1"` 31 | 32 | Run composer update to pull down the latest version. 33 | 34 | Or run 35 | 36 | `composer require dacastro4/laravel-gmail` 37 | 38 | Now open up `config/app.php` and add the service provider to your providers array. 39 | 40 | ``` php 41 | 'providers' => [ 42 | Dacastro4\LaravelGmail\LaravelGmailServiceProvider::class, 43 | ] 44 | ``` 45 | 46 | Now add the alias. 47 | 48 | ``` php 49 | 'aliases' => [ 50 | 'LaravelGmail' => Dacastro4\LaravelGmail\Facade\LaravelGmail::class, 51 | ] 52 | ``` 53 | 54 | For laravel >=5.5 that's all. This package supports Laravel new [Package Discovery](https://laravel.com/docs/5.5/packages#package-discovery). 55 | 56 | For <= PHP 7.4 compatibility use version `v5.0` 57 | 58 | # Migration from 5.0 to 6.0 59 | 60 | Requires Laravel 9 and you have to change the dependency to `"laravel/laravel": "^9.0"` 61 | Please, follow [Upgrading To 9.0 From 8.x Guide](https://laravel.com/docs/9.x/upgrade) 62 | 63 | # Migration from 4.0 to 5.0 64 | 65 | Requires Laravel 8 and you have to change the dependency to `"laravel/laravel": "^8.0"` 66 | Please, follow [Upgrading To 8.0 From 7.x Guide](https://laravel.com/docs/8.x/upgrade) 67 | 68 | # Migration from 3.0 to 4.0 69 | 70 | Requires Laravel 7 and you have to change the dependency to `"laravel/laravel": "^7.0"` 71 | Please, follow [Upgrading To 7.0 From 6.x Guide](https://laravel.com/docs/7.x/upgrade) 72 | 73 | # Migration from 2.0 to 3.0 74 | 75 | Requires Laravel 6 and you only have to change the dependency to `"laravel/laravel": "^6.0"` 76 | 77 | # Migration from 1.0 to 2.0 78 | The only changed made was the multi credentials feature. 79 | - Change your composer.json from `"dacastro4/laravel-gmail": "^1.0"` to `"dacastro4/laravel-gmail": "^2.0"` 80 | 81 | I had to change version because of a typo and that might break apps calling those attributes. 82 | 83 | All variable with the word "threat" was change to "thread" (yeah, I know.. sorry) 84 | Ex: 85 | 86 | Mail Class 87 | `$threatId` => `$threadId` 88 | 89 | Replyable Class 90 | `$mail->setReplyThreat()` => `$mail->setReplyThread()` 91 | 92 | and so on. 93 | 94 | # Migration from 0.6 to 1.0 95 | The only changed made was the multi credentials feature. 96 | - Change your composer.json from `"dacastro4/laravel-gmail": "^0.6"` to `"dacastro4/laravel-gmail": "^1.0"` 97 | 98 | If you don't want the multi user credentials, you don't have to do anything else, if you do, you're going to have to 99 | login again to create a new credentials file per user. 100 | 101 | 102 | # Configuration 103 | 104 | You only have to set the following variables on your `.env` file and you'll be on your way: 105 | 106 | ``` dotenv 107 | GOOGLE_PROJECT_ID= 108 | GOOGLE_CLIENT_ID= 109 | GOOGLE_CLIENT_SECRET= 110 | GOOGLE_REDIRECT_URI= 111 | GOOGLE_ALLOW_MULTIPLE_CREDENTIALS 112 | GOOGLE_ALLOW_JSON_ENCRYPT 113 | ``` 114 | 115 | To modify the scopes and the credentials file name, just run: 116 | 117 | Run `php artisan vendor:publish --provider="Dacastro4\LaravelGmail\LaravelGmailServiceProvider"` and modify the config file `config/gmail.php`. 118 | 119 | ### Allow multi user credentials 120 | To allow multi user credentials change `allow_multiple_credentials` to `true` in your config file or set the .env variable 121 | `GOOGLE_ALLOW_MULTIPLE_CREDENTIALS` to true if you're not using the config file. 122 | ### Allow encryption for json files 123 | To allow encryption for json files change `allow_json_encrypt` to `true` in your config file or set the .env variable 124 | `GOOGLE_ALLOW_JSON_ENCRYPT` to true if you're not using the config file. 125 | 126 | ### Available Scopes 127 | 128 | * all *(this one doesn't exists on Gmail Scopes, I added it.)* 129 | * compose 130 | * insert 131 | * labels 132 | * metadata 133 | * modify 134 | * readonly 135 | * send 136 | * settings_basic 137 | * settings_sharing 138 | 139 | [More about Gmail API scopes](https://developers.google.com/gmail/api/auth/scopes) 140 | 141 | Note: To change the scopes, users have to logout and login again. 142 | 143 | #### Additional Scopes 144 | If for some reason you need to add additional scopes. 145 | 146 | Add additional scopes in URL Style in config/gmail.php 147 | 148 | ``` 149 | 'additional_scopes' => [ 150 | 'https://www.googleapis.com/auth/drive', 151 | 'https://www.googleapis.com/auth/documents', 152 | 'https://www.googleapis.com/auth/spreadsheets' 153 | ], 154 | ``` 155 | 156 | 157 | # Example 158 | 159 | ## Welcome Blade: 160 | ```blade 161 | 162 |

{{ LaravelGmail::user() }}

163 | @if(LaravelGmail::check()) 164 | logout 165 | @else 166 | login 167 | @endif 168 | 169 | ``` 170 | 171 | ## Routes: 172 | ```php 173 | Route::get('/oauth/gmail', function (){ 174 | return LaravelGmail::redirect(); 175 | }); 176 | 177 | Route::get('/oauth/gmail/callback', function (){ 178 | LaravelGmail::makeToken(); 179 | return redirect()->to('/'); 180 | }); 181 | 182 | Route::get('/oauth/gmail/logout', function (){ 183 | LaravelGmail::logout(); //It returns exception if fails 184 | return redirect()->to('/'); 185 | }); 186 | ``` 187 | 188 | Then if in your controller or wherever you want to do your logic, you do something like: 189 | ```php 190 | $messages = LaravelGmail::message()->subject('test')->unread()->preload()->all(); 191 | foreach ( $messages as $message ) { 192 | $body = $message->getHtmlBody(); 193 | $subject = $message->getSubject(); 194 | } 195 | ``` 196 | Note that if you don't preload the messages you have to do something like: 197 | ` $body = $message->load()->getSubject();` 198 | and after that you don't have to call it again. 199 | 200 | ## Pagination 201 | Use `$messages->hasNextPage()` to check whether next page is available. 202 | Use `$messages->next()` to get the next page results, which uses the same parameters (result per page, filters, etc.) when you loaded the first page. 203 | Use `$messages->getPageToken()` to get the unique identifier for the next page token. This is useful in creating a unique idendifier when storing the result in cache. 204 | Generally speaking, it is a bad practice to use API for pagination. It is slow and costly. Therefore, it is recommended to retrieve the cached result moving between pages and only flush the cache when have to. 205 | 206 | # Documentation 207 | 208 | ## Basic 209 | 210 | `LaravelGmail::getAuthUrl` Gets the URL to auth the user. 211 | 212 | `LaravelGmail::redirect` You can use this as a direct method `Login` 213 | 214 | `LaravelGmail::makeToken()` Set and Save AccessToken in json file (useful in the callback) 215 | 216 | `LaravelGmail::logout` Logs out the user 217 | 218 | `LaravelGmail::check` Checks if the user is logged in 219 | 220 | `LaravelGmail::setUserId($account_id)->makeToken()` Set and Save AccessToken for $account_id (added v5.1.2) 221 | 222 | 223 | ## Sending 224 | 225 | ``` 226 | use Dacastro4\LaravelGmail\Services\Message\Mail; 227 | 228 | ... 229 | 230 | $mail = new Mail; 231 | ``` 232 | For `to`, `from`, `cc` and `bcc`, you can set an array of emails and name or a string of email and name. 233 | 234 | 235 | `$mail->using( $token )` If you don't want to use the token file, you can use this function that sets the token to use in the request. It doesn't refresh 236 | 237 | `$mail->to( $to, $name = null )` sets the recipient email and name as optional 238 | 239 | `$mail->from( $from, $name = null )` sets sender's email 240 | 241 | `$mail->cc( $cc, $name = null )` sets carbon copy 242 | 243 | `$mail->bcc( $bcc, $name = null )` sets a blind carbon copy 244 | 245 | `$mail->subject( $subject )` sets the subject of the email 246 | 247 | `$mail->message( $message )` sets the body of the email 248 | 249 | `$mail->view( 'view.name', $dataArray )` sets the body from a blade file 250 | 251 | `$mail->markdown( 'view.name', $dataArray )` sets the body from a markdown file 252 | 253 | `$mail->attach( ...$path )` add file attachments to the email 254 | 255 | `$mail->priority( $priority )` sets the priority of the email from 1 to 5 256 | 257 | `$mail->reply()` replies to an existent email 258 | 259 | `$mail->send()` sends a new email 260 | 261 | `$mail->setHeader( $header, $value )` sets header to the email 262 | 263 | ## Mail 264 | 265 | `$mail->getId` returns the email's ID 266 | 267 | `$mail->getInternalDate` returns date in UNIX format 268 | 269 | `$mail->getDate` returns a Carbon date from the header of the email 270 | 271 | `$mail->getLabels` returns an array of all the labels of the email 272 | 273 | `$mail->getHeaders` returns a collection of the header. Each header is an array with two rows key and value 274 | 275 | `$mail->getSubject` returns an string of the subject 276 | 277 | `$mail->getFrom` Returns an array with name and email of sender 278 | 279 | `$mail->getFromName` Returns string of name 280 | 281 | `$mail->getFromEmail` Returns string of email 282 | 283 | `$mail->getTo` Returns an array with name and email of all recipients 284 | 285 | `$mail->getDeliveredTo` Returns the email of the receiver 286 | 287 | `$mail->getPlainTextBody` Returns the plain text version of the email 288 | 289 | `$mail->getRawPlainTextBody` Returns the raw version of the body base64 encrypted 290 | 291 | `$mail->hasAttachments` Returns a boolean if the email has attachments 292 | 293 | `$mail->load` Load all the information of the email (labels, body, headers). You call this function on a single email. To load from the beginning see [preload()](#preload) 294 | 295 | `$mail->getHeader( $headerName, $regex = null )` Returns the header by name. Optionally, you can execute a regex on the value 296 | 297 | 298 | # Labels 299 | 300 | `$mail->markAsRead` Removes the 'UNREAD' label from the email. 301 | 302 | `$mail->markAsUnread` Adds the 'UNREAD' label to the email. 303 | 304 | `$mail->markAsImportant` Adds the 'IMPORTANT' label to the email. 305 | 306 | `$mail->markAsNotImportant` Removes the 'IMPORTANT' label from the email. 307 | 308 | `$mail->addStar` Adds the 'STARRED' label to the email. 309 | 310 | `$mail->removeStar` Removes the 'STARRED' label from the email. 311 | 312 | `$mail->sendToTrash` Adds the 'TRASH' label to the email. 313 | 314 | `$mail->removeFromTrash` Removes the 'TRASH' label from the email. 315 | 316 | `$mail->addLabel($string|$array)` Add multiple or single label to the email 317 | 318 | `$mail->removeLabel($string|$array)` Removes multiple or single label from the email 319 | 320 | `$mail->getAttachments()` Get a collection of all the attachments on the email 321 | 322 | `$mail->getAttachmentsWithData()` Get a collection of all the attachments on the email including the data 323 | 324 | `Listing`: List all the labels of the email 325 | 326 | https://developers.google.com/gmail/api/reference/rest/v1/users.labels/list 327 | 328 | Example: 329 | 330 | ``` php 331 | $mailbox = new LaravelGmailClass(config(), $account->id); 332 | $labels = $mailbox->labelsList($userEmail); 333 | ``` 334 | 335 | `Create`: Create new label on the email with the labelName 336 | 337 | https://developers.google.com/gmail/api/reference/rest/v1/users.labels/create 338 | 339 | Example: 340 | 341 | ``` php 342 | $mailbox = new LaravelGmailClass(config(), LaravelGmail::user()); 343 | 344 | $label = new \Google_Service_Gmail_Label($this); 345 | $label->setMessageListVisibility('show'); `show || hide` 346 | $label->setLabelListVisibility('labelShow'); `labelShow || labelShowIfUnread || labelHide` 347 | $label->setName('labelName'); 348 | $mailbox->createLabel($userEmail, $label); 349 | ``` 350 | 351 | `FirstOrCreateLabel`: Create new label on the email with the labelName if it doesn't exist 352 | 353 | https://developers.google.com/gmail/api/reference/rest/v1/users.labels/create 354 | 355 | Example: 356 | 357 | ``` php 358 | $mailbox = new LaravelGmailClass(config(), LaravelGmail::user()); 359 | 360 | $label = new \Google_Service_Gmail_Label($this); 361 | $label->setMessageListVisibility('show'); `show || hide` 362 | $label->setLabelListVisibility('labelShow'); `labelShow || labelShowIfUnread || labelHide` 363 | $label->setName('labelName'); 364 | $mailbox->firstOrCreateLabel($userEmail, $label); 365 | ``` 366 | 367 | 368 | ## Attachment 369 | 370 | ``` 371 | use Dacastro4\LaravelGmail\Services\Message\Attachment 372 | ... 373 | 374 | $attachment = new Attachment; 375 | ``` 376 | 377 | `$attachment->getId` Returns the ID of the attachment 378 | 379 | `$attachment->getFileName` Returns the file name of the attachment 380 | 381 | `$attachment->getMimeType` Returns the mime type Ex: application/pdf 382 | 383 | `$attachment->getSize` Returns the size of the attachment in bytes 384 | 385 | `$attachment->getData` Get the all the information from the attachment. If you call `getAttachmentsWithData` you won't need this method. 386 | 387 | `$attachment->saveAttachmentTo($path = null, $filename = null, $disk = 'local')` Saves the attachment on the storage folder. You can pass the path, name and disk to use. 388 | 389 | 390 | ## Messages 391 | 392 | `LaravelGmail::message()->all( $pageToken = null )` Returns all the emails from the inbox 393 | 394 | `LaravelGmail::message()->take(2)->all( $pageToken = null )` The `take` method limits the emails coming from the query by the number set 395 | 396 | `LaravelGmail::message()->get( $id )` Returns a single email with all the information 397 | 398 | ### Modifiers 399 | 400 | You can modify your query with these methods. For example: 401 | 402 | To get all unread emails: `LaravelGmail::message()->unread()->all()` 403 | 404 | `message()->unread()` 405 | 406 | `message()->from( $email )` 407 | 408 | `message()->in( $box = 'inbox' )` 409 | 410 | `message()->hasAttachment()` 411 | 412 | `message()->subject($subject)` 413 | 414 | `->after($date)` and `->before($date)` 415 | 416 | `message()->raw($query)` for customized queries 417 | 418 | All the possible filters are in the [Filterable Trait](https://github.com/dacastro4/laravel-gmail/blob/master/src/Traits/Filterable.php) 419 | 420 | Of course you can use as a fluent api. 421 | 422 | ``` php 423 | 424 | LaravelGmail::message() 425 | ->from('someone@gmail.com') 426 | ->unread() 427 | ->in('TRASH') 428 | ->hasAttachment() 429 | ->all() 430 | ``` 431 | 432 | ## Attachment 433 | 434 | ``` 435 | use Dacastro4\LaravelGmail\Services\Message\Attachment 436 | ... 437 | 438 | $attachment = new Attachment; 439 | ``` 440 | 441 | `$attachment->getId` Returns the ID of the attachment 442 | 443 | `$attachment->getFileName` Returns the file name of the attachment 444 | 445 | `$attachment->getMimeType` Returns the mime type Ex: application/pdf 446 | 447 | `$attachment->getSize` Returns the size of the attachment in bytes 448 | 449 | `$attachment->getData` Get the all the information from the attachment. If you call `getAttachmentsWithData` you won't need this method. 450 | 451 | `$attachment->saveAttachmentTo($path = null, $filename = null, $disk = 'local')` Saves the attachment on the storage folder. You can pass the path, name and disk to use. 452 | 453 | 454 | ## Messages 455 | 456 | `LaravelGmail::message()->all( $pageToken = null )` Returns all the emails from the inbox 457 | 458 | `LaravelGmail::message()->take(2)->all( $pageToken = null )` The `take` method limits the emails coming from the query by the number set 459 | 460 | `LaravelGmail::message()->get( $id )` Returns a single email with all the information 461 | 462 | ### Modifiers 463 | 464 | You can modify your query with these methods. For example: 465 | 466 | To get all unread emails: `LaravelGmail::message()->unread()->all()` 467 | 468 | `message()->unread()` 469 | 470 | `message()->from( $email )` 471 | 472 | `message()->in( $box = 'inbox' )` 473 | 474 | `message()->hasAttachment()` 475 | 476 | `message()->subject($subject)` 477 | 478 | `->after($date)` and `->before($date)` 479 | 480 | `message()->raw($query)` for customized queries 481 | 482 | All the possible filters are in the [Filterable Trait](https://github.com/dacastro4/laravel-gmail/blob/master/src/Traits/Filterable.php) 483 | 484 | Of course you can use as a fluent api. 485 | 486 | ``` php 487 | 488 | LaravelGmail::message() 489 | ->from('someone@gmail.com') 490 | ->unread() 491 | ->in('TRASH') 492 | ->hasAttachment() 493 | ->all() 494 | ``` 495 | 496 | ### Preload 497 | 498 | You can preload the body, header and the rest of every single email just by calling this method. 499 | 500 | `LaravelGmail::preload()` 501 | 502 | Example: 503 | 504 | ``` php 505 | 506 | LaravelGmail::message() 507 | ->from('someone@gmail.com') 508 | ->unread() 509 | ->in('TRASH') 510 | ->hasAttachment() 511 | ->preload() 512 | ->all() 513 | ``` 514 | 515 | ### Watch 516 | https://developers.google.com/gmail/api/reference/rest/v1/users/watch 517 | 518 | Example: 519 | 520 | ``` php 521 | $mailbox = new LaravelGmailClass(config(), $account->id); 522 | 523 | // One watch per account + need reinit every 24h+ 524 | $mailbox->stopWatch('example@gmail.com'); 525 | 526 | // Set watch for topic 527 | $rq = new \Google_Service_Gmail_WatchRequest(); 528 | $rq->setTopicName('projects/YOUR_PROJECT_ID/topics/gmail'); 529 | $mailbox->setWatch('example@gmail.com', $rq); 530 | ``` 531 | 532 | 533 | ### History 534 | https://developers.google.com/gmail/api/reference/rest/v1/users.history 535 | 536 | Example: 537 | 538 | ``` php 539 | $historyList = (new LaravelGmailClass(config(), $account->id)) 540 | ->historyList($data['emailAddress'], [ 541 | 'startHistoryId' => $startHistoryId, 542 | ]); 543 | foreach ($historyList->history as $chunk) { 544 | foreach ($chunk->messages as $msg) { 545 | ... 546 | } 547 | } 548 | ``` 549 | 550 | 551 | ### Frequent Issues 552 | 553 | #### Login Required 554 | 555 | If you're getting the `Login Required` error, try creating the `gmail-json.json` file under `/storage/app/gmail/tokens/`. 556 | -------------------------------------------------------------------------------- /src/Exceptions/AuthException.php: -------------------------------------------------------------------------------- 1 | app = Container::getInstance(); 33 | 34 | $this->userId = $userId; 35 | 36 | $this->configConstruct($config); 37 | 38 | $this->configuration = $config; 39 | 40 | parent::__construct($this->getConfigs()); 41 | 42 | $this->configApi(); 43 | 44 | if ($this->checkPreviouslyLoggedIn()) { 45 | $this->refreshTokenIfNeeded(); 46 | } 47 | 48 | } 49 | 50 | /** 51 | * Check and return true if the user has previously logged in without checking if the token needs to refresh 52 | * 53 | * @return bool 54 | */ 55 | public function checkPreviouslyLoggedIn() 56 | { 57 | $fileName = $this->getFileName(); 58 | $file = "gmail/tokens/$fileName.json"; 59 | $allowJsonEncrypt = $this->_config['gmail.allow_json_encrypt']; 60 | 61 | if (Storage::disk('local')->exists($file)) { 62 | if ($allowJsonEncrypt) { 63 | $savedConfigToken = json_decode(decrypt(Storage::disk('local')->get($file)), true); 64 | } else { 65 | $savedConfigToken = json_decode(Storage::disk('local')->get($file), true); 66 | } 67 | 68 | return !empty($savedConfigToken['access_token']); 69 | 70 | } 71 | 72 | return false; 73 | } 74 | 75 | /** 76 | * Refresh the auth token if needed 77 | * 78 | * @return mixed|null 79 | */ 80 | private function refreshTokenIfNeeded() 81 | { 82 | if ($this->isAccessTokenExpired()) { 83 | $this->fetchAccessTokenWithRefreshToken($this->getRefreshToken()); 84 | $token = $this->getAccessToken(); 85 | $this->setBothAccessToken($token); 86 | 87 | return $token; 88 | } 89 | 90 | return $this->token; 91 | } 92 | 93 | /** 94 | * Check if token exists and is expired 95 | * Throws an AuthException when the auth file its empty or with the wrong token 96 | * 97 | * 98 | * @return bool Returns True if the access_token is expired. 99 | */ 100 | public function isAccessTokenExpired() 101 | { 102 | $token = $this->getToken(); 103 | 104 | if ($token) { 105 | $this->setAccessToken($token); 106 | } 107 | 108 | return parent::isAccessTokenExpired(); 109 | } 110 | 111 | public function getToken() 112 | { 113 | return parent::getAccessToken() ?: $this->config(); 114 | } 115 | 116 | public function setToken($token) 117 | { 118 | $this->setAccessToken($token); 119 | } 120 | 121 | public function getAccessToken() 122 | { 123 | $token = parent::getAccessToken() ?: $this->config(); 124 | 125 | return $token; 126 | } 127 | 128 | /** 129 | * @param array|string $token 130 | */ 131 | public function setAccessToken($token) 132 | { 133 | parent::setAccessToken($token); 134 | } 135 | 136 | /** 137 | * @param $token 138 | */ 139 | public function setBothAccessToken($token) 140 | { 141 | $this->setAccessToken($token); 142 | $this->saveAccessToken($token); 143 | } 144 | 145 | /** 146 | * Save the credentials in a file 147 | * 148 | * @param array $config 149 | */ 150 | public function saveAccessToken(array $config) 151 | { 152 | $disk = Storage::disk('local'); 153 | $fileName = $this->getFileName(); 154 | $file = "gmail/tokens/$fileName.json"; 155 | $allowJsonEncrypt = $this->_config['gmail.allow_json_encrypt']; 156 | $config['email'] = $this->emailAddress; 157 | 158 | if ($disk->exists($file)) { 159 | 160 | if (empty($config['email'])) { 161 | if ($allowJsonEncrypt) { 162 | $savedConfigToken = json_decode(decrypt($disk->get($file)), true); 163 | } else { 164 | $savedConfigToken = json_decode($disk->get($file), true); 165 | } 166 | if (isset($savedConfigToken['email'])) { 167 | $config['email'] = $savedConfigToken['email']; 168 | } 169 | } 170 | 171 | $disk->delete($file); 172 | } 173 | 174 | if ($allowJsonEncrypt) { 175 | $disk->put($file, encrypt(json_encode($config))); 176 | } else { 177 | $disk->put($file, json_encode($config)); 178 | } 179 | 180 | } 181 | 182 | /** 183 | * @return array|string 184 | * @throws \Exception 185 | */ 186 | public function makeToken() 187 | { 188 | if (!$this->check()) { 189 | $request = Request::capture(); 190 | $code = (string)$request->input('code', null); 191 | if (!is_null($code) && !empty($code)) { 192 | $accessToken = $this->fetchAccessTokenWithAuthCode($code); 193 | if ($this->haveReadScope()) { 194 | $me = $this->getProfile(); 195 | if (property_exists($me, 'emailAddress')) { 196 | $this->emailAddress = $me->emailAddress; 197 | $accessToken['email'] = $me->emailAddress; 198 | } 199 | } 200 | $this->setBothAccessToken($accessToken); 201 | 202 | return $accessToken; 203 | } else { 204 | throw new \Exception('No access token'); 205 | } 206 | } else { 207 | return $this->getAccessToken(); 208 | } 209 | } 210 | 211 | /** 212 | * Check 213 | * 214 | * @return bool 215 | */ 216 | public function check() 217 | { 218 | return !$this->isAccessTokenExpired(); 219 | } 220 | 221 | /** 222 | * Gets user profile from Gmail 223 | * 224 | * @return \Google_Service_Gmail_Profile 225 | */ 226 | public function getProfile() 227 | { 228 | $service = new Google_Service_Gmail($this); 229 | 230 | return $service->users->getProfile('me'); 231 | } 232 | 233 | /** 234 | * Revokes user's permission and logs them out 235 | */ 236 | public function logout() 237 | { 238 | $this->revokeToken(); 239 | } 240 | 241 | /** 242 | * Delete the credentials in a file 243 | */ 244 | public function deleteAccessToken() 245 | { 246 | $disk = Storage::disk('local'); 247 | $fileName = $this->getFileName(); 248 | $file = "gmail/tokens/$fileName.json"; 249 | 250 | $allowJsonEncrypt = $this->_config['gmail.allow_json_encrypt']; 251 | 252 | if ($disk->exists($file)) { 253 | $disk->delete($file); 254 | } 255 | 256 | if ($allowJsonEncrypt) { 257 | $disk->put($file, encrypt(json_encode([]))); 258 | } else { 259 | $disk->put($file, json_encode([])); 260 | } 261 | 262 | } 263 | 264 | private function haveReadScope() 265 | { 266 | $scopes = $this->getUserScopes(); 267 | 268 | return in_array(Google_Service_Gmail::GMAIL_READONLY, $scopes); 269 | } 270 | 271 | /** 272 | * users.stop receiving push notifications for the given user mailbox. 273 | * 274 | * @param string $userEmail Email address 275 | * @param array $optParams 276 | * @return \Google_Service_Gmail_Stop 277 | */ 278 | public function stopWatch($userEmail, $optParams = []) 279 | { 280 | $service = new Google_Service_Gmail($this); 281 | 282 | return $service->users->stop($userEmail, $optParams); 283 | } 284 | 285 | /** 286 | * Set up or update a push notification watch on the given user mailbox. 287 | * 288 | * @param string $userEmail Email address 289 | * @param Google_Service_Gmail_WatchRequest $postData 290 | * 291 | * @return \Google_Service_Gmail_WatchResponse 292 | */ 293 | public function setWatch($userEmail, \Google_Service_Gmail_WatchRequest $postData): \Google_Service_Gmail_WatchResponse 294 | { 295 | $service = new Google_Service_Gmail($this); 296 | 297 | return $service->users->watch($userEmail, $postData); 298 | } 299 | 300 | /** 301 | * Lists the history of all changes to the given mailbox. History results are returned in chronological order (increasing historyId). 302 | * @param $userEmail 303 | * @param $params 304 | * @return \Google\Service\Gmail\ListHistoryResponse 305 | */ 306 | public function historyList($userEmail, $params) 307 | { 308 | $service = new Google_Service_Gmail($this); 309 | 310 | return $service->users_history->listUsersHistory($userEmail, $params); 311 | } 312 | } 313 | -------------------------------------------------------------------------------- /src/LaravelGmailClass.php: -------------------------------------------------------------------------------- 1 | getToken()) { 27 | throw new AuthException('No credentials found.'); 28 | } 29 | 30 | return new Message($this); 31 | } 32 | 33 | /** 34 | * Returns the Gmail user email 35 | * 36 | * @return \Google_Service_Gmail_Profile 37 | */ 38 | public function user() 39 | { 40 | return $this->config('email'); 41 | } 42 | 43 | /** 44 | * Updates / sets the current userId for the service 45 | * 46 | * @return \Google_Service_Gmail_Profile 47 | */ 48 | public function setUserId($userId) 49 | { 50 | $this->userId = $userId; 51 | return $this; 52 | } 53 | 54 | public function redirect() 55 | { 56 | return Redirect::to($this->getAuthUrl()); 57 | } 58 | 59 | /** 60 | * Gets the URL to authorize the user 61 | * 62 | * @return string 63 | */ 64 | public function getAuthUrl() 65 | { 66 | return $this->createAuthUrl(); 67 | } 68 | 69 | public function logout() 70 | { 71 | $this->revokeToken(); 72 | $this->deleteAccessToken(); 73 | } 74 | 75 | } 76 | -------------------------------------------------------------------------------- /src/LaravelGmailServiceProvider.php: -------------------------------------------------------------------------------- 1 | publishes([__DIR__ . '/config/gmail.php' => App::make('path.config') . '/gmail.php',]); 14 | } 15 | 16 | public function register() 17 | { 18 | 19 | $this->mergeConfigFrom(__DIR__ . '/config/gmail.php', 'gmail'); 20 | 21 | // Main Service 22 | $this->app->bind('laravelgmail', function ($app) { 23 | return new LaravelGmailClass($app['config']); 24 | }); 25 | 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /src/Services/Message.php: -------------------------------------------------------------------------------- 1 | client = $client; 40 | $this->service = new Google_Service_Gmail($client); 41 | } 42 | 43 | /** 44 | * Returns next page if available of messages or an empty collection 45 | * 46 | * @return \Illuminate\Support\Collection 47 | * @throws \Google_Exception 48 | */ 49 | public function next() 50 | { 51 | if ($this->pageToken) { 52 | return $this->all($this->pageToken); 53 | } else { 54 | return new MessageCollection([], $this); 55 | } 56 | } 57 | 58 | /** 59 | * Returns a collection of Mail instances 60 | * 61 | * @param string|null $pageToken 62 | * 63 | * @return \Illuminate\Support\Collection 64 | * @throws \Google_Exception 65 | */ 66 | public function all(string $pageToken = null) 67 | { 68 | if (!is_null($pageToken)) { 69 | $this->add($pageToken, 'pageToken'); 70 | } 71 | 72 | $mails = []; 73 | $response = $this->getMessagesResponse(); 74 | $this->pageToken = method_exists($response, 'getNextPageToken') ? $response->getNextPageToken() : null; 75 | 76 | $messages = $response->getMessages(); 77 | 78 | if (!$this->preload) { 79 | foreach ($messages as $message) { 80 | $mails[] = new Mail($message, $this->preload, $this->client->userId); 81 | } 82 | } else { 83 | $mails = count($messages) > 0 ? $this->batchRequest($messages) : []; 84 | } 85 | 86 | return new MessageCollection($mails, $this); 87 | } 88 | 89 | /** 90 | * Returns boolean if the page token variable is null or not 91 | * 92 | * @return bool 93 | */ 94 | public function hasNextPage() 95 | { 96 | return !!$this->pageToken; 97 | } 98 | 99 | /** 100 | * Limit the messages coming from the queryxw 101 | * 102 | * @param int $number 103 | * 104 | * @return Message 105 | */ 106 | public function take($number) 107 | { 108 | $this->params['maxResults'] = abs((int)$number); 109 | 110 | return $this; 111 | } 112 | 113 | /** 114 | * @param $id 115 | * 116 | * @return Mail 117 | */ 118 | public function get($id) 119 | { 120 | $message = $this->getRequest($id); 121 | 122 | return new Mail($message, false, $this->client->userId); 123 | } 124 | 125 | /** 126 | * Creates a batch request to get all emails in a single call 127 | * 128 | * @param $allMessages 129 | * 130 | * @return array|null 131 | */ 132 | public function batchRequest($allMessages) 133 | { 134 | $this->client->setUseBatch(true); 135 | 136 | $batch = $this->service->createBatch(); 137 | 138 | foreach ($allMessages as $key => $message) { 139 | $batch->add($this->getRequest($message->getId()), $key); 140 | } 141 | 142 | $messagesBatch = $batch->execute(); 143 | 144 | $this->client->setUseBatch(false); 145 | 146 | $messages = []; 147 | 148 | foreach ($messagesBatch as $message) { 149 | $messages[] = new Mail($message, false, $this->client->userId); 150 | } 151 | 152 | return $messages; 153 | } 154 | 155 | /** 156 | * Preload the information on each Mail objects. 157 | * If is not preload you will have to call the load method from the Mail class 158 | * @return $this 159 | * @see Mail::load() 160 | * 161 | */ 162 | public function preload() 163 | { 164 | $this->preload = true; 165 | 166 | return $this; 167 | } 168 | 169 | public function getUser() 170 | { 171 | return $this->client->user(); 172 | } 173 | 174 | /** 175 | * @param $id 176 | * 177 | * @return \Google_Service_Gmail_Message 178 | */ 179 | private function getRequest($id) 180 | { 181 | return $this->service->users_messages->get('me', $id); 182 | } 183 | 184 | /** 185 | * @return \Google_Service_Gmail_ListMessagesResponse|object 186 | * @throws \Google_Exception 187 | */ 188 | private function getMessagesResponse() 189 | { 190 | $responseOrRequest = $this->service->users_messages->listUsersMessages('me', $this->params); 191 | 192 | if (get_class($responseOrRequest) === "GuzzleHttp\Psr7\Request") { 193 | $response = $this->service->getClient()->execute($responseOrRequest, 194 | 'Google_Service_Gmail_ListMessagesResponse'); 195 | 196 | return $response; 197 | } 198 | 199 | return $responseOrRequest; 200 | } 201 | } 202 | -------------------------------------------------------------------------------- /src/Services/Message/Attachment.php: -------------------------------------------------------------------------------- 1 | service = new Google_Service_Gmail($this); 66 | 67 | $body = $part->getBody(); 68 | $this->id = $body->getAttachmentId(); 69 | $this->size = $body->getSize(); 70 | $this->filename = $part->getFilename(); 71 | $this->mimeType = $part->getMimeType(); 72 | $this->messageId = $singleMessageId; 73 | $headers = $part->getHeaders(); 74 | $this->headerDetails = $this->getHeaderDetails($headers); 75 | } 76 | 77 | /** 78 | * Retuns attachment ID 79 | * 80 | * @return string 81 | */ 82 | public function getId() 83 | { 84 | return $this->id; 85 | } 86 | 87 | /** 88 | * Returns attachment file name 89 | * 90 | * @return string 91 | */ 92 | public function getFileName() 93 | { 94 | return $this->filename; 95 | } 96 | 97 | /** 98 | * Returns mime type of the attachment 99 | * 100 | * @return string 101 | */ 102 | public function getMimeType() 103 | { 104 | return $this->mimeType; 105 | } 106 | 107 | /** 108 | * Returns approximate size of the attachment 109 | * 110 | * @return mixed 111 | */ 112 | public function getSize() 113 | { 114 | return $this->size; 115 | } 116 | 117 | /** 118 | * @param string $path 119 | * @param string|null $filename 120 | * 121 | * @param string $disk 122 | * 123 | * @return string 124 | * @throws \Exception 125 | */ 126 | public function saveAttachmentTo($path = null, $filename = null, $disk = 'local') 127 | { 128 | 129 | $data = $this->getDecodedBody($this->getData()); 130 | 131 | if (!$data) { 132 | throw new \Exception('Could not get the attachment.'); 133 | } 134 | 135 | $filename = $filename ?: $this->filename; 136 | 137 | if (is_null($path)) { 138 | $path = '/'; 139 | } else { 140 | if (!Str::endsWith('/', $path)) { 141 | $path = "{$path}/"; 142 | } 143 | } 144 | 145 | $filePathAndName = "{$path}{$filename}"; 146 | 147 | Storage::disk($disk)->put($filePathAndName, $data); 148 | 149 | return $filePathAndName; 150 | 151 | } 152 | 153 | /** 154 | * @throws \Exception 155 | */ 156 | public function getData() 157 | { 158 | $attachment = $this->service->users_messages_attachments->get('me', $this->messageId, $this->id); 159 | 160 | return $attachment->getData(); 161 | } 162 | 163 | /** 164 | * Returns attachment headers 165 | * Contains Content-ID and X-Attachment-Id for embedded images 166 | * 167 | * @return array 168 | */ 169 | public function getHeaderDetails($headers) 170 | { 171 | $headerDetails = []; 172 | 173 | foreach ($headers as $header) { 174 | $headerDetails[$header->name] = $header->value; 175 | } 176 | 177 | return $headerDetails; 178 | } 179 | } 180 | -------------------------------------------------------------------------------- /src/Services/Message/Mail.php: -------------------------------------------------------------------------------- 1 | service = new Google_Service_Gmail($this); 89 | 90 | $this->__rConstruct(); 91 | $this->__mConstruct(); 92 | parent::__construct(config(), $userId); 93 | 94 | if (!is_null($message)) { 95 | if ($preload) { 96 | $message = $this->service->users_messages->get('me', $message->getId()); 97 | } 98 | 99 | $this->setUserId($userId); 100 | 101 | $this->setMessage($message); 102 | 103 | if ($preload) { 104 | $this->setMetadata(); 105 | } 106 | } 107 | } 108 | 109 | /** 110 | * Set user Id 111 | * 112 | * @param int $userId 113 | */ 114 | protected function setUserId($userId) 115 | { 116 | $this->userId = $userId; 117 | } 118 | 119 | /** 120 | * Sets data from mail 121 | * 122 | * @param \Google_Service_Gmail_Message $message 123 | */ 124 | protected function setMessage(\Google_Service_Gmail_Message $message) 125 | { 126 | $this->id = $message->getId(); 127 | $this->internalDate = $message->getInternalDate(); 128 | $this->labels = $message->getLabelIds(); 129 | $this->size = $message->getSizeEstimate(); 130 | $this->threadId = $message->getThreadId(); 131 | $this->historyId = $message->getHistoryId(); 132 | $this->payload = $message->getPayload(); 133 | if ($this->payload) { 134 | $this->parts = collect($this->payload->getParts()); 135 | } 136 | } 137 | 138 | /** 139 | * Sets the metadata from Mail when preloaded 140 | */ 141 | protected function setMetadata() 142 | { 143 | $this->to = $this->getTo(); 144 | $from = $this->getFrom(); 145 | $this->from = isset($from['email']) ? $from['email'] : null; 146 | $this->nameFrom = isset($from['email']) ? $from['email'] : null; 147 | 148 | $this->subject = $this->getSubject(); 149 | } 150 | 151 | /** 152 | * Return a UNIX version of the date 153 | * 154 | * @return int UNIX date 155 | */ 156 | public function getInternalDate() 157 | { 158 | return $this->internalDate; 159 | } 160 | 161 | /** 162 | * Returns the labels of the email 163 | * Example: INBOX, STARRED, UNREAD 164 | * 165 | * @return array 166 | */ 167 | public function getLabels() 168 | { 169 | return $this->labels; 170 | } 171 | 172 | /** 173 | * Returns approximate size of the email 174 | * 175 | * @return mixed 176 | */ 177 | public function getSize() 178 | { 179 | return $this->size; 180 | } 181 | 182 | /** 183 | * Returns thread ID of the email 184 | * 185 | * @return string 186 | */ 187 | public function getThreadId() 188 | { 189 | return $this->threadId; 190 | } 191 | 192 | /** 193 | * Returns history ID of the email 194 | * 195 | * @return string 196 | */ 197 | public function getHistoryId() 198 | { 199 | return $this->historyId; 200 | } 201 | 202 | /** 203 | * Returns all the headers of the email 204 | * 205 | * @return Collection 206 | */ 207 | public function getHeaders() 208 | { 209 | return $this->buildHeaders($this->payload->getHeaders()); 210 | } 211 | 212 | /** 213 | * Returns the subject of the email 214 | * 215 | * @return string 216 | */ 217 | public function getSubject() 218 | { 219 | return $this->getHeader('Subject'); 220 | } 221 | 222 | /** 223 | * Returns the subject of the email 224 | * 225 | * @return array|string 226 | */ 227 | public function getReplyTo() 228 | { 229 | $replyTo = $this->getHeader('Reply-To'); 230 | 231 | return $this->getFrom($replyTo ? $replyTo : $this->getHeader('From')); 232 | } 233 | 234 | /** 235 | * Returns array of name and email of each recipient 236 | * 237 | * @param string|null $email 238 | * @return array 239 | */ 240 | public function getFrom($email = null) 241 | { 242 | $from = $email ? $email : $this->getHeader('From'); 243 | 244 | preg_match('/<(.*)>/', $from, $matches); 245 | 246 | $name = preg_replace('/ <(.*)>/', '', $from); 247 | 248 | return [ 249 | 'name' => $name, 250 | 'email' => isset($matches[1]) ? $matches[1] : null, 251 | ]; 252 | } 253 | 254 | /** 255 | * Returns email of sender 256 | * 257 | * @return string|null 258 | */ 259 | public function getFromEmail() 260 | { 261 | $from = $this->getHeader('From'); 262 | 263 | if (filter_var($from, FILTER_VALIDATE_EMAIL)) { 264 | return $from; 265 | } 266 | 267 | preg_match('/<(.*)>/', $from, $matches); 268 | 269 | return isset($matches[1]) ? $matches[1] : null; 270 | } 271 | 272 | /** 273 | * Returns name of the sender 274 | * 275 | * @return string|null 276 | */ 277 | public function getFromName() 278 | { 279 | $from = $this->getHeader('From'); 280 | 281 | $name = preg_replace('/ <(.*)>/', '', $from); 282 | 283 | return $name; 284 | } 285 | 286 | /** 287 | * Returns array list of recipients 288 | * 289 | * @return array 290 | */ 291 | public function getTo() 292 | { 293 | $allTo = $this->getHeader('To'); 294 | 295 | return $this->formatEmailList($allTo); 296 | } 297 | 298 | /** 299 | * Returns array list of cc recipients 300 | * 301 | * @return array 302 | */ 303 | public function getCc() 304 | { 305 | $allCc = $this->getHeader('Cc'); 306 | 307 | return $this->formatEmailList($allCc); 308 | } 309 | 310 | /** 311 | * Returns array list of bcc recipients 312 | * 313 | * @return array 314 | */ 315 | public function getBcc() 316 | { 317 | $allBcc = $this->getHeader('Bcc'); 318 | 319 | return $this->formatEmailList($allBcc); 320 | } 321 | 322 | /** 323 | * Returns an array of emails from an string in RFC 822 format 324 | * 325 | * @param string $emails email list in RFC 822 format 326 | * 327 | * @return array 328 | */ 329 | public function formatEmailList($emails) 330 | { 331 | $all = []; 332 | $explodedEmails = explode(',', $emails); 333 | 334 | foreach ($explodedEmails as $email) { 335 | 336 | $item = []; 337 | 338 | preg_match('/<(.*)>/', $email, $matches); 339 | 340 | $item['email'] = str_replace(' ', '', isset($matches[1]) ? $matches[1] : $email); 341 | 342 | $name = preg_replace('/ <(.*)>/', '', $email); 343 | 344 | if (Str::startsWith($name, ' ')) { 345 | $name = substr($name, 1); 346 | } 347 | 348 | $item['name'] = str_replace("\"", '', $name ?: null); 349 | 350 | $all[] = $item; 351 | 352 | } 353 | 354 | return $all; 355 | } 356 | 357 | /** 358 | * Returns the original date that the email was sent 359 | * 360 | * @return Carbon 361 | */ 362 | public function getDate() 363 | { 364 | return Carbon::parse($this->getHeader('Date')); 365 | } 366 | 367 | /** 368 | * Returns email of the original recipient 369 | * 370 | * @return string 371 | */ 372 | public function getDeliveredTo() 373 | { 374 | return $this->getHeader('Delivered-To'); 375 | } 376 | 377 | /** 378 | * Base64 version of the body 379 | * 380 | * @return string 381 | */ 382 | public function getRawPlainTextBody() 383 | { 384 | return $this->getPlainTextBody(true); 385 | } 386 | 387 | /** 388 | * @param bool $raw 389 | * 390 | * @return string 391 | */ 392 | public function getPlainTextBody($raw = false) 393 | { 394 | $content = $this->getBody(); 395 | 396 | return $raw ? $content : $this->getDecodedBody($content); 397 | } 398 | 399 | /** 400 | * Returns a specific body part from an email 401 | * 402 | * @param string $type 403 | * 404 | * @return null|string 405 | * @throws \Exception 406 | */ 407 | public function getBody($type = 'text/plain') 408 | { 409 | $parts = $this->getAllParts($this->parts); 410 | 411 | try { 412 | if (!$parts->isEmpty()) { 413 | foreach ($parts as $part) { 414 | if ($part->mimeType == $type) { 415 | return $part->body->data; 416 | //if there are no parts in payload, try to get data from body->data 417 | } elseif ($this->payload->body->data) { 418 | return $this->payload->body->data; 419 | } 420 | } 421 | } else { 422 | return $this->payload->body->data; 423 | } 424 | } catch (\Exception $exception) { 425 | throw new \Exception("Preload or load the single message before getting the body."); 426 | } 427 | 428 | return null; 429 | } 430 | 431 | /** 432 | * True if message has at least one attachment. 433 | * 434 | * @return boolean 435 | */ 436 | public function hasAttachments() 437 | { 438 | $parts = $this->getAllParts($this->parts); 439 | $has = false; 440 | 441 | /** @var Google_Service_Gmail_MessagePart $part */ 442 | foreach ($parts as $part) { 443 | if (!empty($part->body->attachmentId) && $part->getFilename() != null && strlen($part->getFilename()) > 0) { 444 | $has = true; 445 | break; 446 | } 447 | } 448 | 449 | return $has; 450 | } 451 | 452 | /** 453 | * Number of attachments of the message. 454 | * 455 | * @return int 456 | */ 457 | public function countAttachments() 458 | { 459 | $numberOfAttachments = 0; 460 | $parts = $this->getAllParts($this->parts); 461 | 462 | foreach ($parts as $part) { 463 | if (!empty($part->body->attachmentId)) { 464 | $numberOfAttachments++; 465 | } 466 | } 467 | 468 | return $numberOfAttachments; 469 | } 470 | 471 | /** 472 | * Decodes the body from gmail to make it readable 473 | * 474 | * @param $content 475 | * @return bool|string 476 | */ 477 | public function getDecodedBody($content) 478 | { 479 | $content = str_replace('_', '/', str_replace('-', '+', $content)); 480 | 481 | return base64_decode($content); 482 | } 483 | 484 | /** 485 | * @return string base64 version of the body 486 | */ 487 | public function getRawHtmlBody() 488 | { 489 | return $this->getHtmlBody(true); 490 | } 491 | 492 | /** 493 | * Gets the HTML body 494 | * 495 | * @param bool $raw 496 | * 497 | * @return string 498 | */ 499 | public function getHtmlBody($raw = false) 500 | { 501 | $content = $this->getBody('text/html'); 502 | 503 | return $raw ? $content : $this->getDecodedBody($content); 504 | } 505 | 506 | /** 507 | * Get a collection of attachments with full information 508 | * 509 | * @return Collection 510 | * @throws \Exception 511 | */ 512 | public function getAttachmentsWithData() 513 | { 514 | return $this->getAttachments(true); 515 | } 516 | 517 | /** 518 | * Returns a collection of attachments 519 | * 520 | * @param bool $preload Preload only the attachment's 'data'. 521 | * But does not load the other attachment info like filename, mimetype, etc.. 522 | * 523 | * @return Collection 524 | * @throws \Exception 525 | */ 526 | public function getAttachments($preload = false) 527 | { 528 | $attachments = new Collection(); 529 | $parts = $this->getAllParts($this->parts); 530 | 531 | foreach ($parts as $part) { 532 | if (!empty($part->body->attachmentId)) { 533 | $attachment = (new Attachment($part->body->attachmentId, $part, $this->userId)); 534 | 535 | if ($preload) { 536 | $attachment = $attachment->getData(); 537 | } 538 | 539 | $attachments->push($attachment); 540 | } 541 | } 542 | 543 | return $attachments; 544 | } 545 | 546 | /** 547 | * Returns ID of the email 548 | * 549 | * @return string 550 | */ 551 | public function getId() 552 | { 553 | return $this->id; 554 | } 555 | 556 | /** 557 | * Gets the user email from the config file 558 | * 559 | * @return mixed|null 560 | */ 561 | public function getUser() 562 | { 563 | return $this->config('email'); 564 | } 565 | 566 | /** 567 | * Get's the gmail information from the Mail 568 | * 569 | * @return Mail 570 | */ 571 | public function load() 572 | { 573 | $message = $this->service->users_messages->get('me', $this->getId()); 574 | 575 | return new self($message); 576 | } 577 | 578 | /** 579 | * Sets the access token in case we wanna use a different token 580 | * 581 | * @param string $token 582 | * 583 | * @return Mail 584 | */ 585 | public function using($token) 586 | { 587 | $this->setToken($token); 588 | 589 | return $this; 590 | } 591 | 592 | /** 593 | * checks if message has at least one part without iterating through all parts 594 | * 595 | * @return bool 596 | */ 597 | public function hasParts() 598 | { 599 | return !!$this->iterateParts($this->parts, $returnOnFirstFound = true); 600 | } 601 | 602 | /** 603 | * Gets all the headers from an email and returns a collections 604 | * 605 | * @param $emailHeaders 606 | * @return Collection 607 | */ 608 | private function buildHeaders($emailHeaders) 609 | { 610 | $headers = []; 611 | 612 | foreach ($emailHeaders as $header) { 613 | /** @var \Google_Service_Gmail_MessagePartHeader $header */ 614 | 615 | $head = new \stdClass(); 616 | 617 | $head->key = $header->getName(); 618 | $head->value = $header->getValue(); 619 | 620 | $headers[] = $head; 621 | } 622 | 623 | return collect($headers); 624 | } 625 | } 626 | -------------------------------------------------------------------------------- /src/Services/MessageCollection.php: -------------------------------------------------------------------------------- 1 | message = $message; 24 | } 25 | 26 | public function next() 27 | { 28 | return $this->message->next(); 29 | } 30 | 31 | /** 32 | * Returns boolean if the page token variable is null or not 33 | * 34 | * @return bool 35 | */ 36 | public function hasNextPage() 37 | { 38 | return !!$this->message->pageToken; 39 | } 40 | 41 | /** 42 | * Returns the page token or null 43 | * 44 | * @return string 45 | */ 46 | public function getPageToken() 47 | { 48 | return $this->message->pageToken; 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /src/Traits/Configurable.php: -------------------------------------------------------------------------------- 1 | _config = $config; 22 | } 23 | 24 | public function config($string = null) 25 | { 26 | $disk = Storage::disk('local'); 27 | $fileName = $this->getFileName(); 28 | $file = "gmail/tokens/$fileName.json"; 29 | $allowJsonEncrypt = $this->_config['gmail.allow_json_encrypt']; 30 | 31 | if ($disk->exists($file)) { 32 | if ($allowJsonEncrypt) { 33 | $config = json_decode(decrypt($disk->get($file)), true); 34 | } else { 35 | $config = json_decode($disk->get($file), true); 36 | } 37 | 38 | if ($string) { 39 | if (isset($config[$string])) { 40 | return $config[$string]; 41 | } 42 | } else { 43 | return $config; 44 | } 45 | 46 | } 47 | 48 | return null; 49 | } 50 | 51 | private function getFileName() 52 | { 53 | if (property_exists(get_class($this), 'userId') && $this->userId) { 54 | $userId = $this->userId; 55 | } elseif (auth()->user()) { 56 | $userId = auth()->user()->id; 57 | } 58 | 59 | $credentialFilename = $this->_config['gmail.credentials_file_name']; 60 | $allowMultipleCredentials = $this->_config['gmail.allow_multiple_credentials']; 61 | 62 | if (isset($userId) && $allowMultipleCredentials) { 63 | return sprintf('%s-%s', $credentialFilename, $userId); 64 | } 65 | 66 | return $credentialFilename; 67 | } 68 | 69 | /** 70 | * @return array 71 | */ 72 | public function getConfigs() 73 | { 74 | return [ 75 | 'client_secret' => $this->_config['gmail.client_secret'], 76 | 'client_id' => $this->_config['gmail.client_id'], 77 | 'redirect_uri' => url($this->_config['gmail.redirect_url']), 78 | 'state' => isset($this->_config['gmail.state']) ? $this->_config['gmail.state'] : null, 79 | ]; 80 | } 81 | 82 | public function setAdditionalScopes(array $scopes) 83 | { 84 | $this->additionalScopes = $scopes; 85 | 86 | return $this; 87 | } 88 | 89 | private function configApi() 90 | { 91 | $type = $this->_config['gmail.access_type']; 92 | $approval_prompt = $this->_config['gmail.approval_prompt']; 93 | 94 | $this->setScopes($this->getUserScopes()); 95 | 96 | $this->setAccessType($type); 97 | 98 | $this->setApprovalPrompt($approval_prompt); 99 | } 100 | 101 | public abstract function setScopes($scopes); 102 | 103 | private function getUserScopes() 104 | { 105 | return $this->mapScopes(); 106 | } 107 | 108 | private function mapScopes() 109 | { 110 | $scopes = array_merge($this->_config['gmail.scopes'] ?? [], $this->additionalScopes); 111 | $scopes = array_unique(array_filter($scopes)); 112 | $mappedScopes = []; 113 | 114 | if (!empty($scopes)) { 115 | foreach ($scopes as $scope) { 116 | $mappedScopes[] = $this->scopeMap($scope); 117 | } 118 | } 119 | 120 | return array_merge($mappedScopes, $this->_config['gmail.additional_scopes'] ?? []); 121 | } 122 | 123 | private function scopeMap($scope) 124 | { 125 | $scopes = [ 126 | 'all' => Google_Service_Gmail::MAIL_GOOGLE_COM, 127 | 'compose' => Google_Service_Gmail::GMAIL_COMPOSE, 128 | 'insert' => Google_Service_Gmail::GMAIL_INSERT, 129 | 'labels' => Google_Service_Gmail::GMAIL_LABELS, 130 | 'metadata' => Google_Service_Gmail::GMAIL_METADATA, 131 | 'modify' => Google_Service_Gmail::GMAIL_MODIFY, 132 | 'readonly' => Google_Service_Gmail::GMAIL_READONLY, 133 | 'send' => Google_Service_Gmail::GMAIL_SEND, 134 | 'settings_basic' => Google_Service_Gmail::GMAIL_SETTINGS_BASIC, 135 | 'settings_sharing' => Google_Service_Gmail::GMAIL_SETTINGS_SHARING, 136 | ]; 137 | 138 | return Arr::get($scopes, $scope); 139 | } 140 | 141 | public abstract function setAccessType($type); 142 | 143 | public abstract function setApprovalPrompt($approval); 144 | 145 | } 146 | -------------------------------------------------------------------------------- /src/Traits/Filterable.php: -------------------------------------------------------------------------------- 1 | add('is:unread'); 17 | 18 | return $this; 19 | } 20 | 21 | public abstract function add($query, $column = 'q', $encode = true); 22 | 23 | /** 24 | * Filter to get only unread emalis 25 | * 26 | * @param $query 27 | * 28 | * @return self|Message 29 | */ 30 | public function subject($query) 31 | { 32 | $this->add("[{$query}]"); 33 | 34 | return $this; 35 | } 36 | 37 | /** 38 | * Filter to get only emails from a specific email address 39 | * 40 | * @param $email 41 | * 42 | * @return self|Message 43 | */ 44 | public function to($email) 45 | { 46 | $this->add("to:{$email}"); 47 | 48 | return $this; 49 | } 50 | 51 | /** 52 | * add an array of from addresses 53 | * 54 | * @param $emails 55 | * 56 | * @return self|Message 57 | */ 58 | public function fromThese(array $emails) 59 | { 60 | $emailsCount = count($emails); 61 | for ($i = 0; $i < $emailsCount; $i++) { 62 | !$i ? $this->add("{from:$emails[$i]") : ($i == $emailsCount - 1 ? $this->add("from:$emails[$i]}") : $this->from($emails[$i])); 63 | } 64 | 65 | return $this; 66 | } 67 | 68 | /** 69 | * Filter to get only emails from a specific email address 70 | * 71 | * @param $email 72 | * 73 | * @return self|Message 74 | */ 75 | public function from($email) 76 | { 77 | $this->add("from:{$email}"); 78 | 79 | return $this; 80 | } 81 | 82 | /** 83 | * Filter to get only emails after a specific date 84 | * 85 | * @param $date 86 | * 87 | * @return self|Message 88 | */ 89 | public function after($date) 90 | { 91 | $this->add("after:{$date}"); 92 | 93 | return $this; 94 | } 95 | 96 | /** 97 | * Filter to get only emails before a specific date 98 | * 99 | * @param $date 100 | * 101 | * @return self|Message 102 | */ 103 | public function before($date) 104 | { 105 | $this->add("before:{$date}"); 106 | 107 | return $this; 108 | } 109 | 110 | /** 111 | * Filter by a Gmail raw query 112 | * Label should be the last thing to put in the raw query 113 | * 114 | * @param $query 115 | * 116 | * @return self|Message 117 | */ 118 | public function raw($query) 119 | { 120 | $this->add($query, 'q', false); 121 | 122 | return $this; 123 | } 124 | 125 | /** 126 | * Filters emails by tag 127 | * Example: 128 | * * starred 129 | * * inbox 130 | * * spam 131 | * * chats 132 | * * sent 133 | * * draft 134 | * * trash 135 | * 136 | * @param $box 137 | * 138 | * @return self|Message 139 | */ 140 | public function in($box = 'inbox') 141 | { 142 | $this->add("in:{$box}"); 143 | 144 | return $this; 145 | } 146 | 147 | /** 148 | * Determines if the email has attachments 149 | * 150 | * @return self|Message 151 | */ 152 | public function hasAttachment() 153 | { 154 | $this->add('has:attachment'); 155 | 156 | return $this; 157 | } 158 | } 159 | -------------------------------------------------------------------------------- /src/Traits/HasDecodableBody.php: -------------------------------------------------------------------------------- 1 | getHeaders(); 20 | 21 | $value = null; 22 | 23 | foreach ($headers as $header) { 24 | if ($header->key === $headerName) { 25 | $value = $header->value; 26 | if (!is_null($regex)) { 27 | preg_match_all($regex, $header->value, $value); 28 | } 29 | break; 30 | } 31 | } 32 | 33 | if (is_array($value)) { 34 | return isset($value[1]) ? $value[1] : null; 35 | } 36 | 37 | return $value; 38 | } 39 | 40 | public abstract function getHeaders(); 41 | 42 | } 43 | -------------------------------------------------------------------------------- /src/Traits/HasLabels.php: -------------------------------------------------------------------------------- 1 | users_labels->listUsersLabels($userEmail); 21 | } 22 | 23 | /** 24 | * Create new label by name. 25 | * 26 | * @param $userEmail 27 | * @param $label 28 | * 29 | * @return \Google\Service\Gmail\Label 30 | */ 31 | public function createLabel($userEmail, $label) 32 | { 33 | $service = new Google_Service_Gmail($this); 34 | 35 | return $service->users_labels->create($userEmail, $label); 36 | } 37 | 38 | /** 39 | * first or create label in the user's mailbox. 40 | * 41 | * @param $userEmail 42 | * @param $nLabel 43 | * @return \Google\Service\Gmail\Label 44 | */ 45 | public function firstOrCreateLabel($userEmail, $newLabel) 46 | { 47 | $labels = $this->labelsList($userEmail); 48 | 49 | foreach ($labels->getLabels() as $existLabel) { 50 | if ($existLabel->getName() == $newLabel->getName()) { 51 | return $existLabel; 52 | } 53 | } 54 | 55 | $service = new Google_Service_Gmail($this); 56 | 57 | return $service->users_labels->create($userEmail, $newLabel); 58 | } 59 | } -------------------------------------------------------------------------------- /src/Traits/HasParts.php: -------------------------------------------------------------------------------- 1 | payload]) 21 | * 22 | * @return Collection of all 'parts' flattened 23 | */ 24 | private function getAllParts($partsContainer) 25 | { 26 | $this->iterateParts($partsContainer); 27 | 28 | return collect($this->allParts); 29 | } 30 | 31 | 32 | /** 33 | * Recursive Method. Iterates through a collection, 34 | * finding all 'parts'. 35 | * 36 | * @param collection $partsContainer 37 | * @param bool $returnOnFirstFound 38 | * 39 | * @return Collection|boolean 40 | */ 41 | 42 | private function iterateParts($partsContainer, $returnOnFirstFound = false) 43 | { 44 | $parts = []; 45 | 46 | $plucked = $partsContainer->flatten()->filter(); 47 | 48 | if ($plucked->count()) { 49 | $parts = $plucked; 50 | } else { 51 | if ($partsContainer->count()) { 52 | $parts = $partsContainer; 53 | } 54 | } 55 | 56 | if ($parts) { 57 | /** @var Google_Service_Gmail_MessagePart $part */ 58 | foreach ($parts as $part) { 59 | if ($part) { 60 | if ($returnOnFirstFound) { 61 | return true; 62 | } 63 | 64 | $this->allParts[$part->getPartId()] = $part; 65 | $this->iterateParts(collect($part->getParts())); 66 | } 67 | } 68 | } 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /src/Traits/Modifiable.php: -------------------------------------------------------------------------------- 1 | __mlConstruct(); 22 | } 23 | 24 | /** 25 | * Marks emails as "READ". Returns string of message if fail 26 | * 27 | * @return Mail|string 28 | */ 29 | public function markAsRead() 30 | { 31 | try { 32 | return $this->removeLabel('UNREAD'); 33 | } catch (\Exception $e) { 34 | return "Couldn't mark email as read: {$e->getMessage()}"; 35 | } 36 | } 37 | 38 | /** 39 | * Marks emails as unread 40 | * 41 | * @return Mail|string 42 | * @throws \Exception 43 | */ 44 | public function markAsUnread() 45 | { 46 | try { 47 | return $this->addLabel('UNREAD'); 48 | } catch (\Exception $e) { 49 | throw new \Exception("Couldn't mark email as unread: {$e->getMessage()}"); 50 | } 51 | } 52 | 53 | /** 54 | * @return Mail|string 55 | * @throws \Exception 56 | */ 57 | public function markAsImportant() 58 | { 59 | try { 60 | return $this->addLabel('IMPORTANT'); 61 | } catch (\Exception $e) { 62 | throw new \Exception("Couldn't remove mark email as important.: {$e->getMessage()}"); 63 | } 64 | } 65 | 66 | /** 67 | * @return Mail|string 68 | * @throws \Exception 69 | */ 70 | public function markAsNotImportant() 71 | { 72 | try { 73 | return $this->removeLabel('IMPORTANT'); 74 | } catch (\Exception $e) { 75 | throw new \Exception("Couldn't mark email as unread: {$e->getMessage()}"); 76 | } 77 | } 78 | 79 | /** 80 | * @return Mail|string 81 | * @throws \Exception 82 | */ 83 | public function addStar() 84 | { 85 | try { 86 | return $this->addLabel('STARRED'); 87 | } catch (\Exception $e) { 88 | throw new \Exception("Couldn't remove mark email as important.: {$e->getMessage()}"); 89 | } 90 | } 91 | 92 | /** 93 | * @return Mail|string 94 | * @throws \Exception 95 | */ 96 | public function removeStar() 97 | { 98 | try { 99 | return $this->removeLabel('STARRED'); 100 | } catch (\Exception $e) { 101 | throw new \Exception("Couldn't mark email as unread: {$e->getMessage()}"); 102 | } 103 | } 104 | 105 | /** 106 | * Send the email to the trash 107 | * 108 | * @return \Dacastro4\LaravelGmail\Services\Message\Mail|\Exception 109 | */ 110 | public function sendToTrash() 111 | { 112 | try { 113 | return $this->addLabel('TRASH'); 114 | } catch (\Exception $e) { 115 | return new \Exception("Couldn't mark email as trash: {$e->getMessage()}"); 116 | } 117 | } 118 | 119 | public function removeFromTrash() 120 | { 121 | try { 122 | return $this->removeLabel('TRASH'); 123 | } catch (\Exception $e) { 124 | return new \Exception("Couldn't untrash the email: {$e->getMessage()}"); 125 | } 126 | } 127 | } 128 | -------------------------------------------------------------------------------- /src/Traits/ModifiesLabels.php: -------------------------------------------------------------------------------- 1 | messageRequest = new Google_Service_Gmail_ModifyMessageRequest(); 16 | } 17 | 18 | /** 19 | * Adds labels to the email 20 | * 21 | * @param string|array $labels 22 | * 23 | * @return Mail|string 24 | * @throws \Exception 25 | */ 26 | public function addLabel($labels) 27 | { 28 | if (is_string($labels)) { 29 | $labels = [$labels]; 30 | } 31 | 32 | $this->messageRequest->setAddLabelIds($labels); 33 | 34 | try { 35 | return $this->modify(); 36 | } catch (\Exception $e) { 37 | throw new \Exception("Couldn't add labels: {$e->getMessage()}"); 38 | } 39 | } 40 | 41 | /** 42 | * Executes the modification 43 | * 44 | * @return Mail 45 | */ 46 | private function modify() 47 | { 48 | return new Mail($this->service->users_messages->modify('me', $this->getId(), $this->messageRequest)); 49 | } 50 | 51 | public abstract function getId(); 52 | 53 | /** 54 | * Removes labels from the email 55 | * 56 | * @param string|array $labels 57 | * 58 | * @return Mail|string 59 | * @throws \Exception 60 | */ 61 | public function removeLabel($labels) 62 | { 63 | if (is_string($labels)) { 64 | $labels = [$labels]; 65 | } 66 | 67 | $this->messageRequest->setRemoveLabelIds($labels); 68 | 69 | try { 70 | return $this->modify(); 71 | } catch (\Exception $e) { 72 | throw new \Exception("Couldn't remove labels: {$e->getMessage()}"); 73 | } 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /src/Traits/Replyable.php: -------------------------------------------------------------------------------- 1 | symfonyEmail = new Email(); 112 | } 113 | 114 | /** 115 | * Receives the recipient's 116 | * If multiple recipients will receive the message an array should be used. 117 | * Example: array('receiver@domain.org', 'other@domain.org' => 'A name') 118 | * 119 | * If $name is passed and the first parameter is a string, this name will be 120 | * associated with the address. 121 | * 122 | * @param string|array $to 123 | * 124 | * @param string|null $name 125 | * 126 | * @return Replyable 127 | */ 128 | public function to($to, $name = null) 129 | { 130 | $this->to = $this->emailList($to, $name); 131 | $this->nameTo = $name; 132 | 133 | return $this; 134 | } 135 | 136 | public function from($from, $name = null) 137 | { 138 | $this->from = $from; 139 | $this->nameFrom = $name; 140 | 141 | return $this; 142 | } 143 | 144 | /** 145 | * @param array|string $cc 146 | * 147 | * @param string|null $name 148 | * 149 | * @return Replyable 150 | */ 151 | public function cc($cc, $name = null) 152 | { 153 | $this->cc = $this->emailList($cc, $name); 154 | $this->nameCc = $name; 155 | 156 | return $this; 157 | } 158 | 159 | private function emailList($list, $name = null) 160 | { 161 | if (is_array($list)) { 162 | return $this->convertEmailList($list, $name); 163 | } else { 164 | return $list; 165 | } 166 | } 167 | 168 | private function convertEmailList($emails, $name = null) 169 | { 170 | $newList = []; 171 | $count = 0; 172 | foreach ($emails as $key => $email) { 173 | $emailName = isset($name[$count]) ? $name[$count] : explode('@', $email)[0]; 174 | $newList[$email] = $emailName; 175 | $count = $count + 1; 176 | } 177 | 178 | return $newList; 179 | } 180 | 181 | /** 182 | * @param array|string $bcc 183 | * 184 | * @param string|null $name 185 | * 186 | * @return Replyable 187 | */ 188 | public function bcc($bcc, $name = null) 189 | { 190 | $this->bcc = $this->emailList($bcc, $name); 191 | $this->nameBcc = $name; 192 | 193 | return $this; 194 | } 195 | 196 | /** 197 | * @param string $subject 198 | * 199 | * @return Replyable 200 | */ 201 | public function subject($subject) 202 | { 203 | $this->subject = $subject; 204 | 205 | return $this; 206 | } 207 | 208 | /** 209 | * @param string $view 210 | * @param array $data 211 | * @param array $mergeData 212 | * 213 | * @return Replyable 214 | * @throws \Throwable 215 | */ 216 | public function view($view, $data = [], $mergeData = []) 217 | { 218 | $this->message = view($view, $data, $mergeData)->render(); 219 | 220 | return $this; 221 | } 222 | 223 | /** 224 | * loads markdown file for message body 225 | * 226 | * @return Replyable 227 | * @throws \Throwable 228 | */ 229 | public function markdown(string $markdown_view, array $data = []) 230 | { 231 | $markdown = Container::getInstance()->make(Markdown::class); 232 | 233 | if (config('mail.markdown.theme')) { 234 | $markdown->theme(config('mail.markdown.theme')); 235 | } 236 | 237 | $this->message = $markdown->render($markdown_view, $data); 238 | 239 | return $this; 240 | } 241 | 242 | /** 243 | * @param string $message 244 | * 245 | * @return Replyable 246 | */ 247 | public function message($message) 248 | { 249 | $this->message = $message; 250 | 251 | return $this; 252 | } 253 | 254 | /** 255 | * Attaches new file to the email from the Storage folder 256 | * 257 | * @param array $files comma separated of files 258 | * 259 | * @return Replyable 260 | * @throws \Exception 261 | */ 262 | public function attach(...$files) 263 | { 264 | 265 | foreach ($files as $file) { 266 | 267 | if (!file_exists($file)) { 268 | throw new FileNotFoundException($file); 269 | } 270 | 271 | array_push($this->attachments, $file); 272 | } 273 | 274 | return $this; 275 | } 276 | 277 | /** 278 | * The value is an integer where 1 is the highest priority and 5 is the lowest. 279 | * 280 | * @param int $priority 281 | * 282 | * @return Replyable 283 | */ 284 | public function priority($priority) 285 | { 286 | $this->priority = $priority; 287 | 288 | return $this; 289 | } 290 | 291 | /** 292 | * @param array $parameters 293 | * 294 | * @return Replyable 295 | */ 296 | public function optionalParameters(array $parameters) 297 | { 298 | $this->parameters = $parameters; 299 | 300 | return $this; 301 | } 302 | 303 | /** 304 | * Reply to a specific email 305 | * 306 | * @return Mail 307 | * @throws \Exception 308 | */ 309 | public function reply() 310 | { 311 | if (!$this->getId()) { 312 | throw new \Exception('This is a new email. Use send().'); 313 | } 314 | 315 | $this->setReplyThread(); 316 | $this->setReplySubject(); 317 | $this->setReplyTo(); 318 | $this->setReplyFrom(); 319 | $body = $this->getMessageBody(); 320 | $body->setThreadId($this->getThreadId()); 321 | 322 | return new Mail($this->service->users_messages->send('me', $body, $this->parameters)); 323 | } 324 | 325 | public abstract function getId(); 326 | 327 | private function setReplyThread() 328 | { 329 | $threadId = $this->getThreadId(); 330 | if ($threadId) { 331 | $this->setHeader('In-Reply-To', $this->getMessageIdHeader()); 332 | $this->setHeader('References', $this->getHeader('References')); 333 | $this->setHeader('Message-ID', $this->getMessageIdHeader()); 334 | } 335 | } 336 | 337 | private function getMessageIdHeader() 338 | { 339 | if ($messageId = $this->getHeader('Message-ID')) { 340 | return $messageId; 341 | } 342 | 343 | if ($messageId = $this->getHeader('Message-Id')) { 344 | return $messageId; 345 | } 346 | return null; 347 | } 348 | 349 | public abstract function getThreadId(); 350 | 351 | /** 352 | * Add a header to the email 353 | * 354 | * @param string $header 355 | * @param string $value 356 | */ 357 | public function setHeader($header, $value) 358 | { 359 | $headers = $this->symfonyEmail->getHeaders(); 360 | 361 | $headers->addTextHeader($header, $value); 362 | 363 | } 364 | 365 | private function setReplySubject() 366 | { 367 | if (!$this->subject) { 368 | $this->subject = $this->getSubject(); 369 | } 370 | } 371 | 372 | private function setReplyTo() 373 | { 374 | if (!$this->to) { 375 | $replyTo = $this->getReplyTo(); 376 | 377 | $this->to = $replyTo['email']; 378 | $this->nameTo = $replyTo['name']; 379 | } 380 | } 381 | 382 | private function setReplyFrom() 383 | { 384 | if (!$this->from) { 385 | $this->from = $this->getUser(); 386 | if (!$this->from) { 387 | throw new \Exception('Reply from is not defined'); 388 | } 389 | } 390 | } 391 | 392 | public abstract function getSubject(); 393 | 394 | public abstract function getReplyTo(); 395 | 396 | public abstract function getUser(); 397 | 398 | /** 399 | * @return Google_Service_Gmail_Message 400 | */ 401 | private function getMessageBody() 402 | { 403 | $body = new Google_Service_Gmail_Message(); 404 | 405 | $this->symfonyEmail 406 | ->from($this->fromAddress()) 407 | ->to($this->toAddress()) 408 | ->cc($this->returnCopies($this->cc)) 409 | ->bcc($this->returnCopies($this->bcc)) 410 | ->subject($this->subject) 411 | ->html($this->message) 412 | ->priority($this->priority); 413 | 414 | foreach ($this->attachments as $file) { 415 | $this->symfonyEmail->attachFromPath($file); 416 | } 417 | 418 | $body->setRaw($this->base64_encode($this->symfonyEmail->toString())); 419 | 420 | return $body; 421 | } 422 | 423 | /** 424 | * @param array|string $cc 425 | * @return array|string 426 | */ 427 | public function returnCopies($cc) 428 | { 429 | if ($cc) { 430 | $final = $this->cc; 431 | 432 | if (is_array($this->cc)) { 433 | foreach ($this->cc as $emailCc => $nameCc) { 434 | $final[] = new Address($emailCc, $nameCc); 435 | } 436 | } 437 | 438 | return $final; 439 | } 440 | 441 | return []; 442 | } 443 | 444 | public function toAddress() 445 | { 446 | if ($this->to) { 447 | return new Address($this->to, $this->nameTo ?: ''); 448 | } 449 | 450 | return []; 451 | } 452 | 453 | public function fromAddress() 454 | { 455 | if ($this->from) { 456 | return new Address($this->from, $this->nameFrom ?: ''); 457 | } 458 | 459 | return []; 460 | } 461 | 462 | private function base64_encode($data) 463 | { 464 | return rtrim(strtr(base64_encode($data), ['+' => '-', '/' => '_']), '='); 465 | } 466 | 467 | /** 468 | * Sends a new email 469 | * 470 | * @return self|Mail 471 | */ 472 | public function send() 473 | { 474 | $body = $this->getMessageBody(); 475 | 476 | $this->setMessage($this->service->users_messages->send('me', $body, $this->parameters)); 477 | 478 | return $this; 479 | } 480 | 481 | protected abstract function setMessage(\Google_Service_Gmail_Message $message); 482 | } 483 | -------------------------------------------------------------------------------- /src/Traits/SendsParameters.php: -------------------------------------------------------------------------------- 1 | params[$column])) { 22 | if ($column === 'pageToken') { 23 | $this->params[$column] = $query; 24 | } else { 25 | $this->params[$column] = "{$this->params[$column]} $query"; 26 | } 27 | } else { 28 | $this->params = Arr::add($this->params, $column, $query); 29 | } 30 | 31 | } 32 | 33 | public function addPageToken($token) 34 | { 35 | $this->params['pageToken'] = $token; 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /src/config/gmail.php: -------------------------------------------------------------------------------- 1 | env('GOOGLE_PROJECT_ID'), 32 | 'client_id' => env('GOOGLE_CLIENT_ID'), 33 | 'client_secret' => env('GOOGLE_CLIENT_SECRET'), 34 | 'redirect_url' => env('GOOGLE_REDIRECT_URI', '/'), 35 | 36 | 'state' => null, 37 | 38 | 'scopes' => [ 39 | 'readonly', 40 | 'modify', 41 | ], 42 | 43 | /* 44 | |-------------------------------------------------------------------------- 45 | | Additional Scopes [URL Style] 46 | |-------------------------------------------------------------------------- 47 | | 48 | | 'additional_scopes' => [ 49 | | 'https://www.googleapis.com/auth/drive', 50 | | 'https://www.googleapis.com/auth/documents' 51 | | ], 52 | | 53 | | 54 | */ 55 | 56 | 'additional_scopes' => [ 57 | 58 | ], 59 | 60 | 'access_type' => 'offline', 61 | 62 | 'approval_prompt' => 'force', 63 | 64 | /* 65 | |-------------------------------------------------------------------------- 66 | | Credentials File Name 67 | |-------------------------------------------------------------------------- 68 | | 69 | | :email to use, clients email on the file 70 | | 71 | | 72 | */ 73 | 74 | 'credentials_file_name' => env('GOOGLE_CREDENTIALS_NAME', 'gmail-json'), 75 | 76 | /* 77 | |-------------------------------------------------------------------------- 78 | | Allow Multiple Credentials 79 | |-------------------------------------------------------------------------- 80 | | 81 | | Allow the application to store multiple credential json files. 82 | | 83 | | 84 | */ 85 | 86 | 'allow_multiple_credentials' => env('GOOGLE_ALLOW_MULTIPLE_CREDENTIALS', false), 87 | 88 | /* 89 | |-------------------------------------------------------------------------- 90 | | Allow Encryption for json Files 91 | |-------------------------------------------------------------------------- 92 | | 93 | | Use Laravel Encrypt in json Files 94 | | 95 | | 96 | */ 97 | 98 | 'allow_json_encrypt' => env('GOOGLE_ALLOW_JSON_ENCRYPT', false), 99 | ]; 100 | -------------------------------------------------------------------------------- /stale.yml: -------------------------------------------------------------------------------- 1 | # Number of days of inactivity before an issue becomes stale 2 | daysUntilStale: 30 3 | # Number of days of inactivity before a stale issue is closed 4 | daysUntilClose: 7 5 | # Issues with these labels will never be considered stale 6 | exemptLabels: 7 | - pinned 8 | - security 9 | # Label to use when marking an issue as stale 10 | staleLabel: wontfix 11 | # Comment to post when marking an issue as stale. Set to `false` to disable 12 | markComment: > 13 | This issue has been automatically marked as stale because it has not had 14 | recent activity. It will be closed if no further activity occurs. Thank you 15 | for your contributions. 16 | # Comment to post when closing a stale issue. Set to `false` to disable 17 | closeComment: false 18 | -------------------------------------------------------------------------------- /tests/LaravelGmailTest.php: -------------------------------------------------------------------------------- 1 | instance(Markdown::class, $mocked_markdown); 16 | 17 | // expectations 18 | $mocked_markdown->shouldReceive('theme')->once()->with(config('mail.markdown.theme')); 19 | $mocked_markdown->shouldReceive('render')->once()->with( 20 | 'sample-markdown', 21 | [ 'url' => 'https://www.google.com' ] 22 | ); 23 | 24 | // trigger 25 | (new Mail())->markdown( 26 | 'sample-markdown', 27 | [ 'url' => 'https://www.google.com' ] 28 | ); 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /tests/TestCase.php: -------------------------------------------------------------------------------- 1 |