diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 48cc1e2e73f..5f8e3ee2c3f 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -250,7 +250,7 @@ jobs: # Test - name: Upgrade Chrome Driver - run: php artisan dusk:chrome-driver $(google-chrome -version | awk '{ print $3 }' | cut -d . -f 1) + run: php artisan dusk:chrome-driver --detect - name: Start Chrome Driver run: | chmod -R 0755 vendor/laravel/dusk/bin/ diff --git a/app/Console/Commands/ImportAccounts.php b/app/Console/Commands/ImportAccounts.php new file mode 100644 index 00000000000..286e3db7db8 --- /dev/null +++ b/app/Console/Commands/ImportAccounts.php @@ -0,0 +1,134 @@ +option('ldap_uri') ?? '127.0.0.1'; + $ldap_attr_mail = $this->option('ldap_attr_mail') ?? 'mail'; + $ldap_attr_firstname = $this->option('ldap_attr_firstname') ?? 'givenName'; + $ldap_attr_lastname = $this->option('ldap_attr_lastname') ?? 'sn'; + + $ldap_user = $this->option('ldap_user'); + if (empty($ldap_user)) { + $this->error($this::ERROR_MISSING_LDAP_USER); + } + + $ldap_pass = $this->option('ldap_pass'); + if (empty($ldap_pass)) { + $this->error($this::ERROR_MISSING_LDAP_PASS); + } + + $ldap_base = $this->option('ldap_base'); + if (empty($ldap_base)) { + $this->error($this::ERROR_MISSING_LDAP_BASE); + } + + $ldap_filter = $this->option('ldap_filter'); + if (empty($ldap_filter)) { + $this->error($this::ERROR_MISSING_LDAP_FILTER); + } + + if (empty($ldap_user) || empty($ldap_pass) || empty($ldap_base) || empty($ldap_filter)) { + return; + } + + $ldap_conn = ldap_connect($ldap_uri); + if (! $ldap_conn) { + $this->error('Could not connect to LDAP URI'); + + return; + } + if (! ldap_set_option($ldap_conn, LDAP_OPT_PROTOCOL_VERSION, 3)) { + $this->error('Could not set LDAP protocol v3'); + + return false; + } + + try { + $bind = ldap_bind($ldap_conn, $ldap_user, $ldap_pass); + if (! $bind) { + $this->error('Could not bind with given LDAP credentials'); + + return; + } + } catch (\Exception $e) { + $this->error($e->getMessage()); + + return; + } + + $ldap_res = []; + try { + $ldap_res = ldap_search($ldap_conn, $ldap_base, $ldap_filter, [$ldap_attr_mail, $ldap_attr_firstname, $ldap_attr_lastname]); + } catch (\Exception $e) { + $this->error($e->getMessage()); + + return; + } + + $ldap_data = ldap_get_entries($ldap_conn, $ldap_res); + + for ($i = 0; $i < $ldap_data['count']; $i++) { + if (! (isset($ldap_data[$i][$ldap_attr_mail]) && $ldap_data[$i][$ldap_attr_mail]['count'] > 0)) { + continue; + } + $user_mail = $ldap_data[$i][$ldap_attr_mail][0]; + $user_firstname = 'John'; + $user_lastname = 'Doe'; + $user_password = bin2hex(random_bytes(64)); + if (isset($ldap_data[$i][$ldap_attr_firstname]) && $ldap_data[$i][$ldap_attr_firstname]['count'] > 0) { + $user_firstname = $ldap_data[$i][$ldap_attr_firstname][0]; + } + if (isset($ldap_data[$i][$ldap_attr_lastname]) && $ldap_data[$i][$ldap_attr_lastname]['count'] > 0) { + $user_lastname = $ldap_data[$i][$ldap_attr_lastname][0]; + } + $this->info('Importing user "'.$user_mail.'"'); + try { + Account::createDefault($user_firstname, $user_lastname, $user_mail, $user_password); + } catch (\Exception $import_error) { + $this->warn('Could not import user "'.$user_mail.'": '.$import_error->getMessage()); + } + } + } +} diff --git a/app/Helpers/CountriesHelper.php b/app/Helpers/CountriesHelper.php index 5e4e31c7acb..a88de923dc5 100644 --- a/app/Helpers/CountriesHelper.php +++ b/app/Helpers/CountriesHelper.php @@ -198,6 +198,6 @@ public static function getDefaultTimezone($country): string break; } - return $timezone; + return $timezone ?? config('app.timezone'); } } diff --git a/app/Http/Controllers/ContactsController.php b/app/Http/Controllers/ContactsController.php index 9a0a7c298e0..d2bc8215a7d 100644 --- a/app/Http/Controllers/ContactsController.php +++ b/app/Http/Controllers/ContactsController.php @@ -85,6 +85,8 @@ private function contacts(Request $request, bool $active) } $tagsCount = Tag::contactsCount(); + $contactsWithoutTagsCount = (clone $contacts)->doesntHave('tags')->count(); + $tags = null; $url = null; $count = 1; @@ -135,7 +137,8 @@ private function contacts(Request $request, bool $active) ->withTagsCount($tagsCount) ->withUrl($url) ->withTagCount($count) - ->withTagLess($request->input('no_tag') ?? false); + ->withTagLess($request->input('no_tag') ?? false) + ->with('contactsWithoutTagsCount', $contactsWithoutTagsCount); } /** diff --git a/app/Http/Controllers/SettingsController.php b/app/Http/Controllers/SettingsController.php index 1ef753a542e..b41cdd0f4f0 100644 --- a/app/Http/Controllers/SettingsController.php +++ b/app/Http/Controllers/SettingsController.php @@ -59,7 +59,9 @@ public function index() if (auth()->user()->me_contact_id) { $meContact = Contact::where('account_id', auth()->user()->account_id) ->find(auth()->user()->me_contact_id); - $existingContacts->prepend($meContact); + if ($meContact) { + $existingContacts->prepend($meContact); + } } $accountHasLimitations = AccountHelper::hasLimitations(auth()->user()->account); diff --git a/composer.json b/composer.json index 255babfb749..d75cb05b848 100644 --- a/composer.json +++ b/composer.json @@ -66,7 +66,7 @@ "barryvdh/laravel-debugbar": "^3", "fakerphp/faker": "^1.10", "khanamiryan/qrcode-detector-decoder": "^2.0", - "laravel/dusk": "^7.0", + "laravel/dusk": "^7.11", "laravel/legacy-factories": "^1.0", "laravel/tinker": "^2.6", "matthiasnoback/live-code-coverage": "^1", diff --git a/composer.lock b/composer.lock index 1c92f14a84f..80ce1325b91 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "97cdf44688f77f53c96285c17f322f1b", + "content-hash": "8c2a9ddbec63b22cf6bb27edd741f02d", "packages": [ { "name": "asbiin/laravel-adorable", @@ -13729,21 +13729,22 @@ }, { "name": "laravel/dusk", - "version": "v7.4.0", + "version": "v7.11.3", "source": { "type": "git", "url": "https://github.com/laravel/dusk.git", - "reference": "c15e0e2d2d7fe3281c015e6172df9288c6597cbb" + "reference": "ef474f54ab24989f480c77ba92dc39d07d499dee" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/laravel/dusk/zipball/c15e0e2d2d7fe3281c015e6172df9288c6597cbb", - "reference": "c15e0e2d2d7fe3281c015e6172df9288c6597cbb", + "url": "https://api.github.com/repos/laravel/dusk/zipball/ef474f54ab24989f480c77ba92dc39d07d499dee", + "reference": "ef474f54ab24989f480c77ba92dc39d07d499dee", "shasum": "" }, "require": { "ext-json": "*", "ext-zip": "*", + "guzzlehttp/guzzle": "^7.2", "illuminate/console": "^9.0|^10.0", "illuminate/support": "^9.0|^10.0", "nesbot/carbon": "^2.0", @@ -13756,8 +13757,10 @@ }, "require-dev": { "mockery/mockery": "^1.4.2", - "orchestra/testbench": "^7.0|^8.0", - "phpunit/phpunit": "^9.0" + "orchestra/testbench": "^7.33|^8.13", + "phpstan/phpstan": "^1.10", + "phpunit/phpunit": "^9.5.10|^10.0.1", + "psy/psysh": "^0.11.12" }, "suggest": { "ext-pcntl": "Used to gracefully terminate Dusk when tests are running." @@ -13796,9 +13799,9 @@ ], "support": { "issues": "https://github.com/laravel/dusk/issues", - "source": "https://github.com/laravel/dusk/tree/v7.4.0" + "source": "https://github.com/laravel/dusk/tree/v7.11.3" }, - "time": "2023-01-06T16:04:32+00:00" + "time": "2023-10-17T13:53:17+00:00" }, { "name": "laravel/legacy-factories", diff --git a/config/mail.php b/config/mail.php index 75c353d56a0..7547756b577 100644 --- a/config/mail.php +++ b/config/mail.php @@ -41,6 +41,7 @@ 'encryption' => env('MAIL_ENCRYPTION', 'tls'), 'username' => env('MAIL_USERNAME'), 'password' => env('MAIL_PASSWORD'), + 'verify_peer' => env('MAIL_VERIFY_PEER', true), ], 'ses' => [ @@ -57,7 +58,7 @@ 'sendmail' => [ 'transport' => 'sendmail', - 'path' => '/usr/sbin/sendmail -bs', + 'path' => env('MAIL_SENDMAIL_PATH', '/usr/sbin/sendmail -bs -i'), ], 'log' => [ diff --git a/database/migrations/2017_01_28_222114_remove_viewed_at_from_contacts.php b/database/migrations/2017_01_28_222114_remove_viewed_at_from_contacts.php index c572a5a67e8..e8c0971d52f 100644 --- a/database/migrations/2017_01_28_222114_remove_viewed_at_from_contacts.php +++ b/database/migrations/2017_01_28_222114_remove_viewed_at_from_contacts.php @@ -13,11 +13,11 @@ class RemoveViewedAtFromContacts extends Migration */ public function up() { - Schema::table('contacts', function (Blueprint $table) { - $table->dropColumn( - 'viewed_at' - ); - }); + if (Schema::hasColumn('contacts', 'viewed_at')) { + Schema::table('contacts', function (Blueprint $table) { + $table->dropColumn('viewed_at'); + }); + } } /** @@ -27,8 +27,10 @@ public function up() */ public function down() { - Schema::table('contacts', function (Blueprint $table) { - $table->dateTime('viewed_at')->nullable(); - }); + if (! Schema::hasColumn('contacts', 'viewed_at')) { + Schema::table('contacts', function (Blueprint $table) { + $table->dateTime('viewed_at')->nullable(); + }); + } } } diff --git a/docs/installation/providers/ubuntu.md b/docs/installation/providers/ubuntu.md index e85fe1a2082..b4b706b35e1 100644 --- a/docs/installation/providers/ubuntu.md +++ b/docs/installation/providers/ubuntu.md @@ -2,7 +2,7 @@ Ubuntu -Monica can run on [Ubuntu 18.04 (Bionic Beaver)](http://releases.ubuntu.com/18.04/). +Monica can run on [Ubuntu 22.04 (Jammy Jellyfish)](http://releases.ubuntu.com/22.04/). - [Prerequisites](#prerequisites) - [Types of databases](#types-of-databases) @@ -35,6 +35,13 @@ sudo apt update sudo apt install -y git ``` +**Unzip:** Unzip is required but was not installed by default. Install it with: + +```sh +sudo apt update +sudo apt install -y unzip +``` + **Apache:** Apache should come pre-installed with your server. If it's not, install it with: ```sh @@ -55,9 +62,7 @@ Then install php 8.1 with these extensions: ```sh sudo apt update -sudo apt install -y php8.1 php8.1-bcmath php8.1-cli php8.1-curl php8.1-common \ - php8.1-fpm php8.1-gd php8.1-gmp php8.1-intl php-json php8.1-mbstring \ - php8.1-mysql php8.1-opcache php8.1-redis php8.1-xml php8.1-zip +sudo apt install -y php8.1-{bcmath,cli,curl,common,fpm,gd,gmp,intl,mbstring,mysql,opcache,redis,xml,zip} ``` **Composer:** After you're done installing PHP, you'll need the [Composer](https://getcomposer.org/download/) dependency manager. @@ -74,7 +79,7 @@ rm -f composer-setup.php **Node.js:** Install node.js with package manager. ```sh -curl -fsSL https://deb.nodesource.com/setup_16.x | sudo -E bash - +curl -fsSL https://deb.nodesource.com/setup_18.x | sudo -E bash - sudo apt install -y nodejs ``` @@ -115,7 +120,7 @@ cd /var/www/monica # Get latest tags from GitHub git fetch # Clone the desired version -git checkout tags/v2.18.0 +git checkout tags/v4.0.0 ``` ### 2. Setup the database @@ -157,16 +162,23 @@ exit 1. `cp .env.example .env` to create your own version of all the environment variables needed for the project to work. 2. Update `.env` to your specific needs - - set `DB_USERNAME` and `DB_PASSWORD` with the settings used behind. + - Update database information. + ```diff + - DB_USERNAME=homestead + - DB_PASSWORD=secret + + DB_USERNAME=monica + # Use the password you created. + + DB_PASSWORD=strongpassword + ``` - configure a [mailserver](/docs/installation/mail.md) for registration & reminders to work correctly. - set the `APP_ENV` variable to `production`, `local` is only used for the development version. Beware: setting `APP_ENV` to `production` will force HTTPS. Skip this if you're running Monica locally. -3. Run `composer install --no-interaction --no-dev` to install all packages. -4. Run `yarn install` to install frontend packages, then `yarn run production` to build the assets (js, css). -5. Run `php artisan key:generate` to generate an application key. This will set `APP_KEY` with the right value automatically. -6. Run `php artisan setup:production -v` to run the migrations, seed the database and symlink folders. +4. Run `composer install --no-interaction --no-dev` to install all packages. +5. Run `yarn install` to install frontend packages, then `yarn run production` to build the assets (js, css). +6. Run `php artisan key:generate` to generate an application key. This will set `APP_KEY` with the right value automatically. +7. Run `php artisan setup:production -v` to run the migrations, seed the database and symlink folders. - You can use `email` and `password` parameter to setup a first account directly: `php artisan setup:production --email=your@email.com --password=yourpassword -v` -7. _Optional_: Setup the queues with Redis, Beanstalk or Amazon SQS: see optional instruction of [generic installation](generic.md#setup-queues) -8. _Optional_: Setup the access tokens to use the API follow optional instruction of [generic installation](generic.md#setup-access-tokens) +8. _Optional_: Setup the queues with Redis, Beanstalk or Amazon SQS: see optional instruction of [generic installation](generic.md#setup-queues) +9. _Optional_: Setup the access tokens to use the API follow optional instruction of [generic installation](generic.md#setup-access-tokens) ### 4. Configure cron job @@ -207,11 +219,11 @@ sudo a2enmod rewrite sudo nano /etc/apache2/sites-available/monica.conf ``` -Then, in the `nano` text editor window you just opened, copy the following - swapping the `**YOUR IP ADDRESS/DOMAIN**` with your server's IP address/associated domain: +Then, in the `nano` text editor window you just opened, copy the following - swapping the `monica.example.com` with your server's IP address/associated domain: ```html - ServerName **YOUR IP ADDRESS/DOMAIN** + ServerName monica.example.com ServerAdmin webmaster@localhost DocumentRoot /var/www/monica/public diff --git a/phpstan.neon b/phpstan.neon index be6ad5c12af..1820375adc8 100644 --- a/phpstan.neon +++ b/phpstan.neon @@ -48,3 +48,5 @@ parameters: - */Http/Resources/**/*.php - */ExportResources/**/*.php - */ExportResources/*.php + - */Console/Commands/ImportAccounts.php + diff --git a/resources/lang/he/app.php b/resources/lang/he/app.php index 4929107b682..5605183f74b 100644 --- a/resources/lang/he/app.php +++ b/resources/lang/he/app.php @@ -180,10 +180,10 @@ 'relationship_type_lovedby_female_with_name' => 'מאהבת סודית של :name', 'relationship_type_lovedby_male_with_name' => 'מאהב סודי של :name', - 'relationship_type_ex' => 'ex-partner', + 'relationship_type_ex' => 'שותף לשעבר לחיים', 'relationship_type_ex_female' => 'חברה לשעבר', 'relationship_type_ex_male' => 'חבר לשעבר', - 'relationship_type_ex_with_name' => ':name’s ex-partner', + 'relationship_type_ex_with_name' => 'שותף לחיים לשעבר של :name', 'relationship_type_ex_female_with_name' => 'חברה לשעבר של :name', 'relationship_type_ex_male_with_name' => 'חבר לשעבר של :name', @@ -222,17 +222,17 @@ 'relationship_type_sibling_female_with_name' => 'אחות של :name', 'relationship_type_sibling_male_with_name' => 'אח של :name', - 'relationship_type_grandparent' => 'grandparent', + 'relationship_type_grandparent' => 'סב', 'relationship_type_grandparent_female' => 'סבתא', 'relationship_type_grandparent_male' => 'סבא', - 'relationship_type_grandparent_with_name' => ':name’s grandparent', + 'relationship_type_grandparent_with_name' => 'הסבא או הסבתא של :name', 'relationship_type_grandparent_female_with_name' => 'סבתא של :name', 'relationship_type_grandparent_male_with_name' => 'סבא של :name', 'relationship_type_grandchild' => 'נכד/ה', 'relationship_type_grandchild_female' => 'נכדה', 'relationship_type_grandchild_male' => 'נכד', - 'relationship_type_grandchild_with_name' => ':name’s grandchild', + 'relationship_type_grandchild_with_name' => 'הנכד או הנכדה של :name', 'relationship_type_grandchild_female_with_name' => 'נכדה של :name', 'relationship_type_grandchild_male_with_name' => 'נכד של :name', @@ -257,10 +257,10 @@ 'relationship_type_cousin_female_with_name' => 'בת דודה של :name', 'relationship_type_cousin_male_with_name' => 'בן דוד של :name', - 'relationship_type_godfather' => 'godparent', + 'relationship_type_godfather' => 'אפוטרופוס', 'relationship_type_godfather_female' => 'סנדקית', 'relationship_type_godfather_male' => 'סנדק', - 'relationship_type_godfather_with_name' => ':name’s godparent', + 'relationship_type_godfather_with_name' => 'האפוטרופוס/ית של :name', 'relationship_type_godfather_female_with_name' => 'הסנדקית של :name', 'relationship_type_godfather_male_with_name' => 'הסנדק של :name', diff --git a/resources/lang/he/settings.php b/resources/lang/he/settings.php index 60cabc650a0..6beab18117b 100644 --- a/resources/lang/he/settings.php +++ b/resources/lang/he/settings.php @@ -42,7 +42,7 @@ 'export_title_json' => 'ייצוא ל־Json', 'export_submitted' => 'הייצוא שלך הוגש, הוא יהיה זמין בעוד מספר רגעים…', 'export_json_explanation' => 'הנתונים שלך מיוצאים בתצורת Json למטרות גיבוי.', - 'export_json_beta' => 'Json export is in preview mode. Tell us what you think about it:', + 'export_json_beta' => 'ייצוא Json הוא במצב תצוגה מקדימה. נא לספר לנו מה דעתך עליו:', 'export_json_cta' => 'ייצוא ל־Json', 'export_header_type' => 'סוג', 'export_header_timestamp' => 'מועד יצירה', diff --git a/resources/sass/people.scss b/resources/sass/people.scss index 1321115c45a..3e3bc6f03da 100644 --- a/resources/sass/people.scss +++ b/resources/sass/people.scss @@ -41,7 +41,8 @@ position: absolute; } - .number-contacts-per-tag { + .number-contacts-per-tag, + .number-contacts-without-tag { @if $htmldir == ltr { float: right; } @else { @@ -49,6 +50,9 @@ } } } + .number-contacts-without-tag{ + font-size: 1rem; + } } .list { diff --git a/resources/views/journal/add.blade.php b/resources/views/journal/add.blade.php index 4c8a84ba4a1..b0846219f1a 100644 --- a/resources/views/journal/add.blade.php +++ b/resources/views/journal/add.blade.php @@ -39,7 +39,7 @@
- +
diff --git a/resources/views/people/index.blade.php b/resources/views/people/index.blade.php index a222e3e03e2..5eb0e0d5073 100644 --- a/resources/views/people/index.blade.php +++ b/resources/views/people/index.blade.php @@ -144,6 +144,7 @@ @if ($tagsCount->count() != 0)
  • {{ trans('people.people_list_untagged') }} + {{ trans_choice('people.people_list_contacts_per_tags', $contactsWithoutTagsCount) }}
  • @endif