]> BookStack Code Mirror - bookstack/commitdiff
Merge branch 'api-endpoint-users' into users_api
authorDan Brown <redacted>
Thu, 3 Feb 2022 11:38:55 +0000 (11:38 +0000)
committerDan Brown <redacted>
Thu, 3 Feb 2022 11:38:55 +0000 (11:38 +0000)
1  2 
app/Api/ListingResponseBuilder.php
app/Auth/UserRepo.php
app/Http/Controllers/Api/ApiController.php
routes/api.php

index 02b3f680cf0a3bc1f362313468dfad88ff7401a0,06802808ef5b1757a828d7884ff474c3f9965ba2..3dbe954b8b7693bbeb0ec5c43bbfe948b2814b7f
@@@ -1,6 -1,4 +1,6 @@@
 -<?php namespace BookStack\Api;
 +<?php
 +
 +namespace BookStack\Api;
  
  use Illuminate\Database\Eloquent\Builder;
  use Illuminate\Database\Eloquent\Collection;
@@@ -8,9 -6,11 +8,10 @@@ use Illuminate\Http\Request
  
  class ListingResponseBuilder
  {
 -
      protected $query;
      protected $request;
      protected $fields;
+     protected $hiddenFields;
  
      protected $filterOperators = [
          'eq'   => '=',
          'lt'   => '<',
          'gte'  => '>=',
          'lte'  => '<=',
 -        'like' => 'like'
 +        'like' => 'like',
      ];
  
      /**
       * ListingResponseBuilder constructor.
       */
-     public function __construct(Builder $query, Request $request, array $fields)
+     public function __construct(Builder $query, Request $request, array $fields, array $hiddenFields )
      {
          $this->query = $query;
          $this->request = $request;
          $this->fields = $fields;
+         $this->hiddenFields = $hiddenFields;
      }
  
      /**
  
          $total = $filteredQuery->count();
          $data = $this->fetchData($filteredQuery);
+         $data = $data->makeVisible($this->hiddenFields);
  
          return response()->json([
 -            'data' => $data,
 +            'data'  => $data,
              'total' => $total,
          ]);
      }
@@@ -55,7 -57,6 +58,7 @@@
      {
          $query = $this->countAndOffsetQuery($query);
          $query = $this->sortQuery($query);
 +
          return $query->get($this->fields);
      }
  
          }
  
          $queryOperator = $this->filterOperators[$filterOperator];
 +
          return [$field, $queryOperator, $value];
      }
  
diff --combined app/Auth/UserRepo.php
index ff2e91ee23a8ad8c7b3dfdee003ff741a04db1a4,4444c734c35077a557fcdfe8daf8664b082bfe3d..0dea4172528326eabb7240f3cc327c61a7a6e748
@@@ -1,7 -1,6 +1,7 @@@
 -<?php namespace BookStack\Auth;
 +<?php
 +
 +namespace BookStack\Auth;
  
 -use Activity;
  use BookStack\Entities\EntityProvider;
  use BookStack\Entities\Models\Book;
  use BookStack\Entities\Models\Bookshelf;
@@@ -9,12 -8,14 +9,12 @@@ use BookStack\Entities\Models\Chapter
  use BookStack\Entities\Models\Page;
  use BookStack\Exceptions\NotFoundException;
  use BookStack\Exceptions\UserUpdateException;
 -use BookStack\Uploads\Image;
  use BookStack\Uploads\UserAvatars;
  use Exception;
  use Illuminate\Database\Eloquent\Builder;
  use Illuminate\Database\Eloquent\Collection;
  use Illuminate\Pagination\LengthAwarePaginator;
 -use Images;
 -use Log;
 +use Illuminate\Support\Facades\Log;
  
  class UserRepo
  {
          return User::query()->with('roles', 'avatar')->orderBy('name', 'asc')->get();
      }
  
+     /**
+      * Get all users as Builder for API
+      */
+     public function getUsersBuilder(int $id = null ) : Builder
+     {
+         $query = User::query()->select(['*'])
+             ->withLastActivityAt()
+             ->with(['roles', 'avatar']);
+         return $query;
+     }
      /**
       * Get all the users with their permissions in a paginated format.
 +     * Note: Due to the use of email search this should only be used when
 +     * user is assumed to be trusted. (Admin users).
 +     * Email search can be abused to extract email addresses.
       */
      public function getAllUsersPaginatedAndSorted(int $count, array $sortData): LengthAwarePaginator
      {
          $sort = $sortData['sort'];
  
          $query = User::query()->select(['*'])
 -            ->withLastActivityAt()
 +            ->scopes(['withLastActivityAt'])
              ->with(['roles', 'avatar'])
 +            ->withCount('mfaValues')
              ->orderBy($sort, $sortData['order']);
  
          if ($sortData['search']) {
@@@ -87,7 -94,7 +97,7 @@@
          return $query->paginate($count);
      }
  
 -     /**
 +    /**
       * Creates a new user and attaches a role to them.
       */
      public function registerNew(array $data, bool $emailConfirmed = false): User
  
      /**
       * Assign a user to a system-level role.
 +     *
       * @throws NotFoundException
       */
      public function attachSystemRole(User $user, string $systemRoleName)
  
      /**
       * Set the assigned user roles via an array of role IDs.
 +     *
       * @throws UserUpdateException
       */
      public function setUserRoles(User $user, array $roles)
       * Check if the given user is the last admin and their new roles no longer
       * contains the admin role.
       */
 -    protected function demotingLastAdmin(User $user, array $newRoles) : bool
 +    protected function demotingLastAdmin(User $user, array $newRoles): bool
      {
          if ($this->isOnlyAdmin($user)) {
              $adminRole = Role::getSystemRole('admin');
      public function create(array $data, bool $emailConfirmed = false): User
      {
          $details = [
 -            'name'     => $data['name'],
 -            'email'    => $data['email'],
 -            'password' => bcrypt($data['password']),
 -            'email_confirmed' => $emailConfirmed,
 +            'name'             => $data['name'],
 +            'email'            => $data['email'],
 +            'password'         => bcrypt($data['password']),
 +            'email_confirmed'  => $emailConfirmed,
              'external_auth_id' => $data['external_auth_id'] ?? '',
          ];
  
  
      /**
       * Remove the given user from storage, Delete all related content.
 +     *
       * @throws Exception
       */
      public function destroy(User $user, ?int $newOwnerId = null)
      {
          $user->socialAccounts()->delete();
          $user->apiTokens()->delete();
 +        $user->favourites()->delete();
 +        $user->mfaValues()->delete();
          $user->delete();
 -        
 -        // Delete user profile images
 -        $profileImages = Image::query()->where('type', '=', 'user')
 -            ->where('uploaded_to', '=', $user->id)
 -            ->get();
  
 -        foreach ($profileImages as $image) {
 -            Images::destroy($image);
 -        }
 +        // Delete user profile images
 +        $this->userAvatar->destroyAllForUser($user);
  
          if (!empty($newOwnerId)) {
              $newOwner = User::query()->find($newOwnerId);
       */
      protected function migrateOwnership(User $fromUser, User $toUser)
      {
 -        $entities = (new EntityProvider)->all();
 +        $entities = (new EntityProvider())->all();
          foreach ($entities as $instance) {
              $instance->newQuery()->where('owned_by', '=', $fromUser->id)
                  ->update(['owned_by' => $toUser->id]);
          }
      }
  
 -    /**
 -     * Get the latest activity for a user.
 -     */
 -    public function getActivity(User $user, int $count = 20, int $page = 0): array
 -    {
 -        return Activity::userActivity($user, $count, $page);
 -    }
 -
      /**
       * Get the recently created content for this given user.
       */
      public function getAssetCounts(User $user): array
      {
          $createdBy = ['created_by' => $user->id];
 +
          return [
 -            'pages'    =>  Page::visible()->where($createdBy)->count(),
 -            'chapters'    =>  Chapter::visible()->where($createdBy)->count(),
 -            'books'    =>  Book::visible()->where($createdBy)->count(),
 -            'shelves'    =>  Bookshelf::visible()->where($createdBy)->count(),
 +            'pages'       => Page::visible()->where($createdBy)->count(),
 +            'chapters'    => Chapter::visible()->where($createdBy)->count(),
 +            'books'       => Book::visible()->where($createdBy)->count(),
 +            'shelves'     => Bookshelf::visible()->where($createdBy)->count(),
          ];
      }
  
index 3f049a08c8afaba8523448137624b80dc078a0d1,5eb8b1e3d286f0f82ce23b41362b1dbd557f7507..5d6f4a926c9ce4c5a186106f986f52361ef46583
@@@ -1,6 -1,4 +1,6 @@@
 -<?php namespace BookStack\Http\Controllers\Api;
 +<?php
 +
 +namespace BookStack\Http\Controllers\Api;
  
  use BookStack\Api\ListingResponseBuilder;
  use BookStack\Http\Controllers\Controller;
@@@ -9,29 -7,25 +9,30 @@@ use Illuminate\Http\JsonResponse
  
  abstract class ApiController extends Controller
  {
 -
      protected $rules = [];
+     protected $printHidden = [];
  
      /**
       * Provide a paginated listing JSON response in a standard format
       * taking into account any pagination parameters passed by the user.
       */
-     protected function apiListingResponse(Builder $query, array $fields): JsonResponse
+     protected function apiListingResponse(Builder $query, array $fields, array $protectedFieldsToPrint = []): JsonResponse
      {
-         $listing = new ListingResponseBuilder($query, request(), $fields);
+         $listing = new ListingResponseBuilder($query, request(), $fields, $protectedFieldsToPrint);
 +
          return $listing->toResponse();
      }
  
      /**
       * Get the validation rules for this controller.
 +     * Defaults to a $rules property but can be a rules() method.
       */
      public function getValdationRules(): array
      {
 +        if (method_exists($this, 'rules')) {
 +            return $this->rules();
 +        }
 +
          return $this->rules;
      }
  }
diff --combined routes/api.php
index 7876ba6d45c0a279a67b5ecbdb1f546a3ef1fef3,063fbd72a964889a460f82fa6b8bc37040746b7f..cd8dd355a6f95629dc50abe2ee1e080314252771
@@@ -1,67 -1,49 +1,70 @@@
  <?php
  
 +use BookStack\Http\Controllers\Api\ApiDocsController;
 +use BookStack\Http\Controllers\Api\AttachmentApiController;
 +use BookStack\Http\Controllers\Api\BookApiController;
 +use BookStack\Http\Controllers\Api\BookExportApiController;
 +use BookStack\Http\Controllers\Api\BookshelfApiController;
 +use BookStack\Http\Controllers\Api\ChapterApiController;
 +use BookStack\Http\Controllers\Api\ChapterExportApiController;
 +use BookStack\Http\Controllers\Api\PageApiController;
 +use BookStack\Http\Controllers\Api\PageExportApiController;
 +use BookStack\Http\Controllers\Api\SearchApiController;
 +use Illuminate\Support\Facades\Route;
 +
  /**
   * Routes for the BookStack API.
   * Routes have a uri prefix of /api/.
 - * Controllers are all within app/Http/Controllers/Api
 + * Controllers are all within app/Http/Controllers/Api.
   */
 +Route::get('docs.json', [ApiDocsController::class, 'json']);
 +
 +Route::get('attachments', [AttachmentApiController::class, 'list']);
 +Route::post('attachments', [AttachmentApiController::class, 'create']);
 +Route::get('attachments/{id}', [AttachmentApiController::class, 'read']);
 +Route::put('attachments/{id}', [AttachmentApiController::class, 'update']);
 +Route::delete('attachments/{id}', [AttachmentApiController::class, 'delete']);
 +
 +Route::get('books', [BookApiController::class, 'list']);
 +Route::post('books', [BookApiController::class, 'create']);
 +Route::get('books/{id}', [BookApiController::class, 'read']);
 +Route::put('books/{id}', [BookApiController::class, 'update']);
 +Route::delete('books/{id}', [BookApiController::class, 'delete']);
 +
 +Route::get('books/{id}/export/html', [BookExportApiController::class, 'exportHtml']);
 +Route::get('books/{id}/export/pdf', [BookExportApiController::class, 'exportPdf']);
 +Route::get('books/{id}/export/plaintext', [BookExportApiController::class, 'exportPlainText']);
 +Route::get('books/{id}/export/markdown', [BookExportApiController::class, 'exportMarkdown']);
 +
 +Route::get('chapters', [ChapterApiController::class, 'list']);
 +Route::post('chapters', [ChapterApiController::class, 'create']);
 +Route::get('chapters/{id}', [ChapterApiController::class, 'read']);
 +Route::put('chapters/{id}', [ChapterApiController::class, 'update']);
 +Route::delete('chapters/{id}', [ChapterApiController::class, 'delete']);
 +
 +Route::get('chapters/{id}/export/html', [ChapterExportApiController::class, 'exportHtml']);
 +Route::get('chapters/{id}/export/pdf', [ChapterExportApiController::class, 'exportPdf']);
 +Route::get('chapters/{id}/export/plaintext', [ChapterExportApiController::class, 'exportPlainText']);
 +Route::get('chapters/{id}/export/markdown', [ChapterExportApiController::class, 'exportMarkdown']);
 +
 +Route::get('pages', [PageApiController::class, 'list']);
 +Route::post('pages', [PageApiController::class, 'create']);
 +Route::get('pages/{id}', [PageApiController::class, 'read']);
 +Route::put('pages/{id}', [PageApiController::class, 'update']);
 +Route::delete('pages/{id}', [PageApiController::class, 'delete']);
 +
 +Route::get('pages/{id}/export/html', [PageExportApiController::class, 'exportHtml']);
 +Route::get('pages/{id}/export/pdf', [PageExportApiController::class, 'exportPdf']);
 +Route::get('pages/{id}/export/plaintext', [PageExportApiController::class, 'exportPlainText']);
 +Route::get('pages/{id}/export/markdown', [PageExportApiController::class, 'exportMarkDown']);
 +
 +Route::get('search', [SearchApiController::class, 'all']);
  
 -Route::get('docs', 'ApiDocsController@display');
 -Route::get('docs.json', 'ApiDocsController@json');
 -
 -Route::get('books', 'BookApiController@list');
 -Route::post('books', 'BookApiController@create');
 -Route::get('books/{id}', 'BookApiController@read');
 -Route::put('books/{id}', 'BookApiController@update');
 -Route::delete('books/{id}', 'BookApiController@delete');
 -
 -Route::get('books/{id}/export/html', 'BookExportApiController@exportHtml');
 -Route::get('books/{id}/export/pdf', 'BookExportApiController@exportPdf');
 -Route::get('books/{id}/export/plaintext', 'BookExportApiController@exportPlainText');
 -
 -Route::get('chapters', 'ChapterApiController@list');
 -Route::post('chapters', 'ChapterApiController@create');
 -Route::get('chapters/{id}', 'ChapterApiController@read');
 -Route::put('chapters/{id}', 'ChapterApiController@update');
 -Route::delete('chapters/{id}', 'ChapterApiController@delete');
 -
 -Route::get('chapters/{id}/export/html', 'ChapterExportApiController@exportHtml');
 -Route::get('chapters/{id}/export/pdf', 'ChapterExportApiController@exportPdf');
 -Route::get('chapters/{id}/export/plaintext', 'ChapterExportApiController@exportPlainText');
 -
 -Route::get('pages', 'PageApiController@list');
 -Route::post('pages', 'PageApiController@create');
 -Route::get('pages/{id}', 'PageApiController@read');
 -Route::put('pages/{id}', 'PageApiController@update');
 -Route::delete('pages/{id}', 'PageApiController@delete');
 -
 -Route::get('pages/{id}/export/html', 'PageExportApiController@exportHtml');
 -Route::get('pages/{id}/export/pdf', 'PageExportApiController@exportPdf');
 -Route::get('pages/{id}/export/plaintext', 'PageExportApiController@exportPlainText');
 -
 -Route::get('shelves', 'BookshelfApiController@list');
 -Route::post('shelves', 'BookshelfApiController@create');
 -Route::get('shelves/{id}', 'BookshelfApiController@read');
 -Route::put('shelves/{id}', 'BookshelfApiController@update');
 -Route::delete('shelves/{id}', 'BookshelfApiController@delete');
 +Route::get('shelves', [BookshelfApiController::class, 'list']);
 +Route::post('shelves', [BookshelfApiController::class, 'create']);
 +Route::get('shelves/{id}', [BookshelfApiController::class, 'read']);
 +Route::put('shelves/{id}', [BookshelfApiController::class, 'update']);
 +Route::delete('shelves/{id}', [BookshelfApiController::class, 'delete']);
+ Route::get('users', 'UserApiController@list');
 -Route::get('users/{id}', 'UserApiController@read');
++Route::get('users/{id}', 'UserApiController@read');