Compare commits

...

146 Commits

Author SHA1 Message Date
C Montero-Luque 304cf28f4a 8.2.1 RC1 2015-11-03 17:33:55 -05:00
Morris Jobke 6073704ace Merge pull request #20241 from owncloud/backport-20191-stable8.2
[backport] [stable8.2] add method to count users by loginname and use it for diagnose test, …
2015-11-03 08:45:29 +01:00
Vincent Petry bc1c247a13 Merge pull request #20240 from owncloud/backport-ldap-tooltip
Backport of "Add tooltip to ldap server wizard"
2015-11-03 07:54:02 +01:00
Hendrik Leppelsack 3a5ba033d5 add tooltip to ldap server wizard 2015-11-02 21:31:43 +01:00
Arthur Schiwon 6fd0734944 add method to count users by loginname and use it for diagnose test, fixes #20151 2015-11-02 21:26:00 +01:00
Raghu Nayyar ee05b1b6d6 Merge pull request #20187 from owncloud/stable8.2-login-arrow
Stable8.2: remove unneeded icons from input fields to not distract from submit button, fix #18940
2015-11-02 17:27:31 +05:30
Jan-Christoph Borchardt af7616743e remove unneeded icons from input fields to not distract from submit button, fix #18940 2015-10-30 16:43:47 +01:00
Thomas Müller 0cd61b01fd Merge pull request #20158 from owncloud/stable8.2-files-consolidateiconupdate
[stable8.2] Fix icon update to be more consistent
2015-10-29 23:49:27 +01:00
Vincent Petry 1c370506e4 Fix icon update to be more consistent
Makes the details bar show the same icon as in the list.
2015-10-29 16:49:52 +01:00
Morris Jobke b61bb83680 Merge pull request #20154 from owncloud/backport-19871
[8.2] Backport 19871
2015-10-29 15:36:54 +01:00
Joas Schilling 01816a2ba2 Use the method 2015-10-29 15:13:36 +01:00
Joas Schilling 3f3bcbeb47 Add a repair step that checks for group membership on shares 2015-10-29 15:13:25 +01:00
Joas Schilling b71cf0b903 Remove shares where the parent does not exist anymore 2015-10-29 15:13:11 +01:00
Morris Jobke 07257762e2 Merge pull request #20138 from owncloud/LukasReschke-patch-3
[stable8.2] Update certificate bundle
2015-10-29 14:27:14 +01:00
Thomas Müller 5a177b42df Merge pull request #19991 from owncloud/stable8.2_3rdparty_sabre_2.1.7
[stable8.2] 3rdparty bump for full upgrade of sabre/dav-2.1.7
2015-10-29 10:43:37 +01:00
Lukas Reschke 24be4e5add [stable8.2] Update certificate bundle
Backport of #20126
2015-10-29 10:37:53 +01:00
Roeland Jago Douma efe8384f5c 3rdparty bump for full upgrade of sabre/dav-2.1.7 2015-10-29 09:26:15 +01:00
Thomas Müller 044db63190 Merge pull request #20104 from owncloud/stable8.2-backport-20097
[stable8.2] Revert "setting to skip migration tests by default"
2015-10-28 12:43:41 +01:00
Vincent Petry 5fc440b98a Merge pull request #20101 from owncloud/stable8.2-ie9-share-donotsendplaceholderaspassword
[stable8.2] [IE9] Don't send link share password placeholder
2015-10-28 12:41:19 +01:00
Morris Jobke 99b26d6916 Revert "setting to skip migration tests by default"
This reverts commit 7cbdd9b90b.
2015-10-28 09:51:17 +01:00
Vincent Petry ee1e7d9726 [IE9] Don't send link share password placeholder
When exiting the password field in the share dialog, IE9 would
mistakenly think that the password has changed and would send the
placeholder.

This fix prevents changing the password whenever the placeholder is set
as value.
2015-10-28 09:03:58 +01:00
Morris Jobke daf14861e4 Merge pull request #20091 from owncloud/backport-tipsy-body
Backport: append tipsys to body
2015-10-27 17:53:29 +01:00
Hendrik Leppelsack cbcdcda436 append tipsys to body 2015-10-27 16:49:52 +01:00
Joas Schilling 4e9e85816d Merge pull request #20077 from owncloud/stable8.2-20076
[Stable8.2] Verify the src exists in webdav MOVE
2015-10-27 13:28:22 +01:00
Roeland Jago Douma 997c307251 Verify the src exists in webdav MOVE
* Unit test added
2015-10-27 10:30:36 +01:00
Joas Schilling e627c7942e Merge pull request #20072 from owncloud/backport-apps-donotdisplaybrokenappiconininternetexplorer-stable8.2
Disable app icon preview in apps page for IE
2015-10-27 08:39:44 +01:00
Vincent Petry 54ad29c3ed Disable app icon preview in apps page for IE
All IE versions are not able to properly upscale SVG icons unless the
said SVG files contain a "viewBox" attribute, which is not always the
case. Also we cannot guarantee that all third party apps will have this
attribute in their icons.

So for now, app icons will not be displayed in IE instead of broken
ones.
2015-10-26 23:25:47 +01:00
Thomas Müller 7573ba9a4d Merge pull request #19976 from owncloud/db-keep-shared-locks-82
[8.2] Keep shared locks until the end of the request so we can reuse them
2015-10-26 17:14:15 +01:00
Thomas Müller 0dc31ee2fe Merge pull request #20052 from owncloud/stable8.2-share-clicklinkfocus
[stable8.2] Fix share link focus on click
2015-10-26 17:13:44 +01:00
Thomas Müller 24eb04f724 Merge pull request #20041 from owncloud/backport-fix-link-sharing-regression-master-stable8.2
Backport fix link sharing regression master stable8.2
2015-10-26 15:22:05 +01:00
Vincent Petry e1b59ad7f0 Fix share link focus on click
Clicking on the link share must focus and select it
2015-10-26 15:14:27 +01:00
Lukas Reschke 73750f0c1e Merge pull request #20048 from owncloud/dont-lock-user-files-82
[8.2] Dont lock /$user/files
2015-10-26 14:31:52 +01:00
Robin Appelman ad95e308ba Dont lock /$user/files 2015-10-26 13:50:47 +01:00
Morris Jobke 937a1b2b93 Merge pull request #20042 from owncloud/stable8.2-ext-dropbox-removecacheafterupload
[stable8.2] Remove Dropbox metadata from cache after upload
2015-10-26 13:50:10 +01:00
Vincent Petry 019e72040a Remove Dropbox metadata from cache after upload
This will make sure that the next calls that read the mtime will get the
correct value.
2015-10-26 11:55:39 +01:00
Roeland Jago Douma 6d0e167c4d Fix for broken ajax/share.php endpoint
Even more code mess :(
All tests pass again. But I'm really not happy with this endpoint.
2015-10-26 11:54:55 +01:00
Thomas Müller 2a0460d885 Ensure the password is only hashed in case it's changed on the client - fixes #19950 2015-10-26 11:54:48 +01:00
Morris Jobke 7d2bb6c028 Merge pull request #20036 from owncloud/stable8.2-backport-19957
Stable8.2 backport 19957
2015-10-26 10:59:22 +01:00
Steffen Lindner dbec295647 Add syslog_tag docu to sample config 2015-10-26 10:01:44 +01:00
Volker Fröhlich a2be470dce Expose syslog tag in the configuration 2015-10-26 10:01:31 +01:00
Lukas Reschke 0aca631216 Merge pull request #19997 from owncloud/backport-fix-deleted-ldap-user-sharing-stable8.2
[8.2] handle NoUserException in sharing code
2015-10-23 18:14:33 +02:00
Thomas Müller d935bdfd6a Merge pull request #20001 from owncloud/make-getCurrentUser-behave-similar-to-auth
[stable8.2] Make get current user behave similar to auth
2015-10-23 14:38:13 +02:00
Joas Schilling 25ef9909f0 Merge pull request #19999 from owncloud/backport-19970
[8.2] Fix "Call to a member function getUID() on boolean" in Crypt
2015-10-23 14:22:07 +02:00
Lukas Reschke e1642400f5 Make getCurrentUser behaviour similar to auth
If DAV_AUTHENTICATED is a null value this is a valid web session and we should return the user name as we also do in `auth`.
2015-10-23 13:39:12 +02:00
Joas Schilling 2b4ac38a08 Fix "Call to a member function getUID() on boolean" in Crypt 2015-10-23 13:01:57 +02:00
Morris Jobke eb12cac973 handle NoUserException in sharing code
* setup LDAP users
* share from an LDAP user
* delete that LDAP user
* log in as share recipient
* before: unhandled NoUserException
* after: NoUserEception is logged and share is not shown anymore
2015-10-23 12:20:11 +02:00
Robin Appelman 86b822dd52 Keep shared locks until the end of the request so we can reuse them 2015-10-22 14:50:43 +02:00
Lukas Reschke 79932c6d98 Merge pull request #19942 from owncloud/backport-19918
[8.2] Update: state which step we are going to start and warn if it might b…
2015-10-22 00:13:23 +02:00
Lukas Reschke 6209b40647 Merge pull request #19955 from owncloud/backport-19954
[stable8.2] Use IRequest's `getScriptName` functionality instead of $_SERVER
2015-10-21 20:44:14 +02:00
Lukas Reschke 8f04555e22 Merge pull request #19956 from owncloud/stable8.2-fix-webui-upload-and-conflicts
[stable8.2] Fix file upload, conflict dialog, also in public link
2015-10-21 20:19:37 +02:00
Vincent Petry d3d18b1abc Fix file upload, conflict dialog, also in public link
- Use "FileList" instead of "OCA.Files.App.fileList" that doesn't exist in public
link page.
- Fix public link upload by properly adding the form data using a new
  utility function "addFormData". That one is needed because IE8 upload
  and regular upload use a different format...
2015-10-21 18:19:55 +02:00
Lukas Reschke 4035bcf26a Use IRequest's getScriptName functionality instead of $_SERVER['SCRIPT_NAME'] 2015-10-21 17:41:49 +02:00
Lukas Reschke 74e54da304 Remove dependency on ICrypto + use XOR 2015-10-21 17:41:43 +02:00
Joas Schilling 70be8a309f Update: state which step we are going to start and warn if it might be slow 2015-10-21 14:51:14 +02:00
Lukas Reschke 74a33b0e56 Merge pull request #19939 from owncloud/stable8.2-detailsview-donotopenonrename
[stable8.2] Do not display details bar after rename
2015-10-21 14:27:26 +02:00
Vincent Petry ea6917d18a Do not display details bar after rename 2015-10-21 14:16:07 +02:00
Thomas Müller a399905aff Merge pull request #19913 from owncloud/fix-memcached-warning-stable8.2
[stable8.2] Fix memcached/memcache module check
2015-10-21 10:33:47 +02:00
Vincent Petry aba6b938f9 Merge pull request #19896 from owncloud/backport-19895
Backport #19895
2015-10-21 10:19:54 +02:00
Thomas Müller 84e69f4931 Merge pull request #19882 from owncloud/backport-ceph-stop-script-changes
Backport objectstore fixes to 8.2
2015-10-21 09:50:58 +02:00
Robin McCorkell ab87fc7d3a Fix memcached/memcache module check 2015-10-20 22:57:33 +01:00
Hendrik Leppelsack 234d9d8a58 fix pagination on public file list 2015-10-20 16:14:15 +02:00
Morris Jobke c2f5fd44c5 Merge pull request #19893 from owncloud/locking_exception_8.2
[stable8.2] catch all exception if table doesn't exists
2015-10-20 15:56:06 +02:00
Morris Jobke 06036df9f7 Merge pull request #19777 from owncloud/backport-19764
Variables don't have a class, so we can't use toString() on it
2015-10-20 15:54:22 +02:00
Morris Jobke 462fc21b06 Merge pull request #19765 from owncloud/backport-19580
[8.2.1] Make sure that remote shares use the correct uid casing
2015-10-20 15:52:47 +02:00
Morris Jobke 7a2ef950b3 Merge pull request #19876 from owncloud/backport-19820-stable8.2
[stable8.2] When sharing with the owner show the path
2015-10-20 15:51:56 +02:00
Morris Jobke a4d9d9f845 Merge pull request #19766 from owncloud/stable8.2-proppatch-lastmodified
[stable8.2] Fix mtime PROPPATCH to be "lastmodified" instead of "getlastmodified"
2015-10-20 15:50:30 +02:00
Morris Jobke 7164178cd3 Merge pull request #19844 from owncloud/stable8.2-share-fix-spinner-position
[stable8.2] Fix spinner positions in share tab
2015-10-20 15:49:28 +02:00
Morris Jobke 0f3adb856f Merge pull request #19819 from owncloud/stable8.2-filelist-preventerrorwhennosidebar
[stable8.2] Do not register sidebar panels when no sidebar
2015-10-20 15:48:52 +02:00
Morris Jobke a2b5a2f109 Merge pull request #19782 from owncloud/stable8.2-passwordchange-hidestrengthify-2
[stable8.2] [resend] Properly hide strengthify after password change
2015-10-20 15:48:16 +02:00
Morris Jobke f3d58eebfc Merge pull request #19780 from owncloud/stable8.2-files-delete-hidenotif
[stable8.2] Hide notification on delete
2015-10-20 15:47:30 +02:00
Morris Jobke 0ea4274c8d Merge pull request #19779 from owncloud/stable8.2-trash-deletesectionposition-2
[stable8.2] Move trashbin specific CSS that modifies sidebar
2015-10-20 15:45:26 +02:00
Morris Jobke b0cecfb92a Merge pull request #19772 from owncloud/stable8.2-sidebar-alt-favorite
[stable8.2] Move alt text for favorite action to image
2015-10-20 15:44:54 +02:00
Morris Jobke cddf111dbb Merge pull request #19778 from owncloud/stable8.2-files-canceluploaddebounce-re
[stable8.2] Debounce cancel upload message
2015-10-20 15:43:06 +02:00
Morris Jobke 1cf1119b0b Merge pull request #19774 from owncloud/stable8.2-notif-showtemporary
[stable8.2] showTemporary instead of show for notifications
2015-10-20 15:42:27 +02:00
Morris Jobke 394751a985 Merge pull request #19775 from owncloud/stable8.2-trashbin-removesidebar
[stable8.2] Remove sidebar for trashbin view
2015-10-20 15:37:24 +02:00
Thomas Müller 26d98e9772 Merge pull request #19800 from owncloud/backport-19731
[8.2] Update list of deprecated methods
2015-10-20 15:35:17 +02:00
Thomas Müller 7ddbaf649f Merge pull request #19835 from owncloud/stable8.2-share-checkboxuniqueids
[stable8.2] Fix DOM element ids in share dialog
2015-10-20 15:34:54 +02:00
Morris Jobke 9c5dcb4cbf Merge pull request #19771 from owncloud/stable8.2-share-emailsenterror
[stable8.2] Fix share email sending feedback
2015-10-20 15:33:48 +02:00
Morris Jobke dbf5c266c7 Merge pull request #19769 from owncloud/stable8.2-users-deletebeforecreate
[stable8.2] Delete last undoable user before user creation
2015-10-20 15:27:05 +02:00
Morris Jobke 7010f42986 Merge pull request #19770 from owncloud/stable8.2-fix-ie-pain
[stable8.2] Fix more IE issues
2015-10-20 15:21:10 +02:00
Björn Schießle 530f313dff catch all exception if table doesn't exists 2015-10-20 14:57:40 +02:00
Thomas Müller f4e68ca36a Fix termination of the ceph docker 2015-10-20 09:52:05 +02:00
Thomas Müller 4408507fc7 Fix termination of the ceph docker 2015-10-20 09:52:05 +02:00
Robin Appelman 68a92f8186 fix encryption migration test 2015-10-20 09:51:27 +02:00
Robin Appelman 541474d5ec Fix listing of trash files in test 2015-10-20 09:51:27 +02:00
Robin Appelman cf84a1128d Fix trashbin handling of unknown/unlimited free space 2015-10-20 09:51:27 +02:00
Robin Appelman 490101a3a1 Fix rename shared versions test 2015-10-20 09:51:27 +02:00
Robin Appelman 7cbc348780 handle versions expire for home storages with unlimited quota 2015-10-20 09:51:27 +02:00
Robin Appelman 331750a3b7 fix delete orphan shares test with object home storage 2015-10-20 09:51:27 +02:00
Robin Appelman ca811d3079 Fix preserving file ids when restoring a file with object storage 2015-10-20 09:51:27 +02:00
Robin Appelman 37efd1d001 fix objectstore files having create permissions 2015-10-20 09:50:24 +02:00
Robin Appelman 4dd5efd464 dont assume home storage is local in trash test 2015-10-20 09:50:06 +02:00
Robin Appelman 9ca706b046 Make shared folder size propagation test work with object home storage 2015-10-20 09:49:59 +02:00
Robin Appelman 970a9c2382 detect object homestorage in share code 2015-10-20 09:49:52 +02:00
Robin Appelman be9df999fc Fix shared storage tests for non local home storage 2015-10-20 09:49:44 +02:00
Robin Appelman 96feb6f05b fix sabre connector tests when using a non local home storage 2015-10-20 09:49:36 +02:00
Robin Appelman be9e72084d Add tests for double cache rename 2015-10-20 09:49:29 +02:00
Robin Appelman 38c7902795 Skip checkupdate test for swift 2015-10-20 09:49:17 +02:00
Jörn Friedrich Dreyer 36066e1382 don't move files in cache twice, fixes renaming for objectstores 2015-10-20 09:49:02 +02:00
Thomas Müller e3a35506c4 Fix error in stop script 2015-10-20 09:43:57 +02:00
Roeland Jago Douma 99b78345a0 Check for error when resharing 2015-10-19 20:31:06 +02:00
Roeland Jago Douma 206eb29331 When sharing with the owner show the path
The error message should contain the path that is being shared not the
numeric id.
2015-10-19 20:28:41 +02:00
C Montero-Luque 8ee2009de3 8.2.0 2015-10-19 08:24:40 -04:00
C. Montero Luque ab0fc36a5f Merge pull request #19867 from owncloud/stable8.2-GreenArchon-fix-memberof-regression
[stable8.2] green archon fix memberof regression
2015-10-19 07:48:02 -04:00
Lukas Reschke 0cdece9bd4 Adjust unit tests 2015-10-19 13:20:39 +02:00
Lukas Reschke f8870a07b4 Fix style 2015-10-19 13:20:35 +02:00
Frédéric Fortier 01bff3bc24 Revert "adjust to nested group fix
This reverts commit 845485cfe, which fixes #19816 regression.
2015-10-19 13:20:32 +02:00
Morris Jobke 6bd1b03288 Merge pull request #19860 from owncloud/fix-streamed-download-of-subfolders-stable8.2
The full name has to be tested if it's a dir
2015-10-19 10:43:37 +02:00
Thomas Müller 90d436e1e0 The full name has to be tested if it's a dir - fixes #19854 2015-10-19 10:09:27 +02:00
Lukas Reschke bd835f9ecc Merge pull request #19850 from owncloud/increase-app-versions-8.2
Increase the 2nd digit of shipped apps for 8.2
2015-10-18 19:29:13 +02:00
Joas Schilling 0d868a9843 Increase the 2nd digit of shipped apps for 8.2 2015-10-17 08:54:12 +02:00
C Montero-Luque ab84f785d3 8.2 RC3 2015-10-16 14:30:26 -04:00
Vincent Petry 063c2db084 Fix spinner positions in share tab
Also fix missing spinner when removing link share
2015-10-16 18:38:54 +02:00
Vincent Petry 68f0d107f5 Fix DOM element ids in share dialog
- Rely on class names instead of global ids
- When global ids are needed for label+checkbox, append the view id
  (cid) to the element's id

This fixes the checkboxes when multiple sidebars exist in the DOM.
2015-10-16 15:27:42 +02:00
Vincent Petry 1d04317fbc Do not register sidebar panels when no sidebar 2015-10-16 08:40:39 +02:00
Thomas Müller dc3aee5acc Merge pull request #19783 from owncloud/stable8.2_backport_19727
[Stable8.2] Return path instead of itemsource
2015-10-15 19:58:05 +02:00
Morris Jobke 2729c0417d Merge pull request #19796 from owncloud/stable8.2-language-compat
[stable8.2] Remove arbitrary expression in empty
2015-10-15 10:41:58 +02:00
Joas Schilling c4c8296495 The constant is now deprecated 2015-10-15 08:40:26 +02:00
Morris Jobke 0eb374fe12 update deprecation message 2015-10-15 08:40:18 +02:00
Joas Schilling 8165c08cc5 Update list of deprecated methods 2015-10-15 08:40:08 +02:00
Frank Karlitschek 614a22cf32 Merge pull request #19763 from owncloud/backport-19744
Backport admin checkboxes #19744 to stable8.2
2015-10-14 21:18:55 -03:00
Lukas Reschke 6f20f60f5c Remove arbitrary expression in empty
Those are only allowed in PHP 5.5, thus making our code incompatible with PHP 5.4

Fixes https://github.com/owncloud/core/issues/19793
2015-10-14 22:40:31 +02:00
Roeland Jago Douma 8ac5398f4e Return path instead of itemsource
Fixes #19678

Errors should contain paths and not internal ids
2015-10-14 15:46:10 +02:00
Vincent Petry ba46cfed3a Properly hide strengthify after password change 2015-10-14 15:11:17 +02:00
Lukas Reschke c8da73667b Merge pull request #19781 from owncloud/revert-19768-stable8.2-passwordchange-hidestrengthify
Revert "[stable8.2] Properly hide strengthify after password change"
2015-10-14 15:09:14 +02:00
Lukas Reschke a50643ebc9 Revert "[stable8.2] Properly hide strengthify after password change" 2015-10-14 15:09:07 +02:00
Lukas Reschke 718edf7828 Merge pull request #19768 from owncloud/stable8.2-passwordchange-hidestrengthify
[stable8.2] Properly hide strengthify after password change
2015-10-14 15:08:45 +02:00
Vincent Petry 71144522c9 Hide notification on delete
In case a permanent notification "storage full" was displayed, it will
be hidden after deleting a file.

Directly after that a getstoragestats.php call is made that will decide
whether to reshow the notification based on the new free space.
2015-10-14 15:02:06 +02:00
Vincent Petry 9f052e44c8 Move trashbin specific CSS that modifies sidebar
Only when trashbin is enabled, its sidebar nav element must be fixed at
the bottom.
2015-10-14 14:56:08 +02:00
Vincent Petry cc03a1df0b Debounce cancel upload message 2015-10-14 14:44:42 +02:00
Joas Schilling a470b4917c Variables don't have a class, so we can't use toString() on it 2015-10-14 14:32:33 +02:00
Vincent Petry 4a0ca37cb3 Remove sidebar for trashbin view 2015-10-14 14:28:11 +02:00
Vincent Petry a4cbdfdc1f showTemporary instead of show for notifications 2015-10-14 14:26:16 +02:00
Vincent Petry b6f5e13b32 Move alt text for favorite action to image 2015-10-14 14:22:01 +02:00
Roeland Jago Douma da3cfb8710 Add unit tests for sending e-mail for link shares 2015-10-14 14:20:09 +02:00
Vincent Petry b0abb583c9 Fix share email sending feedback
Redisplay email address after failure
2015-10-14 14:20:04 +02:00
Vincent Petry f2ed3e726a [IE8] Fix button background color
IE8 doesn't support rgba...
2015-10-14 14:17:49 +02:00
Vincent Petry 2ff3e87f74 [IE8] Fix "new" button width 2015-10-14 14:17:45 +02:00
Vincent Petry f4ffa94e7f [IE9] Set explicit height to file actions menu icon
IE9 flattened the icons, so give it an explicit height.
2015-10-14 14:17:42 +02:00
Vincent Petry a1ce7ac872 [IE] Add border to dropdown menus
As a fallback because box-shadow doesn't give the same effect and drop
shadow doesn't work with IE.
2015-10-14 14:17:38 +02:00
Vincent Petry f7b62abcce Delete last undoable user before user creation 2015-10-14 14:14:50 +02:00
Vincent Petry baac1ad489 Properly hide strengthify after password change 2015-10-14 14:13:41 +02:00
Vincent Petry 7b00b943ee Fix mtime PROPPATCH to be "lastmodified" instead of "getlastmodified"
Fix regression that makes PROPPATCH of mtime work like it did in OC <=
8.0.
The PROPPATCH must be done on the "lastmodified" property.
The "getlastmodified" now return 403 again.
2015-10-14 14:02:59 +02:00
Joas Schilling 02493563a2 Make sure that remote shares use the correct uid casing 2015-10-14 13:30:42 +02:00
Hendrik Leppelsack ee80cea2bd apply new checkbox style to admin settings 2015-10-14 13:08:06 +02:00
C Montero-Luque 06bfe1139b 8.2 RC2 2015-10-13 19:13:27 -04:00
132 changed files with 2030 additions and 759 deletions
+1 -1
View File
@@ -1,4 +1,4 @@
# Version: 8.2.0
# Version: 8.2.1
<IfModule mod_headers.c>
<IfModule mod_fcgid.c>
<IfModule mod_setenvif.c>
+2 -1
View File
@@ -14,18 +14,19 @@
<name>Default encryption module</name>
<license>AGPL</license>
<author>Bjoern Schiessle, Clark Tomlinson</author>
<requiremin>8</requiremin>
<shipped>true</shipped>
<documentation>
<user>user-encryption</user>
<admin>admin-encryption</admin>
</documentation>
<rememberlogin>false</rememberlogin>
<version>1.1.0</version>
<types>
<filesystem/>
</types>
<dependencies>
<lib>openssl</lib>
<owncloud min-version="8.2" />
</dependencies>
</info>
-1
View File
@@ -1 +0,0 @@
1.0.0
+4 -4
View File
@@ -53,7 +53,7 @@ class Crypt {
*/
private $logger;
/**
* @var IUser
* @var string
*/
private $user;
/**
@@ -73,7 +73,7 @@ class Crypt {
*/
public function __construct(ILogger $logger, IUserSession $userSession, IConfig $config) {
$this->logger = $logger;
$this->user = $userSession && $userSession->isLoggedIn() ? $userSession->getUser() : false;
$this->user = $userSession && $userSession->isLoggedIn() ? $userSession->getUser()->getUID() : '"no user given"';
$this->config = $config;
$this->supportedKeyFormats = ['hash', 'password'];
}
@@ -89,7 +89,7 @@ class Crypt {
$res = $this->getOpenSSLPKey();
if (!$res) {
$log->error("Encryption Library couldn't generate users key-pair for {$this->user->getUID()}",
$log->error("Encryption Library couldn't generate users key-pair for {$this->user}",
['app' => 'encryption']);
if (openssl_error_string()) {
@@ -108,7 +108,7 @@ class Crypt {
'privateKey' => $privateKey
];
}
$log->error('Encryption library couldn\'t export users private key, please check your servers OpenSSL configuration.' . $this->user->getUID(),
$log->error('Encryption library couldn\'t export users private key, please check your servers OpenSSL configuration.' . $this->user,
['app' => 'encryption']);
if (openssl_error_string()) {
$log->error('Encryption Library:' . openssl_error_string(),
+26 -1
View File
@@ -62,6 +62,8 @@ class MigrationTest extends \Test\TestCase {
}
protected function createDummyShareKeys($uid) {
$this->loginAsUser($uid);
$this->view->mkdir($uid . '/files_encryption/keys/folder1/folder2/folder3/file3');
$this->view->mkdir($uid . '/files_encryption/keys/folder1/folder2/file2');
$this->view->mkdir($uid . '/files_encryption/keys/folder1/file.1');
@@ -87,6 +89,8 @@ class MigrationTest extends \Test\TestCase {
}
protected function createDummyUserKeys($uid) {
$this->loginAsUser($uid);
$this->view->mkdir($uid . '/files_encryption/');
$this->view->mkdir('/files_encryption/public_keys');
$this->view->file_put_contents($uid . '/files_encryption/' . $uid . '.privateKey', 'privateKey');
@@ -94,6 +98,8 @@ class MigrationTest extends \Test\TestCase {
}
protected function createDummyFileKeys($uid) {
$this->loginAsUser($uid);
$this->view->mkdir($uid . '/files_encryption/keys/folder1/folder2/folder3/file3');
$this->view->mkdir($uid . '/files_encryption/keys/folder1/folder2/file2');
$this->view->mkdir($uid . '/files_encryption/keys/folder1/file.1');
@@ -105,6 +111,8 @@ class MigrationTest extends \Test\TestCase {
}
protected function createDummyFiles($uid) {
$this->loginAsUser($uid);
$this->view->mkdir($uid . '/files/folder1/folder2/folder3/file3');
$this->view->mkdir($uid . '/files/folder1/folder2/file2');
$this->view->mkdir($uid . '/files/folder1/file.1');
@@ -116,6 +124,8 @@ class MigrationTest extends \Test\TestCase {
}
protected function createDummyFilesInTrash($uid) {
$this->loginAsUser($uid);
$this->view->mkdir($uid . '/files_trashbin/keys/file1.d5457864');
$this->view->mkdir($uid . '/files_trashbin/keys/folder1.d7437648723/file2');
$this->view->file_put_contents($uid . '/files_trashbin/keys/file1.d5457864/' . self::TEST_ENCRYPTION_MIGRATION_USER1 . '.shareKey' , 'data');
@@ -165,6 +175,7 @@ class MigrationTest extends \Test\TestCase {
$this->createDummySystemWideKeys();
/** @var \PHPUnit_Framework_MockObject_MockObject|\OCA\Encryption\Migration $m */
$m = $this->getMockBuilder('OCA\Encryption\Migration')
->setConstructorArgs(
[
@@ -176,27 +187,38 @@ class MigrationTest extends \Test\TestCase {
)->setMethods(['getSystemMountPoints'])->getMock();
$m->expects($this->any())->method('getSystemMountPoints')
->willReturn([['mountpoint' => 'folder1'], ['mountpoint' => 'folder2']]);
->will($this->returnValue([['mountpoint' => 'folder1'], ['mountpoint' => 'folder2']]));
$m->reorganizeFolderStructure();
// even if it runs twice folder should always move only once
$m->reorganizeFolderStructure();
$this->loginAsUser(self::TEST_ENCRYPTION_MIGRATION_USER1);
$this->assertTrue(
$this->view->file_exists(
self::TEST_ENCRYPTION_MIGRATION_USER1 . '/files_encryption/' .
$this->moduleId . '/' . self::TEST_ENCRYPTION_MIGRATION_USER1 . '.publicKey')
);
$this->loginAsUser(self::TEST_ENCRYPTION_MIGRATION_USER2);
$this->assertTrue(
$this->view->file_exists(
self::TEST_ENCRYPTION_MIGRATION_USER2 . '/files_encryption/' .
$this->moduleId . '/' . self::TEST_ENCRYPTION_MIGRATION_USER2 . '.publicKey')
);
$this->loginAsUser(self::TEST_ENCRYPTION_MIGRATION_USER3);
$this->assertTrue(
$this->view->file_exists(
self::TEST_ENCRYPTION_MIGRATION_USER3 . '/files_encryption/' .
$this->moduleId . '/' . self::TEST_ENCRYPTION_MIGRATION_USER3 . '.publicKey')
);
$this->loginAsUser(self::TEST_ENCRYPTION_MIGRATION_USER1);
$this->assertTrue(
$this->view->file_exists(
'/files_encryption/' . $this->moduleId . '/systemwide_1.publicKey')
@@ -217,6 +239,8 @@ class MigrationTest extends \Test\TestCase {
}
protected function verifyFilesInTrash($uid) {
$this->loginAsUser($uid);
// share keys
$this->assertTrue(
$this->view->file_exists($uid . '/files_encryption/keys/files_trashbin/file1.d5457864/' . $this->moduleId . '/' . self::TEST_ENCRYPTION_MIGRATION_USER1 . '.shareKey')
@@ -244,6 +268,7 @@ class MigrationTest extends \Test\TestCase {
protected function verifyNewKeyPath($uid) {
// private key
if ($uid !== '') {
$this->loginAsUser($uid);
$this->assertTrue($this->view->file_exists($uid . '/files_encryption/' . $this->moduleId . '/'. $uid . '.privateKey'));
}
// file keys
+4 -1
View File
@@ -5,10 +5,10 @@
<description>File Management</description>
<licence>AGPL</licence>
<author>Robin Appelman, Vincent Petry</author>
<requiremin>4.93</requiremin>
<shipped>true</shipped>
<standalone/>
<default_enable/>
<version>1.2.0</version>
<types>
<filesystem/>
</types>
@@ -16,6 +16,9 @@
<files>appinfo/remote.php</files>
<webdav>appinfo/remote.php</webdav>
</remote>
<dependencies>
<owncloud min-version="8.2" />
</dependencies>
<documentation>
<user>user-files</user>
</documentation>
-1
View File
@@ -1 +0,0 @@
1.1.11
+5 -13
View File
@@ -85,19 +85,6 @@
background-image: url('../img/delete.svg');
}
/* move Deleted Files to bottom of sidebar */
.nav-trashbin {
position: fixed !important;
bottom: 44px;
width: inherit !important;
background-color: #fff;
border-right: 1px solid #eee;
}
/* double padding to account for Deleted files entry, issue with Firefox */
.app-files #app-navigation > ul li:nth-last-child(2) {
margin-bottom: 44px;
}
#app-navigation .nav-files a.nav-icon-files {
width: auto;
}
@@ -745,6 +732,10 @@ table.dragshadow td.size {
margin: -10px;
}
html.ie8 #controls .button.new {
padding-right: 0;
}
.newFileMenu {
width: 140px;
margin-left: -56px;
@@ -796,3 +787,4 @@ table.dragshadow td.size {
color: #000;
padding: 0;
}
+38 -17
View File
@@ -18,7 +18,7 @@
* - TODO music upload button
*/
/* global jQuery, oc_requesttoken, humanFileSize */
/* global jQuery, oc_requesttoken, humanFileSize, FileList */
/**
* Function that will allow us to know if Ajax uploads are supported
@@ -47,6 +47,26 @@ function supportAjaxUploadWithProgress() {
}
}
/**
* Add form data into the given form data
*
* @param {Array|Object} formData form data which can either be an array or an object
* @param {Object} newData key-values to add to the form data
*
* @return updated form data
*/
function addFormData(formData, newData) {
// in IE8, formData is an array instead of object
if (_.isArray(formData)) {
_.each(newData, function(value, key) {
formData.push({name: key, value: value});
});
} else {
formData = _.extend(formData, newData);
}
return formData;
}
/**
* keeps track of uploads in progress and implements callbacks for the conflicts dialog
* @namespace
@@ -75,6 +95,9 @@ OC.Upload = {
this._uploads.push(jqXHR);
}
},
showUploadCancelMessage: _.debounce(function() {
OC.Notification.showTemporary(t('files', 'Upload cancelled.'), {timeout: 10});
}, 500),
/**
* Checks the currently known uploads.
* returns true if any hxr has the state 'pending'
@@ -140,9 +163,9 @@ OC.Upload = {
data.data.append('resolution', 'replace');
} else {
if (!data.formData) {
data.formData = [];
data.formData = {};
}
data.formData.push({name:'resolution', value:'replace'}); //hack for ie8
addFormData(data.formData, {resolution: 'replace'});
}
data.submit();
},
@@ -156,9 +179,9 @@ OC.Upload = {
data.data.append('resolution', 'autorename');
} else {
if (!data.formData) {
data.formData = [];
data.formData = {};
}
data.formData.push({name:'resolution', value:'autorename'}); //hack for ie8
addFormData(data.formData, {resolution: 'autorename'});
}
data.submit();
},
@@ -182,7 +205,7 @@ OC.Upload = {
* @param {function} callbacks.onCancel
*/
checkExistingFiles: function (selection, callbacks) {
var fileList = OCA.Files.App.fileList;
var fileList = FileList;
var conflicts = [];
// only keep non-conflicting uploads
selection.uploads = _.filter(selection.uploads, function(upload) {
@@ -399,26 +422,28 @@ OC.Upload = {
submit: function(e, data) {
OC.Upload.rememberUpload(data);
if (!data.formData) {
data.formData = [];
data.formData = {};
}
var fileDirectory = '';
if(typeof data.files[0].relativePath !== 'undefined') {
fileDirectory = data.files[0].relativePath;
}
// FIXME: prevent re-adding the same
data.formData.push({name: 'requesttoken', value: oc_requesttoken});
data.formData.push({name: 'dir', value: data.targetDir || FileList.getCurrentDirectory()});
data.formData.push({name: 'file_directory', value: fileDirectory});
addFormData(data.formData, {
requesttoken: oc_requesttoken,
dir: data.targetDir || FileList.getCurrentDirectory(),
file_directory: fileDirectory
});
},
fail: function(e, data) {
OC.Upload.log('fail', e, data);
if (typeof data.textStatus !== 'undefined' && data.textStatus !== 'success' ) {
if (data.textStatus === 'abort') {
OC.Notification.show(t('files', 'Upload cancelled.'));
OC.Upload.showUploadCancelMessage();
} else {
// HTTP connection problem
OC.Notification.show(data.errorThrown);
OC.Notification.showTemporary(data.errorThrown, {timeout: 10});
if (data.result) {
var result = JSON.parse(data.result);
if (result && result[0] && result[0].data && result[0].data.code === 'targetnotfound') {
@@ -427,10 +452,6 @@ OC.Upload = {
}
}
}
//hide notification after 10 sec
setTimeout(function() {
OC.Notification.hide();
}, 10000);
}
OC.Upload.deleteUpload(data);
},
+41 -28
View File
@@ -386,12 +386,15 @@
* Update the details view to display the given file
*
* @param {string} fileName file name from the current list
* @param {boolean} [show=true] whether to open the sidebar if it was closed
*/
_updateDetailsView: function(fileName) {
_updateDetailsView: function(fileName, show) {
if (!this._detailsView) {
return;
}
// show defaults to true
show = _.isUndefined(show) || !!show;
var oldFileInfo = this._detailsView.getFileInfo();
if (oldFileInfo) {
// TODO: use more efficient way, maybe track the highlight
@@ -409,7 +412,7 @@
return;
}
if (this._detailsView.$el.hasClass('disappear')) {
if (show && this._detailsView.$el.hasClass('disappear')) {
OC.Apps.showAppSidebar(this._detailsView.$el);
}
@@ -760,7 +763,7 @@
*/
elementToFile: function($el){
$el = $($el);
return {
var data = {
id: parseInt($el.attr('data-id'), 10),
name: $el.attr('data-file'),
mimetype: $el.attr('data-mime'),
@@ -770,6 +773,15 @@
etag: $el.attr('data-etag'),
permissions: parseInt($el.attr('data-permissions'), 10)
};
var icon = $el.attr('data-icon');
if (icon) {
data.icon = icon;
}
var mountType = $el.attr('data-mounttype');
if (mountType) {
data.mountType = mountType;
}
return data;
},
/**
@@ -892,11 +904,12 @@
mtime = parseInt(fileData.mtime, 10),
mime = fileData.mimetype,
path = fileData.path,
dataIcon = null,
linkUrl;
options = options || {};
if (isNaN(mtime)) {
mtime = new Date().getTime()
mtime = new Date().getTime();
}
if (type === 'dir') {
@@ -904,6 +917,7 @@
if (fileData.mountType && fileData.mountType.indexOf('external') === 0) {
icon = OC.MimeType.getIconUrl('dir-external');
dataIcon = icon;
}
}
@@ -919,6 +933,11 @@
"data-permissions": fileData.permissions || this.getDirectoryPermissions()
});
if (dataIcon) {
// icon override
tr.attr('data-icon', dataIcon);
}
if (fileData.mountType) {
tr.attr('data-mounttype', fileData.mountType);
}
@@ -1170,7 +1189,7 @@
// display actions
this.fileActions.display(filenameTd, !options.silent, this);
if (fileData.isPreviewAvailable) {
if (fileData.isPreviewAvailable && mime !== 'httpd/unix-directory') {
var iconDiv = filenameTd.find('.thumbnail');
// lazy load / newly inserted td ?
// the typeof check ensures that the default value of animate is true
@@ -1350,7 +1369,7 @@
) {
OC.redirect(OC.generateUrl('apps/files'));
}
OC.Notification.show(result.data.message);
OC.Notification.showTemporary(result.data.message);
return false;
}
@@ -1358,7 +1377,7 @@
if (result.status === 403) {
// Go home
this.changeDirectory('/');
OC.Notification.show(t('files', 'This operation is forbidden'));
OC.Notification.showTemporary(t('files', 'This operation is forbidden'));
return false;
}
@@ -1366,7 +1385,7 @@
if (result.status === 500) {
// Go home
this.changeDirectory('/');
OC.Notification.show(t('files', 'This directory is unavailable, please check the logs or contact the administrator'));
OC.Notification.showTemporary(t('files', 'This directory is unavailable, please check the logs or contact the administrator'));
return false;
}
@@ -1640,15 +1659,11 @@
} else {
OC.Notification.hide();
if (result.status === 'error' && result.data.message) {
OC.Notification.show(result.data.message);
OC.Notification.showTemporary(result.data.message);
}
else {
OC.Notification.show(t('files', 'Error moving file.'));
OC.Notification.showTemporary(t('files', 'Error moving file.'));
}
// hide notification after 10 sec
setTimeout(function() {
OC.Notification.hide();
}, 10000);
}
} else {
OC.dialogs.alert(t('files', 'Error moving file'), t('files', 'Error'));
@@ -1771,7 +1786,7 @@
tr.remove();
tr = self.add(fileInfo, {updateSummary: false, silent: true});
self.$fileList.trigger($.Event('fileActionsReady', {fileList: self, $files: $(tr)}));
self._updateDetailsView(fileInfo.name);
self._updateDetailsView(fileInfo.name, false);
}
});
} else {
@@ -2011,17 +2026,15 @@
self.fileSummary.update();
self.updateSelectionSummary();
self.updateStorageStatistics();
// in case there was a "storage full" permanent notification
OC.Notification.hide();
} else {
if (result.status === 'error' && result.data.message) {
OC.Notification.show(result.data.message);
OC.Notification.showTemporary(result.data.message);
}
else {
OC.Notification.show(t('files', 'Error deleting file.'));
OC.Notification.showTemporary(t('files', 'Error deleting file.'));
}
// hide notification after 10 sec
setTimeout(function() {
OC.Notification.hide();
}, 10000);
if (params.allfiles) {
// reload the page as we don't know what files were deleted
// and which ones remain
@@ -2262,11 +2275,7 @@
*/
_showPermissionDeniedNotification: function() {
var message = t('core', 'You dont have permission to upload or create files here');
OC.Notification.show(message);
//hide notification after 10 sec
setTimeout(function() {
OC.Notification.hide();
}, 5000);
OC.Notification.showTemporary(message);
},
/**
@@ -2620,14 +2629,18 @@
* Register a tab view to be added to all views
*/
registerTabView: function(tabView) {
this._detailsView.addTabView(tabView);
if (this._detailsView) {
this._detailsView.addTabView(tabView);
}
},
/**
* Register a detail view to be added to all views
*/
registerDetailView: function(detailView) {
this._detailsView.addDetailView(detailView);
if (this._detailsView) {
this._detailsView.addDetailView(detailView);
}
}
};
+5 -6
View File
@@ -15,9 +15,8 @@
'<div class="fileName"><h3 title="{{name}}" class="ellipsis">{{name}}</h3></div>' +
' <div class="file-details ellipsis">' +
' <a href="#" ' +
' alt="{{starAltText}}"' +
' class="action action-favorite favorite">' +
' <img class="svg" src="{{starIcon}}" />' +
' <img class="svg" alt="{{starAltText}}" src="{{starIcon}}" />' +
' </a>' +
' {{#if hasSize}}<span class="size" title="{{altSize}}">{{size}}</span>, {{/if}}<span class="date" title="{{altDate}}">{{date}}</span>' +
' </div>' +
@@ -66,10 +65,10 @@
this._fileList = options.fileList;
this._fileActions = options.fileActions;
if (!this._fileList) {
throw 'Missing requird parameter "fileList"';
throw 'Missing required parameter "fileList"';
}
if (!this._fileActions) {
throw 'Missing requird parameter "fileActions"';
throw 'Missing required parameter "fileActions"';
}
},
@@ -129,8 +128,8 @@
$iconDiv.addClass('icon-loading icon-32');
this.loadPreview(this.model.getFullPath(), this.model.get('mimetype'), this.model.get('etag'), $iconDiv, $container, this.model.isImage());
} else {
// TODO: special icons / shared / external
$iconDiv.css('background-image', 'url("' + OC.MimeType.getIconUrl('dir') + '")');
var iconUrl = this.model.get('icon') || OC.MimeType.getIconUrl('dir');
$iconDiv.css('background-image', 'url("' + iconUrl + '")');
OC.Util.scaleFixForIE8($iconDiv);
}
this.$el.find('[title]').tooltip({placement: 'bottom'});
@@ -22,6 +22,7 @@
namespace OCA\Files\Tests\Command;
use OCA\Files\Command\DeleteOrphanedFiles;
use OCP\Files\StorageNotAvailableException;
class DeleteOrphanedFilesTest extends \Test\TestCase {
@@ -110,7 +111,11 @@ class DeleteOrphanedFilesTest extends \Test\TestCase {
$this->assertCount(0, $this->getFile($fileInfo->getId()), 'Asserts that file gets cleaned up');
$view->unlink('files/test');
// since we deleted the storage it might throw a (valid) StorageNotAvailableException
try {
$view->unlink('files/test');
} catch (StorageNotAvailableException $e) {
}
}
}
+96
View File
@@ -117,4 +117,100 @@ describe('OC.Upload tests', function() {
);
});
});
describe('Upload conflicts', function() {
var oldFileList;
var conflictDialogStub;
var callbacks;
beforeEach(function() {
oldFileList = FileList;
$('#testArea').append(
'<div id="tableContainer">' +
'<table id="filestable">' +
'<thead><tr>' +
'<th id="headerName" class="hidden column-name">' +
'<input type="checkbox" id="select_all_files" class="select-all">' +
'<a class="name columntitle" data-sort="name"><span>Name</span><span class="sort-indicator"></span></a>' +
'<span id="selectedActionsList" class="selectedActions hidden">' +
'<a href class="download"><img src="actions/download.svg">Download</a>' +
'<a href class="delete-selected">Delete</a></span>' +
'</th>' +
'<th class="hidden column-size"><a class="columntitle" data-sort="size"><span class="sort-indicator"></span></a></th>' +
'<th class="hidden column-mtime"><a class="columntitle" data-sort="mtime"><span class="sort-indicator"></span></a></th>' +
'</tr></thead>' +
'<tbody id="fileList"></tbody>' +
'<tfoot></tfoot>' +
'</table>' +
'</div>'
);
FileList = new OCA.Files.FileList($('#tableContainer'));
FileList.add({name: 'conflict.txt', mimetype: 'text/plain'});
FileList.add({name: 'conflict2.txt', mimetype: 'text/plain'});
conflictDialogStub = sinon.stub(OC.dialogs, 'fileexists');
callbacks = {
onNoConflicts: sinon.stub()
};
});
afterEach(function() {
conflictDialogStub.restore();
FileList.destroy();
FileList = oldFileList;
});
it('does not show conflict dialog when no client side conflict', function() {
var selection = {
// yes, the format of uploads is weird...
uploads: [
{files: [{name: 'noconflict.txt'}]},
{files: [{name: 'noconflict2.txt'}]}
]
};
OC.Upload.checkExistingFiles(selection, callbacks);
expect(conflictDialogStub.notCalled).toEqual(true);
expect(callbacks.onNoConflicts.calledOnce).toEqual(true);
expect(callbacks.onNoConflicts.calledWith(selection)).toEqual(true);
});
it('shows conflict dialog when no client side conflict', function() {
var selection = {
// yes, the format of uploads is weird...
uploads: [
{files: [{name: 'conflict.txt'}]},
{files: [{name: 'conflict2.txt'}]},
{files: [{name: 'noconflict.txt'}]}
]
};
var deferred = $.Deferred();
conflictDialogStub.returns(deferred.promise());
deferred.resolve();
OC.Upload.checkExistingFiles(selection, callbacks);
expect(conflictDialogStub.callCount).toEqual(3);
expect(conflictDialogStub.getCall(1).args[0])
.toEqual({files: [ { name: 'conflict.txt' } ]});
expect(conflictDialogStub.getCall(1).args[1])
.toEqual({ name: 'conflict.txt', mimetype: 'text/plain', directory: '/' });
expect(conflictDialogStub.getCall(1).args[2]).toEqual({ name: 'conflict.txt' });
// yes, the dialog must be called several times...
expect(conflictDialogStub.getCall(2).args[0]).toEqual({
files: [ { name: 'conflict2.txt' } ]
});
expect(conflictDialogStub.getCall(2).args[1])
.toEqual({ name: 'conflict2.txt', mimetype: 'text/plain', directory: '/' });
expect(conflictDialogStub.getCall(2).args[2]).toEqual({ name: 'conflict2.txt' });
expect(callbacks.onNoConflicts.calledOnce).toEqual(true);
expect(callbacks.onNoConflicts.calledWith({
uploads: [
{files: [{name: 'noconflict.txt'}]}
]
})).toEqual(true);
});
});
});
+85 -5
View File
@@ -1166,7 +1166,7 @@ describe('OCA.Files.FileList tests', function() {
it('renders provided icon for file when provided', function() {
var fileData = {
type: 'file',
name: 'test dir',
name: 'test file',
icon: OC.webroot + '/core/img/filetypes/application-pdf.svg',
mimetype: 'application/pdf'
};
@@ -1178,7 +1178,7 @@ describe('OCA.Files.FileList tests', function() {
it('renders preview when no icon was provided and preview is available', function() {
var fileData = {
type: 'file',
name: 'test dir',
name: 'test file',
isPreviewAvailable: true
};
var $tr = fileList.add(fileData);
@@ -1192,7 +1192,7 @@ describe('OCA.Files.FileList tests', function() {
it('renders default file type icon when no icon was provided and no preview is available', function() {
var fileData = {
type: 'file',
name: 'test dir',
name: 'test file',
isPreviewAvailable: false
};
var $tr = fileList.add(fileData);
@@ -1200,6 +1200,47 @@ describe('OCA.Files.FileList tests', function() {
expect(OC.TestUtil.getImageUrl($imgDiv)).toEqual(OC.webroot + '/core/img/filetypes/file.svg');
expect(previewLoadStub.notCalled).toEqual(true);
});
it('does not render preview for directories', function() {
var fileData = {
type: 'dir',
mimetype: 'httpd/unix-directory',
name: 'test dir',
isPreviewAvailable: true
};
var $tr = fileList.add(fileData);
var $td = $tr.find('td.filename');
expect(OC.TestUtil.getImageUrl($td.find('.thumbnail'))).toEqual(OC.webroot + '/core/img/filetypes/folder.svg');
expect(previewLoadStub.notCalled).toEqual(true);
});
it('render external storage icon for external storage root', function() {
var fileData = {
type: 'dir',
mimetype: 'httpd/unix-directory',
name: 'test dir',
isPreviewAvailable: true,
mountType: 'external-root'
};
var $tr = fileList.add(fileData);
var $td = $tr.find('td.filename');
expect(OC.TestUtil.getImageUrl($td.find('.thumbnail'))).toEqual(OC.webroot + '/core/img/filetypes/folder-external.svg');
expect(previewLoadStub.notCalled).toEqual(true);
});
it('render external storage icon for external storage subdir', function() {
var fileData = {
type: 'dir',
mimetype: 'httpd/unix-directory',
name: 'test dir',
isPreviewAvailable: true,
mountType: 'external'
};
var $tr = fileList.add(fileData);
var $td = $tr.find('td.filename');
expect(OC.TestUtil.getImageUrl($td.find('.thumbnail'))).toEqual(OC.webroot + '/core/img/filetypes/folder-external.svg');
expect(previewLoadStub.notCalled).toEqual(true);
// default icon override
expect($tr.attr('data-icon')).toEqual(OC.webroot + '/core/img/filetypes/folder-external.svg');
});
});
describe('viewer mode', function() {
it('enabling viewer mode hides files table and action buttons', function() {
@@ -1879,15 +1920,54 @@ describe('OCA.Files.FileList tests', function() {
$tr2.find('td.filename .name').trigger(e);
expect(fileList.getSelectedFiles().length).toEqual(0);
});
})
});
});
describe('Details sidebar', function() {
beforeEach(function() {
fileList.setFiles(testFiles);
fileList.showDetailsView('Two.jpg');
});
describe('registering', function() {
var addTabStub;
var addDetailStub;
beforeEach(function() {
addTabStub = sinon.stub(OCA.Files.DetailsView.prototype, 'addTabView');
addDetailStub = sinon.stub(OCA.Files.DetailsView.prototype, 'addDetailView');
});
afterEach(function() {
addTabStub.restore();
addDetailStub.restore();
});
it('forward the registered views to the underlying DetailsView', function() {
fileList.destroy();
fileList = new OCA.Files.FileList($('#app-content-files'), {
detailsViewEnabled: true
});
fileList.registerTabView(new OCA.Files.DetailTabView());
fileList.registerDetailView(new OCA.Files.DetailFileInfoView());
expect(addTabStub.calledOnce).toEqual(true);
// twice because the filelist already registers one by default
expect(addDetailStub.calledTwice).toEqual(true);
});
it('does not error when registering panels when not details view configured', function() {
fileList.destroy();
fileList = new OCA.Files.FileList($('#app-content-files'), {
detailsViewEnabled: false
});
fileList.registerTabView(new OCA.Files.DetailTabView());
fileList.registerDetailView(new OCA.Files.DetailFileInfoView());
expect(addTabStub.notCalled).toEqual(true);
expect(addDetailStub.notCalled).toEqual(true);
});
});
it('triggers file action when clicking on row if no details view configured', function() {
fileList._detailsView = null;
fileList.destroy();
fileList = new OCA.Files.FileList($('#app-content-files'), {
detailsViewEnabled: false
});
var updateDetailsViewStub = sinon.stub(fileList, '_updateDetailsView');
var actionStub = sinon.stub();
fileList.setFiles(testFiles);
@@ -112,6 +112,20 @@ describe('OCA.Files.MainFileInfoDetailView tests', function() {
lazyLoadPreviewStub.restore();
});
it('uses icon from model if present in model', function() {
var lazyLoadPreviewStub = sinon.stub(fileList, 'lazyLoadPreview');
testFileInfo.set('mimetype', 'httpd/unix-directory');
testFileInfo.set('icon', OC.MimeType.getIconUrl('dir-external'));
view.setFileInfo(testFileInfo);
expect(lazyLoadPreviewStub.notCalled).toEqual(true);
expect(view.$el.find('.thumbnail').hasClass('icon-loading')).toEqual(false);
expect(view.$el.find('.thumbnail').css('background-image'))
.toContain('filetypes/folder-external.svg');
lazyLoadPreviewStub.restore();
});
it('displays thumbnail', function() {
var lazyLoadPreviewStub = sinon.stub(fileList, 'lazyLoadPreview');
+2 -1
View File
@@ -14,12 +14,13 @@
<admin>admin-external-storage</admin>
</documentation>
<rememberlogin>false</rememberlogin>
<version>0.3.0</version>
<types>
<filesystem/>
</types>
<ocsid>166048</ocsid>
<dependencies>
<owncloud min-version="8" />
<owncloud min-version="8.2" />
</dependencies>
</info>
-1
View File
@@ -1 +0,0 @@
0.2.3
+1
View File
@@ -288,6 +288,7 @@ class Dropbox extends \OC\Files\Storage\Common {
try {
$this->dropbox->putFile(self::$tempFiles[$tmpFile], $handle);
unlink($tmpFile);
$this->deleteMetaData(self::$tempFiles[$tmpFile]);
} catch (\Exception $exception) {
\OCP\Util::writeLog('files_external', $exception->getMessage(), \OCP\Util::ERROR);
}
+3 -3
View File
@@ -26,7 +26,7 @@
<?php $checkboxId = uniqid("checkbox_"); ?>
<input type="checkbox"
id="<?php p($checkboxId); ?>"
<?php if (!empty($classes)): ?> class="<?php p(implode(' ', $classes)); ?>"<?php endif; ?>
<?php if (!empty($classes)): ?> class="checkbox <?php p(implode(' ', $classes)); ?>"<?php endif; ?>
data-parameter="<?php p($parameter->getName()); ?>"
<?php if ($value === true): ?> checked="checked"<?php endif; ?>
/>
@@ -191,7 +191,7 @@
<?php if ($_['isAdminPage']): ?>
<br />
<input type="checkbox" name="allowUserMounting" id="allowUserMounting"
<input type="checkbox" name="allowUserMounting" id="allowUserMounting" class="checkbox"
value="1" <?php if ($_['allowUserMounting'] == 'yes') print_unescaped(' checked="checked"'); ?> />
<label for="allowUserMounting"><?php p($l->t('Enable User External Storage')); ?></label> <span id="userMountingMsg" class="msg"></span>
@@ -201,7 +201,7 @@
<?php if ($deprecateTo = $backend->getDeprecateTo()): ?>
<input type="hidden" id="allowUserMountingBackends<?php p($i); ?>" name="allowUserMountingBackends[]" value="<?php p($backend->getIdentifier()); ?>" data-deprecate-to="<?php p($deprecateTo->getIdentifier()); ?>" />
<?php else: ?>
<input type="checkbox" id="allowUserMountingBackends<?php p($i); ?>" name="allowUserMountingBackends[]" value="<?php p($backend->getIdentifier()); ?>" <?php if ($backend->isVisibleFor(BackendService::VISIBILITY_PERSONAL)) print_unescaped(' checked="checked"'); ?> />
<input type="checkbox" id="allowUserMountingBackends<?php p($i); ?>" class="checkbox" name="allowUserMountingBackends[]" value="<?php p($backend->getIdentifier()); ?>" <?php if ($backend->isVisibleFor(BackendService::VISIBILITY_PERSONAL)) print_unescaped(' checked="checked"'); ?> />
<label for="allowUserMountingBackends<?php p($i); ?>"><?php p($backend->getText()); ?></label> <br />
<?php endif; ?>
<?php $i++; ?>
+1
View File
@@ -53,6 +53,7 @@ class Server2Server {
return new \OC_OCS_Result(null, 400, 'The mountpoint name contains invalid characters.');
}
// FIXME this should be a method in the user management instead
\OCP\Util::writeLog('files_sharing', 'shareWith before, ' . $shareWith, \OCP\Util::DEBUG);
\OCP\Util::emitHook(
'\OCA\Files_Sharing\API\Server2Server',
+4 -1
View File
@@ -9,12 +9,15 @@ Turning the feature off removes shared files and folders on the server for all s
</description>
<licence>AGPL</licence>
<author>Michael Gapczynski, Bjoern Schiessle</author>
<requiremin>4.93</requiremin>
<shipped>true</shipped>
<default_enable/>
<version>0.7.0</version>
<types>
<filesystem/>
</types>
<dependencies>
<owncloud min-version="8.2" />
</dependencies>
<public>
<files>public.php</files>
<webdav>publicwebdav.php</webdav>
-1
View File
@@ -1 +0,0 @@
0.6.3
-4
View File
@@ -18,7 +18,3 @@ input[type='submit'] {
fieldset > p {
position: relative;
}
#password-icon {
top: 20px;
}
+11 -3
View File
@@ -6,7 +6,6 @@
.shareTabView .shareWithLoading {
padding-left: 10px;
position: relative;
right: 30px;
top: 2px;
}
@@ -73,9 +72,18 @@
}
.shareTabView .icon-loading-small {
position: absolute;
display: inline-block;
z-index: 1;
background-color: white;
padding: 2px;
padding: 2px 0;
}
.shareTabView .shareWithList .icon-loading-small,
.shareTabView .linkShareView .icon-loading-small {
position: absolute;
}
.shareTabView .linkPass .icon-loading-small {
margin-top: 9px;
}
+1 -1
View File
@@ -54,7 +54,7 @@ OCA.Sharing.PublicApp = {
$el,
{
id: 'files.public',
scrollContainer: $(window),
scrollContainer: $('#content-wrapper'),
dragOptions: dragOptions,
folderDropOptions: folderDropOptions,
fileActions: fileActions,
+7
View File
@@ -130,6 +130,13 @@
// remove icon, if applicable
OC.Share.markFileAsShared($tr, false, false);
}
var newIcon = $tr.attr('data-icon');
// in case markFileAsShared decided to change the icon,
// we need to modify the model
// (FIXME: yes, this is hacky)
if (fileInfoModel.get('icon') !== newIcon) {
fileInfoModel.set('icon', newIcon);
}
});
fileList.registerTabView(shareTab);
},
+7 -1
View File
@@ -32,6 +32,7 @@
namespace OC\Files\Cache;
use OC\User\NoUserException;
use OCP\Share_Backend_Collection;
/**
@@ -64,7 +65,12 @@ class Shared_Cache extends Cache {
}
$source = \OC_Share_Backend_File::getSource($target, $this->storage->getShare());
if (isset($source['path']) && isset($source['fileOwner'])) {
\OC\Files\Filesystem::initMountPoints($source['fileOwner']);
try {
\OC\Files\Filesystem::initMountPoints($source['fileOwner']);
} catch(NoUserException $e) {
\OC::$server->getLogger()->logException($e, ['app' => 'files_sharing']);
return false;
}
$mounts = \OC\Files\Filesystem::getMountByNumericId($source['storage']);
if (is_array($mounts) and !empty($mounts)) {
$fullPath = $mounts[0]->getMountPoint() . $source['path'];
@@ -18,7 +18,6 @@
placeholder="<?php p($l->t('Password')); ?>" value=""
autocomplete="off" autocapitalize="off" autocorrect="off"
autofocus />
<img class="svg" id="password-icon" src="<?php print_unescaped(image_path('', 'actions/password.svg')); ?>" alt=""/>
<input type="submit" value=""
class="svg icon-confirm input-button-inline" />
</p>
@@ -9,7 +9,7 @@
href="<?php p(link_to_docs('admin-sharing-federated')); ?>"></a>
<p>
<input type="checkbox" name="outgoing_server2server_share_enabled" id="outgoingServer2serverShareEnabled"
<input type="checkbox" name="outgoing_server2server_share_enabled" id="outgoingServer2serverShareEnabled" class="checkbox"
value="1" <?php if ($_['outgoingServer2serverShareEnabled']) print_unescaped('checked="checked"'); ?> />
<label for="outgoingServer2serverShareEnabled">
<?php p($l->t('Allow users on this server to send shares to other servers'));?>
@@ -17,7 +17,7 @@
</p>
<p>
<input type="checkbox" name="incoming_server2server_share_enabled" id="incomingServer2serverShareEnabled"
<input type="checkbox" name="incoming_server2server_share_enabled" id="incomingServer2serverShareEnabled" class="checkbox"
value="1" <?php if ($_['incomingServer2serverShareEnabled']) print_unescaped('checked="checked"'); ?> />
<label for="incomingServer2serverShareEnabled">
<?php p($l->t('Allow users on this server to receive shares from other servers'));?>
+7 -4
View File
@@ -47,8 +47,10 @@ class Test_Files_Sharing_Storage extends OCA\Files_sharing\Tests\TestCase {
}
protected function tearDown() {
$this->view->unlink($this->folder);
$this->view->unlink($this->filename);
if ($this->view) {
$this->view->unlink($this->folder);
$this->view->unlink($this->filename);
}
\OC\Files\Filesystem::getLoader()->removeStorageWrapper('oc_trashbin');
@@ -85,8 +87,9 @@ class Test_Files_Sharing_Storage extends OCA\Files_sharing\Tests\TestCase {
$this->assertFalse($user2View->is_dir($this->folder));
// delete the local folder
$fullPath = \OC_Config::getValue('datadirectory') . '/' . self::TEST_FILES_SHARING_API_USER2 . '/files/localfolder';
rmdir($fullPath);
/** @var \OC\Files\Storage\Storage $storage */
list($storage, $internalPath) = \OC\Files\Filesystem::resolvePath('/' . self::TEST_FILES_SHARING_API_USER2 . '/files/localfolder');
$storage->rmdir($internalPath);
//enforce reload of the mount points
self::loginHelper(self::TEST_FILES_SHARING_API_USER2);
+4 -6
View File
@@ -108,9 +108,8 @@ class Test_Files_Sharing_Watcher extends OCA\Files_sharing\Tests\TestCase {
$this->sharedCache->put('', array('mtime' => 10, 'storage_mtime' => 10, 'size' => '-1', 'mimetype' => 'httpd/unix-directory'));
// run the propagation code
$result = $this->sharedStorage->getWatcher()->checkUpdate('');
$this->assertTrue($result);
$this->sharedStorage->getWatcher()->checkUpdate('');
$this->sharedStorage->getCache()->correctFolderSize('');
// the owner's parent dirs must have increase size
$newSizes = self::getOwnerDirSizes('files/container/shareddir');
@@ -139,9 +138,8 @@ class Test_Files_Sharing_Watcher extends OCA\Files_sharing\Tests\TestCase {
$this->sharedCache->put('subdir', array('mtime' => 10, 'storage_mtime' => 10, 'size' => $dataLen, 'mimetype' => 'text/plain'));
// run the propagation code
$result = $this->sharedStorage->getWatcher()->checkUpdate('subdir');
$this->assertTrue($result);
$this->sharedStorage->getWatcher()->checkUpdate('subdir');
$this->sharedStorage->getCache()->correctFolderSize('subdir');
// the owner's parent dirs must have increase size
$newSizes = self::getOwnerDirSizes('files/container/shareddir/subdir');
+4 -1
View File
@@ -10,11 +10,14 @@ To prevent a user from running out of disk space, the ownCloud Deleted files app
<licence>AGPL</licence>
<author>Bjoern Schiessle</author>
<shipped>true</shipped>
<requiremin>4.9</requiremin>
<default_enable/>
<version>0.7.0</version>
<types>
<filesystem/>
</types>
<dependencies>
<owncloud min-version="8.2" />
</dependencies>
<documentation>
<user>user-trashbin</user>
</documentation>
-1
View File
@@ -1 +0,0 @@
0.6.3
+18
View File
@@ -18,3 +18,21 @@
#app-content-trashbin #filestable .summary .filesize {
display: none;
}
#app-navigation > ul {
padding-bottom: 44px;
}
/* move Deleted Files to bottom of sidebar */
.nav-trashbin {
position: fixed !important;
bottom: 44px;
width: inherit !important;
background-color: #fff;
border-right: 1px solid #eee;
}
/* double padding to account for Deleted files entry, issue with Firefox */
.app-files #app-navigation > ul li:nth-last-child(2) {
margin-bottom: 44px;
}
+2 -1
View File
@@ -26,7 +26,8 @@ OCA.Trashbin.App = {
this.fileList = new OCA.Trashbin.FileList(
$('#app-content-trashbin'), {
scrollContainer: $('#app-content'),
fileActions: this._createFileActions()
fileActions: this._createFileActions(),
detailsViewEnabled: false
}
);
},
+3 -2
View File
@@ -581,8 +581,9 @@ class Trashbin {
if ($quota === null || $quota === 'none') {
$quota = \OC\Files\Filesystem::free_space('/');
$softQuota = false;
if ($quota === \OCP\Files\FileInfo::SPACE_UNKNOWN) {
$quota = 0;
// inf or unknown free space
if ($quota < 0) {
$quota = PHP_INT_MAX;
}
} else {
$quota = \OCP\Util::computerFileSize($quota);
+17 -13
View File
@@ -236,6 +236,8 @@ class Test_Trashbin extends \Test\TestCase {
// user2-1.txt should have been expired
$this->verifyArray($filesInTrashUser2AfterDelete, array('user2-2.txt', 'user1-4.txt'));
self::loginHelper(self::TEST_TRASHBIN_USER1);
// user1-1.txt and user1-3.txt should have been expired
$filesInTrashUser1AfterDelete = OCA\Files_Trashbin\Helper::getTrashFiles('/', self::TEST_TRASHBIN_USER1);
@@ -600,22 +602,24 @@ class Test_Trashbin extends \Test\TestCase {
// delete source folder
list($storage, $internalPath) = $this->rootView->resolvePath('/' . self::TEST_TRASHBIN_USER1 . '/files/folder');
$folderAbsPath = $storage->getSourcePath($internalPath);
// make folder read-only
chmod($folderAbsPath, 0555);
if ($storage instanceof \OC\Files\Storage\Local) {
$folderAbsPath = $storage->getSourcePath($internalPath);
// make folder read-only
chmod($folderAbsPath, 0555);
$this->assertTrue(
OCA\Files_Trashbin\Trashbin::restore(
'file1.txt.d' . $trashedFile->getMtime(),
$trashedFile->getName(),
$trashedFile->getMtime()
)
);
$this->assertTrue(
OCA\Files_Trashbin\Trashbin::restore(
'file1.txt.d' . $trashedFile->getMtime(),
$trashedFile->getName(),
$trashedFile->getMtime()
)
);
$file = $userFolder->get('file1.txt');
$this->assertEquals('foo', $file->getContent());
$file = $userFolder->get('file1.txt');
$this->assertEquals('foo', $file->getContent());
chmod($folderAbsPath, 0755);
chmod($folderAbsPath, 0755);
}
}
/**
+4 -1
View File
@@ -4,16 +4,19 @@
<name>Versions</name>
<licence>AGPL</licence>
<author>Frank Karlitschek, Bjoern Schiessle</author>
<requiremin>4.93</requiremin>
<shipped>true</shipped>
<description>
This application enables ownCloud to automatically maintain older versions of files that are changed. When enabled, a hidden versions folder is provisioned in every users directory and is used to store old file versions. A user can revert to an older version through the web interface at any time, with the replaced file becoming a version. ownCloud then automatically manages the versions folder to ensure the user doesnt run out of Quota because of versions.
In addition to the expiry of versions, ownClouds versions app makes certain never to use more than 50% of the users currently available free space. If stored versions exceed this limit, ownCloud will delete the oldest versions first until it meets this limit. More information is available in the Versions documentation.
</description>
<version>1.1.0</version>
<types>
<filesystem/>
</types>
<dependencies>
<owncloud min-version="8.2" />
</dependencies>
<documentation>
<user>user-versions</user>
</documentation>
-1
View File
@@ -1 +0,0 @@
1.0.6
+26 -9
View File
@@ -347,7 +347,20 @@ class Storage {
$view->lockFile($path1, ILockingProvider::LOCK_EXCLUSIVE);
$view->lockFile($path2, ILockingProvider::LOCK_EXCLUSIVE);
$result = $storage2->moveFromStorage($storage1, $internalPath1, $internalPath2);
// TODO add a proper way of overwriting a file while maintaining file ids
if ($storage1->instanceOfStorage('\OC\Files\ObjectStore\ObjectStoreStorage') || $storage2->instanceOfStorage('\OC\Files\ObjectStore\ObjectStoreStorage')) {
$source = $storage1->fopen($internalPath1, 'r');
$target = $storage2->fopen($internalPath2, 'w');
list(, $result) = \OC_Helper::streamCopy($source, $target);
fclose($source);
fclose($target);
if ($result !== false) {
$storage1->unlink($internalPath1);
}
} else {
$result = $storage2->moveFromStorage($storage1, $internalPath1, $internalPath2);
}
$view->unlockFile($path1, ILockingProvider::LOCK_EXCLUSIVE);
$view->unlockFile($path2, ILockingProvider::LOCK_EXCLUSIVE);
@@ -663,17 +676,21 @@ class Storage {
// calculate available space for version history
// subtract size of files and current versions size from quota
if ($softQuota) {
$files_view = new \OC\Files\View('/'.$uid.'/files');
$rootInfo = $files_view->getFileInfo('/', false);
$free = $quota-$rootInfo['size']; // remaining free space for user
if ( $free > 0 ) {
$availableSpace = ($free * self::DEFAULTMAXSIZE / 100) - ($versionsSize + $offset); // how much space can be used for versions
if ($quota >= 0) {
if ($softQuota) {
$files_view = new \OC\Files\View('/' . $uid . '/files');
$rootInfo = $files_view->getFileInfo('/', false);
$free = $quota - $rootInfo['size']; // remaining free space for user
if ($free > 0) {
$availableSpace = ($free * self::DEFAULTMAXSIZE / 100) - ($versionsSize + $offset); // how much space can be used for versions
} else {
$availableSpace = $free - $versionsSize - $offset;
}
} else {
$availableSpace = $free - $versionsSize - $offset;
$availableSpace = $quota - $offset;
}
} else {
$availableSpace = $quota - $offset;
$availableSpace = PHP_INT_MAX;
}
$allVersions = Storage::getVersions($uid, $filename);
+2 -3
View File
@@ -301,11 +301,10 @@ class Test_Files_Versioning extends \Test\TestCase {
// execute rename hook of versions app
\OC\Files\Filesystem::rename('/folder1/test.txt', '/folder1/folder2/test.txt');
self::loginHelper(self::TEST_VERSIONS_USER2);
$this->runCommands();
self::loginHelper(self::TEST_VERSIONS_USER);
$this->assertFalse($this->rootView->file_exists($v1));
$this->assertFalse($this->rootView->file_exists($v2));
+4 -1
View File
@@ -13,14 +13,17 @@
</description>
<licence>AGPL</licence>
<author>Tom Needham</author>
<requiremin>8</requiremin>
<shipped>true</shipped>
<default_enable/>
<documentation>
<admin>admin-provisioning-api</admin>
</documentation>
<version>0.3.0</version>
<types>
<!-- this is used to disable the feature of enabling an app for specific groups only because this would break this app -->
<filesystem/>
</types>
<dependencies>
<owncloud min-version="8.2" />
</dependencies>
</info>
-1
View File
@@ -1 +0,0 @@
0.2
+2 -1
View File
@@ -9,8 +9,8 @@ A user logs into ownCloud with their LDAP or AD credentials, and is granted acce
</description>
<licence>AGPL</licence>
<author>Dominik Schmidt and Arthur Schiwon</author>
<requiremin>4.93</requiremin>
<shipped>true</shipped>
<version>0.7.0</version>
<types>
<authentication/>
</types>
@@ -19,5 +19,6 @@ A user logs into ownCloud with their LDAP or AD credentials, and is granted acce
</documentation>
<dependencies>
<lib>ldap</lib>
<owncloud min-version="8.2" />
</dependencies>
</info>
-1
View File
@@ -1 +0,0 @@
0.6.3
+2 -7
View File
@@ -33,7 +33,6 @@ namespace OCA\user_ldap;
use OCA\user_ldap\lib\Access;
use OCA\user_ldap\lib\BackendUtility;
use OCA\user_ldap\lib\user\User;
class GROUP_LDAP extends BackendUtility implements \OCP\GroupInterface {
protected $enabled = false;
@@ -198,11 +197,7 @@ class GROUP_LDAP extends BackendUtility implements \OCP\GroupInterface {
return array();
}
$seen[$DN] = 1;
$user = $this->access->userManager->get($DN);
if(!$user instanceof User) {
return array();
}
$groups = $user->getMemberOfGroups();
$groups = $this->access->readAttribute($DN, 'memberOf');
if (!is_array($groups)) {
return array();
}
@@ -214,7 +209,7 @@ class GROUP_LDAP extends BackendUtility implements \OCP\GroupInterface {
$subGroups = $this->_getGroupDNsFromMemberOf($group, $seen);
$allGroups = array_merge($allGroups, $subGroups);
}
}
}
return $allGroups;
}
+1
View File
@@ -352,6 +352,7 @@ OCA = OCA || {};
$('.ldap_submit').button();
$('.ldap_action_test_connection').button();
$('#ldapSettings').tabs({ beforeActivate: this.onTabChange });
$('#ldapSettings :input').tooltip({placement: "right", container: "body", trigger: "hover"});
this.initControls();
this.disableTabs();
+14
View File
@@ -671,6 +671,20 @@ class Access extends LDAPUtility implements user\IUserTools {
return $users;
}
/**
* counts the number of users according to a provided loginName and
* utilizing the login filter.
*
* @param string $loginName
* @return array
*/
public function countUsersByLoginName($loginName) {
$loginName = $this->escapeFilterPart($loginName);
$filter = str_replace('%uid', $loginName, $this->connection->ldapLoginFilter);
$users = $this->countUsers($filter);
return $users;
}
/**
* @param string $filter
* @param string|string[] $attr
+3 -2
View File
@@ -150,8 +150,9 @@ class Configuration {
$setMethod = 'setRawValue';
break;
case 'homeFolderNamingRule':
if(!empty(trim($val)) && strpos($val, 'attr:') === false) {
$val = 'attr:'.trim($val);
$trimmedVal = trim($val);
if(!empty($trimmedVal) && strpos($val, 'attr:') === false) {
$val = 'attr:'.$trimmedVal;
}
break;
case 'ldapBase':
+2 -2
View File
@@ -631,12 +631,12 @@ class Wizard extends LDAPUtility {
throw new \Exception('missing placeholder');
}
$users = $this->access->fetchUsersByLoginName($loginName);
$users = $this->access->countUsersByLoginName($loginName);
if($this->ldap->errno($cr) !== 0) {
throw new \Exception($this->ldap->error($cr));
}
$filter = str_replace('%uid', $loginName, $this->access->connection->ldapLoginFilter);
$this->result->addChange('ldap_test_loginname', count($users));
$this->result->addChange('ldap_test_loginname', $users);
$this->result->addChange('ldap_test_effective_filter', $filter);
return $this->result;
}
+3 -8
View File
@@ -42,14 +42,9 @@ class Test_Group_Ldap extends \Test\TestCase {
$connector = $this->getMock('\OCA\user_ldap\lib\Connection',
$conMethods,
array($lw, null, null));
$um = new \OCA\user_ldap\lib\user\Manager(
$this->getMock('\OCP\IConfig'),
$this->getMock('\OCA\user_ldap\lib\FilesystemHelper'),
$this->getMock('\OCA\user_ldap\lib\LogWrapper'),
$this->getMock('\OCP\IAvatarManager'),
$this->getMock('\OCP\Image'),
$this->getMock('\OCP\IDBConnection')
);
$um = $this->getMockBuilder('\OCA\user_ldap\lib\user\Manager')
->disableOriginalConstructor()
->getMock();
$access = $this->getMock('\OCA\user_ldap\lib\Access',
$accMethods,
array($connector, $lw, $um));
@@ -29,5 +29,8 @@ namespace OCA\user_ldap\tests\integration;
* scope of these tests, we replace it with a mock.
*/
class FakeManager extends \OCA\user_ldap\lib\user\Manager {
public function __construct() {}
public function __construct() {
$this->ocConfig = \OC::$server->getConfig();
$this->image = new \OCP\Image();
}
}
@@ -0,0 +1,68 @@
<?php
/**
* @author Arthur Schiwon <blizzz@owncloud.com>
*
* @copyright Copyright (c) 2015, ownCloud, Inc.
* @license AGPL-3.0
*
* This code is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License, version 3,
* as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License, version 3,
* along with this program. If not, see <http://www.gnu.org/licenses/>
*
*/
namespace OCA\user_ldap\tests\integration\lib;
use OCA\user_ldap\lib\user\Manager as LDAPUserManager;
use OCA\user_ldap\tests\integration\AbstractIntegrationTest;
use OCA\User_LDAP\Mapping\UserMapping;
use OCA\user_ldap\USER_LDAP;
require_once __DIR__ . '/../../../../../lib/base.php';
class IntegrationTestUserHome extends AbstractIntegrationTest {
/**
* prepares the LDAP environment and sets up a test configuration for
* the LDAP backend.
*/
public function init() {
require(__DIR__ . '/../setup-scripts/createExplicitUsers.php');
parent::init();
}
/**
* tests countUsersByLoginName where it is expected that the login name does
* not match any LDAP user
*
* @return bool
*/
protected function case1() {
$result = $this->access->countUsersByLoginName('nothere');
return $result === 0;
}
/**
* tests countUsersByLoginName where it is expected that the login name does
* match one LDAP user
*
* @return bool
*/
protected function case2() {
$result = $this->access->countUsersByLoginName('alice');
return $result === 1;
}
}
require_once(__DIR__ . '/../setup-scripts/config.php');
$test = new IntegrationTestUserHome($host, $port, $adn, $apwd, $bdn);
$test->init();
$test->run();
@@ -0,0 +1,79 @@
<?php
/**
* @author Arthur Schiwon <blizzz@owncloud.com>
*
* @copyright Copyright (c) 2015, ownCloud, Inc.
* @license AGPL-3.0
*
* This code is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License, version 3,
* as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License, version 3,
* along with this program. If not, see <http://www.gnu.org/licenses/>
*
*/
namespace OCA\user_ldap\tests\integration\lib;
use OCA\user_ldap\lib\user\Manager as LDAPUserManager;
use OCA\user_ldap\tests\integration\AbstractIntegrationTest;
use OCA\User_LDAP\Mapping\UserMapping;
use OCA\user_ldap\USER_LDAP;
require_once __DIR__ . '/../../../../../lib/base.php';
class IntegrationTestUserHome extends AbstractIntegrationTest {
/** @var UserMapping */
protected $mapping;
/** @var USER_LDAP */
protected $backend;
/**
* prepares the LDAP environment and sets up a test configuration for
* the LDAP backend.
*/
public function init() {
require(__DIR__ . '/../setup-scripts/createExplicitUsers.php');
parent::init();
$this->mapping = new UserMapping(\OC::$server->getDatabaseConnection());
$this->mapping->clear();
$this->access->setUserMapper($this->mapping);
$this->backend = new \OCA\user_ldap\USER_LDAP($this->access, \OC::$server->getConfig());
}
/**
* tests fetchUserByLoginName where it is expected that the login name does
* not match any LDAP user
*
* @return bool
*/
protected function case1() {
$result = $this->access->fetchUsersByLoginName('nothere');
return $result === [];
}
/**
* tests fetchUserByLoginName where it is expected that the login name does
* match one LDAP user
*
* @return bool
*/
protected function case2() {
$result = $this->access->fetchUsersByLoginName('alice');
return count($result) === 1;
}
}
require_once(__DIR__ . '/../setup-scripts/config.php');
$test = new IntegrationTestUserHome($host, $port, $adn, $apwd, $bdn);
$test->init();
$test->run();
+1
View File
@@ -270,6 +270,7 @@ function execute_tests {
fi
if [ "$PRIMARY_STORAGE_CONFIG" == "swift" ] ; then
cd ..
echo "Kill the swift docker"
tests/objectstore/stop-swift-ceph.sh
fi
+8 -9
View File
@@ -513,6 +513,14 @@ $CONFIG = array(
*/
'loglevel' => 2,
/**
* If you maintain different instances and aggregate the logs, you may want
* to distinguish between them. ``syslog_tag`` can be set per instance
* with a unique id. Only available if ``log_type`` is set to ``syslog``.
* The default value is ``ownCloud``.
*/
'syslog_tag' => 'ownCloud',
/**
* Log condition for log level increase based on conditions. Once one of these
* conditions is met, the required log level is set to debug. This allows to
@@ -1144,15 +1152,6 @@ $CONFIG = array(
*/
'debug' => false,
/**
* Skips the migration test during upgrades
*
* If this is set to true the migration test are deactivated during upgrade.
* This is only recommended in installations where upgrade tests are run in
* advance with the same data on a test system.
*/
'update.skip-migration-test' => false,
/**
* This entry is just here to show a warning in case somebody copied the sample
* configuration. DO NOT ADD THIS SWITCH TO YOUR CONFIGURATION!
+23 -3
View File
@@ -48,9 +48,28 @@ if (isset($_POST['action']) && isset($_POST['itemType']) && isset($_POST['itemSo
$shareType = (int)$_POST['shareType'];
$shareWith = $_POST['shareWith'];
$itemSourceName = isset($_POST['itemSourceName']) ? (string)$_POST['itemSourceName'] : null;
if ($shareType === OCP\Share::SHARE_TYPE_LINK && $shareWith == '') {
$shareWith = null;
/*
* Nasty nasty fix for https://github.com/owncloud/core/issues/19950
*/
$passwordChanged = null;
if (is_array($shareWith)) {
$passwordChanged = ($shareWith['passwordChanged'] === 'true');
if ($shareType === OCP\Share::SHARE_TYPE_LINK && $shareWith['password'] === '') {
$shareWith = null;
} else {
$shareWith = $shareWith['password'];
}
} else {
/*
* We need this branch since the calendar and contacts also use this
* endpoint
*/
if ($shareType === OCP\Share::SHARE_TYPE_LINK && $shareWith === '') {
$shareWith = null;
}
}
$itemSourceName=(isset($_POST['itemSourceName'])) ? (string)$_POST['itemSourceName']:'';
$token = OCP\Share::shareItem(
@@ -60,7 +79,8 @@ if (isset($_POST['action']) && isset($_POST['itemType']) && isset($_POST['itemSo
$shareWith,
$_POST['permissions'],
$itemSourceName,
(!empty($_POST['expirationDate']) ? new \DateTime((string)$_POST['expirationDate']) : null)
(!empty($_POST['expirationDate']) ? new \DateTime((string)$_POST['expirationDate']) : null),
$passwordChanged
);
if (is_string($token)) {
+13 -10
View File
@@ -41,21 +41,12 @@ if (OC::checkUpgrade(false)) {
// avoid side effects
\OC_User::setIncognitoMode(true);
$logger = \OC::$server->getLogger();
$config = \OC::$server->getConfig();
$updater = new \OC\Updater(
\OC::$server->getHTTPHelper(),
$config,
\OC::$server->getConfig(),
$logger
);
if ($config->getSystemValue('update.skip-migration-test', false)) {
$eventSource->send('success', (string)$l->t('Migration tests are skipped - "update.skip-migration-test" is activated in config.php'));
$updater->setSimulateStepEnabled(false);
}
$incompatibleApps = [];
$disabledThirdPartyApps = [];
@@ -68,12 +59,24 @@ if (OC::checkUpgrade(false)) {
$updater->listen('\OC\Updater', 'maintenanceActive', function () use ($eventSource, $l) {
$eventSource->send('success', (string)$l->t('Maintenance mode is kept active'));
});
$updater->listen('\OC\Updater', 'dbUpgradeBefore', function () use($eventSource, $l) {
$eventSource->send('success', (string)$l->t('Updating database schema'));
});
$updater->listen('\OC\Updater', 'dbUpgrade', function () use ($eventSource, $l) {
$eventSource->send('success', (string)$l->t('Updated database'));
});
$updater->listen('\OC\Updater', 'dbSimulateUpgradeBefore', function () use($eventSource, $l) {
$eventSource->send('success', (string)$l->t('Checking whether the database schema can be updated (this can take a long time depending on the database size)'));
});
$updater->listen('\OC\Updater', 'dbSimulateUpgrade', function () use ($eventSource, $l) {
$eventSource->send('success', (string)$l->t('Checked database schema update'));
});
$updater->listen('\OC\Updater', 'appUpgradeCheckBefore', function () use ($eventSource, $l) {
$eventSource->send('success', (string)$l->t('Checking updates of apps'));
});
$updater->listen('\OC\Updater', 'appSimulateUpdate', function ($app) use ($eventSource, $l) {
$eventSource->send('success', (string)$l->t('Checking whether the database schema for %s can be updated (this can take a long time depending on the database size)', [$app]));
});
$updater->listen('\OC\Updater', 'appUpgradeCheck', function () use ($eventSource, $l) {
$eventSource->send('success', (string)$l->t('Checked database schema update for apps'));
});
+16 -1
View File
@@ -26,6 +26,7 @@ namespace OC\Core\Command\Maintenance;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Output\OutputInterface;
class Repair extends Command {
@@ -49,10 +50,24 @@ class Repair extends Command {
protected function configure() {
$this
->setName('maintenance:repair')
->setDescription('repair this installation');
->setDescription('repair this installation')
->addOption(
'include-expensive',
null,
InputOption::VALUE_NONE,
'Use this option when you want to include resource and load expensive tasks'
)
;
}
protected function execute(InputInterface $input, OutputInterface $output) {
$includeExpensive = $input->getOption('include-expensive');
if ($includeExpensive) {
foreach ($this->repair->getExpensiveRepairSteps() as $step) {
$this->repair->addStep($step);
}
}
$maintenanceMode = $this->config->getSystemValue('maintenance', false);
$this->config->setSystemValue('maintenance', true);
+12 -6
View File
@@ -96,12 +96,6 @@ class Upgrade extends Command {
$updateStepEnabled = true;
$skip3rdPartyAppsDisable = false;
if ($this->config->getSystemValue('update.skip-migration-test', false)) {
$output->writeln(
'<info>"skip-migration-test" is activated via config.php</info>'
);
$simulateStepEnabled = false;
}
if ($input->getOption('skip-migration-test')) {
$simulateStepEnabled = false;
}
@@ -155,9 +149,15 @@ class Upgrade extends Command {
}
$output->writeln($message);
});
$updater->listen('\OC\Updater', 'dbUpgradeBefore', function () use($output) {
$output->writeln('<info>Updating database schema</info>');
});
$updater->listen('\OC\Updater', 'dbUpgrade', function () use($output) {
$output->writeln('<info>Updated database</info>');
});
$updater->listen('\OC\Updater', 'dbSimulateUpgradeBefore', function () use($output) {
$output->writeln('<info>Checking whether the database schema can be updated (this can take a long time depending on the database size)</info>');
});
$updater->listen('\OC\Updater', 'dbSimulateUpgrade', function () use($output) {
$output->writeln('<info>Checked database schema update</info>');
});
@@ -176,6 +176,12 @@ class Upgrade extends Command {
$updater->listen('\OC\Updater', 'repairError', function ($app) use($output) {
$output->writeln('<error>Repair error: ' . $app . '</error>');
});
$updater->listen('\OC\Updater', 'appUpgradeCheckBefore', function () use ($output) {
$output->writeln('<info>Checking updates of apps</info>');
});
$updater->listen('\OC\Updater', 'appSimulateUpdate', function ($app) use ($output) {
$output->writeln("<info>Checking whether the database schema for <$app> can be updated (this can take a long time depending on the database size)</info>");
});
$updater->listen('\OC\Updater', 'appUpgradeCheck', function () use ($output) {
$output->writeln('<info>Checked database schema update for apps</info>');
});
+7 -2
View File
@@ -35,7 +35,6 @@
height: 100%;
width: inherit;
overflow: auto;
padding-bottom: 44px;
-moz-box-sizing: border-box; box-sizing: border-box;
}
#app-navigation li {
@@ -308,8 +307,13 @@
-o-filter: drop-shadow(0 0 5px rgba(150, 150, 150, 0.75));
filter: drop-shadow(0 0 5px rgba(150, 150, 150, 0.75));
}
.ie8 .bubble {
.ie .bubble,
.ie #app-navigation .app-navigation-entry-menu,
.ie .bubble:after,
.ie #app-navigation .app-navigation-entry-menu:after {
border: 1px solid #eee;
}
.ie8 .bubble {
margin-top: 18px;
}
.ie8 .bubble:after {
@@ -691,6 +695,7 @@ em {
.popovermenu .menuitem .no-icon {
display: inline-block;
width: 16px;
height: 16px;
margin-right: 10px;
}
+6
View File
@@ -42,6 +42,12 @@ select {
background-image: url('../img/actions/settings.png');
}
.lte9 input[type="submit"], .lte9 input[type="button"],
.lte9 button, .lte9 .button,
.lte9 #quota, .lte9 select, .lte9 .pager li a {
background-color: #f1f1f1;
}
/* IE8 needs PNG image for header logo */
.ie8 #header .logo {
background-image: url(../img/logo-icon-175px.png);
-25
View File
@@ -585,30 +585,6 @@ input[type="submit"].enabled {
color: #ccc;
}
/* Icons for username and password fields to better recognize them */
#adminlogin,
#adminpass,
input[name='adminpass-clone'],
#user,
#password,
input[name='password-clone'] {
width: 223px !important;
padding-left: 36px !important;
}
#adminlogin+label+img,
#adminpass-icon,
#user+label+img,
#password-icon {
position: absolute;
left: 16px;
top: 22px;
-ms-filter: "progid:DXImageTransform.Microsoft.Alpha(Opacity=30)";
opacity: .3;
}
#adminpass-icon, #password-icon {
top: 17px;
}
/* General new input field look */
#body-login input[type="text"],
#body-login input[type="password"],
@@ -1204,4 +1180,3 @@ fieldset.warning legend + p, fieldset.update legend + p {
opacity: 0;
-ms-filter: "progid:DXImageTransform.Microsoft.Alpha(Opacity=0)";
}
+11 -1
View File
@@ -1632,6 +1632,15 @@ OC.Util = {
return $el;
},
/**
* Returns whether this is IE
*
* @return {bool} true if this is IE, false otherwise
*/
isIE: function() {
return $('html').hasClass('ie');
},
/**
* Returns whether this is IE8
*
@@ -1989,7 +1998,8 @@ jQuery.fn.tipsy = function(argument) {
placement: 'bottom',
delay: { 'show': 0, 'hide': 0},
trigger: 'hover',
html: false
html: false,
container: 'body'
};
if(argument.gravity) {
switch(argument.gravity) {
+2
View File
@@ -46,6 +46,8 @@ OC.MimeType = {
return 'folder';
} else if (mimeType === 'dir-shared' && $.inArray('folder-shared', files) !== -1) {
return 'folder-shared';
} else if (mimeType === 'dir-public' && $.inArray('folder-public', files) !== -1) {
return 'folder-public';
} else if (mimeType === 'dir-external' && $.inArray('folder-external', files) !== -1) {
return 'folder-external';
} else if ($.inArray(icon, files) !== -1) {
+14 -3
View File
@@ -253,14 +253,25 @@ OC.Share = _.extend(OC.Share || {}, {
// update folder icon
if (type === 'dir' && (hasShares || hasLink || owner)) {
if (hasLink) {
shareFolderIcon = OC.imagePath('core', 'filetypes/folder-public');
shareFolderIcon = OC.MimeType.getIconUrl('dir-public');
}
else {
shareFolderIcon = OC.imagePath('core', 'filetypes/folder-shared');
shareFolderIcon = OC.MimeType.getIconUrl('dir-shared');
}
$tr.find('.filename .thumbnail').css('background-image', 'url(' + shareFolderIcon + ')');
$tr.attr('data-icon', shareFolderIcon);
} else if (type === 'dir') {
shareFolderIcon = OC.imagePath('core', 'filetypes/folder');
var mountType = $tr.attr('data-mounttype');
// FIXME: duplicate of FileList._createRow logic for external folder,
// need to refactor the icon logic into a single code path eventually
if (mountType && mountType.indexOf('external') === 0) {
shareFolderIcon = OC.MimeType.getIconUrl('dir-external');
$tr.attr('data-icon', shareFolderIcon);
} else {
shareFolderIcon = OC.MimeType.getIconUrl('dir');
// back to default
$tr.removeAttr('data-icon');
}
$tr.find('.filename .thumbnail').css('background-image', 'url(' + shareFolderIcon + ')');
}
// update share action text / icon
+9 -7
View File
@@ -8,6 +8,8 @@
*
*/
/* global moment */
(function() {
if (!OC.Share) {
OC.Share = {};
@@ -19,9 +21,9 @@
// in the LinkShareView to ease reusing it in future. Then,
// modifications (getting rid of IDs) are still necessary.
'{{#if isLinkShare}}' +
'<input type="checkbox" name="expirationCheckbox" class="expirationCheckbox checkbox" id="expirationCheckbox" value="1" ' +
'<input type="checkbox" name="expirationCheckbox" class="expirationCheckbox checkbox" id="expirationCheckbox-{{cid}}" value="1" ' +
'{{#if isExpirationSet}}checked="checked"{{/if}} {{#if disableCheckbox}}disabled="disabled"{{/if}} />' +
'<label for="expirationCheckbox">{{setExpirationLabel}}</label>' +
'<label for="expirationCheckbox-{{cid}}">{{setExpirationLabel}}</label>' +
'<div class="expirationDateContainer {{#unless isExpirationSet}}hidden{{/unless}}">' +
' <label for="expirationDate" class="hidden-visually" value="{{expirationDate}}">{{expirationLabel}}</label>' +
' <input id="expirationDate" class="datepicker" type="text" placeholder="{{expirationDatePlaceholder}}" value="{{expirationValue}}" />' +
@@ -134,11 +136,11 @@
var expiration;
if (isExpirationSet) {
expiration = moment(this.model.get('linkShare').expiration, 'YYYY-MM-DD').format('DD-MM-YYYY')
expiration = moment(this.model.get('linkShare').expiration, 'YYYY-MM-DD').format('DD-MM-YYYY');
}
var expirationTemplate = this.template();
this.$el.html(expirationTemplate({
this.$el.html(this.template({
cid: this.cid,
setExpirationLabel: t('core', 'Set expiration date'),
expirationLabel: t('core', 'Expiration'),
expirationDatePlaceholder: t('core', 'Expiration date'),
@@ -186,11 +188,11 @@
* @returns {Function} from Handlebars
* @private
*/
template: function () {
template: function (data) {
if (!this._template) {
this._template = Handlebars.compile(TEMPLATE);
}
return this._template;
return this._template(data);
}
});
+79 -55
View File
@@ -13,36 +13,42 @@
OC.Share = {};
}
var PASSWORD_PLACEHOLDER = '**********';
var PASSWORD_PLACEHOLDER_MESSAGE = t('core', 'Choose a password for the public link');
var TEMPLATE =
'{{#if shareAllowed}}' +
'<span class="icon-loading-small hidden"></span>' +
'<input type="checkbox" name="linkCheckbox" id="linkCheckbox" class="checkbox" value="1" {{#if isLinkShare}}checked="checked"{{/if}} /><label for="linkCheckbox">{{linkShareLabel}}</label>' +
'<input type="checkbox" name="linkCheckbox" id="linkCheckbox-{{cid}}" class="checkbox linkCheckbox" value="1" {{#if isLinkShare}}checked="checked"{{/if}} />' +
'<label for="linkCheckbox-{{cid}}">{{linkShareLabel}}</label>' +
'<br />' +
'<label for="linkText" class="hidden-visually">{{urlLabel}}</label>' +
'<input id="linkText" {{#unless isLinkShare}}class="hidden"{{/unless}} type="text" readonly="readonly" value="{{shareLinkURL}}" />' +
'<label for="linkText-{{cid}}" class="hidden-visually">{{urlLabel}}</label>' +
'<input id="linkText-{{cid}}" class="linkText {{#unless isLinkShare}}hidden{{/unless}}" type="text" readonly="readonly" value="{{shareLinkURL}}" />' +
' {{#if showPasswordCheckBox}}' +
'<input type="checkbox" name="showPassword" id="showPassword" class="checkbox" {{#if isPasswordSet}}checked="checked"{{/if}} value="1" /><label for="showPassword">{{enablePasswordLabel}}</label>' +
'<input type="checkbox" name="showPassword" id="showPassword-{{cid}}" class="checkbox showPasswordCheckbox" {{#if isPasswordSet}}checked="checked"{{/if}} value="1" />' +
'<label for="showPassword-{{cid}}">{{enablePasswordLabel}}</label>' +
' {{/if}}' +
'<div id="linkPass" {{#unless isPasswordSet}}class="hidden"{{/unless}}>' +
' <label for="linkPassText" class="hidden-visually">{{passwordLabel}}</label>' +
' <input id="linkPassText" type="password" placeholder="{{passwordPlaceholder}}" />' +
'<div id="linkPass" class="linkPass {{#unless isPasswordSet}}hidden{{/unless}}">' +
' <label for="linkPassText-{{cid}}" class="hidden-visually">{{passwordLabel}}</label>' +
' <input id="linkPassText-{{cid}}" class="linkPassText" type="password" placeholder="{{passwordPlaceholder}}" />' +
' <span class="icon-loading-small hidden"></span>' +
'</div>' +
' {{#if publicUpload}}' +
'<div id="allowPublicUploadWrapper">' +
' <span class="icon-loading-small hidden"></span>' +
' <input type="checkbox" value="1" name="allowPublicUpload" id="sharingDialogAllowPublicUpload" class="checkbox" {{{publicUploadChecked}}} />' +
'<label for="sharingDialogAllowPublicUpload">{{publicUploadLabel}}</label>' +
' <input type="checkbox" value="1" name="allowPublicUpload" id="sharingDialogAllowPublicUpload-{{cid}}" class="checkbox publicUploadCheckbox" {{{publicUploadChecked}}} />' +
'<label for="sharingDialogAllowPublicUpload-{{cid}}">{{publicUploadLabel}}</label>' +
'</div>' +
' {{/if}}' +
' {{#if mailPublicNotificationEnabled}}' +
'<form id="emailPrivateLink" class="emailPrivateLinkForm">' +
' <input id="email" value="" placeholder="{{mailPrivatePlaceholder}}" type="text" />' +
' <input id="emailButton" type="submit" value="{{mailButtonText}}" />' +
' <input id="email" class="emailField" value="" placeholder="{{mailPrivatePlaceholder}}" type="text" />' +
' <input id="emailButton" class="emailButton" type="submit" value="{{mailButtonText}}" />' +
'</form>' +
' {{/if}}' +
'{{else}}' +
'<input id="shareWith" type="text" placeholder="{{noSharingPlaceholder}}" disabled="disabled"/>' +
// FIXME: this doesn't belong in this view
'<input id="shareWith-{{cid}}" class="shareWithField" type="text" placeholder="{{noSharingPlaceholder}}" disabled="disabled"/>' +
'{{/if}}'
;
@@ -70,7 +76,13 @@
showLink: true,
events: {
'submit .emailPrivateLinkForm': '_onEmailPrivateLink'
'submit .emailPrivateLinkForm': '_onEmailPrivateLink',
'focusout input.linkPassText': 'onPasswordEntered',
'keyup input.linkPassText': 'onPasswordKeyUp',
'click .linkCheckbox': 'onLinkCheckBoxChange',
'click .linkText': 'onLinkTextClick',
'change .publicUploadCheckbox': 'onAllowPublicUploadChange',
'click .showPasswordCheckbox': 'onShowPasswordClick'
},
initialize: function(options) {
@@ -98,12 +110,20 @@
throw 'missing OC.Share.ShareConfigModel';
}
_.bindAll(this, 'onLinkCheckBoxChange', 'onPasswordEntered',
'onShowPasswordClick', 'onAllowPublicUploadChange');
_.bindAll(
this,
'_onEmailPrivateLink',
'onLinkCheckBoxChange',
'onPasswordEntered',
'onPasswordKeyUp',
'onLinkTextClick',
'onShowPasswordClick',
'onAllowPublicUploadChange'
);
},
onLinkCheckBoxChange: function() {
var $checkBox = this.$el.find('#linkCheckbox');
var $checkBox = this.$el.find('.linkCheckbox');
var $loading = $checkBox.siblings('.icon-loading-small');
if(!$loading.hasClass('hidden')) {
return false;
@@ -115,49 +135,53 @@
// this will create it
this.model.saveLinkShare();
} else {
this.$el.find('#linkPass').slideToggle(OC.menuSpeed);
// TODO drop with IE8 drop
if($('html').hasClass('ie8')) {
this.$el.find('#linkPassText').attr('placeholder', null);
this.$el.find('#linkPassText').val('');
}
this.$el.find('#linkPassText').focus();
this.$el.find('.linkPass').slideToggle(OC.menuSpeed);
this.$el.find('.linkPassText').focus();
}
} else {
$loading.removeClass('hidden');
if (this.model.get('linkShare').isLinkShare) {
this.model.removeLinkShare();
} else {
this.$el.find('#linkPass').slideToggle(OC.menuSpeed);
this.$el.find('.linkPass').slideToggle(OC.menuSpeed);
}
}
},
onLinkTextClick: function() {
this.focus();
this.select();
var $el = this.$el.find('.linkText');
$el.focus();
$el.select();
},
onShowPasswordClick: function() {
this.$el.find('#linkPass').slideToggle(OC.menuSpeed);
if(!this.$el.find('#showPassword').is(':checked')) {
this.$el.find('.linkPass').slideToggle(OC.menuSpeed);
if(!this.$el.find('.showPasswordCheckbox').is(':checked')) {
this.model.setPassword('');
this.model.saveLinkShare();
} else {
this.$el.find('#linkPassText').focus();
this.$el.find('.linkPassText').focus();
}
},
onPasswordKeyUp: function(event) {
if(event.keyCode == 13) {
this.onPasswordEntered();
}
},
onPasswordEntered: function() {
var self = this;
var $loading = this.$el.find('#linkPass .icon-loading-small');
var $loading = this.$el.find('.linkPass .icon-loading-small');
if (!$loading.hasClass('hidden')) {
// still in process
return;
}
var $input = this.$el.find('#linkPassText');
var $input = this.$el.find('.linkPassText');
$input.removeClass('error');
var password = $input.val();
if(password === '') {
// in IE9 the password might be the placeholder due to bugs in the placeholders polyfill
if(password === '' || password === PASSWORD_PLACEHOLDER || password === PASSWORD_PLACEHOLDER_MESSAGE) {
return;
}
@@ -178,29 +202,35 @@
},
onAllowPublicUploadChange: function() {
this.$el.find('#sharingDialogAllowPublicUpload')
.siblings('.icon-loading-small').removeClass('hidden').addClass('inlineblock');
this.model.setPublicUpload(this.$el.find('#sharingDialogAllowPublicUpload').is(':checked'));
var $checkbox = this.$('.publicUploadCheckbox');
$checkbox.siblings('.icon-loading-small').removeClass('hidden').addClass('inlineblock');
this.model.setPublicUpload($checkbox.is(':checked'));
this.model.saveLinkShare();
},
_onEmailPrivateLink: function(event) {
event.preventDefault();
var $emailField = this.$el.find('#email');
var $emailButton = this.$el.find('#emailButton');
var email = this.$el.find('#email').val();
var $emailField = this.$el.find('.emailField');
var $emailButton = this.$el.find('.emailButton');
var email = $emailField.val();
if (email !== '') {
$emailField.prop('disabled', true);
$emailButton.prop('disabled', true);
$emailField.val(t('core', 'Sending ...'));
this.model.sendEmailPrivateLink(email).then(function() {
this.model.sendEmailPrivateLink(email).done(function() {
$emailField.css('font-weight', 'bold').val(t('core','Email sent'));
setTimeout(function() {
$emailField.css('font-weight', 'normal').val('');
$emailField.val('');
$emailField.css('font-weight', 'normal');
$emailField.prop('disabled', false);
$emailButton.prop('disabled', false);
}, 2000);
}).fail(function() {
$emailField.val(email);
$emailField.css('font-weight', 'normal');
$emailField.prop('disabled', false);
$emailButton.prop('disabled', false);
});
}
return false;
@@ -237,6 +267,7 @@
|| !this.model.get('linkShare').password);
this.$el.html(linkShareTemplate({
cid: this.cid,
shareAllowed: true,
isLinkShare: isLinkShare,
shareLinkURL: this.model.get('linkShare').link,
@@ -244,7 +275,7 @@
urlLabel: t('core', 'Link'),
enablePasswordLabel: t('core', 'Password protect'),
passwordLabel: t('core', 'Password'),
passwordPlaceholder: isPasswordSet ? '**********' : t('core', 'Choose a password for the public link'),
passwordPlaceholder: isPasswordSet ? PASSWORD_PLACEHOLDER : PASSWORD_PLACEHOLDER_MESSAGE,
isPasswordSet: isPasswordSet,
showPasswordCheckBox: showPasswordCheckBox,
publicUpload: publicUpload && isLinkShare,
@@ -255,20 +286,7 @@
mailButtonText: t('core', 'Send')
}));
// TODO: move this to delegate events instead
this.$el.find('#linkCheckbox').click(this.onLinkCheckBoxChange);
this.$el.find('#sharingDialogAllowPublicUpload').change(this.onAllowPublicUploadChange);
this.$el.find('#linkText').click(this.onLinkTextClick);
this.$el.find('#showPassword').click(this.onShowPasswordClick);
this.$el.find('#linkPassText').focusout(this.onPasswordEntered);
var view = this;
this.$el.find('#linkPassText').keyup(function(event) {
if(event.keyCode == 13) {
view.onPasswordEntered();
}
});
var $emailField = this.$el.find('#email');
var $emailField = this.$el.find('.emailField');
if (isLinkShare && $emailField.length !== 0) {
$emailField.autocomplete({
minLength: 1,
@@ -295,6 +313,12 @@
};
}
// TODO drop with IE8 drop
if($('html').hasClass('ie8')) {
this.$el.find('#linkPassText').removeAttr('placeholder');
this.$el.find('#linkPassText').val('');
}
this.delegateEvents();
return this;
+18 -17
View File
@@ -14,7 +14,7 @@
}
var TEMPLATE =
'<ul id="shareWithList">' +
'<ul id="shareWithList" class="shareWithList">' +
'{{#each sharees}}' +
' {{#if isCollection}}' +
' <li data-collection="{{collectionID}}">{{text}}</li>' +
@@ -27,31 +27,31 @@
' {{/if}}' +
' <span class="username">{{shareWithDisplayName}}</span>' +
' {{#if mailPublicNotificationEnabled}} {{#unless isRemoteShare}}' +
' <input id="mail-{{shareWith}}" type="checkbox" name="mailNotification" class="mailNotification checkbox" {{#if wasMailSent}}checked="checked"{{/if}} />' +
' <label for="mail-{{shareWith}}">{{notifyByMailLabel}}</label>' +
' <input id="mail-{{cid}}-{{shareWith}}" type="checkbox" name="mailNotification" class="mailNotification checkbox" {{#if wasMailSent}}checked="checked"{{/if}} />' +
' <label for="mail-{{cid}}-{{shareWith}}">{{notifyByMailLabel}}</label>' +
' {{/unless}} {{/if}}' +
' {{#if isResharingAllowed}} {{#if sharePermissionPossible}} {{#unless isRemoteShare}}' +
' <input id="canShare-{{shareWith}}" type="checkbox" name="share" class="permissions checkbox" {{#if hasSharePermission}}checked="checked"{{/if}} data-permissions="{{sharePermission}}" />' +
' <label for="canShare-{{shareWith}}">{{canShareLabel}}</label>' +
' <input id="canShare-{{cid}}-{{shareWith}}" type="checkbox" name="share" class="permissions checkbox" {{#if hasSharePermission}}checked="checked"{{/if}} data-permissions="{{sharePermission}}" />' +
' <label for="canShare-{{cid}}-{{shareWith}}">{{canShareLabel}}</label>' +
' {{/unless}} {{/if}} {{/if}}' +
' {{#if editPermissionPossible}}' +
' <input id="canEdit-{{shareWith}}" type="checkbox" name="edit" class="permissions checkbox" {{#if hasEditPermission}}checked="checked"{{/if}} />' +
' <label for="canEdit-{{shareWith}}">{{canEditLabel}}</label>' +
' <input id="canEdit-{{cid}}-{{shareWith}}" type="checkbox" name="edit" class="permissions checkbox" {{#if hasEditPermission}}checked="checked"{{/if}} />' +
' <label for="canEdit-{{cid}}-{{shareWith}}">{{canEditLabel}}</label>' +
' {{/if}}' +
' {{#unless isRemoteShare}}' +
' <a href="#" class="showCruds"><img class="svg" alt="{{crudsLabel}}" src="{{triangleSImage}}"/></a>' +
' <div class="cruds hidden">' +
' {{#if createPermissionPossible}}' +
' <input id="canCreate-{{shareWith}}" type="checkbox" name="create" class="permissions checkbox" {{#if hasCreatePermission}}checked="checked"{{/if}} data-permissions="{{createPermission}}"/>' +
' <label for="canCreate-{{shareWith}}">{{createPermissionLabel}}</label>' +
' <input id="canCreate-{{cid}}-{{shareWith}}" type="checkbox" name="create" class="permissions checkbox" {{#if hasCreatePermission}}checked="checked"{{/if}} data-permissions="{{createPermission}}"/>' +
' <label for="canCreate-{{cid}}-{{shareWith}}">{{createPermissionLabel}}</label>' +
' {{/if}}' +
' {{#if updatePermissionPossible}}' +
' <input id="canUpdate-{{shareWith}}" type="checkbox" name="update" class="permissions checkbox" {{#if hasUpdatePermission}}checked="checked"{{/if}} data-permissions="{{updatePermission}}"/>' +
' <label for="canUpdate-{{shareWith}}">{{updatePermissionLabel}}</label>' +
' <input id="canUpdate-{{cid}}-{{shareWith}}" type="checkbox" name="update" class="permissions checkbox" {{#if hasUpdatePermission}}checked="checked"{{/if}} data-permissions="{{updatePermission}}"/>' +
' <label for="canUpdate-{{cid}}-{{shareWith}}">{{updatePermissionLabel}}</label>' +
' {{/if}}' +
' {{#if deletePermissionPossible}} {{#unless isRemoteShare}}' +
' <input id="canDelete-{{shareWith}}" type="checkbox" name="delete" class="permissions checkbox" {{#if hasDeletePermission}}checked="checked"{{/if}} data-permissions="{{deletePermission}}"/>' +
' <label for="canDelete-{{shareWith}}">{{deletePermissionLabel}}</label>' +
' <input id="canDelete-{{cid}}-{{shareWith}}" type="checkbox" name="delete" class="permissions checkbox" {{#if hasDeletePermission}}checked="checked"{{/if}} data-permissions="{{deletePermission}}"/>' +
' <label for="canDelete-{{cid}}-{{shareWith}}">{{deletePermissionLabel}}</label>' +
' {{/unless}} {{/if}}' +
' </div>' +
' {{/unless}}' +
@@ -146,6 +146,7 @@
}
return _.extend(hasPermissionOverride, {
cid: this.cid,
hasSharePermission: this.model.hasSharePermission(shareIndex),
hasEditPermission: this.model.hasEditPermission(shareIndex),
hasCreatePermission: this.model.hasCreatePermission(shareIndex),
@@ -209,8 +210,8 @@
},
render: function() {
var shareeListTemplate = this.template();
this.$el.html(shareeListTemplate({
this.$el.html(this.template({
cid: this.cid,
sharees: this.getShareeList()
}));
@@ -235,11 +236,11 @@
* @returns {Function} from Handlebars
* @private
*/
template: function () {
template: function (data) {
if (!this._template) {
this._template = Handlebars.compile(TEMPLATE);
}
return this._template;
return this._template(data);
},
onUnshare: function(event) {
+6 -5
View File
@@ -16,9 +16,9 @@
var TEMPLATE_BASE =
'<div class="resharerInfoView subView"></div>' +
'{{#if isSharingAllowed}}' +
'<label for="shareWith" class="hidden-visually">{{shareLabel}}</label>' +
'<label for="shareWith-{{cid}}" class="hidden-visually">{{shareLabel}}</label>' +
'<div class="oneline">' +
' <input id="shareWith" type="text" placeholder="{{sharePlaceholder}}" />' +
' <input id="shareWith-{{cid}}" class="shareWithField" type="text" placeholder="{{sharePlaceholder}}" />' +
' <span class="shareWithLoading icon-loading-small hidden"></span>'+
'{{{remoteShareInfo}}}' +
'</div>' +
@@ -127,7 +127,7 @@
$loading.addClass('hidden');
$loading.removeClass('inlineblock');
if (result.status == 'success' && result.data.length > 0) {
$("#shareWith").autocomplete("option", "autoFocus", true);
$('.shareWithField').autocomplete("option", "autoFocus", true);
response(result.data);
} else {
response();
@@ -184,7 +184,7 @@
this._loadingOnce = true;
// the first time, focus on the share field after the spinner disappeared
_.defer(function() {
self.$('#shareWith').focus();
self.$('.shareWithField').focus();
});
}
},
@@ -193,13 +193,14 @@
var baseTemplate = this._getTemplate('base', TEMPLATE_BASE);
this.$el.html(baseTemplate({
cid: this.cid,
shareLabel: t('core', 'Share'),
sharePlaceholder: this._renderSharePlaceholderPart(),
remoteShareInfo: this._renderRemoteShareInfoPart(),
isSharingAllowed: this.model.sharePermissionPossible()
}));
var $shareField = this.$el.find('#shareWith');
var $shareField = this.$el.find('.shareWithField');
if ($shareField.length) {
$shareField.autocomplete({
minLength: 2,
+16 -3
View File
@@ -116,7 +116,8 @@
// TODO: use backbone's default value mechanism once this is a separate model
var requiredAttributes = [
{ name: 'password', defaultValue: '' },
{ name: 'password', defaultValue: '' },
{ name: 'passwordChanged', defaultValue: false },
{ name: 'permissions', defaultValue: OC.PERMISSION_READ },
{ name: 'expiration', defaultValue: this.configModel.getDefaultExpirationDateString() }
];
@@ -136,11 +137,16 @@
}
});
var password = {
password: attributes.password,
passwordChanged: attributes.passwordChanged
};
OC.Share.share(
itemType,
itemSource,
OC.Share.SHARE_TYPE_LINK,
attributes.password,
password,
attributes.permissions,
this.fileInfoModel.get('name'),
attributes.expiration,
@@ -208,6 +214,7 @@
*/
setPassword: function(password) {
this.get('linkShare').password = password;
this.get('linkShare').passwordChanged = true;
},
addShare: function(attributes, options) {
@@ -522,11 +529,12 @@
* @param {string} recipientEmail recipient email address
*/
sendEmailPrivateLink: function(recipientEmail) {
var deferred = $.Deferred();
var itemType = this.get('itemType');
var itemSource = this.get('itemSource');
var linkShare = this.get('linkShare');
return $.post(
$.post(
OC.generateUrl('core/ajax/share.php'), {
action: 'email',
toaddress: recipientEmail,
@@ -540,8 +548,13 @@
if (!result || result.status !== 'success') {
// FIXME: a model should not show dialogs
OC.dialogs.alert(result.data.message, t('core', 'Error while sending notification'));
deferred.reject();
} else {
deferred.resolve();
}
});
return deferred.promise();
},
/**
+7
View File
@@ -142,6 +142,13 @@ describe('OC.Share tests', function() {
checkIcon('filetypes/folder-public');
});
it('shows external storage icon if external mount point', function() {
$file.attr('data-type', 'dir');
$file.attr('data-mountType', 'external');
OC.Share.markFileAsShared($file, false, false);
checkIcon('filetypes/folder-external');
});
});
describe('displaying the recipoients', function() {
+134 -45
View File
@@ -80,6 +80,9 @@ describe('OC.Share.ShareDialogView', function() {
model: shareModel
});
// required for proper event propagation when simulating clicks in some cases (jquery bugs)
$('#testArea').append(dialog.$el);
// triggers rendering
shareModel.set({
shares: [],
@@ -111,6 +114,7 @@ describe('OC.Share.ShareDialogView', function() {
/* jshint camelcase:false */
oc_appconfig.core = oldAppConfig;
dialog.remove();
fetchStub.restore();
autocompleteStub.restore();
@@ -127,7 +131,7 @@ describe('OC.Share.ShareDialogView', function() {
dialog.render();
// Toggle linkshare
dialog.$el.find('[name=linkCheckbox]').click();
dialog.$el.find('.linkCheckbox').click();
fakeServer.requests[0].respond(
200,
{ 'Content-Type': 'application/json' },
@@ -136,13 +140,14 @@ describe('OC.Share.ShareDialogView', function() {
// Enable password, enter password and focusout
dialog.$el.find('[name=showPassword]').click();
dialog.$el.find('#linkPassText').focus();
dialog.$el.find('#linkPassText').val('foo');
dialog.$el.find('#linkPassText').focusout();
dialog.$el.find('.linkPassText').focus();
dialog.$el.find('.linkPassText').val('foo');
dialog.$el.find('.linkPassText').focusout();
expect(fakeServer.requests[1].method).toEqual('POST');
var body = OC.parseQueryString(fakeServer.requests[1].requestBody);
expect(body.shareWith).toEqual('foo');
expect(body['shareWith[password]']).toEqual('foo');
expect(body['shareWith[passwordChanged]']).toEqual('true');
fetchStub.reset();
@@ -157,8 +162,8 @@ describe('OC.Share.ShareDialogView', function() {
// fetching the model will rerender the view
dialog.render();
expect(dialog.$el.find('#linkPassText').val()).toEqual('');
expect(dialog.$el.find('#linkPassText').attr('placeholder')).toEqual('**********');
expect(dialog.$el.find('.linkPassText').val()).toEqual('');
expect(dialog.$el.find('.linkPassText').attr('placeholder')).toEqual('**********');
});
it('update password on enter', function() {
$('#allowShareWithLink').val('yes');
@@ -166,7 +171,7 @@ describe('OC.Share.ShareDialogView', function() {
dialog.render();
// Toggle linkshare
dialog.$el.find('[name=linkCheckbox]').click();
dialog.$el.find('.linkCheckbox').click();
fakeServer.requests[0].respond(
200,
{ 'Content-Type': 'application/json' },
@@ -175,13 +180,14 @@ describe('OC.Share.ShareDialogView', function() {
// Enable password and enter password
dialog.$el.find('[name=showPassword]').click();
dialog.$el.find('#linkPassText').focus();
dialog.$el.find('#linkPassText').val('foo');
dialog.$el.find('#linkPassText').trigger(new $.Event('keyup', {keyCode: 13}));
dialog.$el.find('.linkPassText').focus();
dialog.$el.find('.linkPassText').val('foo');
dialog.$el.find('.linkPassText').trigger(new $.Event('keyup', {keyCode: 13}));
expect(fakeServer.requests[1].method).toEqual('POST');
var body = OC.parseQueryString(fakeServer.requests[1].requestBody);
expect(body.shareWith).toEqual('foo');
expect(body['shareWith[password]']).toEqual('foo');
expect(body['shareWith[passwordChanged]']).toEqual('true');
fetchStub.reset();
@@ -196,22 +202,22 @@ describe('OC.Share.ShareDialogView', function() {
// fetching the model will rerender the view
dialog.render();
expect(dialog.$el.find('#linkPassText').val()).toEqual('');
expect(dialog.$el.find('#linkPassText').attr('placeholder')).toEqual('**********');
expect(dialog.$el.find('.linkPassText').val()).toEqual('');
expect(dialog.$el.find('.linkPassText').attr('placeholder')).toEqual('**********');
});
it('shows share with link checkbox when allowed', function() {
$('#allowShareWithLink').val('yes');
dialog.render();
expect(dialog.$el.find('#linkCheckbox').length).toEqual(1);
expect(dialog.$el.find('.linkCheckbox').length).toEqual(1);
});
it('does not show share with link checkbox when not allowed', function() {
$('#allowShareWithLink').val('no');
dialog.render();
expect(dialog.$el.find('#linkCheckbox').length).toEqual(0);
expect(dialog.$el.find('.linkCheckbox').length).toEqual(0);
});
it('shows populated link share when a link share exists', function() {
// this is how the OC.Share class does it...
@@ -228,8 +234,31 @@ describe('OC.Share.ShareDialogView', function() {
dialog.render();
expect(dialog.$el.find('#linkCheckbox').prop('checked')).toEqual(true);
expect(dialog.$el.find('#linkText').val()).toEqual(link);
expect(dialog.$el.find('.linkCheckbox').prop('checked')).toEqual(true);
expect(dialog.$el.find('.linkText').val()).toEqual(link);
});
it('autofocus link text when clicked', function() {
$('#allowShareWithLink').val('yes');
dialog.render();
// Toggle linkshare
dialog.$el.find('.linkCheckbox').click();
fakeServer.requests[0].respond(
200,
{ 'Content-Type': 'application/json' },
JSON.stringify({data: {token: 'xyz'}, status: 'success'})
);
var focusStub = sinon.stub($.fn, 'focus');
var selectStub = sinon.stub($.fn, 'select');
dialog.$el.find('.linkText').click();
expect(focusStub.calledOnce).toEqual(true);
expect(selectStub.calledOnce).toEqual(true);
focusStub.restore();
selectStub.restore();
});
describe('password', function() {
var slideToggleStub;
@@ -250,7 +279,7 @@ describe('OC.Share.ShareDialogView', function() {
configModel.set('enforcePasswordForPublicLink', true);
dialog.render();
dialog.$el.find('[name=linkCheckbox]').click();
dialog.$el.find('.linkCheckbox').click();
// The password linkPass field is shown (slideToggle is called).
// No request is made yet
@@ -259,7 +288,7 @@ describe('OC.Share.ShareDialogView', function() {
expect(fakeServer.requests.length).toEqual(0);
// Now untoggle share by link
dialog.$el.find('[name=linkCheckbox]').click();
dialog.$el.find('.linkCheckbox').click();
dialog.render();
// Password field disappears and no ajax requests have been made
@@ -302,31 +331,31 @@ describe('OC.Share.ShareDialogView', function() {
dialog.render();
expect(dialog.$el.find('[name=expirationCheckbox]').prop('checked')).toEqual(false);
expect(dialog.$el.find('#expirationDate').val()).toEqual('');
expect(dialog.$el.find('.datepicker').val()).toEqual('');
});
it('does not check expiration date checkbox for new share', function() {
dialog.render();
expect(dialog.$el.find('[name=expirationCheckbox]').prop('checked')).toEqual(false);
expect(dialog.$el.find('#expirationDate').val()).toEqual('');
expect(dialog.$el.find('.datepicker').val()).toEqual('');
});
it('checks expiration date checkbox and populates field when expiration date was set', function() {
shareModel.get('linkShare').expiration = '2014-02-01 00:00:00';
dialog.render();
expect(dialog.$el.find('[name=expirationCheckbox]').prop('checked')).toEqual(true);
expect(dialog.$el.find('#expirationDate').val()).toEqual('01-02-2014');
expect(dialog.$el.find('.datepicker').val()).toEqual('01-02-2014');
});
it('sets default date when default date setting is enabled', function() {
configModel.set('isDefaultExpireDateEnabled', true);
dialog.render();
dialog.$el.find('[name=linkCheckbox]').click();
dialog.$el.find('.linkCheckbox').click();
// here fetch would be called and the server returns the expiration date
shareModel.get('linkShare').expiration = '2014-1-27 00:00:00';
dialog.render();
// enabled by default
expect(dialog.$el.find('[name=expirationCheckbox]').prop('checked')).toEqual(true);
expect(dialog.$el.find('#expirationDate').val()).toEqual('27-01-2014');
expect(dialog.$el.find('.datepicker').val()).toEqual('27-01-2014');
// disabling is allowed
dialog.$el.find('[name=expirationCheckbox]').click();
@@ -338,13 +367,13 @@ describe('OC.Share.ShareDialogView', function() {
isDefaultExpireDateEnforced: true
});
dialog.render();
dialog.$el.find('[name=linkCheckbox]').click();
dialog.$el.find('.linkCheckbox').click();
// here fetch would be called and the server returns the expiration date
shareModel.get('linkShare').expiration = '2014-1-27 00:00:00';
dialog.render();
expect(dialog.$el.find('[name=expirationCheckbox]').prop('checked')).toEqual(true);
expect(dialog.$el.find('#expirationDate').val()).toEqual('27-01-2014');
expect(dialog.$el.find('.datepicker').val()).toEqual('27-01-2014');
// disabling is not allowed
expect(dialog.$el.find('[name=expirationCheckbox]').prop('disabled')).toEqual(true);
@@ -358,14 +387,14 @@ describe('OC.Share.ShareDialogView', function() {
isDefaultExpireDateEnforced: true
});
dialog.render();
dialog.$el.find('[name=linkCheckbox]').click();
dialog.$el.find('.linkCheckbox').click();
// here fetch would be called and the server returns the expiration date
shareModel.get('linkShare').expiration = '2014-1-27 00:00:00';
dialog.render();
//Enter password
dialog.$el.find('#linkPassText').val('foo');
dialog.$el.find('#linkPassText').trigger(new $.Event('keyup', {keyCode: 13}));
dialog.$el.find('.linkPassText').val('foo');
dialog.$el.find('.linkPassText').trigger(new $.Event('keyup', {keyCode: 13}));
fakeServer.requests[0].respond(
200,
{ 'Content-Type': 'application/json' },
@@ -373,26 +402,16 @@ describe('OC.Share.ShareDialogView', function() {
);
expect(dialog.$el.find('[name=expirationCheckbox]').prop('checked')).toEqual(true);
expect(dialog.$el.find('#expirationDate').val()).toEqual('27-01-2014');
expect(dialog.$el.find('.datepicker').val()).toEqual('27-01-2014');
// disabling is not allowed
expect(dialog.$el.find('[name=expirationCheckbox]').prop('disabled')).toEqual(true);
dialog.$el.find('[name=expirationCheckbox]').click();
expect(dialog.$el.find('[name=expirationCheckbox]').prop('checked')).toEqual(true);
});
it('displayes email form when sending emails is enabled', function() {
$('input[name=mailPublicNotificationEnabled]').val('yes');
dialog.render();
expect(dialog.$('#emailPrivateLink').length).toEqual(1);
});
it('not renders email form when sending emails is disabled', function() {
$('input[name=mailPublicNotificationEnabled]').val('no');
dialog.render();
expect(dialog.$('#emailPrivateLink').length).toEqual(0);
});
it('sets picker minDate to today and no maxDate by default', function() {
dialog.render();
dialog.$el.find('[name=linkCheckbox]').click();
dialog.$el.find('.linkCheckbox').click();
dialog.$el.find('[name=expirationCheckbox]').click();
expect($.datepicker._defaults.minDate).toEqual(expectedMinDate);
expect($.datepicker._defaults.maxDate).toEqual(null);
@@ -403,7 +422,7 @@ describe('OC.Share.ShareDialogView', function() {
isDefaultExpireDateEnforced: true
});
dialog.render();
dialog.$el.find('[name=linkCheckbox]').click();
dialog.$el.find('.linkCheckbox').click();
expect($.datepicker._defaults.minDate).toEqual(expectedMinDate);
expect($.datepicker._defaults.maxDate).toEqual(new Date(2014, 0, 27, 0, 0, 0, 0));
});
@@ -422,6 +441,76 @@ describe('OC.Share.ShareDialogView', function() {
expect($.datepicker._defaults.maxDate).toEqual(new Date(2014, 0, 27, 0, 0, 0, 0));
});
});
describe('send link by email', function() {
var sendEmailPrivateLinkStub;
var clock;
beforeEach(function() {
configModel.set({
isMailPublicNotificationEnabled: true
});
shareModel.set('linkShare', {
isLinkShare: true,
token: 'tehtoken',
permissions: OC.PERMISSION_READ,
expiration: null
});
sendEmailPrivateLinkStub = sinon.stub(dialog.model, "sendEmailPrivateLink");
clock = sinon.useFakeTimers();
});
afterEach(function() {
sendEmailPrivateLinkStub.restore();
clock.restore();
});
it('displayes form when sending emails is enabled', function() {
$('input[name=mailPublicNotificationEnabled]').val('yes');
dialog.render();
expect(dialog.$('#emailPrivateLink').length).toEqual(1);
});
it('form not rendered when sending emails is disabled', function() {
$('input[name=mailPublicNotificationEnabled]').val('no');
dialog.render();
expect(dialog.$('#emailPrivateLink').length).toEqual(0);
});
it('input cleared on success', function() {
var defer = $.Deferred();
sendEmailPrivateLinkStub.returns(defer.promise());
$('input[name=mailPublicNotificationEnabled]').val('yes');
dialog.render();
dialog.$el.find('#emailPrivateLink #email').val('a@b.c');
dialog.$el.find('#emailPrivateLink').trigger('submit');
expect(sendEmailPrivateLinkStub.callCount).toEqual(1);
expect(dialog.$el.find('#emailPrivateLink #email').val()).toEqual('Sending ...');
defer.resolve();
expect(dialog.$el.find('#emailPrivateLink #email').val()).toEqual('Email sent');
clock.tick(2000);
expect(dialog.$el.find('#emailPrivateLink #email').val()).toEqual('');
});
it('input not cleared on failure', function() {
var defer = $.Deferred();
sendEmailPrivateLinkStub.returns(defer.promise());
$('input[name=mailPublicNotificationEnabled]').val('yes');
dialog.render();
dialog.$el.find('#emailPrivateLink #email').val('a@b.c');
dialog.$el.find('#emailPrivateLink').trigger('submit');
expect(sendEmailPrivateLinkStub.callCount).toEqual(1);
expect(dialog.$el.find('#emailPrivateLink #email').val()).toEqual('Sending ...');
defer.reject();
expect(dialog.$el.find('#emailPrivateLink #email').val()).toEqual('a@b.c');
});
});
});
describe('check for avatar', function() {
beforeEach(function() {
@@ -472,7 +561,7 @@ describe('OC.Share.ShareDialogView', function() {
it('test correct function calls', function() {
expect(avatarStub.calledTwice).toEqual(true);
expect(placeholderStub.calledTwice).toEqual(true);
expect(dialog.$('#shareWithList').children().length).toEqual(3);
expect(dialog.$('.shareWithList').children().length).toEqual(3);
expect(dialog.$('.avatar').length).toEqual(4);
});
@@ -586,7 +675,7 @@ describe('OC.Share.ShareDialogView', function() {
permissions: OC.PERMISSION_READ
});
dialog.render();
expect(dialog.$el.find('#shareWith').prop('disabled')).toEqual(true);
expect(dialog.$el.find('.shareWithField').prop('disabled')).toEqual(true);
});
it('shows reshare owner', function() {
shareModel.set({
+100
View File
@@ -378,5 +378,105 @@ describe('OC.Share.ShareItemModel', function() {
});
});
describe('sendEmailPrivateLink', function() {
it('succeeds', function() {
loadItemStub.yields({
shares: [{
displayname_owner: 'root',
expiration: null,
file_source: 123,
file_target: '/folder',
id: 20,
item_source: '123',
item_type: 'folder',
mail_send: '0',
parent: null,
path: '/folder',
permissions: OC.PERMISSION_READ,
share_type: OC.Share.SHARE_TYPE_LINK,
share_with: null,
stime: 1403884258,
storage: 1,
token: 'tehtoken',
uid_owner: 'root'
}]
});
model.fetch();
var res = model.sendEmailPrivateLink('foo@bar.com');
expect(res.state()).toEqual('pending');
expect(fakeServer.requests.length).toEqual(1);
expect(fakeServer.requests[0].url).toEqual(OC.generateUrl('core/ajax/share.php'));
expect(OC.parseQueryString(fakeServer.requests[0].requestBody)).toEqual(
{
action: 'email',
toaddress: 'foo@bar.com',
link: model.get('linkShare').link,
itemType: 'file',
itemSource: '123',
file: 'shared_file_name.txt',
expiration: ''
}
)
fakeServer.requests[0].respond(
200,
{ 'Content-Type': 'application/json' },
JSON.stringify({status: 'success'})
);
expect(res.state()).toEqual('resolved');
});
it('fails', function() {
loadItemStub.yields({
shares: [{
displayname_owner: 'root',
expiration: null,
file_source: 123,
file_target: '/folder',
id: 20,
item_source: '123',
item_type: 'folder',
mail_send: '0',
parent: null,
path: '/folder',
permissions: OC.PERMISSION_READ,
share_type: OC.Share.SHARE_TYPE_LINK,
share_with: null,
stime: 1403884258,
storage: 1,
token: 'tehtoken',
uid_owner: 'root'
}]
});
model.fetch();
var res = model.sendEmailPrivateLink('foo@bar.com');
expect(res.state()).toEqual('pending');
expect(fakeServer.requests.length).toEqual(1);
expect(fakeServer.requests[0].url).toEqual(OC.generateUrl('core/ajax/share.php'));
expect(OC.parseQueryString(fakeServer.requests[0].requestBody)).toEqual(
{
action: 'email',
toaddress: 'foo@bar.com',
link: model.get('linkShare').link,
itemType: 'file',
itemSource: '123',
file: 'shared_file_name.txt',
expiration: ''
}
)
fakeServer.requests[0].respond(
200,
{ 'Content-Type': 'application/json' },
JSON.stringify({data: {message: 'fail'}, status: 'error'})
);
expect(res.state()).toEqual('rejected');
});
});
});
-4
View File
@@ -5,7 +5,3 @@
.text-center {
text-align: center;
}
#password-icon {
top: 20px;
}
@@ -31,7 +31,6 @@ script('core', 'lostpassword');
<p>
<label for="password" class="infield"><?php p($l->t('New password')); ?></label>
<input type="password" name="password" id="password" value="" placeholder="<?php p($l->t('New Password')); ?>" required />
<img class="svg" id="password-icon" src="<?php print_unescaped(image_path('', 'actions/password.svg')); ?>" alt=""/>
</p>
<input type="submit" id="submit" value="<?php p($l->t('Reset password')); ?>" />
<p class="text-center">
-2
View File
@@ -43,7 +43,6 @@ script('core', [
value="<?php p($_['adminlogin']); ?>"
autocomplete="off" autocapitalize="off" autocorrect="off" autofocus required>
<label for="adminlogin" class="infield"><?php p($l->t( 'Username' )); ?></label>
<img class="svg" src="<?php p(image_path('', 'actions/user.svg')); ?>" alt="">
</p>
<p class="groupbottom">
<input type="password" name="adminpass" data-typetoggle="#show" id="adminpass"
@@ -51,7 +50,6 @@ script('core', [
value="<?php p($_['adminpass']); ?>"
autocomplete="off" autocapitalize="off" autocorrect="off" required>
<label for="adminpass" class="infield"><?php p($l->t( 'Password' )); ?></label>
<img class="svg" id="adminpass-icon" src="<?php print_unescaped(image_path('', 'actions/password.svg')); ?>" alt="">
<input type="checkbox" id="show" name="show">
<label for="show" class="svg"></label>
<div class="strengthify-wrapper"></div>
-2
View File
@@ -45,7 +45,6 @@ script('core', [
<?php p($_['user_autofocus'] ? 'autofocus' : ''); ?>
autocomplete="on" autocapitalize="off" autocorrect="off" required>
<label for="user" class="infield"><?php p($l->t('Username')); ?></label>
<img class="svg" src="<?php print_unescaped(image_path('', 'actions/user.svg')); ?>" alt=""/>
</p>
<p class="groupbottom">
@@ -54,7 +53,6 @@ script('core', [
<?php p($_['user_autofocus'] ? '' : 'autofocus'); ?>
autocomplete="on" autocapitalize="off" autocorrect="off" required>
<label for="password" class="infield"><?php p($l->t('Password')); ?></label>
<img class="svg" id="password-icon" src="<?php print_unescaped(image_path('', 'actions/password.svg')); ?>" alt=""/>
<input type="submit" id="submit" class="login primary icon-confirm svg" title="<?php p($l->t('Log in')); ?>" value="" disabled="disabled"/>
</p>
+1 -1
View File
@@ -58,7 +58,7 @@
dataType: 'script',
url: options.zxcvbn
}).done(function() {
me.bind('keyup input', function() {
me.bind('keyup input change', function() {
var password = $(this).val(),
// hide strengthigy if no input is provided
opacity = (password === '') ? 0 : 1,
+21 -1
View File
@@ -133,7 +133,18 @@ class OC {
OC_Config::$object = new \OC\Config(self::$configDir);
OC::$SUBURI = str_replace("\\", "/", substr(realpath($_SERVER["SCRIPT_FILENAME"]), strlen(OC::$SERVERROOT)));
$scriptName = $_SERVER['SCRIPT_NAME'];
/**
* FIXME: The following lines are required because we can't yet instantiiate
* \OC::$server->getRequest() since \OC::$server does not yet exist.
*/
$params = [
'server' => [
'SCRIPT_NAME' => $_SERVER['SCRIPT_NAME'],
'SCRIPT_FILENAME' => $_SERVER['SCRIPT_FILENAME'],
],
];
$fakeRequest = new \OC\AppFramework\Http\Request($params, null, new \OC\AllConfig(new \OC\SystemConfig()));
$scriptName = $fakeRequest->getScriptName();
if (substr($scriptName, -1) == '/') {
$scriptName .= 'index.php';
//make sure suburi follows the same rules as scriptName
@@ -145,6 +156,7 @@ class OC {
}
}
if (OC::$CLI) {
OC::$WEBROOT = OC_Config::getValue('overwritewebroot', '');
} else {
@@ -608,6 +620,14 @@ class OC {
OC_User::useBackend(new OC_User_Database());
OC_Group::useBackend(new OC_Group_Database());
// Subscribe to the hook
\OCP\Util::connectHook(
'\OCA\Files_Sharing\API\Server2Server',
'preLoginNameUsedAsUserName',
'\OC_User_Database',
'preLoginNameUsedAsUserName'
);
//setup extra user backends
if (!self::checkUpgrade(false)) {
OC_User::setupBackends();
@@ -34,6 +34,8 @@ class DeprecationCheck extends AbstractCheck implements ICheck {
*/
protected function getLocalClasses() {
return [
'OC_JSON' => '8.2.0',
'OCP\Config' => '8.0.0',
'OCP\Contacts' => '8.1.0',
'OCP\DB' => '8.1.0',
@@ -49,6 +51,15 @@ class DeprecationCheck extends AbstractCheck implements ICheck {
*/
protected function getLocalConstants() {
return [
'OC_API::GUEST_AUTH' => '8.2.0',
'OC_API::USER_AUTH' => '8.2.0',
'OC_API::SUBADMIN_AUTH' => '8.2.0',
'OC_API::ADMIN_AUTH' => '8.2.0',
'OC_API::RESPOND_UNAUTHORISED' => '8.2.0',
'OC_API::RESPOND_SERVER_ERROR' => '8.2.0',
'OC_API::RESPOND_NOT_FOUND' => '8.2.0',
'OC_API::RESPOND_UNKNOWN_ERROR' => '8.2.0',
'OCP::PERMISSION_CREATE' => '8.0.0',
'OCP::PERMISSION_READ' => '8.0.0',
'OCP::PERMISSION_UPDATE' => '8.0.0',
@@ -80,8 +91,13 @@ class DeprecationCheck extends AbstractCheck implements ICheck {
*/
protected function getLocalMethods() {
return [
'OC_L10N::get' => '8.2.0',
'OCP\Activity\IManager::publishActivity' => '8.2.0',
'OCP\App::register' => '8.1.0',
'OCP\App::addNavigationEntry' => '8.1.0',
'OCP\App::getActiveNavigationEntry' => '8.2.0',
'OCP\App::setActiveNavigationEntry' => '8.1.0',
'OCP\AppFramework\Controller::params' => '7.0.0',
@@ -115,8 +131,12 @@ class DeprecationCheck extends AbstractCheck implements ICheck {
'OCP\IAppConfig::setValue' => '8.0.0',
'OCP\IAppConfig::deleteApp' => '8.0.0',
'OCP\IDBConnection::createQueryBuilder' => '8.2.0',
'OCP\IDBConnection::getExpressionBuilder' => '8.2.0',
'OCP\ISearch::search' => '8.0.0',
'OCP\IServerContainer::getCache' => '8.2.0',
'OCP\IServerContainer::getDb' => '8.1.0',
'OCP\IServerContainer::getHTTPHelper' => '8.1.0',
@@ -128,20 +148,21 @@ class DeprecationCheck extends AbstractCheck implements ICheck {
'OCP\User::logout' => '8.1.0',
'OCP\User::checkPassword' => '8.1.0',
'OCP\Util::sendMail' => '8.1.0',
'OCP\Util::formatDate' => '8.0.0',
'OCP\Util::encryptedFiles' => '8.1.0',
'OCP\Util::linkToRoute' => '8.1.0',
'OCP\Util::linkTo' => '8.1.0',
'OCP\Util::formatDate' => '8.0.0',
'OCP\Util::generateRandomBytes' => '8.1.0',
'OCP\Util::getServerHost' => '8.1.0',
'OCP\Util::getServerProtocol' => '8.1.0',
'OCP\Util::getRequestUri' => '8.1.0',
'OCP\Util::getScriptName' => '8.1.0',
'OCP\Util::imagePath' => '8.1.0',
'OCP\Util::isValidFileName' => '8.1.0',
'OCP\Util::generateRandomBytes' => '8.1.0',
'OCP\Util::linkToRoute' => '8.1.0',
'OCP\Util::linkTo' => '8.1.0',
'OCP\Util::logException' => '8.2.0',
'OCP\Util::mb_str_replace' => '8.2.0',
'OCP\Util::mb_substr_replace' => '8.2.0',
'OCP\Util::sendMail' => '8.1.0',
];
}
}
+2 -2
View File
@@ -167,9 +167,9 @@ class NodeVisitor extends NodeVisitorAbstract {
* $c = "OC_API";
* $n = $i::ADMIN_AUTH;
*/
} else {
$this->checkBlackListConstant($node->class->toString(), $node->name, $node);
}
$this->checkBlackListConstant($node->class->toString(), $node->name, $node);
}
}
if ($node instanceof Node\Expr\New_) {
+4 -11
View File
@@ -88,20 +88,17 @@ class Request implements \ArrayAccess, \Countable, IRequest {
* - string 'method' the request method (GET, POST etc)
* - string|false 'requesttoken' the requesttoken or false when not available
* @param ISecureRandom $secureRandom
* @param ICrypto $crypto
* @param IConfig $config
* @param string $stream
* @see http://www.php.net/manual/en/reserved.variables.php
*/
public function __construct(array $vars=array(),
ISecureRandom $secureRandom = null,
ICrypto $crypto,
IConfig $config,
$stream='php://input') {
$this->inputStream = $stream;
$this->items['params'] = array();
$this->secureRandom = $secureRandom;
$this->crypto = $crypto;
$this->config = $config;
if(!array_key_exists('method', $vars)) {
@@ -439,22 +436,18 @@ class Request implements \ArrayAccess, \Countable, IRequest {
return false;
}
// Decrypt token to prevent BREACH like attacks
// Deobfuscate token to prevent BREACH like attacks
$token = explode(':', $token);
if (count($token) !== 2) {
return false;
}
$encryptedToken = $token[0];
$obfuscatedToken = $token[0];
$secret = $token[1];
try {
$decryptedToken = $this->crypto->decrypt($encryptedToken, $secret);
} catch (\Exception $e) {
return false;
}
$deobfuscatedToken = base64_decode($obfuscatedToken) ^ $secret;
// Check if the token is valid
if(\OCP\Security\StringUtils::equals($decryptedToken, $this->items['requesttoken'])) {
if(\OCP\Security\StringUtils::equals($deobfuscatedToken, $this->items['requesttoken'])) {
return true;
} else {
return false;
+6
View File
@@ -99,6 +99,11 @@ class Auth extends AbstractBasic {
if($user && $this->isDavAuthenticated($user)) {
return $user;
}
if($user && is_null(\OC::$server->getSession()->get(self::DAV_AUTHENTICATED))) {
return $user;
}
return null;
}
@@ -114,6 +119,7 @@ class Auth extends AbstractBasic {
* @param string $realm
* @return bool
* @throws ServiceUnavailable
* @throws NotAuthenticated
*/
public function authenticate(\Sabre\DAV\Server $server, $realm) {
+7 -3
View File
@@ -41,7 +41,7 @@ class FilesPlugin extends \Sabre\DAV\ServerPlugin {
const DOWNLOADURL_PROPERTYNAME = '{http://owncloud.org/ns}downloadURL';
const SIZE_PROPERTYNAME = '{http://owncloud.org/ns}size';
const GETETAG_PROPERTYNAME = '{DAV:}getetag';
const GETLASTMODIFIED_PROPERTYNAME = '{DAV:}getlastmodified';
const LASTMODIFIED_PROPERTYNAME = '{DAV:}lastmodified';
/**
* Reference to main server object
@@ -101,7 +101,7 @@ class FilesPlugin extends \Sabre\DAV\ServerPlugin {
$server->protectedProperties[] = self::DOWNLOADURL_PROPERTYNAME;
// normally these cannot be changed (RFC4918), but we want them modifiable through PROPPATCH
$allowedProperties = ['{DAV:}getetag', '{DAV:}getlastmodified'];
$allowedProperties = ['{DAV:}getetag'];
$server->protectedProperties = array_diff($server->protectedProperties, $allowedProperties);
$this->server = $server;
@@ -132,6 +132,10 @@ class FilesPlugin extends \Sabre\DAV\ServerPlugin {
if ($sourceDir !== $destinationDir) {
$sourceFileInfo = $this->fileView->getFileInfo($source);
if ($sourceFileInfo === false) {
throw new \Sabre\DAV\Exception\NotFound($source . ' does not exist');
}
if (!$sourceFileInfo->isDeletable()) {
throw new \Sabre\DAV\Exception\Forbidden($source . " cannot be deleted");
}
@@ -208,7 +212,7 @@ class FilesPlugin extends \Sabre\DAV\ServerPlugin {
* @return void
*/
public function handleUpdateProperties($path, PropPatch $propPatch) {
$propPatch->handle(self::GETLASTMODIFIED_PROPERTYNAME, function($time) use ($path) {
$propPatch->handle(self::LASTMODIFIED_PROPERTYNAME, function($time) use ($path) {
if (empty($time)) {
return false;
}
+1 -1
View File
@@ -86,7 +86,7 @@ class OC_Files {
if (!is_array($files)) {
$filename = $dir . '/' . $files;
if (!$view->is_dir($files)) {
if (!$view->is_dir($filename)) {
self::getSingleFile($view, $dir, $files, $onlyHeader);
return;
}
+9 -7
View File
@@ -171,13 +171,15 @@ class Updater {
if ($sourceStorage && $targetStorage) {
$targetCache = $targetStorage->getCache($sourceInternalPath);
if ($targetCache->inCache($targetInternalPath)) {
$targetCache->remove($targetInternalPath);
}
if ($sourceStorage === $targetStorage) {
$targetCache->move($sourceInternalPath, $targetInternalPath);
} else {
$targetCache->moveFromCache($sourceStorage->getCache(), $sourceInternalPath, $targetInternalPath);
if ($sourceStorage->getCache($sourceInternalPath)->inCache($sourceInternalPath)) {
if ($targetCache->inCache($targetInternalPath)) {
$targetCache->remove($targetInternalPath);
}
if ($sourceStorage === $targetStorage) {
$targetCache->move($sourceInternalPath, $targetInternalPath);
} else {
$targetCache->moveFromCache($sourceStorage->getCache(), $sourceInternalPath, $targetInternalPath);
}
}
if (pathinfo($sourceInternalPath, PATHINFO_EXTENSION) !== pathinfo($targetInternalPath, PATHINFO_EXTENSION)) {
@@ -337,7 +337,7 @@ class ObjectStoreStorage extends \OC\Files\Storage\Common {
'size' => 0,
'mtime' => $mtime,
'storage_mtime' => $mtime,
'permissions' => \OCP\Constants::PERMISSION_ALL,
'permissions' => \OCP\Constants::PERMISSION_ALL - \OCP\Constants::PERMISSION_CREATE,
);
$fileId = $this->getCache()->put($path, $stat);
try {
@@ -362,7 +362,7 @@ class ObjectStoreStorage extends \OC\Files\Storage\Common {
if (empty($stat)) {
// create new file
$stat = array(
'permissions' => \OCP\Constants::PERMISSION_ALL,
'permissions' => \OCP\Constants::PERMISSION_ALL - \OCP\Constants::PERMISSION_CREATE,
);
}
// update stat with new data
+1 -1
View File
@@ -1941,7 +1941,7 @@ class View {
$pathSegments = explode('/', $path);
if (isset($pathSegments[2])) {
// E.g.: /username/files/path-to-file
return $pathSegments[2] === 'files';
return ($pathSegments[2] === 'files') && (count($pathSegments) > 3);
}
return true;
@@ -33,6 +33,21 @@ abstract class AbstractLockingProvider implements ILockingProvider {
'exclusive' => []
];
/**
* Check if we've locally acquired a lock
*
* @param string $path
* @param int $type
* @return bool
*/
protected function hasAcquiredLock($path, $type) {
if ($type === self::LOCK_SHARED) {
return isset($this->acquiredLocks['shared'][$path]) && $this->acquiredLocks['shared'][$path] > 0;
} else {
return isset($this->acquiredLocks['exclusive'][$path]) && $this->acquiredLocks['exclusive'][$path] === true;
}
}
/**
* Mark a locally acquired lock
*
+93 -19
View File
@@ -25,6 +25,7 @@ namespace OC\Lock;
use OCP\AppFramework\Utility\ITimeFactory;
use OCP\IDBConnection;
use OCP\ILogger;
use OCP\Lock\ILockingProvider;
use OCP\Lock\LockedException;
/**
@@ -46,8 +47,48 @@ class DBLockingProvider extends AbstractLockingProvider {
*/
private $timeFactory;
private $sharedLocks = [];
const TTL = 3600; // how long until we clear stray locks in seconds
/**
* Check if we have an open shared lock for a path
*
* @param string $path
* @return bool
*/
protected function isLocallyLocked($path) {
return isset($this->sharedLocks[$path]) && $this->sharedLocks[$path];
}
/**
* Mark a locally acquired lock
*
* @param string $path
* @param int $type self::LOCK_SHARED or self::LOCK_EXCLUSIVE
*/
protected function markAcquire($path, $type) {
parent::markAcquire($path, $type);
if ($type === self::LOCK_SHARED) {
$this->sharedLocks[$path] = true;
}
}
/**
* Change the type of an existing tracked lock
*
* @param string $path
* @param int $targetType self::LOCK_SHARED or self::LOCK_EXCLUSIVE
*/
protected function markChange($path, $targetType) {
parent::markChange($path, $targetType);
if ($targetType === self::LOCK_SHARED) {
$this->sharedLocks[$path] = true;
} else if ($targetType === self::LOCK_EXCLUSIVE) {
$this->sharedLocks[$path] = false;
}
}
/**
* @param \OCP\IDBConnection $connection
* @param \OCP\ILogger $logger
@@ -85,11 +126,19 @@ class DBLockingProvider extends AbstractLockingProvider {
* @return bool
*/
public function isLocked($path, $type) {
if ($this->hasAcquiredLock($path, $type)) {
return true;
}
$query = $this->connection->prepare('SELECT `lock` from `*PREFIX*file_locks` WHERE `key` = ?');
$query->execute([$path]);
$lockValue = (int)$query->fetchColumn();
if ($type === self::LOCK_SHARED) {
return $lockValue > 0;
if ($this->isLocallyLocked($path)) {
// if we have a shared lock we kept open locally but it's released we always have at least 1 shared lock in the db
return $lockValue > 1;
} else {
return $lockValue > 0;
}
} else if ($type === self::LOCK_EXCLUSIVE) {
return $lockValue === -1;
} else {
@@ -105,19 +154,27 @@ class DBLockingProvider extends AbstractLockingProvider {
public function acquireLock($path, $type) {
$expire = $this->getExpireTime();
if ($type === self::LOCK_SHARED) {
$result = $this->initLockField($path,1);
if ($result <= 0) {
$result = $this->connection->executeUpdate (
'UPDATE `*PREFIX*file_locks` SET `lock` = `lock` + 1, `ttl` = ? WHERE `key` = ? AND `lock` >= 0',
[$expire, $path]
);
if (!$this->isLocallyLocked($path)) {
$result = $this->initLockField($path, 1);
if ($result <= 0) {
$result = $this->connection->executeUpdate(
'UPDATE `*PREFIX*file_locks` SET `lock` = `lock` + 1, `ttl` = ? WHERE `key` = ? AND `lock` >= 0',
[$expire, $path]
);
}
} else {
$result = 1;
}
} else {
$result = $this->initLockField($path,-1);
$existing = 0;
if ($this->hasAcquiredLock($path, ILockingProvider::LOCK_SHARED) === false && $this->isLocallyLocked($path)) {
$existing = 1;
}
$result = $this->initLockField($path, -1);
if ($result <= 0) {
$result = $this->connection->executeUpdate(
'UPDATE `*PREFIX*file_locks` SET `lock` = -1, `ttl` = ? WHERE `key` = ? AND `lock` = 0',
[$expire, $path]
'UPDATE `*PREFIX*file_locks` SET `lock` = -1, `ttl` = ? WHERE `key` = ? AND `lock` = ?',
[$expire, $path, $existing]
);
}
}
@@ -132,19 +189,15 @@ class DBLockingProvider extends AbstractLockingProvider {
* @param int $type self::LOCK_SHARED or self::LOCK_EXCLUSIVE
*/
public function releaseLock($path, $type) {
if ($type === self::LOCK_SHARED) {
$this->connection->executeUpdate(
'UPDATE `*PREFIX*file_locks` SET `lock` = `lock` - 1 WHERE `key` = ? AND `lock` > 0',
[$path]
);
} else {
$this->markRelease($path, $type);
// we keep shared locks till the end of the request so we can re-use them
if ($type === self::LOCK_EXCLUSIVE) {
$this->connection->executeUpdate(
'UPDATE `*PREFIX*file_locks` SET `lock` = 0 WHERE `key` = ? AND `lock` = -1',
[$path]
);
}
$this->markRelease($path, $type);
}
/**
@@ -162,6 +215,10 @@ class DBLockingProvider extends AbstractLockingProvider {
[$expire, $path]
);
} else {
// since we only keep one shared lock in the db we need to check if we have more then one shared lock locally manually
if (isset($this->acquiredLocks['shared'][$path]) && $this->acquiredLocks['shared'][$path] > 1) {
throw new LockedException($path);
}
$result = $this->connection->executeUpdate(
'UPDATE `*PREFIX*file_locks` SET `lock` = -1, `ttl` = ? WHERE `key` = ? AND `lock` = 1',
[$expire, $path]
@@ -184,10 +241,27 @@ class DBLockingProvider extends AbstractLockingProvider {
);
}
/**
* release all lock acquired by this instance which were marked using the mark* methods
*/
public function releaseAll() {
parent::releaseAll();
// since we keep shared locks we need to manually clean those
foreach ($this->sharedLocks as $path => $lock) {
if ($lock) {
$this->connection->executeUpdate(
'UPDATE `*PREFIX*file_locks` SET `lock` = `lock` - 1 WHERE `key` = ? AND `lock` > 0',
[$path]
);
}
}
}
public function __destruct() {
try {
$this->cleanEmptyLocks();
} catch (\PDOException $e) {
} catch (\Exception $e) {
// If the table is missing, the clean up was successful
if ($this->connection->tableExists('file_locks')) {
throw $e;
+1 -1
View File
@@ -34,7 +34,7 @@ class OC_Log_Syslog {
* Init class data
*/
public static function init() {
openlog('ownCloud', LOG_PID | LOG_CONS, LOG_USER);
openlog(OC_Config::getValue("syslog_tag", "ownCloud"), LOG_PID | LOG_CONS, LOG_USER);
// Close at shutdown
register_shutdown_function('closelog');
}
+13
View File
@@ -34,6 +34,7 @@ use OC\Repair\AssetCache;
use OC\Repair\CleanTags;
use OC\Repair\Collation;
use OC\Repair\DropOldJobs;
use OC\Repair\OldGroupMembershipShares;
use OC\Repair\RemoveGetETagEntries;
use OC\Repair\SqliteAutoincrement;
use OC\Repair\DropOldTables;
@@ -118,6 +119,18 @@ class Repair extends BasicEmitter {
];
}
/**
* Returns expensive repair steps to be run on the
* command line with a special option.
*
* @return array of RepairStep instances
*/
public static function getExpensiveRepairSteps() {
return [
new OldGroupMembershipShares(\OC::$server->getDatabaseConnection(), \OC::$server->getGroupManager()),
];
}
/**
* Returns the repair steps to be run before an
* upgrade.
-2
View File
@@ -438,7 +438,6 @@ class Server extends SimpleContainer implements IServerContainer {
'requesttoken' => $requestToken,
],
$this->getSecureRandom(),
$this->getCrypto(),
$this->getConfig(),
$stream
);
@@ -512,7 +511,6 @@ class Server extends SimpleContainer implements IServerContainer {
: null,
],
new SecureRandom(),
$c->getCrypto(),
$c->getConfig()
);
+32 -15
View File
@@ -597,11 +597,12 @@ class Share extends Constants {
* @param int $permissions CRUDS
* @param string $itemSourceName
* @param \DateTime $expirationDate
* @param bool $passwordChanged
* @return boolean|string Returns true on success or false on failure, Returns token on success for links
* @throws \OC\HintException when the share type is remote and the shareWith is invalid
* @throws \Exception
*/
public static function shareItem($itemType, $itemSource, $shareType, $shareWith, $permissions, $itemSourceName = null, \DateTime $expirationDate = null) {
public static function shareItem($itemType, $itemSource, $shareType, $shareWith, $permissions, $itemSourceName = null, \DateTime $expirationDate = null, $passwordChanged = null) {
$backend = self::getBackend($itemType);
$l = \OC::$server->getL10N('lib');
@@ -619,10 +620,13 @@ class Share extends Constants {
if (is_null($itemSourceName)) {
$itemSourceName = $itemSource;
}
$itemName = $itemSourceName;
// check if file can be shared
if ($itemType === 'file' or $itemType === 'folder') {
$path = \OC\Files\Filesystem::getPath($itemSource);
$itemName = $path;
// verify that the file exists before we try to share it
if (!$path) {
$message = 'Sharing %s failed, because the file does not exist';
@@ -633,8 +637,8 @@ class Share extends Constants {
// verify that the user has share permission
if (!\OC\Files\Filesystem::isSharable($path)) {
$message = 'You are not allowed to share %s';
$message_t = $l->t('You are not allowed to share %s', array($itemSourceName));
\OCP\Util::writeLog('OCP\Share', sprintf($message, $itemSourceName), \OCP\Util::DEBUG);
$message_t = $l->t('You are not allowed to share %s', [$path]);
\OCP\Util::writeLog('OCP\Share', sprintf($message, $path), \OCP\Util::DEBUG);
throw new \Exception($message_t);
}
}
@@ -677,9 +681,9 @@ class Share extends Constants {
// Verify share type and sharing conditions are met
if ($shareType === self::SHARE_TYPE_USER) {
if ($shareWith == $uidOwner) {
$message = 'Sharing %s failed, because the user %s is the item owner';
$message_t = $l->t('Sharing %s failed, because the user %s is the item owner', array($itemSourceName, $shareWith));
\OCP\Util::writeLog('OCP\Share', sprintf($message, $itemSourceName, $shareWith), \OCP\Util::DEBUG);
$message = 'Sharing %s failed, because you can not share with yourself';
$message_t = $l->t('Sharing %s failed, because you can not share with yourself', [$itemName]);
\OCP\Util::writeLog('OCP\Share', sprintf($message, $itemSourceName), \OCP\Util::DEBUG);
throw new \Exception($message_t);
}
if (!\OC_User::userExists($shareWith)) {
@@ -772,14 +776,25 @@ class Share extends Constants {
$updateExistingShare = true;
}
// Generate hash of password - same method as user passwords
if (is_string($shareWith) && $shareWith !== '') {
self::verifyPassword($shareWith);
$shareWith = \OC::$server->getHasher()->hash($shareWith);
if ($passwordChanged === null) {
// Generate hash of password - same method as user passwords
if (is_string($shareWith) && $shareWith !== '') {
self::verifyPassword($shareWith);
$shareWith = \OC::$server->getHasher()->hash($shareWith);
} else {
// reuse the already set password, but only if we change permissions
// otherwise the user disabled the password protection
if ($checkExists && (int)$permissions !== (int)$oldPermissions) {
$shareWith = $checkExists['share_with'];
}
}
} else {
// reuse the already set password, but only if we change permissions
// otherwise the user disabled the password protection
if ($checkExists && (int)$permissions !== (int)$oldPermissions) {
if ($passwordChanged === true) {
if (is_string($shareWith) && $shareWith !== '') {
self::verifyPassword($shareWith);
$shareWith = \OC::$server->getHasher()->hash($shareWith);
}
} else if ($updateExistingShare) {
$shareWith = $checkExists['share_with'];
}
}
@@ -2208,7 +2223,7 @@ class Share extends Constants {
// Check if attempting to share back to owner
if ($checkReshare['uid_owner'] == $shareWith && $shareType == self::SHARE_TYPE_USER) {
$message = 'Sharing %s failed, because the user %s is the original sharer';
$message_t = $l->t('Sharing %s failed, because the user %s is the original sharer', array($itemSourceName, $shareWith));
$message_t = $l->t('Sharing failed, because the user %s is the original sharer', [$shareWith]);
\OCP\Util::writeLog('OCP\Share', sprintf($message, $itemSourceName, $shareWith), \OCP\Util::DEBUG);
throw new \Exception($message_t);
@@ -2632,7 +2647,9 @@ class Share extends Constants {
*/
private static function isFileReachable($path, $ownerStorageId) {
// if outside the home storage, file is always considered reachable
if (!(substr($ownerStorageId, 0, 6) === 'home::')) {
if (!(substr($ownerStorageId, 0, 6) === 'home::' ||
substr($ownerStorageId, 0, 13) === 'object::user:'
)) {
return true;
}
+6
View File
@@ -337,6 +337,8 @@ class Updater extends BasicEmitter {
}
protected function checkCoreUpgrade() {
$this->emit('\OC\Updater', 'dbSimulateUpgradeBefore');
// simulate core DB upgrade
\OC_DB::simulateUpdateDbFromStructure(\OC::$SERVERROOT . '/db_structure.xml');
@@ -344,6 +346,8 @@ class Updater extends BasicEmitter {
}
protected function doCoreUpgrade() {
$this->emit('\OC\Updater', 'dbUpgradeBefore');
// do the real upgrade
\OC_DB::updateDbFromStructure(\OC::$SERVERROOT . '/db_structure.xml');
@@ -355,6 +359,7 @@ class Updater extends BasicEmitter {
*/
protected function checkAppUpgrade($version) {
$apps = \OC_App::getEnabledApps();
$this->emit('\OC\Updater', 'appUpgradeCheckBefore');
foreach ($apps as $appId) {
$info = \OC_App::getAppInfo($appId);
@@ -372,6 +377,7 @@ class Updater extends BasicEmitter {
$this->includePreUpdate($appId);
}
if (file_exists(\OC_App::getAppPath($appId) . '/appinfo/database.xml')) {
$this->emit('\OC\Updater', 'appSimulateUpdate', array($appId));
\OC_DB::simulateUpdateDbFromStructure(\OC_App::getAppPath($appId) . '/appinfo/database.xml');
}
}
+32
View File
@@ -294,6 +294,20 @@ class OC_User_Database extends OC_User_Backend implements \OCP\IUserBackend {
return $result->fetchOne();
}
/**
* returns the username for the given login name in the correct casing
*
* @param string $loginName
* @return string|false
*/
public function loginName2UserName($loginName) {
if ($this->userExists($loginName)) {
return $this->cache[$loginName]['uid'];
}
return false;
}
/**
* Backend name to be shown in user management
* @return string the name of the backend to be shown
@@ -302,4 +316,22 @@ class OC_User_Database extends OC_User_Backend implements \OCP\IUserBackend {
return 'Database';
}
public static function preLoginNameUsedAsUserName($param) {
if(!isset($param['uid'])) {
throw new \Exception('key uid is expected to be set in $param');
}
$backends = \OC::$server->getUserManager()->getBackends();
foreach ($backends as $backend) {
if ($backend instanceof \OC_User_Database) {
/** @var \OC_User_Database $backend */
$uid = $backend->loginName2UserName($param['uid']);
if ($uid !== false) {
$param['uid'] = $uid;
return;
}
}
}
}
}
+12 -10
View File
@@ -1093,7 +1093,7 @@ class OC_Util {
return $id;
}
protected static $encryptedToken;
protected static $obfuscatedToken;
/**
* Register an get/post call. Important to prevent CSRF attacks.
*
@@ -1107,24 +1107,27 @@ class OC_Util {
*/
public static function callRegister() {
// Use existing token if function has already been called
if(isset(self::$encryptedToken)) {
return self::$encryptedToken;
if(isset(self::$obfuscatedToken)) {
return self::$obfuscatedToken;
}
$tokenLength = 30;
// Check if a token exists
if (!\OC::$server->getSession()->exists('requesttoken')) {
// No valid token found, generate a new one.
$requestToken = \OC::$server->getSecureRandom()->getMediumStrengthGenerator()->generate(30);
$requestToken = \OC::$server->getSecureRandom()->getMediumStrengthGenerator()->generate($tokenLength);
\OC::$server->getSession()->set('requesttoken', $requestToken);
} else {
// Valid token already exists, send it
$requestToken = \OC::$server->getSession()->get('requesttoken');
}
// Encrypt the token to mitigate breach-like attacks
$sharedSecret = \OC::$server->getSecureRandom()->getMediumStrengthGenerator()->generate(10);
self::$encryptedToken = \OC::$server->getCrypto()->encrypt($requestToken, $sharedSecret) . ':' . $sharedSecret;
return self::$encryptedToken;
// XOR the token to mitigate breach-like attacks
$sharedSecret = \OC::$server->getSecureRandom()->getMediumStrengthGenerator()->generate($tokenLength);
self::$obfuscatedToken = base64_encode($requestToken ^ $sharedSecret) .':'.$sharedSecret;
return self::$obfuscatedToken;
}
/**
@@ -1300,12 +1303,11 @@ class OC_Util {
/**
* Get URL content
* @param string $url Url to get content
* @deprecated Use \OC::$server->getHTTPHelper()->getUrlContent($url);
* @throws Exception If the URL does not start with http:// or https://
* @return string of the response or false on error
* This function get the content of a page via curl, if curl is enabled.
* If not, file_get_contents is used.
* @deprecated Use \OCP\Http\Client\IClientService
* @deprecated Use \OC::$server->getHTTPClientService()->newClient()->get($url);
*/
public static function getUrlContent($url) {
try {
+4 -3
View File
@@ -255,13 +255,14 @@ class Share extends \OC\Share\Constants {
* @param int $permissions CRUDS
* @param string $itemSourceName
* @param \DateTime $expirationDate
* @param bool $passwordChanged
* @return bool|string Returns true on success or false on failure, Returns token on success for links
* @throws \OC\HintException when the share type is remote and the shareWith is invalid
* @throws \Exception
* @since 5.0.0 - parameter $itemSourceName was added in 6.0.0, parameter $expirationDate was added in 7.0.0
* @since 5.0.0 - parameter $itemSourceName was added in 6.0.0, parameter $expirationDate was added in 7.0.0, paramter $passwordChanged added in 9.0.0
*/
public static function shareItem($itemType, $itemSource, $shareType, $shareWith, $permissions, $itemSourceName = null, \DateTime $expirationDate = null) {
return \OC\Share\Share::shareItem($itemType, $itemSource, $shareType, $shareWith, $permissions, $itemSourceName, $expirationDate);
public static function shareItem($itemType, $itemSource, $shareType, $shareWith, $permissions, $itemSourceName = null, \DateTime $expirationDate = null, $passwordChanged = null) {
return \OC\Share\Share::shareItem($itemType, $itemSource, $shareType, $shareWith, $permissions, $itemSourceName, $expirationDate, $passwordChanged);
}
/**

Some files were not shown because too many files have changed in this diff Show More