Laravel viewing picture in local disk - php

I am making like a dropbox clone for a school project, i made an upload fuction that uploads to the local disk so it cant be accessed by everyone.
Upload Function
public function updatedUpload($upload){
$object = $this->currentTeam->objects()->make(['parent_id' => $this->object->id]);
$object->objectable()->associate(
$this->currentTeam->files()->create([
'name' => $upload->getClientOriginalName(),
'size' => $upload->getSize(),
'path' => $upload->storePublicly('files', ['disk' => 'local'])
])
);
$object->save();
$this->object = $this->object->fresh();
}
But I want to put it in an in the home.blade.
How do I make this and only the person who uploaded it can access it?

You can create a specific link for the user that can see this resource.
For example in your routes/web.php
Route::get('/private_resource',[\App\Http\Controllers\PrivateResource::class, 'getLocalResource'])
->name('get_private_resource')->middleware('auth');
Define PrivateResource controller
class PrivateResource
{
public function getLocalResource($filename){
// Get your resource, for example
$file = File::where('name', $filename)->first();
// Check whether user can access this resource
// For example
if(\Auth::id() != $file->user_id) abort(403);
return \Storage::disk('local')->download($filename);
}
}
Extra: If you want extra privacy, you can use signed route using below function.
\URL::signedRoute('get_private_resource');
And add signed middleware to your route
Route::get('/private_resource',[\App\Http\Controllers\PrivateResource::class, 'getLocalResource'])
->name('get_private_resource')->middleware('auth','signed');

Related

PHPUnit how to write a Laravel Nova Observer test

I would like to write a test for my CommentObserver. This observer is only registered in the NovaServiceProvider but not the AppServiceProvider. This means I cannot test my observer by using my own Controllers.
In my eyes I have 3 ways to test my observer:
Either performing a feature test by sending a post request to the Nova API
Mocking the observer by calling the function in the observer to check if the function perfoms as desired
Trying to register my observer on the fly in the AppServiceProvider, performing a request and deregistering the observer in the AppServiceProvider again.
I tried to find a solution for any of these 3 ways to test my observer but unfortunately I faild with any of them.
Problems:
For way 1 I always get a validation error and Nova tells me that my input is invalid.
For way 2 I fail at mocking the observer function
For way 3 I didn't find any solution on how to register and deregister the oberserver on the fly at the AppServiceProvider
Do you guys have idea and solition on how I can test my CommentObserver (which is as written above only registered in my NovaServiceProvider).
Update:
So, here is the code of my observer. I need to have an valid request to test my observer in order to have the ability to access the $request->input('images') variable. I do know I can also use $comment->content instead of request()->input('content') because $comment->content already contains the new content which is not saved it this point.
The reason why I need a valid request is that the variable images is not part of the Comment model. So I cannot use $comment->images because it simply doesn't exist. That's why I need to access the request input. What my observer is basically doing is to extract the base64 images from the content, saves them to the server and replaces them by an image link.
class CommentObserver
{
public function updating(Comment $comment)
{
if (!request()->input('content')) {
return;
}
if (request()->input('content') == $comment->getRawOriginal('content')) {
return;
}
$images = request()->input('images');
if(!is_array($images)) {
$images = json_decode(request()->input('images'));
}
checkExistingImagesAndDeleteWhenNotFound($comment, request()->input('content'), 'comments', 'medium');
$comment->content = addBase64ImagesToModelFromContent($comment, request()->input('content'), $images, 'comments', 'medium');
}
}
This is my test so far. I choose way 1 but as described already this always leads to an validation error by the nova controller and I cannot figure out what is the error/what is missing or wrong.
class CommentObserverTest extends TestCase
{
/** #test */
public function it_test()
{
$user = User::factory()->create([
'role_id' => Role::getIdByName('admin')
]);
$product = Product::factory()->create();
$comment = Comment::factory()->create(['user_id' => $user->id, 'content' => '<p>Das ist wirklich ein super Preis!</p>', 'commentable_type' => 'App\Models\Product', 'commentable_id' => $product->id]);
$data = [
'content' => '<p>Das ist wirklich ein HAMMER Preis!</p>',
'contentDraftId' => '278350e2-1b6b-4009-b4a5-05b92aedaae6',
'pageStatus' => PageStatus::getIdByStatus('publish'),
'pageStatus_trashed' => false,
'commentable' => $product->id,
'commentable_type' => 'App\Models\Product',
'commentable_trashed' => false,
'user' => $user->id,
'user_trashed' => false,
'_method' => 'PUT',
'_retrieved_at' => now()
];
$this->actingAs($user);
$response = $this->put('http://nova.mywebsiteproject.test/nova-api/comments/' . $comment->id, $data);
dd($response->decodeResponseJson());
$das = new CommentObserver();
}
}
Kind regards and thank you
Why depend on the boot method in your NovaServiceProvider? It is possible to call the observe() method on the fly in your test:
class ExampleTest extends TestCase
{
/** #test */
public function observe_test()
{
Model::observe(ModelObserver::class);
// If you need the request helper, you can add input like so:
request()->merge([
'content' => 'test'
]);
// Fire model event by updating model
$model->update([
'someField' => 'someValue',
]);
// Updating should be triggered in ModelObserver
}
}
It should be now be possible in your observer class:
public function updating(Model $model)
{
dd(request()->input('content')); // returns 'test'
}

Laravel URL facade temporarySignedRoute to external url

I'm using Laravel 8 to generate a temporary signed route and pass some params, but I'd like my URL to take me to some URL of my choosing rather than a page in my project.
For context, my Laravel 8 project is an API, so there are no views, my API is then consumed by a front-end project written in Nuxt.
I've tried adding my URL to the first arg of temporarySignedRoute but it says that my route isn't found.
$verifyURL = URL::temporarySignedRoute(
'https://example.com/account/verify', Carbon::now()->addHours(24), ['contact' => 5, 'team' => 'john']
);
What am I missing or what workaround is there here?
UPDATE
So it turns out that I don't need to take the user to an external URL, but it seems that the wrong URL is being generated by URL::temporarySignedRoute.
The start of my generated URL is (for example) https://example.com/api/contact/verify and I need the URL to be https://api.example.com/api/contact/verify
So the same domain, except a sub-domain.
It looks like the APP_URL isn't being read because I changed it and it has no impact, and besides, this is used elsewhere, so I tried updating the URL with:
$verifyURL = URL::temporarySignedRoute(
'contact.verify', Carbon::now()->addHours(24), ['contact' => 5, 'team' => 'john]
);
// fix for wrong URL
$verifyURL = str_replace('example.com', 'api.example.com', $verifyURL);
However, this appears to have an invalid signature when the link provided by $verifyURL is clicked? How can I get the api part at the beginning?
URL::temporarySignedRoute() has a fourth parameter called $absolute which is a boolean. So if you want to prepend a custom url rather than the default url used by Laravel, this is the variable to change.
The default value for $absolute is true. Therefore in order to prepend your own custom url, be sure to change it to false first as below:
$verifyURL = URL::temporarySignedRoute(
'contact.verify', Carbon::now()->addHours(24), ['contact' => 5, 'team' => 'john], false // The $absolute value
);
Then concatenate your custom url:
$your_custom_url . $verifyURL;
First step, in the AppServiceProvider boot method, you need to register custom URL signing:
public function boot()
{
// This allows us to generate a temporary url for report downloading
Storage::disk('reports')->buildTemporaryUrlsUsing(function ($path, $expiration, $options) {
return URL::temporarySignedRoute(
'report.download',
$expiration,
array_merge($options, ['path' => $path])
);
});
}
To create the URL:
Storage::disk('reports')->temporaryUrl($pathname, Carbon::now()->addSeconds(10))
To download the file, this is my controller:
<?php
declare(strict_types=1);
namespace App\Http\Controllers;
use Illuminate\Http\Request;
use Storage;
use URL;
class ReportDownloadController extends Controller
{
public function download(Request $request)
{
if (!URL::signatureHasNotExpired($request)) {
return response('The URL has expired.');
}
if (!URL::hasCorrectSignature($request)) {
return response('Invalid URL provided');
}
return Storage::disk('reports')->download($request->get('path'));
}
}

Laravel - How to stop printing URL and load page content?

Today I face a strange problem (as I face this first time so it is a strange problem for me). After saving the content of a model I just write the following line of code return route('organization'); so that it will redirect to the naming route organization after saving the content.
Once the content of the organization model saves it just print the URL of the page http//xyz.laravel/organization rather than printing the content of the page itself!
When I manually type and hit the dashboard URL it surprisingly prints the dashboard URL rather than loading the dashboard content! like the below image:
Everything was working fine before I tried to store the content of that model. Once the content is stored the application starts strange behavior. Here is the code of that model:
public function store(Request $request)
{
$validated = $request->validate([
'organization_name' => 'required|unique:organizations|max:255',
'abn_number' => 'required',
'address_one' => 'required|max:100',
'state' => 'required',
'post_code' => 'required'
]);
// check organization exist or not
$org = Organization::where('organization_name', $request->organization_name)->get();
if( count( $org ) > 0 ) {
//
} else {
$organization = new Organization();
$organization->organization_name = $request->organization_name;
$organization->abn_number = $request->abn_number;
$organization->address_one = $request->address_one;
$organization->address_two = $request->address_two;
$organization->state = $request->state;
$organization->post_code = $request->post_code;
$organization->created_by = Auth::user()->id;
$organization->created_at = Carbon::now();
$organization->save();
return route('organization');
}
}
Can anyone tell me what's actually happen and how can I fix this issue?
return route('organization'); will generate the URL link to the route and print it
You can use
return redirect()->route('organization);
You can get more info from https://laravel.com/docs/8.x/redirects
This is because you are not redirecting to that route but you are returning route url as a string, to redirect a user to a named route you can use global redirect() helper as below
return redirect()->route('organization'); instead of return route('organization');
for more see
documentation

Owncloud + Hooks

I am trying to add some Hooks to my OwnCloud app called Metadata, and i can't seem to figure it out (the hook is not being fired).
I tried following the content https://doc.owncloud.org/server/8.2/developer_manual/app/init.html and https://doc.owncloud.org/server/8.2/developer_manual/app/hooks.html (although it seems like the second one is outdated).
Basically all i am trying to do for now is the catch the pre-rename hook and write something to a file.
My code is :
myapp/appinfo/app.php
namespace OCA\Metadata\AppInfo;
use OCP\AppFramework\App;
$app = new App('metadata');
$container = $app->getContainer();
$container->query('OCP\INavigationManager')->add(function () use ($container) {
$urlGenerator = $container->query('OCP\IURLGenerator');
$l10n = $container->query('OCP\IL10N');
return [
// the string under which your app will be referenced in owncloud
'id' => 'metadata',
// sorting weight for the navigation. The higher the number, the higher
// will it be listed in the navigation
'order' => 10,
// the route that will be shown on startup
'href' => $urlGenerator->linkToRoute('metadata.page.index'),
// the icon that will be shown in the navigation
// this file needs to exist in img/
'icon' => $urlGenerator->imagePath('metadata', 'app.svg'),
// the title of your application. This will be used in the
// navigation or on the settings page of your app
'name' => $l10n->t('Metadata'),
];
});
\OCP\Util::connectHook('OC_Filesystem', 'post_rename', 'OC\Metadata\Hooks', 'postRename');
and then myapp/hooks.php
<?php
namespace OCA\Metadata;
use OC\Files\Filesystem;
use OC\Files\View;
class Hooks {
// private $userManager;
public static function postRename($params) {
file_put_contents("/var/www/data/owncloud_print2.log", "post_rename");
}
}
nothing ever gets written to the file. i have also tried other approaches all with no luck. anyone knows what i am doing wrong??
my connecthook was wrong. it should be:
\OCP\Util::connectHook('OC_Filesystem', 'post_rename', 'OCA\Metadata\Hooks', 'postRename');

Yii: How to make Dropzone Extension work?

I'm trying to add Dropzone Extension to my application in Yii, which allows asynchronous file uploading. http://www.yiiframework.com/extension/yii-dropzone/
The first thing i did was putting the downloaded folder called "dropzone" into my extensions folder "C:\xampp\htdocs\site\protected\extensions".
And here is my code for the action in the controller (MainController.php)
public function actionUpload()
{
$test = rand(100000, 999999); //TEST
var_dump($test);
$model = new UploadFile;
if(isset($_FILES['images'])){
$model->images = CUploadedFile::getInstancesByName('images');
$path = Yii::getPathOfAlias('webroot').'/uploads/';
//Save the images
foreach($model->images as $image)
{
$image->saveAs($path);
}
}
$this->render('upload', array('model' => $model));
}
the view (upload.php)
<?php
$this->widget('ext.dropzone.EDropzone', array(
'model' => $model,
'attribute' => 'images',
'url' => $this->createUrl('file/upload'),
'mimeTypes' => array('image/jpeg', 'image/png'),
'options' => array(),
));
?>
and the model (UploadFile.php)
<?php
class UploadFile extends CFormModel
{
public $images;
public function rules(){
return array
(
array(
"images",
'file',
'types' => 'jpg,gif,png',
),
);
}
}
When I run it I can see the Dropzone interface and I can add images dragging them or selection them from the file explorer.
It appears their respective progress bar and a success mark, but nothing appear in the directory of uploads, and any error is shown neither in the IDE (Netbeans) nor in the Chrome console.
I did some print tests and I realize that the code inside the 'actionUpload' is being executed only the first time (when it draws the view), but when its called from the dropzone widget it do nothing.
I'd really appreciate if you have a solution for this. I'd love if someone could give me a simple working example of this extension. Thanks.
As I understand, dropzone uploads files one by one, not all together. So $model->images holds only one image object. And foreach cycle fails.

Categories