diff --git a/.github/CONTRIBUTING.md b/.github/CONTRIBUTING.md new file mode 100644 index 0000000..b0ee5d8 --- /dev/null +++ b/.github/CONTRIBUTING.md @@ -0,0 +1,55 @@ +# Contributing + +Contributions are **welcome** and will be fully **credited**. + +Please read and understand the contribution guide before creating an issue or pull request. + +## Etiquette + +This project is open source, and as such, the maintainers give their free time to build and maintain the source code +held within. They make the code freely available in the hope that it will be of use to other developers. It would be +extremely unfair for them to suffer abuse or anger for their hard work. + +Please be considerate towards maintainers when raising issues or presenting pull requests. Let's show the +world that developers are civilized and selfless people. + +It's the duty of the maintainer to ensure that all submissions to the project are of sufficient +quality to benefit the project. Many developers have different skills, strengths, and weaknesses. Respect the maintainer's decision, and do not be upset or abusive if your submission is not used. + +## Viability + +When requesting or submitting new features, first consider whether it might be useful to others. Open +source projects are used by many developers, who may have entirely different needs to your own. Think about +whether or not your feature is likely to be used by other users of the project. + +## Procedure + +Before filing an issue: + +- Attempt to replicate the problem, to ensure that it wasn't a coincidental incident. +- Check to make sure your feature suggestion isn't already present within the project. +- Check the pull requests tab to ensure that the bug doesn't have a fix in progress. +- Check the pull requests tab to ensure that the feature isn't already in progress. + +Before submitting a pull request: + +- Check the codebase to ensure that your feature doesn't already exist. +- Check the pull requests to ensure that another person hasn't already submitted the feature or fix. + +## Requirements + +If the project maintainer has any additional requirements, you will find them listed here. + +- **[PSR-2 Coding Standard](https://github.com/php-fig/fig-standards/blob/master/accepted/PSR-2-coding-style-guide.md)** - The easiest way to apply the conventions is to install [PHP Code Sniffer](https://pear.php.net/package/PHP_CodeSniffer). + +- **Add tests!** - Your patch won't be accepted if it doesn't have tests. + +- **Document any change in behaviour** - Make sure the `README.md` and any other relevant documentation are kept up-to-date. + +- **Consider our release cycle** - We try to follow [SemVer v2.0.0](https://semver.org/). Randomly breaking public APIs is not an option. + +- **One pull request per feature** - If you want to do more than one thing, send multiple pull requests. + +- **Send coherent history** - Make sure each individual commit in your pull request is meaningful. If you had to make multiple intermediate commits while developing, please [squash them](https://www.git-scm.com/book/en/v2/Git-Tools-Rewriting-History#Changing-Multiple-Commit-Messages) before submitting. + +**Happy coding**! diff --git a/.github/FUNDING.yml b/.github/FUNDING.yml new file mode 100644 index 0000000..892ba05 --- /dev/null +++ b/.github/FUNDING.yml @@ -0,0 +1 @@ +github: [3x1io] diff --git a/.github/ISSUE_TEMPLATE/bug.yml b/.github/ISSUE_TEMPLATE/bug.yml new file mode 100644 index 0000000..8fa85ce --- /dev/null +++ b/.github/ISSUE_TEMPLATE/bug.yml @@ -0,0 +1,66 @@ +name: Bug Report +description: Report an Issue or Bug with the Package +title: "[Bug]: " +labels: ["bug"] +body: + - type: markdown + attributes: + value: | + We're sorry to hear you have a problem. Can you help us solve it by providing the following details. + - type: textarea + id: what-happened + attributes: + label: What happened? + description: What did you expect to happen? + placeholder: I cannot currently do X thing because when I do, it breaks X thing. + validations: + required: true + - type: textarea + id: how-to-reproduce + attributes: + label: How to reproduce the bug + description: How did this occur, please add any config values used and provide a set of reliable steps if possible. + placeholder: When I do X I see Y. + validations: + required: true + - type: input + id: package-version + attributes: + label: Package Version + description: What version of our Package are you running? Please be as specific as possible + placeholder: 2.0.0 + validations: + required: true + - type: input + id: php-version + attributes: + label: PHP Version + description: What version of PHP are you running? Please be as specific as possible + placeholder: 8.2.0 + validations: + required: true + - type: input + id: laravel-version + attributes: + label: Laravel Version + description: What version of Laravel are you running? Please be as specific as possible + placeholder: 9.0.0 + validations: + required: true + - type: dropdown + id: operating-systems + attributes: + label: Which operating systems does with happen with? + description: You may select more than one. + multiple: true + options: + - macOS + - Windows + - Linux + - type: textarea + id: notes + attributes: + label: Notes + description: Use this field to provide any other notes that you feel might be relevant to the issue. + validations: + required: false diff --git a/.github/ISSUE_TEMPLATE/config.yaml b/.github/ISSUE_TEMPLATE/config.yaml new file mode 100644 index 0000000..0509ad5 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/config.yaml @@ -0,0 +1,11 @@ +blank_issues_enabled: false +contact_links: + - name: Ask a question + url: https://github.com/tomatophp/filament-cms-behance/discussions/new?category=q-a + about: Ask the community for help + - name: Request a feature + url: https://github.com/tomatophp/filament-cms-behance/discussions/new?category=ideas + about: Share ideas for new features + - name: Report a security issue + url: https://github.com/tomatophp/filament-cms-behance/security/policy + about: Learn how to notify us for sensitive bugs diff --git a/.github/SECURITY.md b/.github/SECURITY.md new file mode 100644 index 0000000..b2490a9 --- /dev/null +++ b/.github/SECURITY.md @@ -0,0 +1,3 @@ +# Security Policy + +If you discover any security related issues, please email info@3x1.io instead of using the issue tracker. diff --git a/.github/dependabot.yml b/.github/dependabot.yml new file mode 100644 index 0000000..0bc378d --- /dev/null +++ b/.github/dependabot.yml @@ -0,0 +1,12 @@ +# Please see the documentation for all configuration options: +# https://help.github.com/github/administering-a-repository/configuration-options-for-dependency-updates + +version: 2 +updates: + + - package-ecosystem: "github-actions" + directory: "/" + schedule: + interval: "weekly" + labels: + - "dependencies" diff --git a/.github/workflows/dependabot-auto-merge.yml b/.github/workflows/dependabot-auto-merge.yml new file mode 100644 index 0000000..27c23a4 --- /dev/null +++ b/.github/workflows/dependabot-auto-merge.yml @@ -0,0 +1,32 @@ +name: dependabot-auto-merge +on: pull_request_target + +permissions: + pull-requests: write + contents: write + +jobs: + dependabot: + runs-on: ubuntu-latest + if: ${{ github.actor == 'dependabot[bot]' }} + steps: + + - name: Dependabot metadata + id: metadata + uses: dependabot/fetch-metadata@v2.2.0 + with: + github-token: "${{ secrets.GITHUB_TOKEN }}" + + - name: Auto-merge Dependabot PRs for semver-minor updates + if: ${{steps.metadata.outputs.update-type == 'version-update:semver-minor'}} + run: gh pr merge --auto --merge "$PR_URL" + env: + PR_URL: ${{github.event.pull_request.html_url}} + GITHUB_TOKEN: ${{secrets.GITHUB_TOKEN}} + + - name: Auto-merge Dependabot PRs for semver-patch updates + if: ${{steps.metadata.outputs.update-type == 'version-update:semver-patch'}} + run: gh pr merge --auto --merge "$PR_URL" + env: + PR_URL: ${{github.event.pull_request.html_url}} + GITHUB_TOKEN: ${{secrets.GITHUB_TOKEN}} diff --git a/.github/workflows/fix-php-code-styling.yml b/.github/workflows/fix-php-code-styling.yml new file mode 100644 index 0000000..e71024d --- /dev/null +++ b/.github/workflows/fix-php-code-styling.yml @@ -0,0 +1,30 @@ +name: 'PHP Code Styling' + +on: + workflow_dispatch: + push: + branches: + - master + paths: + - '**.php' + +permissions: + contents: write + +jobs: + lint: + runs-on: ubuntu-latest + steps: + - name: Checkout code + uses: actions/checkout@v4 + with: + ref: ${{ github.head_ref }} + + - name: Fix PHP code style issues + uses: aglipanci/laravel-pint-action@v2 + + - name: Commit changes + uses: stefanzweifel/git-auto-commit-action@v5 + with: + commit_message: "Format Code" + commit_user_name: 'GitHub Actions' diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml new file mode 100644 index 0000000..14a3ed3 --- /dev/null +++ b/.github/workflows/tests.yml @@ -0,0 +1,68 @@ +name: "Tests" + +on: + workflow_dispatch: + push: + branches: + - master + paths: + - '**.php' + pull_request: + types: + - opened + - synchronize + branches: + - master + paths: + - '**.php' + - '.github/workflows/tests.yml' + - 'phpunit.xml.dist' + - 'composer.json' + - 'composer.lock' + +jobs: + test: + runs-on: ${{ matrix.os }} + strategy: + fail-fast: true + matrix: + os: [ubuntu-latest] + php: [8.3, 8.2] + laravel: [11.*] + stability: [prefer-stable] + include: + - laravel: 11.* + testbench: 9.* + carbon: 3.* + collision: 8.* + name: P${{ matrix.php }} - L${{ matrix.laravel }} - ${{ matrix.stability }} - ${{ matrix.os }} + steps: + - name: Checkout Code + uses: actions/checkout@v4 + + - name: Cache Dependencies + uses: actions/cache@v4 + with: + path: ~/.composer/cache/files + key: dependencies-laravel-${{ matrix.laravel }}-php-${{ matrix.php }}-composer-${{ hashFiles('composer.json') }} + + - name: Setup PHP + uses: shivammathur/setup-php@v2 + with: + php-version: ${{ matrix.php }} + extensions: dom, curl, libxml, mbstring, zip, pcntl, pdo, sqlite, pdo_sqlite, bcmath, soap, intl, gd, exif, iconv, imagick, fileinfo + coverage: none + + - name: Install Dependencies + run: | + echo "::add-matcher::${{ runner.tool_cache }}/php.json" + echo "::add-matcher::${{ runner.tool_cache }}/phpunit.json" + + - name: Install Dependencies + run: | + composer require "laravel/framework:${{ matrix.laravel }}" "orchestra/testbench:${{ matrix.testbench }}" "nesbot/carbon:${{ matrix.carbon }}" "nunomaduro/collision:${{ matrix.collision }}" --no-interaction --no-update + composer update --${{ matrix.stability }} --prefer-dist --no-interaction + composer db + + - name: Execute tests + run: vendor/bin/pest diff --git a/.php-cs-fixer.dist.php b/.php-cs-fixer.dist.php new file mode 100644 index 0000000..4123157 --- /dev/null +++ b/.php-cs-fixer.dist.php @@ -0,0 +1,37 @@ +notPath('bootstrap/*') + ->notPath('storage/*') + ->notPath('resources/view/mail/*') + ->in([ + __DIR__ . '/src', + __DIR__ . '/tests', + ]) + ->name('*.php') + ->notName('*.blade.php') + ->ignoreDotFiles(true) + ->ignoreVCS(true); + +return (new PhpCsFixer\Config()) + ->setRules([ + '@PSR2' => true, + 'array_syntax' => ['syntax' => 'short'], + 'ordered_imports' => ['sort_algorithm' => 'alpha'], + 'no_unused_imports' => true, + 'not_operator_with_successor_space' => true, + 'trailing_comma_in_multiline' => true, + 'phpdoc_scalar' => true, + 'unary_operator_spaces' => true, + 'binary_operator_spaces' => true, + 'blank_line_before_statement' => [ + 'statements' => ['break', 'continue', 'declare', 'return', 'throw', 'try'], + ], + 'phpdoc_single_line_var_spacing' => true, + 'phpdoc_var_without_name' => true, + 'method_argument_space' => [ + 'on_multiline' => 'ensure_fully_multiline', + 'keep_multiple_spaces_after_comma' => true, + ] + ]) + ->setFinder($finder); diff --git a/3x1io-tomato-cms-behance.md b/3x1io-tomato-cms-behance.md new file mode 100644 index 0000000..8a1d596 --- /dev/null +++ b/3x1io-tomato-cms-behance.md @@ -0,0 +1,14 @@ +--- +name: Behance CMS Extension +slug: 3x1io-tomato-cms-behance +author_slug: 3x1io +categories: [developer-tools] +description: Behance Extension for Filament CMS Builder to import data from Behance +discord_url: +docs_url: https://raw.githubusercontent.com/tomatophp/filament-cms-behance/master/README.md +github_repository: tomatophp/filament-cms-behance +has_dark_theme: true +has_translations: true +versions: [3] +publish_date: 2024-11-27 +--- diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 0000000..e0a5086 --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,3 @@ +# V1.0.0 + +First release of the package diff --git a/CODE_OF_CONDUCT.md b/CODE_OF_CONDUCT.md new file mode 100644 index 0000000..18c9147 --- /dev/null +++ b/CODE_OF_CONDUCT.md @@ -0,0 +1,128 @@ +# Contributor Covenant Code of Conduct + +## Our Pledge + +We as members, contributors, and leaders pledge to make participation in our +community a harassment-free experience for everyone, regardless of age, body +size, visible or invisible disability, ethnicity, sex characteristics, gender +identity and expression, level of experience, education, socio-economic status, +nationality, personal appearance, race, religion, or sexual identity +and orientation. + +We pledge to act and interact in ways that contribute to an open, welcoming, +diverse, inclusive, and healthy community. + +## Our Standards + +Examples of behavior that contributes to a positive environment for our +community include: + +* Demonstrating empathy and kindness toward other people +* Being respectful of differing opinions, viewpoints, and experiences +* Giving and gracefully accepting constructive feedback +* Accepting responsibility and apologizing to those affected by our mistakes, + and learning from the experience +* Focusing on what is best not just for us as individuals, but for the + overall community + +Examples of unacceptable behavior include: + +* The use of sexualized language or imagery, and sexual attention or + advances of any kind +* Trolling, insulting or derogatory comments, and personal or political attacks +* Public or private harassment +* Publishing others' private information, such as a physical or email + address, without their explicit permission +* Other conduct which could reasonably be considered inappropriate in a + professional setting + +## Enforcement Responsibilities + +Community leaders are responsible for clarifying and enforcing our standards of +acceptable behavior and will take appropriate and fair corrective action in +response to any behavior that they deem inappropriate, threatening, offensive, +or harmful. + +Community leaders have the right and responsibility to remove, edit, or reject +comments, commits, code, wiki edits, issues, and other contributions that are +not aligned to this Code of Conduct, and will communicate reasons for moderation +decisions when appropriate. + +## Scope + +This Code of Conduct applies within all community spaces, and also applies when +an individual is officially representing the community in public spaces. +Examples of representing our community include using an official e-mail address, +posting via an official social media account, or acting as an appointed +representative at an online or offline event. + +## Enforcement + +Instances of abusive, harassing, or otherwise unacceptable behavior may be +reported to the community leaders responsible for enforcement at +. +All complaints will be reviewed and investigated promptly and fairly. + +All community leaders are obligated to respect the privacy and security of the +reporter of any incident. + +## Enforcement Guidelines + +Community leaders will follow these Community Impact Guidelines in determining +the consequences for any action they deem in violation of this Code of Conduct: + +### 1. Correction + +**Community Impact**: Use of inappropriate language or other behavior deemed +unprofessional or unwelcome in the community. + +**Consequence**: A private, written warning from community leaders, providing +clarity around the nature of the violation and an explanation of why the +behavior was inappropriate. A public apology may be requested. + +### 2. Warning + +**Community Impact**: A violation through a single incident or series +of actions. + +**Consequence**: A warning with consequences for continued behavior. No +interaction with the people involved, including unsolicited interaction with +those enforcing the Code of Conduct, for a specified period of time. This +includes avoiding interactions in community spaces as well as external channels +like social media. Violating these terms may lead to a temporary or +permanent ban. + +### 3. Temporary Ban + +**Community Impact**: A serious violation of community standards, including +sustained inappropriate behavior. + +**Consequence**: A temporary ban from any sort of interaction or public +communication with the community for a specified period of time. No public or +private interaction with the people involved, including unsolicited interaction +with those enforcing the Code of Conduct, is allowed during this period. +Violating these terms may lead to a permanent ban. + +### 4. Permanent Ban + +**Community Impact**: Demonstrating a pattern of violation of community +standards, including sustained inappropriate behavior, harassment of an +individual, or aggression toward or disparagement of classes of individuals. + +**Consequence**: A permanent ban from any sort of public interaction within +the community. + +## Attribution + +This Code of Conduct is adapted from the [Contributor Covenant][homepage], +version 2.0, available at +https://www.contributor-covenant.org/version/2/0/code_of_conduct.html. + +Community Impact Guidelines were inspired by [Mozilla's code of conduct +enforcement ladder](https://github.com/mozilla/diversity). + +[homepage]: https://www.contributor-covenant.org + +For answers to common questions about this code of conduct, see the FAQ at +https://www.contributor-covenant.org/faq. Translations are available at +https://www.contributor-covenant.org/translations. diff --git a/LICENSE.md b/LICENSE.md new file mode 100644 index 0000000..e66364e --- /dev/null +++ b/LICENSE.md @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright (c) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. diff --git a/README.md b/README.md new file mode 100644 index 0000000..91047f2 --- /dev/null +++ b/README.md @@ -0,0 +1,69 @@ +![Screenshot](https://raw.githubusercontent.com/tomatophp/filament-cms-behance/master/art/screenshot.jpg) + +# Filament cms behance + +[![Latest Stable Version](https://poser.pugx.org/tomatophp/filament-cms-behance/version.svg)](https://packagist.org/packages/tomatophp/filament-cms-behance) +[![License](https://poser.pugx.org/tomatophp/filament-cms-behance/license.svg)](https://packagist.org/packages/tomatophp/filament-cms-behance) +[![Downloads](https://poser.pugx.org/tomatophp/filament-cms-behance/d/total.svg)](https://packagist.org/packages/tomatophp/filament-cms-behance) + +Behance Extension for Filament CMS Builder to import data from Behance + +## Installation + +```bash +composer require tomatophp/filament-cms-behance +``` +after install your package please run this command + +```bash +php artisan filament-cms-behance:install +``` + +finally register the plugin on `/app/Providers/Filament/AdminPanelProvider.php` + +```php +->plugin(\TomatoPHP\FilamentCmsBehance\FilamentCmsBehancePlugin::make()) +``` + + +## Publish Assets + +you can publish config file by use this command + +```bash +php artisan vendor:publish --tag="filament-cms-behance-config" +``` + +you can publish views file by use this command + +```bash +php artisan vendor:publish --tag="filament-cms-behance-views" +``` + +you can publish languages file by use this command + +```bash +php artisan vendor:publish --tag="filament-cms-behance-lang" +``` + +you can publish migrations file by use this command + +```bash +php artisan vendor:publish --tag="filament-cms-behance-migrations" +``` + +## Changelog + +Please see [CHANGELOG](CHANGELOG.md) for more information on what has changed recently. + +## Security + +Please see [SECURITY](SECURITY.md) for more information about security. + +## Credits + +- [Fady Mondy](mailto:info@3x1.io) + +## License + +The MIT License (MIT). Please see [License File](LICENSE.md) for more information. diff --git a/SECURITY.md b/SECURITY.md new file mode 100644 index 0000000..b2490a9 --- /dev/null +++ b/SECURITY.md @@ -0,0 +1,3 @@ +# Security Policy + +If you discover any security related issues, please email info@3x1.io instead of using the issue tracker. diff --git a/composer.json b/composer.json new file mode 100644 index 0000000..9880165 --- /dev/null +++ b/composer.json @@ -0,0 +1,68 @@ +{ + "name": "tomatophp/filament-cms-behance", + "type": "library", + "description": "Behance Extension for Filament CMS Builder to import data from Behance", + "keywords": [ + "php", + "laravel", + "template" + ], + "license": "MIT", + "autoload": { + "psr-4": { + "TomatoPHP\\FilamentCmsBehance\\": "src/" + } + }, + "autoload-dev": { + "psr-4": { + "TomatoPHP\\FilamentCmsBehance\\Tests\\": "tests/src/", + "TomatoPHP\\FilamentCmsBehance\\Tests\\Database\\Factories\\": "tests/database/factories" + } + }, + "extra": { + "laravel": { + "providers": [ + "TomatoPHP\\FilamentCmsBehance\\FilamentCmsBehanceServiceProvider" + ] + } + }, + "authors": [ + { + "name": "Fady Mondy", + "email": "info@3x1.io" + } + ], + "config": { + "sort-packages": true, + "allow-plugins": { + "pestphp/pest-plugin": true, + "phpstan/extension-installer": true + } + }, + "scripts": { + "testbench": "vendor/bin/testbench package:discover --ansi", + "db": "vendor/bin/testbench package:create-sqlite-db && vendor/bin/testbench migrate", + "analyse": "vendor/bin/phpstan analyse src tests", + "test": "vendor/bin/pest", + "test-coverage": "vendor/bin/pest --coverage", + "format": "vendor/bin/pint" + }, + "require": { + "php": "^8.1|^8.2", + "tomatophp/console-helpers": "^1.1", + "filament/filament": "^3.2" + }, + "require-dev": { + "laravel/pint": "^1.18", + "livewire/livewire": "^2.10|^3.0", + "nunomaduro/larastan": "^2.9", + "orchestra/testbench": "^9.5", + "pestphp/pest": "^2.36", + "pestphp/pest-plugin-laravel": "^2.4", + "pestphp/pest-plugin-livewire": "^2.1", + "phpstan/extension-installer": "^1.4", + "phpstan/phpstan-deprecation-rules": "^1.2", + "phpstan/phpstan-phpunit": "^1.4" + }, + "version": "v1.0.0" +} diff --git a/config/.gitkeep b/config/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/config/filament-cms-behance.php b/config/filament-cms-behance.php new file mode 100644 index 0000000..11626bf --- /dev/null +++ b/config/filament-cms-behance.php @@ -0,0 +1,7 @@ + [ + "username" => env('BEHANCE_USERNAME', '3x1'), + ] +]; diff --git a/module.json b/module.json new file mode 100644 index 0000000..9432d04 --- /dev/null +++ b/module.json @@ -0,0 +1,29 @@ +{ + "name": "FilamentCmsBehance", + "alias": "filament-cms-behance", + "description": { + "ar": "Behance Extension for Filament CMS Builder to import data from Behance", + "en": "Behance Extension for Filament CMS Builder to import data from Behance", + "gr": "Behance Extension for Filament CMS Builder to import data from Behance", + "sp": "Behance Extension for Filament CMS Builder to import data from Behance" + }, + "keywords": [], + "priority": 0, + "providers": [ + "TomatoPHP\\FilamentCmsBehance\\FilamentCmsBehanceServiceProvider" + ], + "files": [], + "title": { + "ar": "Filament cms behance", + "en": "Filament cms behance", + "gr": "Filament cms behance", + "sp": "Filament cms behance" + }, + "color": "#cc1448", + "icon": "heroicon-c-users", + "placeholder": "https://raw.githubusercontent.com/tomatophp/filament-cms-behance/master/art/screenshot.jpg", + "type": "plugin", + "version": "v1.0.0", + "github" : "https://github.com/tomatophp/filament-cms-behance", + "docs" : "https://github.com/tomatophp/filament-cms-behance" +} diff --git a/phpunit.xml b/phpunit.xml new file mode 100644 index 0000000..e542661 --- /dev/null +++ b/phpunit.xml @@ -0,0 +1,26 @@ + + + + + ./tests/ + + + + + ./src + + + + + + + diff --git a/pint.json b/pint.json new file mode 100644 index 0000000..c6ddb49 --- /dev/null +++ b/pint.json @@ -0,0 +1,14 @@ +{ + "preset": "laravel", + "rules": { + "blank_line_before_statement": true, + "concat_space": { + "spacing": "one" + }, + "method_argument_space": true, + "single_trait_insert_per_statement": true, + "types_spaces": { + "space": "single" + } + } +} diff --git a/resources/lang/.gitkeep b/resources/lang/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/resources/lang/ar/messages.php b/resources/lang/ar/messages.php new file mode 100644 index 0000000..ca5d8ed --- /dev/null +++ b/resources/lang/ar/messages.php @@ -0,0 +1,5 @@ +dusk = new Chrome($this->type); + + try { + if (!$this->show) { + $this->dusk->headless() + ->disableGpu() + ->noSandbox(); + } + + $this->dusk->windowSize(1200, 1200); + $this->dusk->ignoreSslErrors(); + $this->dusk->disableNotifications(); + $this->dusk->disableInfobars(); + $this->agent(); + $this->dusk->start(); + } catch (\Exception $e) { + $this->dusk->stop(); + } + } + + /** + * @return void + */ + private function agent(): void + { + if ($this->type === 'web') { + $webAgent = "Mozilla/5.0 (X11; Linux x86_64) "; + $webAgent .= "AppleWebKit/537.36 (KHTML, like Gecko) "; + $webAgent .= "Chrome/111.0.0.0 Safari/537.36"; + $this->dusk->userAgent($webAgent); + } + else { + $mobileAgent = "Mozilla/5.0 (Linux; Android 7.0; SM-G930V Build/NRD90M) "; + $mobileAgent .= "AppleWebKit/537.36 (KHTML, like Gecko) "; + $mobileAgent .= "Chrome/59.0.3071.125 Mobile Safari/537.36"; + $this->dusk->userAgent($mobileAgent); + } + } + + /** + * @return Chrome|null + */ + public function dusk(): Chrome|null + { + return $this->dusk; + } +} diff --git a/src/Browser/Chrome.php b/src/Browser/Chrome.php new file mode 100644 index 0000000..35e7b1a --- /dev/null +++ b/src/Browser/Chrome.php @@ -0,0 +1,361 @@ +arguments = Collection::make(); + } + + /** + * Start the browser. + * + * @return $this + */ + public function start(): static + { + static::startChromeDriver(); + + return $this; + } + + + /** + * Stop Browser + * + * @return $this + * @throws \Exception + */ + public function stop(): static + { + try { + $this->closeAll(); + } catch (\Exception $e) { + throw $e; + } finally { + static::stopChromeDriver(); + + return $this; + } + } + + /** + * Set the request timeout. + * + * @return $this + */ + public function setRequestTimeout(int $timeout): static + { + $this->requestTimeout = $timeout; + + return $this; + } + + /** + * Set the connect timeout. + * + * @return $this + */ + public function setConnectTimeout(int $timeout): static + { + $this->connectTimeout = $timeout; + + return $this; + } + + /** + * Run the browser in headless mode. + * + * @return $this + */ + public function headless(): static + { + return $this->addArgument('--headless'); + } + + + /** + * Set Remote Port + * + * @return $this + */ + public function remote(): static + { + return $this->addArgument('--remote-debugging-port=9222'); + } + + /** + * Set Cookies Folder For The Browser + * + * @return $this + */ + public function cookie(): static + { + return $this->addArgument('--enable-file-cookies'); + } + + /** + * Disable the browser using gpu. + * + * @return $this + */ + public function disableGpu(): static + { + return $this->addArgument('--disable-gpu'); + } + + /** + * Disable the sandbox. + * + * @return $this + */ + public function noSandbox(): static + { + return $this->addArgument('--no-sandbox'); + } + + /** + * Disables the use of a zygote process for forking child processes. + * + * @return $this + */ + public function noZygote():static + { + return $this->noSandbox()->addArgument('--no-zygote'); + } + + /** + * Ignore SSL certificate error messages. + * + * @return $this + */ + public function ignoreSslErrors():static + { + return $this->addArgument('--ignore-certificate-errors'); + } + + /** + * Set the initial browser window size. + * + * @param int $width the browser width in pixels + * @param int $height the browser height in pixels + * + * @return $this + */ + public function windowSize(int $width, int $height): static + { + return $this->addArgument('--window-size=' . $width . ',' . $height); + } + + /** + * Set the user proxy IP. + * + * @param string $ip the user proxy IP + * + * @return $this + */ + public function proxyServer(string $ip): static + { + return $this->addArgument('--proxy-server=' . $ip); + } + + /** + * Set user Agent for selected browser. + * + * @param string $useragent + * @return $this + */ + public function userAgent(string $useragent): static + { + return $this->addArgument('--user-agent=' . $useragent); + } + + /** + * Disabled Extensions On The Browser + * + * @return $this + */ + public function disableExtensions(): static + { + return $this->addArgument('--disable-extensions'); + } + + /** + * Disabled Browser Notifications + * + * @return $this + */ + public function disableNotifications(): static + { + return $this->addArgument('--disable-notifications'); + } + + /** + * Disabled Info Bar on the browser + * + * @return $this + */ + public function disableInfobars(): static + { + return $this->addArgument('disable-infobars'); + } + + + /** + * Set The Browser Data Base Path + * + * @param string $path + * @return $this + */ + public function browserData(string $path):static + { + return $this->addArgument('--user-data-dir=' . $path); + } + + /** + * @param string $id + * @return $this + */ + public function setProfile(string $id): static + { + return $this->addArgument('--profile-directory=Profile ' . $id); + } + + /** + * Add a browser option. + * + * @param string $argument + * @return $this + */ + protected function addArgument(string $argument): static + { + if (!$this->arguments->contains($argument)) { + $this->arguments->push($argument); + } + + return $this; + } + + /** + * Create the RemoteWebDriver instance. + * + * @return RemoteWebDriver + */ + public function driver(): RemoteWebDriver + { + $options = (new ChromeOptions())->addArguments($this->arguments->toArray()); + $options->setExperimentalOption('excludeSwitches', ['enable-automation']); + + $this->getDriver = RemoteWebDriver::create( + 'http://localhost:9515', + DesiredCapabilities::chrome()->setCapability( + ChromeOptions::CAPABILITY, + $options + ), + $this->connectTimeout, + $this->requestTimeout + ); + + return $this->getDriver; + } + + /** + * Get the browser caller name. + * + * @return string + */ + protected function getCallerName(): string + { + return \str_replace('\\', '_', \get_class($this)) . '_' . $this->callerName; + } +} diff --git a/src/FilamentCmsBehancePlugin.php b/src/FilamentCmsBehancePlugin.php new file mode 100644 index 0000000..e799d8f --- /dev/null +++ b/src/FilamentCmsBehancePlugin.php @@ -0,0 +1,30 @@ +mergeConfigFrom(__DIR__.'/../config/filament-cms-behance.php', 'filament-cms-behance'); + + //Publish Config + $this->publishes([ + __DIR__.'/../config/filament-cms-behance.php' => config_path('filament-cms-behance.php'), + ], 'filament-cms-behance-config'); + + //Register Langs + $this->loadTranslationsFrom(__DIR__.'/../resources/lang', 'filament-cms-behance'); + + //Publish Lang + $this->publishes([ + __DIR__.'/../resources/lang' => base_path('lang/vendor/filament-cms-behance'), + ], 'filament-cms-behance-lang'); + } + + public function boot(): void + { + //you boot methods here + } +} diff --git a/src/Jobs/BehanceMetaGetterJob.php b/src/Jobs/BehanceMetaGetterJob.php new file mode 100644 index 0000000..9d1fdc4 --- /dev/null +++ b/src/Jobs/BehanceMetaGetterJob.php @@ -0,0 +1,46 @@ +username || $this->url){ + $be = new \TomatoPHP\FilamentCms\Services\Behance( + username: $this->username, + url: $this->url, + userId: $this->userId, + userType: $this->userType, + panel: $this->panel, + ); + $be->run(); + } + } +} diff --git a/src/Services/Behance.php b/src/Services/Behance.php new file mode 100644 index 0000000..0d7abaa --- /dev/null +++ b/src/Services/Behance.php @@ -0,0 +1,265 @@ +type); + $this->dusk = $browser->dusk(); + } + + /** + * @return void + * @throws \Throwable + */ + public function run(): void + { + $data = [ + "userType" => $this->userType, + "userId" => $this->userId, + "username" => $this->username, + "url" => $this->url, + ]; + $this->dusk->browse(function (Browser $browser) use ($data){ + try { + $projectsList = []; + $user = $data['userType']::find($data['userId']); + if($data['username']){ + $browser->visit('https://www.behance.net/' . $data['username']); + $browser->pause(2000); + $count = 1200; + for($i=0;$i<6; $i++){ + $browser->script('window.scrollTo(0, '.$count.');'); + $browser->pause(2000); + $count+=1200; + } + $browser->pause(2000); + + $projectsList = $browser->script(" + let projectArray = document.querySelectorAll('.ProjectCoverNeue-root-B1h'); + let projectListArray= []; + for(let i=0; igetProjectByURL($browser,$project['url'], $data); + } + }catch (\Exception $e) { + Log::error($e); + + Notification::make() + ->title('Behance Portfolio import failed') + ->body('Behance Portfolio import failed') + ->error() + ->sendToDatabase($user); + } + } + else { + try { + $this->getProjectByURL($browser,$data['url'], $data); + }catch (\Exception $e) { + Log::error($e); + + Notification::make() + ->title(trans('filament-cms::messages.content.posts.import.behance.notifications.failed_title')) + ->body(trans('filament-cms::messages.content.posts.import.behance.notifications.failed_description')) + ->danger() + ->sendToDatabase($user); + } + } + + $browser->driver->quit(); + Notification::make() + ->title(trans('filament-cms::messages.content.posts.import.behance.notifications.title')) + ->body(trans('filament-cms::messages.content.posts.import.behance.notifications.description', ['name' => $data['url']])) + ->success() + ->sendToDatabase($user); + + }catch (\Exception $e){ + Log::error($e); + + Notification::make() + ->title(trans('filament-cms::messages.content.posts.import.behance.notifications.failed_title')) + ->body(trans('filament-cms::messages.content.posts.import.behance.notifications.failed_description')) + ->danger() + ->sendToDatabase($user); + } + }); + + } + + private function getProjectByURL(Browser $browser,string $url, array $data=[]) + { + try { + $browser->visit($url); + $browser->waitFor("#project-canvas"); + $projectImages = $browser->script(" + let projectBody = document.querySelectorAll('#project-canvas')[0]; + let images = []; + let keywords = ''; + let keywordsContent = document.querySelector('meta[name=keywords]'); + if(keywordsContent){ + keywords = keywordsContent.content; + } + let description = ''; + let descriptionContent = document.querySelector('meta[name=description]'); + if(descriptionContent){ + description = descriptionContent.content; + } + + let cover = ''; + let coverContent = document.querySelector('meta[name=\"twitter:image\"]'); + if(coverContent){ + cover = coverContent.content; + } + + let imagesArray = []; + let textArray = []; + if(projectBody){ + images = projectBody.querySelectorAll('img') + for(let i=0; iurl)->remove('https://www.behance.net/gallery/')->replace('/', '-')->toString(); + $checkIfPostExists = Post::query()->withTrashed()->where('slug', $getSlug)->first(); + if($checkIfPostExists){ + if($checkIfPostExists->deleted_at){ + $checkIfPostExists->restore(); + } + $checkIfPostExists->clearMediaCollection('feature_image'); + $post = $checkIfPostExists; + } + else { + $post = new Post(); + } + + $post->title = [ + "en"=> $project['name'], + "ar"=> $project['name'], + ]; + $post->views = (int)$project['views']; + $post->keywords = [ + "ar" => $project['keywords'], + "en" => $project['keywords'] + ]; + $post->short_description = [ + "ar" => $project['description'], + "en" => $project['description'], + ]; + $body = ""; + foreach($project['texts'] as $text){ + $body .= $text; + } + $post->body = [ + "ar"=> $body, + "en"=> $body + ]; + $post->is_published = true; + $post->published_at = now(); + $post->type = 'portfolio'; + $post->slug = $getSlug; + $post->author_type = $data['userType']; + $post->author_id = $data['userId']; + $post->save(); + + $post->meta('likes', $project['likes']); + $post->meta('comments', $project['comments']); + $post->meta('views', $project['views']); + + $post->addMediaFromUrl($project['cover'])->toMediaCollection('feature_image'); + foreach($project['images'] as $image){ + $post->addMediaFromUrl($image)->toMediaCollection('images'); + } + + Event::dispatch(new PostCreated($post->toArray())); + + + $browser->pause(2000); + }catch (\Exception $e){ + Log::error($e); + } + } +} diff --git a/testbench.yaml b/testbench.yaml new file mode 100644 index 0000000..5205c2e --- /dev/null +++ b/testbench.yaml @@ -0,0 +1,24 @@ +providers: + - BladeUI\Icons\BladeIconsServiceProvider + - BladeUI\Heroicons\BladeHeroiconsServiceProvider + - Filament\Actions\ActionsServiceProvider + - Filament\FilamentServiceProvider + - Filament\Forms\FormsServiceProvider + - Filament\Infolists\InfolistsServiceProvider + - Filament\Notifications\NotificationsServiceProvider + - Filament\Support\SupportServiceProvider + - Filament\Tables\TablesServiceProvider + - Filament\Widgets\WidgetsServiceProvider + - RyanChandler\BladeCaptureDirective\BladeCaptureDirectiveServiceProvider + - TomatoPHP\FilamentCmsBehance\FilamentCmsBehanceServiceProvider + - TomatoPHP\FilamentCmsBehance\Tests\AdminPanelProvider +workbench: + welcome: true + install: true + start: / + guard: testing + discovers: + web: true + api: false + commands: false + views: true diff --git a/tests/Pest.php b/tests/Pest.php new file mode 100644 index 0000000..637c14c --- /dev/null +++ b/tests/Pest.php @@ -0,0 +1,5 @@ +in(__DIR__); diff --git a/tests/database/database.sqlite b/tests/database/database.sqlite new file mode 100644 index 0000000..cb9c456 Binary files /dev/null and b/tests/database/database.sqlite differ diff --git a/tests/database/factories/.gitkeep b/tests/database/factories/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/tests/database/factories/UserFactory.php b/tests/database/factories/UserFactory.php new file mode 100644 index 0000000..0fe9cb2 --- /dev/null +++ b/tests/database/factories/UserFactory.php @@ -0,0 +1,23 @@ + $this->faker->name(), + 'email' => $this->faker->unique()->safeEmail(), + 'email_verified_at' => now(), + 'password' => '$2y$10$92IXUNpkjO0rOQ5byMi.Ye4oKoEa3Ro9llC/.og/at2.uheWG/igi', // password + 'remember_token' => Str::random(10), + ]; + } +} diff --git a/tests/src/AdminPanelProvider.php b/tests/src/AdminPanelProvider.php new file mode 100644 index 0000000..fa87faa --- /dev/null +++ b/tests/src/AdminPanelProvider.php @@ -0,0 +1,50 @@ +default() + ->id('admin') + ->path('admin') + ->login() + ->pages([ + Pages\Dashboard::class, + ]) + ->plugin( + FilamentCmsBehancePlugin::make() + ) + ->middleware([ + EncryptCookies::class, + AddQueuedCookiesToResponse::class, + StartSession::class, + AuthenticateSession::class, + ShareErrorsFromSession::class, + VerifyCsrfToken::class, + SubstituteBindings::class, + DisableBladeIconComponents::class, + DispatchServingFilamentEvent::class, + ]) + ->authMiddleware([ + Authenticate::class, + ]); + } +} diff --git a/tests/src/DebugTest.php b/tests/src/DebugTest.php new file mode 100644 index 0000000..91bf6cd --- /dev/null +++ b/tests/src/DebugTest.php @@ -0,0 +1,5 @@ +each->not->toBeUsed(); +}); diff --git a/tests/src/Models/.gitkeep b/tests/src/Models/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/tests/src/Models/User.php b/tests/src/Models/User.php new file mode 100644 index 0000000..13ab54b --- /dev/null +++ b/tests/src/Models/User.php @@ -0,0 +1,34 @@ +set('database.default', 'sqlite'); + $app['config']->set('database.connections.sqlite.database', __DIR__ . '/../database/database.sqlite'); + + $app['config']->set('view.paths', [ + ...$app['config']->get('view.paths'), + __DIR__ . '/../resources/views', + ]); + } +}