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 @@
+
+
+# Filament cms behance
+
+[](https://packagist.org/packages/tomatophp/filament-cms-behance)
+[](https://packagist.org/packages/tomatophp/filament-cms-behance)
+[](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',
+ ]);
+ }
+}