Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Do not load PDF viewer if public share has a download limit #654

Open
wants to merge 2 commits into
base: master
Choose a base branch
from

Conversation

danxuliu
Copy link
Member

@danxuliu danxuliu commented Sep 6, 2022

The PDF viewer needs to download the file in order to render it in the client. Due to this, if a public share has a download limit, loading the PDF viewer automatically reduces the number of available downloads by one, even if the user does not download the file manually. To prevent that now the PDF viewer is not loaded in public shares if the share has a download limit.

In order to accommodate that change, but also to prevent loading the PDF viewer when not needed, the PDF viewer is now loaded only in public share pages instead of in all public pages (which also matches with the existing JavaScript behaviour, as it was initialized only in public shares for PDF files and ignored in the other public pages).

How to test

  • Enable the files_downloadlimit app
  • Open the Files app
  • Create a public share for a PDF file
  • Set a download limit to that share
  • Close the menu for the share
  • Copy the link to the public share
  • In a private window, open the link
  • In the original window, open again the menu for the share

Result with this pull request

The number of remaining downloads has not changed; the PDF viewer was not loaded in the public share page.

Result without this pull request

The number of remaining downloads was reduced by one, because the PDF viewer was loaded in the public share page and therefore the file was implicitly downloaded.

@danxuliu
Copy link
Member Author

danxuliu commented Sep 6, 2022

/backport to stable24

@danxuliu
Copy link
Member Author

danxuliu commented Sep 6, 2022

/backport to stable23

// If the event has a scope it is not the default share page but, for
// example, the authentication page
if ($event->getScope() !== null) {
return;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Are we sure no other area uses the Template without a scope?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In server OCA\Files_Sharing\Event\BeforeTemplateRenderedEvent is emitted only by the ShareController.

Is it emitted by any other app? I do not know, but in that case it would be the same as before, the JavaScript code would check if it is a public share page and if it is not the PDF viewer will not be loaded.

Comment on lines +66 to +79
private function getDownloadLimit(string $shareToken): int {
try {
$limitMapper = $this->serverContainer->get('\OCA\Files_DownloadLimit\Db\LimitMapper');
} catch (QueryException $e) {
return -1;
}

try {
$shareLimit = $limitMapper->get($shareToken);
} catch (\Exception $e) {
return -1;
}

return $shareLimit->getLimit();
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't like this, this is an anti pattern. Using another app as a dependency is a no-go from me.
If there is a limit, I would make the Files_DownloadLimit handle the event and do its changes though there ?

How do we manage for images/video for example?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't like this, this is an anti pattern. Using another app as a dependency is a no-go from me.
If there is a limit, I would make the Files_DownloadLimit handle the event and do its changes though there ?

Sorry, I did not understand that. How would you prevent the PDF viewer from being loaded by handling the event in the files_downloadlimit app? And wouldn't that make the files_Ddwnloadlimit app also depend on another app?

How do we manage for images/video for example?

Apparently we do not 🤷 I have just checked and the same issue happens with images and videos. In fact, with videos is even worse, because they are downloaded in several chunks, so just opening the public share page of a video reduces the download limit several times.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

maybe we need a new event:

we also have one for when a Zip file is getting created

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The event sounds like a good approach 👍

However, please note that the reason to implement the check directly in the PDF viewer without touching anything else was to be able to easily backport the fix to previous versions.

If I am not mistaken the APIs are not allowed to be changed (not even extended) in maintenance versions. I guess it would be possible to make an exception and allow adding a new event class for this. But if an event class is added only from certain maintenance version, how to handle that in the apps? Just by explicitly checking if the class exists before using it? And could anything else break due to that new event?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

However, please note that the reason to implement the check directly in the PDF viewer without touching anything else was to be able to easily backport the fix to previous versions.

@skjnldsv since you objected to cross-app dependency, are you ok with the approach for backportability or have suggestions for further tweaks ?

@danxuliu I think we can go "clean approach" for Nextcloud 26 with the new event on master and keep your code here for Nextcloud <= 25

The PDF viewer is initialized only in public share pages for PDF files,
but it was loaded in all public pages (for example, in the public
profile page). To reduce the number of unneeded loads now it is loaded
only in public share pages instead.

Signed-off-by: Daniel Calviño Sánchez <danxuliu@gmail.com>
The PDF viewer needs to download the file in order to render it in the
client. Due to this, if a public share has a download limit, loading the
PDF viewer automatically reduces the number of available downloads by
one, even if the user does not download the file manually. To prevent
that now the PDF viewer is not loaded in public shares if the share has
a download limit.

Signed-off-by: Daniel Calviño Sánchez <danxuliu@gmail.com>
@danxuliu danxuliu force-pushed the do-not-load-pdf-viewer-if-public-share-has-a-download-limit branch from 7b062d7 to 77f4c0c Compare September 7, 2022 00:58
@danxuliu
Copy link
Member Author

danxuliu commented Sep 7, 2022

I have force pushed to remove a no longer needed import in the first commit. I did not add that as a fixup as it would conflict with the second commit.

@szaimen
Copy link
Collaborator

szaimen commented Oct 14, 2022

What is missing here?

@PVince81
Copy link
Member

did we explore more potential solutions for solving this and their pros and cons ?

some further potential ideas:

  1. have the download limit app detect a "legit" download from an editor and not count as download, maybe using additional headers ?
  2. have the download limit app hook into the FileActions JS object and filter out any viewer app registered on the "Open" action

@PVince81
Copy link
Member

PVince81 commented Nov 22, 2022

the header could state the app name that is downloading the file, ex: "X-NC-Viewer: files_pdfviewer", might be useful for other things also like debugging or stats in the future

the download limit app could simply check if "X-NC-Viewer" is set regardless of value

the cool thing is that it still protects against people linking to files from another place like forums or so, because the link doesn't contain headers

@PVince81
Copy link
Member

seems the header approach won't work as the pdf viewer is rendered in an iframe, so there's no way to inject headers :-/

@PVince81
Copy link
Member

seems there's a way: mozilla/pdf.js#11792 (comment)

@CarlSchwan
Copy link
Member

Doesn't looks like we are using PDFViewerApplication.open to open a pdf document and I couldn't found where else we open the specified file. Any hints?

@danxuliu
Copy link
Member Author

Doesn't looks like we are using PDFViewerApplication.open to open a pdf document and I couldn't found where else we open the specified file. Any hints?

I guess that PDFViewerApplication.run is called instead when the page finishes loading after being imported by the template. Once the viewer is initialized by PDFViewerApplication.run the file is loaded from the file parameter in the URL, which is set in the iframe src.

But please note that I have not actually verified it, it is just my guess from the code :-)

@susnux
Copy link
Contributor

susnux commented Aug 22, 2024

Is this still something we should consider? There is not much activity for the last ~1.5 years.

@blizzz blizzz modified the milestones: Nextcloud 30, Nextcloud 31 Sep 4, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

8 participants