Completed
Pull Request — master (#3772)
by Robin
28:56
created
lib/public/Files/Search/ISearchQuery.php 1 patch
Indentation   +33 added lines, -33 removed lines patch added patch discarded remove patch
@@ -27,41 +27,41 @@
 block discarded – undo
27 27
  * @since 12.0.0
28 28
  */
29 29
 interface ISearchQuery {
30
-	/**
31
-	 * @return ISearchOperator
32
-	 * @since 12.0.0
33
-	 */
34
-	public function getSearchOperation();
30
+    /**
31
+     * @return ISearchOperator
32
+     * @since 12.0.0
33
+     */
34
+    public function getSearchOperation();
35 35
 
36
-	/**
37
-	 * Get the maximum number of results to return
38
-	 *
39
-	 * @return integer
40
-	 * @since 12.0.0
41
-	 */
42
-	public function getLimit();
36
+    /**
37
+     * Get the maximum number of results to return
38
+     *
39
+     * @return integer
40
+     * @since 12.0.0
41
+     */
42
+    public function getLimit();
43 43
 
44
-	/**
45
-	 * Get the offset for returned results
46
-	 *
47
-	 * @return integer
48
-	 * @since 12.0.0
49
-	 */
50
-	public function getOffset();
44
+    /**
45
+     * Get the offset for returned results
46
+     *
47
+     * @return integer
48
+     * @since 12.0.0
49
+     */
50
+    public function getOffset();
51 51
 
52
-	/**
53
-	 * The fields and directions to order by
54
-	 *
55
-	 * @return ISearchOrder[]
56
-	 * @since 12.0.0
57
-	 */
58
-	public function getOrder();
52
+    /**
53
+     * The fields and directions to order by
54
+     *
55
+     * @return ISearchOrder[]
56
+     * @since 12.0.0
57
+     */
58
+    public function getOrder();
59 59
 
60
-	/**
61
-	 * The user that issued the search
62
-	 *
63
-	 * @return IUser
64
-	 * @since 12.0.0
65
-	 */
66
-	public function getUser();
60
+    /**
61
+     * The user that issued the search
62
+     *
63
+     * @return IUser
64
+     * @since 12.0.0
65
+     */
66
+    public function getUser();
67 67
 }
Please login to merge, or discard this patch.
lib/private/Files/Cache/QuerySearchHelper.php 2 patches
Indentation   +142 added lines, -142 removed lines patch added patch discarded remove patch
@@ -31,158 +31,158 @@
 block discarded – undo
31 31
  * Tools for transforming search queries into database queries
32 32
  */
33 33
 class QuerySearchHelper {
34
-	static protected $searchOperatorMap = [
35
-		ISearchComparison::COMPARE_LIKE => 'iLike',
36
-		ISearchComparison::COMPARE_EQUAL => 'eq',
37
-		ISearchComparison::COMPARE_GREATER_THAN => 'gt',
38
-		ISearchComparison::COMPARE_GREATER_THAN_EQUAL => 'gte',
39
-		ISearchComparison::COMPARE_LESS_THAN => 'lt',
40
-		ISearchComparison::COMPARE_LESS_THAN_EQUAL => 'lte'
41
-	];
34
+    static protected $searchOperatorMap = [
35
+        ISearchComparison::COMPARE_LIKE => 'iLike',
36
+        ISearchComparison::COMPARE_EQUAL => 'eq',
37
+        ISearchComparison::COMPARE_GREATER_THAN => 'gt',
38
+        ISearchComparison::COMPARE_GREATER_THAN_EQUAL => 'gte',
39
+        ISearchComparison::COMPARE_LESS_THAN => 'lt',
40
+        ISearchComparison::COMPARE_LESS_THAN_EQUAL => 'lte'
41
+    ];
42 42
 
43
-	static protected $searchOperatorNegativeMap = [
44
-		ISearchComparison::COMPARE_LIKE => 'notLike',
45
-		ISearchComparison::COMPARE_EQUAL => 'neq',
46
-		ISearchComparison::COMPARE_GREATER_THAN => 'lte',
47
-		ISearchComparison::COMPARE_GREATER_THAN_EQUAL => 'lt',
48
-		ISearchComparison::COMPARE_LESS_THAN => 'gte',
49
-		ISearchComparison::COMPARE_LESS_THAN_EQUAL => 'lt'
50
-	];
43
+    static protected $searchOperatorNegativeMap = [
44
+        ISearchComparison::COMPARE_LIKE => 'notLike',
45
+        ISearchComparison::COMPARE_EQUAL => 'neq',
46
+        ISearchComparison::COMPARE_GREATER_THAN => 'lte',
47
+        ISearchComparison::COMPARE_GREATER_THAN_EQUAL => 'lt',
48
+        ISearchComparison::COMPARE_LESS_THAN => 'gte',
49
+        ISearchComparison::COMPARE_LESS_THAN_EQUAL => 'lt'
50
+    ];
51 51
 
52
-	const TAG_FAVORITE = '_$!<Favorite>!$_';
52
+    const TAG_FAVORITE = '_$!<Favorite>!$_';
53 53
 
54
-	/** @var IMimeTypeLoader */
55
-	private $mimetypeLoader;
54
+    /** @var IMimeTypeLoader */
55
+    private $mimetypeLoader;
56 56
 
57
-	/**
58
-	 * QuerySearchUtil constructor.
59
-	 *
60
-	 * @param IMimeTypeLoader $mimetypeLoader
61
-	 */
62
-	public function __construct(IMimeTypeLoader $mimetypeLoader) {
63
-		$this->mimetypeLoader = $mimetypeLoader;
64
-	}
57
+    /**
58
+     * QuerySearchUtil constructor.
59
+     *
60
+     * @param IMimeTypeLoader $mimetypeLoader
61
+     */
62
+    public function __construct(IMimeTypeLoader $mimetypeLoader) {
63
+        $this->mimetypeLoader = $mimetypeLoader;
64
+    }
65 65
 
66
-	/**
67
-	 * Whether or not the tag tables should be joined to complete the search
68
-	 *
69
-	 * @param ISearchOperator $operator
70
-	 * @return boolean
71
-	 */
72
-	public function shouldJoinTags(ISearchOperator $operator) {
73
-		if ($operator instanceof ISearchBinaryOperator) {
74
-			return array_reduce($operator->getArguments(), function ($shouldJoin, ISearchOperator $operator) {
75
-				return $shouldJoin || $this->shouldJoinTags($operator);
76
-			}, false);
77
-		} else if ($operator instanceof ISearchComparison) {
78
-			return $operator->getField() === 'tagname' || $operator->getField() === 'favorite';
79
-		}
80
-		return false;
81
-	}
66
+    /**
67
+     * Whether or not the tag tables should be joined to complete the search
68
+     *
69
+     * @param ISearchOperator $operator
70
+     * @return boolean
71
+     */
72
+    public function shouldJoinTags(ISearchOperator $operator) {
73
+        if ($operator instanceof ISearchBinaryOperator) {
74
+            return array_reduce($operator->getArguments(), function ($shouldJoin, ISearchOperator $operator) {
75
+                return $shouldJoin || $this->shouldJoinTags($operator);
76
+            }, false);
77
+        } else if ($operator instanceof ISearchComparison) {
78
+            return $operator->getField() === 'tagname' || $operator->getField() === 'favorite';
79
+        }
80
+        return false;
81
+    }
82 82
 
83
-	public function searchOperatorToDBExpr(IQueryBuilder $builder, ISearchOperator $operator) {
84
-		$expr = $builder->expr();
85
-		if ($operator instanceof ISearchBinaryOperator) {
86
-			switch ($operator->getType()) {
87
-				case ISearchBinaryOperator::OPERATOR_NOT:
88
-					$negativeOperator = $operator->getArguments()[0];
89
-					if ($negativeOperator instanceof ISearchComparison) {
90
-						return $this->searchComparisonToDBExpr($builder, $negativeOperator, self::$searchOperatorNegativeMap);
91
-					} else {
92
-						throw new \InvalidArgumentException('Binary operators inside "not" is not supported');
93
-					}
94
-				case ISearchBinaryOperator::OPERATOR_AND:
95
-					return $expr->andX($this->searchOperatorToDBExpr($builder, $operator->getArguments()[0]), $this->searchOperatorToDBExpr($builder, $operator->getArguments()[1]));
96
-				case ISearchBinaryOperator::OPERATOR_OR:
97
-					return $expr->orX($this->searchOperatorToDBExpr($builder, $operator->getArguments()[0]), $this->searchOperatorToDBExpr($builder, $operator->getArguments()[1]));
98
-				default:
99
-					throw new \InvalidArgumentException('Invalid operator type: ' . $operator->getType());
100
-			}
101
-		} else if ($operator instanceof ISearchComparison) {
102
-			return $this->searchComparisonToDBExpr($builder, $operator, self::$searchOperatorMap);
103
-		} else {
104
-			throw new \InvalidArgumentException('Invalid operator type: ' . get_class($operator));
105
-		}
106
-	}
83
+    public function searchOperatorToDBExpr(IQueryBuilder $builder, ISearchOperator $operator) {
84
+        $expr = $builder->expr();
85
+        if ($operator instanceof ISearchBinaryOperator) {
86
+            switch ($operator->getType()) {
87
+                case ISearchBinaryOperator::OPERATOR_NOT:
88
+                    $negativeOperator = $operator->getArguments()[0];
89
+                    if ($negativeOperator instanceof ISearchComparison) {
90
+                        return $this->searchComparisonToDBExpr($builder, $negativeOperator, self::$searchOperatorNegativeMap);
91
+                    } else {
92
+                        throw new \InvalidArgumentException('Binary operators inside "not" is not supported');
93
+                    }
94
+                case ISearchBinaryOperator::OPERATOR_AND:
95
+                    return $expr->andX($this->searchOperatorToDBExpr($builder, $operator->getArguments()[0]), $this->searchOperatorToDBExpr($builder, $operator->getArguments()[1]));
96
+                case ISearchBinaryOperator::OPERATOR_OR:
97
+                    return $expr->orX($this->searchOperatorToDBExpr($builder, $operator->getArguments()[0]), $this->searchOperatorToDBExpr($builder, $operator->getArguments()[1]));
98
+                default:
99
+                    throw new \InvalidArgumentException('Invalid operator type: ' . $operator->getType());
100
+            }
101
+        } else if ($operator instanceof ISearchComparison) {
102
+            return $this->searchComparisonToDBExpr($builder, $operator, self::$searchOperatorMap);
103
+        } else {
104
+            throw new \InvalidArgumentException('Invalid operator type: ' . get_class($operator));
105
+        }
106
+    }
107 107
 
108
-	private function searchComparisonToDBExpr(IQueryBuilder $builder, ISearchComparison $comparison, array $operatorMap) {
109
-		$this->validateComparison($comparison);
108
+    private function searchComparisonToDBExpr(IQueryBuilder $builder, ISearchComparison $comparison, array $operatorMap) {
109
+        $this->validateComparison($comparison);
110 110
 
111
-		list($field, $value, $type) = $this->getOperatorFieldAndValue($comparison);
112
-		if (isset($operatorMap[$type])) {
113
-			$queryOperator = $operatorMap[$type];
114
-			return $builder->expr()->$queryOperator($field, $this->getParameterForValue($builder, $value));
115
-		} else {
116
-			throw new \InvalidArgumentException('Invalid operator type: ' . $comparison->getType());
117
-		}
118
-	}
111
+        list($field, $value, $type) = $this->getOperatorFieldAndValue($comparison);
112
+        if (isset($operatorMap[$type])) {
113
+            $queryOperator = $operatorMap[$type];
114
+            return $builder->expr()->$queryOperator($field, $this->getParameterForValue($builder, $value));
115
+        } else {
116
+            throw new \InvalidArgumentException('Invalid operator type: ' . $comparison->getType());
117
+        }
118
+    }
119 119
 
120
-	private function getOperatorFieldAndValue(ISearchComparison $operator) {
121
-		$field = $operator->getField();
122
-		$value = $operator->getValue();
123
-		$type = $operator->getType();
124
-		if ($field === 'mimetype') {
125
-			if ($operator->getType() === ISearchComparison::COMPARE_EQUAL) {
126
-				$value = $this->mimetypeLoader->getId($value);
127
-			} else if ($operator->getType() === ISearchComparison::COMPARE_LIKE) {
128
-				// transform "mimetype='foo/%'" to "mimepart='foo'"
129
-				if (preg_match('|(.+)/%|', $value, $matches)) {
130
-					$field = 'mimepart';
131
-					$value = $this->mimetypeLoader->getId($matches[1]);
132
-					$type = ISearchComparison::COMPARE_EQUAL;
133
-				}
134
-				if (strpos($value, '%') !== false) {
135
-					throw new \InvalidArgumentException('Unsupported query value for mimetype: ' . $value . ', only values in the format "mime/type" or "mime/%" are supported');
136
-				}
137
-			}
138
-		} else if ($field === 'favorite') {
139
-			$field = 'tag.category';
140
-			$value = self::TAG_FAVORITE;
141
-		} else if ($field === 'tagname') {
142
-			$field = 'tag.category';
143
-		}
144
-		return [$field, $value, $type];
145
-	}
120
+    private function getOperatorFieldAndValue(ISearchComparison $operator) {
121
+        $field = $operator->getField();
122
+        $value = $operator->getValue();
123
+        $type = $operator->getType();
124
+        if ($field === 'mimetype') {
125
+            if ($operator->getType() === ISearchComparison::COMPARE_EQUAL) {
126
+                $value = $this->mimetypeLoader->getId($value);
127
+            } else if ($operator->getType() === ISearchComparison::COMPARE_LIKE) {
128
+                // transform "mimetype='foo/%'" to "mimepart='foo'"
129
+                if (preg_match('|(.+)/%|', $value, $matches)) {
130
+                    $field = 'mimepart';
131
+                    $value = $this->mimetypeLoader->getId($matches[1]);
132
+                    $type = ISearchComparison::COMPARE_EQUAL;
133
+                }
134
+                if (strpos($value, '%') !== false) {
135
+                    throw new \InvalidArgumentException('Unsupported query value for mimetype: ' . $value . ', only values in the format "mime/type" or "mime/%" are supported');
136
+                }
137
+            }
138
+        } else if ($field === 'favorite') {
139
+            $field = 'tag.category';
140
+            $value = self::TAG_FAVORITE;
141
+        } else if ($field === 'tagname') {
142
+            $field = 'tag.category';
143
+        }
144
+        return [$field, $value, $type];
145
+    }
146 146
 
147
-	private function validateComparison(ISearchComparison $operator) {
148
-		$types = [
149
-			'mimetype' => 'string',
150
-			'mtime' => 'integer',
151
-			'name' => 'string',
152
-			'size' => 'integer',
153
-			'tagname' => 'string',
154
-			'favorite' => 'boolean'
155
-		];
156
-		$comparisons = [
157
-			'mimetype' => ['eq', 'like'],
158
-			'mtime' => ['eq', 'gt', 'lt', 'gte', 'lte'],
159
-			'name' => ['eq', 'like'],
160
-			'size' => ['eq', 'gt', 'lt', 'gte', 'lte'],
161
-			'tagname' => ['eq', 'like'],
162
-			'favorite' => ['eq'],
163
-		];
147
+    private function validateComparison(ISearchComparison $operator) {
148
+        $types = [
149
+            'mimetype' => 'string',
150
+            'mtime' => 'integer',
151
+            'name' => 'string',
152
+            'size' => 'integer',
153
+            'tagname' => 'string',
154
+            'favorite' => 'boolean'
155
+        ];
156
+        $comparisons = [
157
+            'mimetype' => ['eq', 'like'],
158
+            'mtime' => ['eq', 'gt', 'lt', 'gte', 'lte'],
159
+            'name' => ['eq', 'like'],
160
+            'size' => ['eq', 'gt', 'lt', 'gte', 'lte'],
161
+            'tagname' => ['eq', 'like'],
162
+            'favorite' => ['eq'],
163
+        ];
164 164
 
165
-		if (!isset($types[$operator->getField()])) {
166
-			throw new \InvalidArgumentException('Unsupported comparison field ' . $operator->getField());
167
-		}
168
-		$type = $types[$operator->getField()];
169
-		if (gettype($operator->getValue()) !== $type) {
170
-			throw new \InvalidArgumentException('Invalid type for field ' . $operator->getField());
171
-		}
172
-		if (!in_array($operator->getType(), $comparisons[$operator->getField()])) {
173
-			throw new \InvalidArgumentException('Unsupported comparison for field  ' . $operator->getField() . ': ' . $operator->getType());
174
-		}
175
-	}
165
+        if (!isset($types[$operator->getField()])) {
166
+            throw new \InvalidArgumentException('Unsupported comparison field ' . $operator->getField());
167
+        }
168
+        $type = $types[$operator->getField()];
169
+        if (gettype($operator->getValue()) !== $type) {
170
+            throw new \InvalidArgumentException('Invalid type for field ' . $operator->getField());
171
+        }
172
+        if (!in_array($operator->getType(), $comparisons[$operator->getField()])) {
173
+            throw new \InvalidArgumentException('Unsupported comparison for field  ' . $operator->getField() . ': ' . $operator->getType());
174
+        }
175
+    }
176 176
 
177
-	private function getParameterForValue(IQueryBuilder $builder, $value) {
178
-		if ($value instanceof \DateTime) {
179
-			$value = $value->getTimestamp();
180
-		}
181
-		if (is_numeric($value)) {
182
-			$type = IQueryBuilder::PARAM_INT;
183
-		} else {
184
-			$type = IQueryBuilder::PARAM_STR;
185
-		}
186
-		return $builder->createNamedParameter($value, $type);
187
-	}
177
+    private function getParameterForValue(IQueryBuilder $builder, $value) {
178
+        if ($value instanceof \DateTime) {
179
+            $value = $value->getTimestamp();
180
+        }
181
+        if (is_numeric($value)) {
182
+            $type = IQueryBuilder::PARAM_INT;
183
+        } else {
184
+            $type = IQueryBuilder::PARAM_STR;
185
+        }
186
+        return $builder->createNamedParameter($value, $type);
187
+    }
188 188
 }
Please login to merge, or discard this patch.
Spacing   +8 added lines, -8 removed lines patch added patch discarded remove patch
@@ -71,7 +71,7 @@  discard block
 block discarded – undo
71 71
 	 */
72 72
 	public function shouldJoinTags(ISearchOperator $operator) {
73 73
 		if ($operator instanceof ISearchBinaryOperator) {
74
-			return array_reduce($operator->getArguments(), function ($shouldJoin, ISearchOperator $operator) {
74
+			return array_reduce($operator->getArguments(), function($shouldJoin, ISearchOperator $operator) {
75 75
 				return $shouldJoin || $this->shouldJoinTags($operator);
76 76
 			}, false);
77 77
 		} else if ($operator instanceof ISearchComparison) {
@@ -96,12 +96,12 @@  discard block
 block discarded – undo
96 96
 				case ISearchBinaryOperator::OPERATOR_OR:
97 97
 					return $expr->orX($this->searchOperatorToDBExpr($builder, $operator->getArguments()[0]), $this->searchOperatorToDBExpr($builder, $operator->getArguments()[1]));
98 98
 				default:
99
-					throw new \InvalidArgumentException('Invalid operator type: ' . $operator->getType());
99
+					throw new \InvalidArgumentException('Invalid operator type: '.$operator->getType());
100 100
 			}
101 101
 		} else if ($operator instanceof ISearchComparison) {
102 102
 			return $this->searchComparisonToDBExpr($builder, $operator, self::$searchOperatorMap);
103 103
 		} else {
104
-			throw new \InvalidArgumentException('Invalid operator type: ' . get_class($operator));
104
+			throw new \InvalidArgumentException('Invalid operator type: '.get_class($operator));
105 105
 		}
106 106
 	}
107 107
 
@@ -113,7 +113,7 @@  discard block
 block discarded – undo
113 113
 			$queryOperator = $operatorMap[$type];
114 114
 			return $builder->expr()->$queryOperator($field, $this->getParameterForValue($builder, $value));
115 115
 		} else {
116
-			throw new \InvalidArgumentException('Invalid operator type: ' . $comparison->getType());
116
+			throw new \InvalidArgumentException('Invalid operator type: '.$comparison->getType());
117 117
 		}
118 118
 	}
119 119
 
@@ -132,7 +132,7 @@  discard block
 block discarded – undo
132 132
 					$type = ISearchComparison::COMPARE_EQUAL;
133 133
 				}
134 134
 				if (strpos($value, '%') !== false) {
135
-					throw new \InvalidArgumentException('Unsupported query value for mimetype: ' . $value . ', only values in the format "mime/type" or "mime/%" are supported');
135
+					throw new \InvalidArgumentException('Unsupported query value for mimetype: '.$value.', only values in the format "mime/type" or "mime/%" are supported');
136 136
 				}
137 137
 			}
138 138
 		} else if ($field === 'favorite') {
@@ -163,14 +163,14 @@  discard block
 block discarded – undo
163 163
 		];
164 164
 
165 165
 		if (!isset($types[$operator->getField()])) {
166
-			throw new \InvalidArgumentException('Unsupported comparison field ' . $operator->getField());
166
+			throw new \InvalidArgumentException('Unsupported comparison field '.$operator->getField());
167 167
 		}
168 168
 		$type = $types[$operator->getField()];
169 169
 		if (gettype($operator->getValue()) !== $type) {
170
-			throw new \InvalidArgumentException('Invalid type for field ' . $operator->getField());
170
+			throw new \InvalidArgumentException('Invalid type for field '.$operator->getField());
171 171
 		}
172 172
 		if (!in_array($operator->getType(), $comparisons[$operator->getField()])) {
173
-			throw new \InvalidArgumentException('Unsupported comparison for field  ' . $operator->getField() . ': ' . $operator->getType());
173
+			throw new \InvalidArgumentException('Unsupported comparison for field  '.$operator->getField().': '.$operator->getType());
174 174
 		}
175 175
 	}
176 176
 
Please login to merge, or discard this patch.
lib/private/Files/Cache/Cache.php 1 patch
Indentation   +806 added lines, -806 removed lines patch added patch discarded remove patch
@@ -54,820 +54,820 @@
 block discarded – undo
54 54
  * - ChangePropagator: updates the mtime and etags of parent folders whenever a change to the cache is made to the cache by the updater
55 55
  */
56 56
 class Cache implements ICache {
57
-	use MoveFromCacheTrait {
58
-		MoveFromCacheTrait::moveFromCache as moveFromCacheFallback;
59
-	}
60
-
61
-	/**
62
-	 * @var array partial data for the cache
63
-	 */
64
-	protected $partial = array();
65
-
66
-	/**
67
-	 * @var string
68
-	 */
69
-	protected $storageId;
70
-
71
-	/**
72
-	 * @var Storage $storageCache
73
-	 */
74
-	protected $storageCache;
75
-
76
-	/** @var IMimeTypeLoader */
77
-	protected $mimetypeLoader;
78
-
79
-	/**
80
-	 * @var IDBConnection
81
-	 */
82
-	protected $connection;
83
-
84
-	/** @var QuerySearchHelper */
85
-	protected $querySearchHelper;
86
-
87
-	/**
88
-	 * @param \OC\Files\Storage\Storage|string $storage
89
-	 */
90
-	public function __construct($storage) {
91
-		if ($storage instanceof \OC\Files\Storage\Storage) {
92
-			$this->storageId = $storage->getId();
93
-		} else {
94
-			$this->storageId = $storage;
95
-		}
96
-		if (strlen($this->storageId) > 64) {
97
-			$this->storageId = md5($this->storageId);
98
-		}
99
-
100
-		$this->storageCache = new Storage($storage);
101
-		$this->mimetypeLoader = \OC::$server->getMimeTypeLoader();
102
-		$this->connection = \OC::$server->getDatabaseConnection();
103
-		$this->querySearchHelper = new QuerySearchHelper($this->mimetypeLoader);
104
-	}
105
-
106
-	/**
107
-	 * Get the numeric storage id for this cache's storage
108
-	 *
109
-	 * @return int
110
-	 */
111
-	public function getNumericStorageId() {
112
-		return $this->storageCache->getNumericId();
113
-	}
114
-
115
-	/**
116
-	 * get the stored metadata of a file or folder
117
-	 *
118
-	 * @param string | int $file either the path of a file or folder or the file id for a file or folder
119
-	 * @return ICacheEntry|false the cache entry as array of false if the file is not found in the cache
120
-	 */
121
-	public function get($file) {
122
-		if (is_string($file) or $file == '') {
123
-			// normalize file
124
-			$file = $this->normalize($file);
125
-
126
-			$where = 'WHERE `storage` = ? AND `path_hash` = ?';
127
-			$params = array($this->getNumericStorageId(), md5($file));
128
-		} else { //file id
129
-			$where = 'WHERE `fileid` = ?';
130
-			$params = array($file);
131
-		}
132
-		$sql = 'SELECT `fileid`, `storage`, `path`, `parent`, `name`, `mimetype`, `mimepart`, `size`, `mtime`,
57
+    use MoveFromCacheTrait {
58
+        MoveFromCacheTrait::moveFromCache as moveFromCacheFallback;
59
+    }
60
+
61
+    /**
62
+     * @var array partial data for the cache
63
+     */
64
+    protected $partial = array();
65
+
66
+    /**
67
+     * @var string
68
+     */
69
+    protected $storageId;
70
+
71
+    /**
72
+     * @var Storage $storageCache
73
+     */
74
+    protected $storageCache;
75
+
76
+    /** @var IMimeTypeLoader */
77
+    protected $mimetypeLoader;
78
+
79
+    /**
80
+     * @var IDBConnection
81
+     */
82
+    protected $connection;
83
+
84
+    /** @var QuerySearchHelper */
85
+    protected $querySearchHelper;
86
+
87
+    /**
88
+     * @param \OC\Files\Storage\Storage|string $storage
89
+     */
90
+    public function __construct($storage) {
91
+        if ($storage instanceof \OC\Files\Storage\Storage) {
92
+            $this->storageId = $storage->getId();
93
+        } else {
94
+            $this->storageId = $storage;
95
+        }
96
+        if (strlen($this->storageId) > 64) {
97
+            $this->storageId = md5($this->storageId);
98
+        }
99
+
100
+        $this->storageCache = new Storage($storage);
101
+        $this->mimetypeLoader = \OC::$server->getMimeTypeLoader();
102
+        $this->connection = \OC::$server->getDatabaseConnection();
103
+        $this->querySearchHelper = new QuerySearchHelper($this->mimetypeLoader);
104
+    }
105
+
106
+    /**
107
+     * Get the numeric storage id for this cache's storage
108
+     *
109
+     * @return int
110
+     */
111
+    public function getNumericStorageId() {
112
+        return $this->storageCache->getNumericId();
113
+    }
114
+
115
+    /**
116
+     * get the stored metadata of a file or folder
117
+     *
118
+     * @param string | int $file either the path of a file or folder or the file id for a file or folder
119
+     * @return ICacheEntry|false the cache entry as array of false if the file is not found in the cache
120
+     */
121
+    public function get($file) {
122
+        if (is_string($file) or $file == '') {
123
+            // normalize file
124
+            $file = $this->normalize($file);
125
+
126
+            $where = 'WHERE `storage` = ? AND `path_hash` = ?';
127
+            $params = array($this->getNumericStorageId(), md5($file));
128
+        } else { //file id
129
+            $where = 'WHERE `fileid` = ?';
130
+            $params = array($file);
131
+        }
132
+        $sql = 'SELECT `fileid`, `storage`, `path`, `parent`, `name`, `mimetype`, `mimepart`, `size`, `mtime`,
133 133
 					   `storage_mtime`, `encrypted`, `etag`, `permissions`, `checksum`
134 134
 				FROM `*PREFIX*filecache` ' . $where;
135
-		$result = $this->connection->executeQuery($sql, $params);
136
-		$data = $result->fetch();
137
-
138
-		//FIXME hide this HACK in the next database layer, or just use doctrine and get rid of MDB2 and PDO
139
-		//PDO returns false, MDB2 returns null, oracle always uses MDB2, so convert null to false
140
-		if ($data === null) {
141
-			$data = false;
142
-		}
143
-
144
-		//merge partial data
145
-		if (!$data and is_string($file)) {
146
-			if (isset($this->partial[$file])) {
147
-				$data = $this->partial[$file];
148
-			}
149
-			return $data;
150
-		} else {
151
-			return self::cacheEntryFromData($data, $this->mimetypeLoader);
152
-		}
153
-	}
154
-
155
-	/**
156
-	 * Create a CacheEntry from database row
157
-	 *
158
-	 * @param array $data
159
-	 * @param IMimeTypeLoader $mimetypeLoader
160
-	 * @return CacheEntry
161
-	 */
162
-	public static function cacheEntryFromData($data, IMimeTypeLoader $mimetypeLoader) {
163
-		//fix types
164
-		$data['fileid'] = (int)$data['fileid'];
165
-		$data['parent'] = (int)$data['parent'];
166
-		$data['size'] = 0 + $data['size'];
167
-		$data['mtime'] = (int)$data['mtime'];
168
-		$data['storage_mtime'] = (int)$data['storage_mtime'];
169
-		$data['encryptedVersion'] = (int)$data['encrypted'];
170
-		$data['encrypted'] = (bool)$data['encrypted'];
171
-		$data['storage_id'] = $data['storage'];
172
-		$data['storage'] = (int)$data['storage'];
173
-		$data['mimetype'] = $mimetypeLoader->getMimetypeById($data['mimetype']);
174
-		$data['mimepart'] = $mimetypeLoader->getMimetypeById($data['mimepart']);
175
-		if ($data['storage_mtime'] == 0) {
176
-			$data['storage_mtime'] = $data['mtime'];
177
-		}
178
-		$data['permissions'] = (int)$data['permissions'];
179
-		return new CacheEntry($data);
180
-	}
181
-
182
-	/**
183
-	 * get the metadata of all files stored in $folder
184
-	 *
185
-	 * @param string $folder
186
-	 * @return ICacheEntry[]
187
-	 */
188
-	public function getFolderContents($folder) {
189
-		$fileId = $this->getId($folder);
190
-		return $this->getFolderContentsById($fileId);
191
-	}
192
-
193
-	/**
194
-	 * get the metadata of all files stored in $folder
195
-	 *
196
-	 * @param int $fileId the file id of the folder
197
-	 * @return ICacheEntry[]
198
-	 */
199
-	public function getFolderContentsById($fileId) {
200
-		if ($fileId > -1) {
201
-			$sql = 'SELECT `fileid`, `storage`, `path`, `parent`, `name`, `mimetype`, `mimepart`, `size`, `mtime`,
135
+        $result = $this->connection->executeQuery($sql, $params);
136
+        $data = $result->fetch();
137
+
138
+        //FIXME hide this HACK in the next database layer, or just use doctrine and get rid of MDB2 and PDO
139
+        //PDO returns false, MDB2 returns null, oracle always uses MDB2, so convert null to false
140
+        if ($data === null) {
141
+            $data = false;
142
+        }
143
+
144
+        //merge partial data
145
+        if (!$data and is_string($file)) {
146
+            if (isset($this->partial[$file])) {
147
+                $data = $this->partial[$file];
148
+            }
149
+            return $data;
150
+        } else {
151
+            return self::cacheEntryFromData($data, $this->mimetypeLoader);
152
+        }
153
+    }
154
+
155
+    /**
156
+     * Create a CacheEntry from database row
157
+     *
158
+     * @param array $data
159
+     * @param IMimeTypeLoader $mimetypeLoader
160
+     * @return CacheEntry
161
+     */
162
+    public static function cacheEntryFromData($data, IMimeTypeLoader $mimetypeLoader) {
163
+        //fix types
164
+        $data['fileid'] = (int)$data['fileid'];
165
+        $data['parent'] = (int)$data['parent'];
166
+        $data['size'] = 0 + $data['size'];
167
+        $data['mtime'] = (int)$data['mtime'];
168
+        $data['storage_mtime'] = (int)$data['storage_mtime'];
169
+        $data['encryptedVersion'] = (int)$data['encrypted'];
170
+        $data['encrypted'] = (bool)$data['encrypted'];
171
+        $data['storage_id'] = $data['storage'];
172
+        $data['storage'] = (int)$data['storage'];
173
+        $data['mimetype'] = $mimetypeLoader->getMimetypeById($data['mimetype']);
174
+        $data['mimepart'] = $mimetypeLoader->getMimetypeById($data['mimepart']);
175
+        if ($data['storage_mtime'] == 0) {
176
+            $data['storage_mtime'] = $data['mtime'];
177
+        }
178
+        $data['permissions'] = (int)$data['permissions'];
179
+        return new CacheEntry($data);
180
+    }
181
+
182
+    /**
183
+     * get the metadata of all files stored in $folder
184
+     *
185
+     * @param string $folder
186
+     * @return ICacheEntry[]
187
+     */
188
+    public function getFolderContents($folder) {
189
+        $fileId = $this->getId($folder);
190
+        return $this->getFolderContentsById($fileId);
191
+    }
192
+
193
+    /**
194
+     * get the metadata of all files stored in $folder
195
+     *
196
+     * @param int $fileId the file id of the folder
197
+     * @return ICacheEntry[]
198
+     */
199
+    public function getFolderContentsById($fileId) {
200
+        if ($fileId > -1) {
201
+            $sql = 'SELECT `fileid`, `storage`, `path`, `parent`, `name`, `mimetype`, `mimepart`, `size`, `mtime`,
202 202
 						   `storage_mtime`, `encrypted`, `etag`, `permissions`, `checksum`
203 203
 					FROM `*PREFIX*filecache` WHERE `parent` = ? ORDER BY `name` ASC';
204
-			$result = $this->connection->executeQuery($sql, [$fileId]);
205
-			$files = $result->fetchAll();
206
-			return array_map(function (array $data) {
207
-				return self::cacheEntryFromData($data, $this->mimetypeLoader);;
208
-			}, $files);
209
-		} else {
210
-			return array();
211
-		}
212
-	}
213
-
214
-	/**
215
-	 * insert or update meta data for a file or folder
216
-	 *
217
-	 * @param string $file
218
-	 * @param array $data
219
-	 *
220
-	 * @return int file id
221
-	 * @throws \RuntimeException
222
-	 */
223
-	public function put($file, array $data) {
224
-		if (($id = $this->getId($file)) > -1) {
225
-			$this->update($id, $data);
226
-			return $id;
227
-		} else {
228
-			return $this->insert($file, $data);
229
-		}
230
-	}
231
-
232
-	/**
233
-	 * insert meta data for a new file or folder
234
-	 *
235
-	 * @param string $file
236
-	 * @param array $data
237
-	 *
238
-	 * @return int file id
239
-	 * @throws \RuntimeException
240
-	 */
241
-	public function insert($file, array $data) {
242
-		// normalize file
243
-		$file = $this->normalize($file);
244
-
245
-		if (isset($this->partial[$file])) { //add any saved partial data
246
-			$data = array_merge($this->partial[$file], $data);
247
-			unset($this->partial[$file]);
248
-		}
249
-
250
-		$requiredFields = array('size', 'mtime', 'mimetype');
251
-		foreach ($requiredFields as $field) {
252
-			if (!isset($data[$field])) { //data not complete save as partial and return
253
-				$this->partial[$file] = $data;
254
-				return -1;
255
-			}
256
-		}
257
-
258
-		$data['path'] = $file;
259
-		$data['parent'] = $this->getParentId($file);
260
-		$data['name'] = \OC_Util::basename($file);
261
-
262
-		list($queryParts, $params) = $this->buildParts($data);
263
-		$queryParts[] = '`storage`';
264
-		$params[] = $this->getNumericStorageId();
265
-
266
-		$queryParts = array_map(function ($item) {
267
-			return trim($item, "`");
268
-		}, $queryParts);
269
-		$values = array_combine($queryParts, $params);
270
-		if (\OC::$server->getDatabaseConnection()->insertIfNotExist('*PREFIX*filecache', $values, [
271
-			'storage',
272
-			'path_hash',
273
-		])
274
-		) {
275
-			return (int)$this->connection->lastInsertId('*PREFIX*filecache');
276
-		}
277
-
278
-		// The file was created in the mean time
279
-		if (($id = $this->getId($file)) > -1) {
280
-			$this->update($id, $data);
281
-			return $id;
282
-		} else {
283
-			throw new \RuntimeException('File entry could not be inserted with insertIfNotExist() but could also not be selected with getId() in order to perform an update. Please try again.');
284
-		}
285
-	}
286
-
287
-	/**
288
-	 * update the metadata of an existing file or folder in the cache
289
-	 *
290
-	 * @param int $id the fileid of the existing file or folder
291
-	 * @param array $data [$key => $value] the metadata to update, only the fields provided in the array will be updated, non-provided values will remain unchanged
292
-	 */
293
-	public function update($id, array $data) {
294
-
295
-		if (isset($data['path'])) {
296
-			// normalize path
297
-			$data['path'] = $this->normalize($data['path']);
298
-		}
299
-
300
-		if (isset($data['name'])) {
301
-			// normalize path
302
-			$data['name'] = $this->normalize($data['name']);
303
-		}
304
-
305
-		list($queryParts, $params) = $this->buildParts($data);
306
-		// duplicate $params because we need the parts twice in the SQL statement
307
-		// once for the SET part, once in the WHERE clause
308
-		$params = array_merge($params, $params);
309
-		$params[] = $id;
310
-
311
-		// don't update if the data we try to set is the same as the one in the record
312
-		// some databases (Postgres) don't like superfluous updates
313
-		$sql = 'UPDATE `*PREFIX*filecache` SET ' . implode(' = ?, ', $queryParts) . '=? ' .
314
-			'WHERE (' .
315
-			implode(' <> ? OR ', $queryParts) . ' <> ? OR ' .
316
-			implode(' IS NULL OR ', $queryParts) . ' IS NULL' .
317
-			') AND `fileid` = ? ';
318
-		$this->connection->executeQuery($sql, $params);
319
-
320
-	}
321
-
322
-	/**
323
-	 * extract query parts and params array from data array
324
-	 *
325
-	 * @param array $data
326
-	 * @return array [$queryParts, $params]
327
-	 *        $queryParts: string[], the (escaped) column names to be set in the query
328
-	 *        $params: mixed[], the new values for the columns, to be passed as params to the query
329
-	 */
330
-	protected function buildParts(array $data) {
331
-		$fields = array(
332
-			'path', 'parent', 'name', 'mimetype', 'size', 'mtime', 'storage_mtime', 'encrypted',
333
-			'etag', 'permissions', 'checksum');
334
-
335
-		$doNotCopyStorageMTime = false;
336
-		if (array_key_exists('mtime', $data) && $data['mtime'] === null) {
337
-			// this horrific magic tells it to not copy storage_mtime to mtime
338
-			unset($data['mtime']);
339
-			$doNotCopyStorageMTime = true;
340
-		}
341
-
342
-		$params = array();
343
-		$queryParts = array();
344
-		foreach ($data as $name => $value) {
345
-			if (array_search($name, $fields) !== false) {
346
-				if ($name === 'path') {
347
-					$params[] = md5($value);
348
-					$queryParts[] = '`path_hash`';
349
-				} elseif ($name === 'mimetype') {
350
-					$params[] = $this->mimetypeLoader->getId(substr($value, 0, strpos($value, '/')));
351
-					$queryParts[] = '`mimepart`';
352
-					$value = $this->mimetypeLoader->getId($value);
353
-				} elseif ($name === 'storage_mtime') {
354
-					if (!$doNotCopyStorageMTime && !isset($data['mtime'])) {
355
-						$params[] = $value;
356
-						$queryParts[] = '`mtime`';
357
-					}
358
-				} elseif ($name === 'encrypted') {
359
-					if (isset($data['encryptedVersion'])) {
360
-						$value = $data['encryptedVersion'];
361
-					} else {
362
-						// Boolean to integer conversion
363
-						$value = $value ? 1 : 0;
364
-					}
365
-				}
366
-				$params[] = $value;
367
-				$queryParts[] = '`' . $name . '`';
368
-			}
369
-		}
370
-		return array($queryParts, $params);
371
-	}
372
-
373
-	/**
374
-	 * get the file id for a file
375
-	 *
376
-	 * A file id is a numeric id for a file or folder that's unique within an owncloud instance which stays the same for the lifetime of a file
377
-	 *
378
-	 * File ids are easiest way for apps to store references to a file since unlike paths they are not affected by renames or sharing
379
-	 *
380
-	 * @param string $file
381
-	 * @return int
382
-	 */
383
-	public function getId($file) {
384
-		// normalize file
385
-		$file = $this->normalize($file);
386
-
387
-		$pathHash = md5($file);
388
-
389
-		$sql = 'SELECT `fileid` FROM `*PREFIX*filecache` WHERE `storage` = ? AND `path_hash` = ?';
390
-		$result = $this->connection->executeQuery($sql, array($this->getNumericStorageId(), $pathHash));
391
-		if ($row = $result->fetch()) {
392
-			return $row['fileid'];
393
-		} else {
394
-			return -1;
395
-		}
396
-	}
397
-
398
-	/**
399
-	 * get the id of the parent folder of a file
400
-	 *
401
-	 * @param string $file
402
-	 * @return int
403
-	 */
404
-	public function getParentId($file) {
405
-		if ($file === '') {
406
-			return -1;
407
-		} else {
408
-			$parent = $this->getParentPath($file);
409
-			return (int)$this->getId($parent);
410
-		}
411
-	}
412
-
413
-	private function getParentPath($path) {
414
-		$parent = dirname($path);
415
-		if ($parent === '.') {
416
-			$parent = '';
417
-		}
418
-		return $parent;
419
-	}
420
-
421
-	/**
422
-	 * check if a file is available in the cache
423
-	 *
424
-	 * @param string $file
425
-	 * @return bool
426
-	 */
427
-	public function inCache($file) {
428
-		return $this->getId($file) != -1;
429
-	}
430
-
431
-	/**
432
-	 * remove a file or folder from the cache
433
-	 *
434
-	 * when removing a folder from the cache all files and folders inside the folder will be removed as well
435
-	 *
436
-	 * @param string $file
437
-	 */
438
-	public function remove($file) {
439
-		$entry = $this->get($file);
440
-		$sql = 'DELETE FROM `*PREFIX*filecache` WHERE `fileid` = ?';
441
-		$this->connection->executeQuery($sql, array($entry['fileid']));
442
-		if ($entry['mimetype'] === 'httpd/unix-directory') {
443
-			$this->removeChildren($entry);
444
-		}
445
-	}
446
-
447
-	/**
448
-	 * Get all sub folders of a folder
449
-	 *
450
-	 * @param array $entry the cache entry of the folder to get the subfolders for
451
-	 * @return array[] the cache entries for the subfolders
452
-	 */
453
-	private function getSubFolders($entry) {
454
-		$children = $this->getFolderContentsById($entry['fileid']);
455
-		return array_filter($children, function ($child) {
456
-			return $child['mimetype'] === 'httpd/unix-directory';
457
-		});
458
-	}
459
-
460
-	/**
461
-	 * Recursively remove all children of a folder
462
-	 *
463
-	 * @param array $entry the cache entry of the folder to remove the children of
464
-	 * @throws \OC\DatabaseException
465
-	 */
466
-	private function removeChildren($entry) {
467
-		$subFolders = $this->getSubFolders($entry);
468
-		foreach ($subFolders as $folder) {
469
-			$this->removeChildren($folder);
470
-		}
471
-		$sql = 'DELETE FROM `*PREFIX*filecache` WHERE `parent` = ?';
472
-		$this->connection->executeQuery($sql, array($entry['fileid']));
473
-	}
474
-
475
-	/**
476
-	 * Move a file or folder in the cache
477
-	 *
478
-	 * @param string $source
479
-	 * @param string $target
480
-	 */
481
-	public function move($source, $target) {
482
-		$this->moveFromCache($this, $source, $target);
483
-	}
484
-
485
-	/**
486
-	 * Get the storage id and path needed for a move
487
-	 *
488
-	 * @param string $path
489
-	 * @return array [$storageId, $internalPath]
490
-	 */
491
-	protected function getMoveInfo($path) {
492
-		return [$this->getNumericStorageId(), $path];
493
-	}
494
-
495
-	/**
496
-	 * Move a file or folder in the cache
497
-	 *
498
-	 * @param \OCP\Files\Cache\ICache $sourceCache
499
-	 * @param string $sourcePath
500
-	 * @param string $targetPath
501
-	 * @throws \OC\DatabaseException
502
-	 */
503
-	public function moveFromCache(ICache $sourceCache, $sourcePath, $targetPath) {
504
-		if ($sourceCache instanceof Cache) {
505
-			// normalize source and target
506
-			$sourcePath = $this->normalize($sourcePath);
507
-			$targetPath = $this->normalize($targetPath);
508
-
509
-			$sourceData = $sourceCache->get($sourcePath);
510
-			$sourceId = $sourceData['fileid'];
511
-			$newParentId = $this->getParentId($targetPath);
512
-
513
-			list($sourceStorageId, $sourcePath) = $sourceCache->getMoveInfo($sourcePath);
514
-			list($targetStorageId, $targetPath) = $this->getMoveInfo($targetPath);
515
-
516
-			// sql for final update
517
-			$moveSql = 'UPDATE `*PREFIX*filecache` SET `storage` =  ?, `path` = ?, `path_hash` = ?, `name` = ?, `parent` =? WHERE `fileid` = ?';
518
-
519
-			if ($sourceData['mimetype'] === 'httpd/unix-directory') {
520
-				//find all child entries
521
-				$sql = 'SELECT `path`, `fileid` FROM `*PREFIX*filecache` WHERE `storage` = ? AND `path` LIKE ?';
522
-				$result = $this->connection->executeQuery($sql, [$sourceStorageId, $this->connection->escapeLikeParameter($sourcePath) . '/%']);
523
-				$childEntries = $result->fetchAll();
524
-				$sourceLength = strlen($sourcePath);
525
-				$this->connection->beginTransaction();
526
-				$query = $this->connection->prepare('UPDATE `*PREFIX*filecache` SET `storage` = ?, `path` = ?, `path_hash` = ? WHERE `fileid` = ?');
527
-
528
-				foreach ($childEntries as $child) {
529
-					$newTargetPath = $targetPath . substr($child['path'], $sourceLength);
530
-					$query->execute([$targetStorageId, $newTargetPath, md5($newTargetPath), $child['fileid']]);
531
-				}
532
-				$this->connection->executeQuery($moveSql, [$targetStorageId, $targetPath, md5($targetPath), basename($targetPath), $newParentId, $sourceId]);
533
-				$this->connection->commit();
534
-			} else {
535
-				$this->connection->executeQuery($moveSql, [$targetStorageId, $targetPath, md5($targetPath), basename($targetPath), $newParentId, $sourceId]);
536
-			}
537
-		} else {
538
-			$this->moveFromCacheFallback($sourceCache, $sourcePath, $targetPath);
539
-		}
540
-	}
541
-
542
-	/**
543
-	 * remove all entries for files that are stored on the storage from the cache
544
-	 */
545
-	public function clear() {
546
-		$sql = 'DELETE FROM `*PREFIX*filecache` WHERE `storage` = ?';
547
-		$this->connection->executeQuery($sql, array($this->getNumericStorageId()));
548
-
549
-		$sql = 'DELETE FROM `*PREFIX*storages` WHERE `id` = ?';
550
-		$this->connection->executeQuery($sql, array($this->storageId));
551
-	}
552
-
553
-	/**
554
-	 * Get the scan status of a file
555
-	 *
556
-	 * - Cache::NOT_FOUND: File is not in the cache
557
-	 * - Cache::PARTIAL: File is not stored in the cache but some incomplete data is known
558
-	 * - Cache::SHALLOW: The folder and it's direct children are in the cache but not all sub folders are fully scanned
559
-	 * - Cache::COMPLETE: The file or folder, with all it's children) are fully scanned
560
-	 *
561
-	 * @param string $file
562
-	 *
563
-	 * @return int Cache::NOT_FOUND, Cache::PARTIAL, Cache::SHALLOW or Cache::COMPLETE
564
-	 */
565
-	public function getStatus($file) {
566
-		// normalize file
567
-		$file = $this->normalize($file);
568
-
569
-		$pathHash = md5($file);
570
-		$sql = 'SELECT `size` FROM `*PREFIX*filecache` WHERE `storage` = ? AND `path_hash` = ?';
571
-		$result = $this->connection->executeQuery($sql, array($this->getNumericStorageId(), $pathHash));
572
-		if ($row = $result->fetch()) {
573
-			if ((int)$row['size'] === -1) {
574
-				return self::SHALLOW;
575
-			} else {
576
-				return self::COMPLETE;
577
-			}
578
-		} else {
579
-			if (isset($this->partial[$file])) {
580
-				return self::PARTIAL;
581
-			} else {
582
-				return self::NOT_FOUND;
583
-			}
584
-		}
585
-	}
586
-
587
-	/**
588
-	 * search for files matching $pattern
589
-	 *
590
-	 * @param string $pattern the search pattern using SQL search syntax (e.g. '%searchstring%')
591
-	 * @return ICacheEntry[] an array of cache entries where the name matches the search pattern
592
-	 */
593
-	public function search($pattern) {
594
-		// normalize pattern
595
-		$pattern = $this->normalize($pattern);
596
-
597
-
598
-		$sql = '
204
+            $result = $this->connection->executeQuery($sql, [$fileId]);
205
+            $files = $result->fetchAll();
206
+            return array_map(function (array $data) {
207
+                return self::cacheEntryFromData($data, $this->mimetypeLoader);;
208
+            }, $files);
209
+        } else {
210
+            return array();
211
+        }
212
+    }
213
+
214
+    /**
215
+     * insert or update meta data for a file or folder
216
+     *
217
+     * @param string $file
218
+     * @param array $data
219
+     *
220
+     * @return int file id
221
+     * @throws \RuntimeException
222
+     */
223
+    public function put($file, array $data) {
224
+        if (($id = $this->getId($file)) > -1) {
225
+            $this->update($id, $data);
226
+            return $id;
227
+        } else {
228
+            return $this->insert($file, $data);
229
+        }
230
+    }
231
+
232
+    /**
233
+     * insert meta data for a new file or folder
234
+     *
235
+     * @param string $file
236
+     * @param array $data
237
+     *
238
+     * @return int file id
239
+     * @throws \RuntimeException
240
+     */
241
+    public function insert($file, array $data) {
242
+        // normalize file
243
+        $file = $this->normalize($file);
244
+
245
+        if (isset($this->partial[$file])) { //add any saved partial data
246
+            $data = array_merge($this->partial[$file], $data);
247
+            unset($this->partial[$file]);
248
+        }
249
+
250
+        $requiredFields = array('size', 'mtime', 'mimetype');
251
+        foreach ($requiredFields as $field) {
252
+            if (!isset($data[$field])) { //data not complete save as partial and return
253
+                $this->partial[$file] = $data;
254
+                return -1;
255
+            }
256
+        }
257
+
258
+        $data['path'] = $file;
259
+        $data['parent'] = $this->getParentId($file);
260
+        $data['name'] = \OC_Util::basename($file);
261
+
262
+        list($queryParts, $params) = $this->buildParts($data);
263
+        $queryParts[] = '`storage`';
264
+        $params[] = $this->getNumericStorageId();
265
+
266
+        $queryParts = array_map(function ($item) {
267
+            return trim($item, "`");
268
+        }, $queryParts);
269
+        $values = array_combine($queryParts, $params);
270
+        if (\OC::$server->getDatabaseConnection()->insertIfNotExist('*PREFIX*filecache', $values, [
271
+            'storage',
272
+            'path_hash',
273
+        ])
274
+        ) {
275
+            return (int)$this->connection->lastInsertId('*PREFIX*filecache');
276
+        }
277
+
278
+        // The file was created in the mean time
279
+        if (($id = $this->getId($file)) > -1) {
280
+            $this->update($id, $data);
281
+            return $id;
282
+        } else {
283
+            throw new \RuntimeException('File entry could not be inserted with insertIfNotExist() but could also not be selected with getId() in order to perform an update. Please try again.');
284
+        }
285
+    }
286
+
287
+    /**
288
+     * update the metadata of an existing file or folder in the cache
289
+     *
290
+     * @param int $id the fileid of the existing file or folder
291
+     * @param array $data [$key => $value] the metadata to update, only the fields provided in the array will be updated, non-provided values will remain unchanged
292
+     */
293
+    public function update($id, array $data) {
294
+
295
+        if (isset($data['path'])) {
296
+            // normalize path
297
+            $data['path'] = $this->normalize($data['path']);
298
+        }
299
+
300
+        if (isset($data['name'])) {
301
+            // normalize path
302
+            $data['name'] = $this->normalize($data['name']);
303
+        }
304
+
305
+        list($queryParts, $params) = $this->buildParts($data);
306
+        // duplicate $params because we need the parts twice in the SQL statement
307
+        // once for the SET part, once in the WHERE clause
308
+        $params = array_merge($params, $params);
309
+        $params[] = $id;
310
+
311
+        // don't update if the data we try to set is the same as the one in the record
312
+        // some databases (Postgres) don't like superfluous updates
313
+        $sql = 'UPDATE `*PREFIX*filecache` SET ' . implode(' = ?, ', $queryParts) . '=? ' .
314
+            'WHERE (' .
315
+            implode(' <> ? OR ', $queryParts) . ' <> ? OR ' .
316
+            implode(' IS NULL OR ', $queryParts) . ' IS NULL' .
317
+            ') AND `fileid` = ? ';
318
+        $this->connection->executeQuery($sql, $params);
319
+
320
+    }
321
+
322
+    /**
323
+     * extract query parts and params array from data array
324
+     *
325
+     * @param array $data
326
+     * @return array [$queryParts, $params]
327
+     *        $queryParts: string[], the (escaped) column names to be set in the query
328
+     *        $params: mixed[], the new values for the columns, to be passed as params to the query
329
+     */
330
+    protected function buildParts(array $data) {
331
+        $fields = array(
332
+            'path', 'parent', 'name', 'mimetype', 'size', 'mtime', 'storage_mtime', 'encrypted',
333
+            'etag', 'permissions', 'checksum');
334
+
335
+        $doNotCopyStorageMTime = false;
336
+        if (array_key_exists('mtime', $data) && $data['mtime'] === null) {
337
+            // this horrific magic tells it to not copy storage_mtime to mtime
338
+            unset($data['mtime']);
339
+            $doNotCopyStorageMTime = true;
340
+        }
341
+
342
+        $params = array();
343
+        $queryParts = array();
344
+        foreach ($data as $name => $value) {
345
+            if (array_search($name, $fields) !== false) {
346
+                if ($name === 'path') {
347
+                    $params[] = md5($value);
348
+                    $queryParts[] = '`path_hash`';
349
+                } elseif ($name === 'mimetype') {
350
+                    $params[] = $this->mimetypeLoader->getId(substr($value, 0, strpos($value, '/')));
351
+                    $queryParts[] = '`mimepart`';
352
+                    $value = $this->mimetypeLoader->getId($value);
353
+                } elseif ($name === 'storage_mtime') {
354
+                    if (!$doNotCopyStorageMTime && !isset($data['mtime'])) {
355
+                        $params[] = $value;
356
+                        $queryParts[] = '`mtime`';
357
+                    }
358
+                } elseif ($name === 'encrypted') {
359
+                    if (isset($data['encryptedVersion'])) {
360
+                        $value = $data['encryptedVersion'];
361
+                    } else {
362
+                        // Boolean to integer conversion
363
+                        $value = $value ? 1 : 0;
364
+                    }
365
+                }
366
+                $params[] = $value;
367
+                $queryParts[] = '`' . $name . '`';
368
+            }
369
+        }
370
+        return array($queryParts, $params);
371
+    }
372
+
373
+    /**
374
+     * get the file id for a file
375
+     *
376
+     * A file id is a numeric id for a file or folder that's unique within an owncloud instance which stays the same for the lifetime of a file
377
+     *
378
+     * File ids are easiest way for apps to store references to a file since unlike paths they are not affected by renames or sharing
379
+     *
380
+     * @param string $file
381
+     * @return int
382
+     */
383
+    public function getId($file) {
384
+        // normalize file
385
+        $file = $this->normalize($file);
386
+
387
+        $pathHash = md5($file);
388
+
389
+        $sql = 'SELECT `fileid` FROM `*PREFIX*filecache` WHERE `storage` = ? AND `path_hash` = ?';
390
+        $result = $this->connection->executeQuery($sql, array($this->getNumericStorageId(), $pathHash));
391
+        if ($row = $result->fetch()) {
392
+            return $row['fileid'];
393
+        } else {
394
+            return -1;
395
+        }
396
+    }
397
+
398
+    /**
399
+     * get the id of the parent folder of a file
400
+     *
401
+     * @param string $file
402
+     * @return int
403
+     */
404
+    public function getParentId($file) {
405
+        if ($file === '') {
406
+            return -1;
407
+        } else {
408
+            $parent = $this->getParentPath($file);
409
+            return (int)$this->getId($parent);
410
+        }
411
+    }
412
+
413
+    private function getParentPath($path) {
414
+        $parent = dirname($path);
415
+        if ($parent === '.') {
416
+            $parent = '';
417
+        }
418
+        return $parent;
419
+    }
420
+
421
+    /**
422
+     * check if a file is available in the cache
423
+     *
424
+     * @param string $file
425
+     * @return bool
426
+     */
427
+    public function inCache($file) {
428
+        return $this->getId($file) != -1;
429
+    }
430
+
431
+    /**
432
+     * remove a file or folder from the cache
433
+     *
434
+     * when removing a folder from the cache all files and folders inside the folder will be removed as well
435
+     *
436
+     * @param string $file
437
+     */
438
+    public function remove($file) {
439
+        $entry = $this->get($file);
440
+        $sql = 'DELETE FROM `*PREFIX*filecache` WHERE `fileid` = ?';
441
+        $this->connection->executeQuery($sql, array($entry['fileid']));
442
+        if ($entry['mimetype'] === 'httpd/unix-directory') {
443
+            $this->removeChildren($entry);
444
+        }
445
+    }
446
+
447
+    /**
448
+     * Get all sub folders of a folder
449
+     *
450
+     * @param array $entry the cache entry of the folder to get the subfolders for
451
+     * @return array[] the cache entries for the subfolders
452
+     */
453
+    private function getSubFolders($entry) {
454
+        $children = $this->getFolderContentsById($entry['fileid']);
455
+        return array_filter($children, function ($child) {
456
+            return $child['mimetype'] === 'httpd/unix-directory';
457
+        });
458
+    }
459
+
460
+    /**
461
+     * Recursively remove all children of a folder
462
+     *
463
+     * @param array $entry the cache entry of the folder to remove the children of
464
+     * @throws \OC\DatabaseException
465
+     */
466
+    private function removeChildren($entry) {
467
+        $subFolders = $this->getSubFolders($entry);
468
+        foreach ($subFolders as $folder) {
469
+            $this->removeChildren($folder);
470
+        }
471
+        $sql = 'DELETE FROM `*PREFIX*filecache` WHERE `parent` = ?';
472
+        $this->connection->executeQuery($sql, array($entry['fileid']));
473
+    }
474
+
475
+    /**
476
+     * Move a file or folder in the cache
477
+     *
478
+     * @param string $source
479
+     * @param string $target
480
+     */
481
+    public function move($source, $target) {
482
+        $this->moveFromCache($this, $source, $target);
483
+    }
484
+
485
+    /**
486
+     * Get the storage id and path needed for a move
487
+     *
488
+     * @param string $path
489
+     * @return array [$storageId, $internalPath]
490
+     */
491
+    protected function getMoveInfo($path) {
492
+        return [$this->getNumericStorageId(), $path];
493
+    }
494
+
495
+    /**
496
+     * Move a file or folder in the cache
497
+     *
498
+     * @param \OCP\Files\Cache\ICache $sourceCache
499
+     * @param string $sourcePath
500
+     * @param string $targetPath
501
+     * @throws \OC\DatabaseException
502
+     */
503
+    public function moveFromCache(ICache $sourceCache, $sourcePath, $targetPath) {
504
+        if ($sourceCache instanceof Cache) {
505
+            // normalize source and target
506
+            $sourcePath = $this->normalize($sourcePath);
507
+            $targetPath = $this->normalize($targetPath);
508
+
509
+            $sourceData = $sourceCache->get($sourcePath);
510
+            $sourceId = $sourceData['fileid'];
511
+            $newParentId = $this->getParentId($targetPath);
512
+
513
+            list($sourceStorageId, $sourcePath) = $sourceCache->getMoveInfo($sourcePath);
514
+            list($targetStorageId, $targetPath) = $this->getMoveInfo($targetPath);
515
+
516
+            // sql for final update
517
+            $moveSql = 'UPDATE `*PREFIX*filecache` SET `storage` =  ?, `path` = ?, `path_hash` = ?, `name` = ?, `parent` =? WHERE `fileid` = ?';
518
+
519
+            if ($sourceData['mimetype'] === 'httpd/unix-directory') {
520
+                //find all child entries
521
+                $sql = 'SELECT `path`, `fileid` FROM `*PREFIX*filecache` WHERE `storage` = ? AND `path` LIKE ?';
522
+                $result = $this->connection->executeQuery($sql, [$sourceStorageId, $this->connection->escapeLikeParameter($sourcePath) . '/%']);
523
+                $childEntries = $result->fetchAll();
524
+                $sourceLength = strlen($sourcePath);
525
+                $this->connection->beginTransaction();
526
+                $query = $this->connection->prepare('UPDATE `*PREFIX*filecache` SET `storage` = ?, `path` = ?, `path_hash` = ? WHERE `fileid` = ?');
527
+
528
+                foreach ($childEntries as $child) {
529
+                    $newTargetPath = $targetPath . substr($child['path'], $sourceLength);
530
+                    $query->execute([$targetStorageId, $newTargetPath, md5($newTargetPath), $child['fileid']]);
531
+                }
532
+                $this->connection->executeQuery($moveSql, [$targetStorageId, $targetPath, md5($targetPath), basename($targetPath), $newParentId, $sourceId]);
533
+                $this->connection->commit();
534
+            } else {
535
+                $this->connection->executeQuery($moveSql, [$targetStorageId, $targetPath, md5($targetPath), basename($targetPath), $newParentId, $sourceId]);
536
+            }
537
+        } else {
538
+            $this->moveFromCacheFallback($sourceCache, $sourcePath, $targetPath);
539
+        }
540
+    }
541
+
542
+    /**
543
+     * remove all entries for files that are stored on the storage from the cache
544
+     */
545
+    public function clear() {
546
+        $sql = 'DELETE FROM `*PREFIX*filecache` WHERE `storage` = ?';
547
+        $this->connection->executeQuery($sql, array($this->getNumericStorageId()));
548
+
549
+        $sql = 'DELETE FROM `*PREFIX*storages` WHERE `id` = ?';
550
+        $this->connection->executeQuery($sql, array($this->storageId));
551
+    }
552
+
553
+    /**
554
+     * Get the scan status of a file
555
+     *
556
+     * - Cache::NOT_FOUND: File is not in the cache
557
+     * - Cache::PARTIAL: File is not stored in the cache but some incomplete data is known
558
+     * - Cache::SHALLOW: The folder and it's direct children are in the cache but not all sub folders are fully scanned
559
+     * - Cache::COMPLETE: The file or folder, with all it's children) are fully scanned
560
+     *
561
+     * @param string $file
562
+     *
563
+     * @return int Cache::NOT_FOUND, Cache::PARTIAL, Cache::SHALLOW or Cache::COMPLETE
564
+     */
565
+    public function getStatus($file) {
566
+        // normalize file
567
+        $file = $this->normalize($file);
568
+
569
+        $pathHash = md5($file);
570
+        $sql = 'SELECT `size` FROM `*PREFIX*filecache` WHERE `storage` = ? AND `path_hash` = ?';
571
+        $result = $this->connection->executeQuery($sql, array($this->getNumericStorageId(), $pathHash));
572
+        if ($row = $result->fetch()) {
573
+            if ((int)$row['size'] === -1) {
574
+                return self::SHALLOW;
575
+            } else {
576
+                return self::COMPLETE;
577
+            }
578
+        } else {
579
+            if (isset($this->partial[$file])) {
580
+                return self::PARTIAL;
581
+            } else {
582
+                return self::NOT_FOUND;
583
+            }
584
+        }
585
+    }
586
+
587
+    /**
588
+     * search for files matching $pattern
589
+     *
590
+     * @param string $pattern the search pattern using SQL search syntax (e.g. '%searchstring%')
591
+     * @return ICacheEntry[] an array of cache entries where the name matches the search pattern
592
+     */
593
+    public function search($pattern) {
594
+        // normalize pattern
595
+        $pattern = $this->normalize($pattern);
596
+
597
+
598
+        $sql = '
599 599
 			SELECT `fileid`, `storage`, `path`, `parent`, `name`,
600 600
 				`mimetype`, `storage_mtime`, `mimepart`, `size`, `mtime`,
601 601
 				 `encrypted`, `etag`, `permissions`, `checksum`
602 602
 			FROM `*PREFIX*filecache`
603 603
 			WHERE `storage` = ? AND `name` ILIKE ?';
604
-		$result = $this->connection->executeQuery($sql,
605
-			[$this->getNumericStorageId(), $pattern]
606
-		);
607
-
608
-		return $this->searchResultToCacheEntries($result);
609
-	}
610
-
611
-	/**
612
-	 * @param Statement $result
613
-	 * @return CacheEntry[]
614
-	 */
615
-	private function searchResultToCacheEntries(Statement $result) {
616
-		$files = $result->fetchAll();
617
-
618
-		return array_map(function (array $data) {
619
-			return self::cacheEntryFromData($data, $this->mimetypeLoader);
620
-		}, $files);
621
-	}
622
-
623
-	/**
624
-	 * search for files by mimetype
625
-	 *
626
-	 * @param string $mimetype either a full mimetype to search ('text/plain') or only the first part of a mimetype ('image')
627
-	 *        where it will search for all mimetypes in the group ('image/*')
628
-	 * @return ICacheEntry[] an array of cache entries where the mimetype matches the search
629
-	 */
630
-	public function searchByMime($mimetype) {
631
-		if (strpos($mimetype, '/')) {
632
-			$where = '`mimetype` = ?';
633
-		} else {
634
-			$where = '`mimepart` = ?';
635
-		}
636
-		$sql = 'SELECT `fileid`, `storage`, `path`, `parent`, `name`, `mimetype`, `mimepart`, `size`, `storage_mtime`, `mtime`, `encrypted`, `etag`, `permissions`, `checksum`
604
+        $result = $this->connection->executeQuery($sql,
605
+            [$this->getNumericStorageId(), $pattern]
606
+        );
607
+
608
+        return $this->searchResultToCacheEntries($result);
609
+    }
610
+
611
+    /**
612
+     * @param Statement $result
613
+     * @return CacheEntry[]
614
+     */
615
+    private function searchResultToCacheEntries(Statement $result) {
616
+        $files = $result->fetchAll();
617
+
618
+        return array_map(function (array $data) {
619
+            return self::cacheEntryFromData($data, $this->mimetypeLoader);
620
+        }, $files);
621
+    }
622
+
623
+    /**
624
+     * search for files by mimetype
625
+     *
626
+     * @param string $mimetype either a full mimetype to search ('text/plain') or only the first part of a mimetype ('image')
627
+     *        where it will search for all mimetypes in the group ('image/*')
628
+     * @return ICacheEntry[] an array of cache entries where the mimetype matches the search
629
+     */
630
+    public function searchByMime($mimetype) {
631
+        if (strpos($mimetype, '/')) {
632
+            $where = '`mimetype` = ?';
633
+        } else {
634
+            $where = '`mimepart` = ?';
635
+        }
636
+        $sql = 'SELECT `fileid`, `storage`, `path`, `parent`, `name`, `mimetype`, `mimepart`, `size`, `storage_mtime`, `mtime`, `encrypted`, `etag`, `permissions`, `checksum`
637 637
 				FROM `*PREFIX*filecache` WHERE ' . $where . ' AND `storage` = ?';
638
-		$mimetype = $this->mimetypeLoader->getId($mimetype);
639
-		$result = $this->connection->executeQuery($sql, array($mimetype, $this->getNumericStorageId()));
640
-
641
-		return $this->searchResultToCacheEntries($result);
642
-	}
643
-
644
-	public function searchQuery(ISearchQuery $searchQuery) {
645
-		$builder = \OC::$server->getDatabaseConnection()->getQueryBuilder();
646
-
647
-		$query = $builder->select(['fileid', 'storage', 'path', 'parent', 'name', 'mimetype', 'mimepart', 'size', 'mtime', 'storage_mtime', 'encrypted', 'etag', 'permissions', 'checksum'])
648
-			->from('filecache', 'file');
649
-
650
-		$query->where($builder->expr()->eq('storage', $builder->createNamedParameter($this->getNumericStorageId())));
651
-
652
-		if ($this->querySearchHelper->shouldJoinTags($searchQuery->getSearchOperation())) {
653
-			$query
654
-				->innerJoin('file', 'vcategory_to_object', 'tagmap', $builder->expr()->eq('file.fileid', 'tagmap.objid'))
655
-				->innerJoin('tagmap', 'vcategory', 'tag', $builder->expr()->andX(
656
-					$builder->expr()->eq('tagmap.type', 'tag.type'),
657
-					$builder->expr()->eq('tagmap.categoryid', 'tag.id')
658
-				))
659
-				->andWhere($builder->expr()->eq('tag.type', $builder->createNamedParameter('files')))
660
-				->andWhere($builder->expr()->eq('tag.uid', $builder->createNamedParameter($searchQuery->getUser()->getUID())));
661
-		}
662
-
663
-		$query->andWhere($this->querySearchHelper->searchOperatorToDBExpr($builder, $searchQuery->getSearchOperation()));
664
-
665
-		if ($searchQuery->getLimit()) {
666
-			$query->setMaxResults($searchQuery->getLimit());
667
-		}
668
-		if ($searchQuery->getOffset()) {
669
-			$query->setFirstResult($searchQuery->getOffset());
670
-		}
671
-
672
-		$result = $query->execute();
673
-		return $this->searchResultToCacheEntries($result);
674
-	}
675
-
676
-	/**
677
-	 * Search for files by tag of a given users.
678
-	 *
679
-	 * Note that every user can tag files differently.
680
-	 *
681
-	 * @param string|int $tag name or tag id
682
-	 * @param string $userId owner of the tags
683
-	 * @return ICacheEntry[] file data
684
-	 */
685
-	public function searchByTag($tag, $userId) {
686
-		$sql = 'SELECT `fileid`, `storage`, `path`, `parent`, `name`, ' .
687
-			'`mimetype`, `mimepart`, `size`, `mtime`, `storage_mtime`, ' .
688
-			'`encrypted`, `etag`, `permissions`, `checksum` ' .
689
-			'FROM `*PREFIX*filecache` `file`, ' .
690
-			'`*PREFIX*vcategory_to_object` `tagmap`, ' .
691
-			'`*PREFIX*vcategory` `tag` ' .
692
-			// JOIN filecache to vcategory_to_object
693
-			'WHERE `file`.`fileid` = `tagmap`.`objid` ' .
694
-			// JOIN vcategory_to_object to vcategory
695
-			'AND `tagmap`.`type` = `tag`.`type` ' .
696
-			'AND `tagmap`.`categoryid` = `tag`.`id` ' .
697
-			// conditions
698
-			'AND `file`.`storage` = ? ' .
699
-			'AND `tag`.`type` = \'files\' ' .
700
-			'AND `tag`.`uid` = ? ';
701
-		if (is_int($tag)) {
702
-			$sql .= 'AND `tag`.`id` = ? ';
703
-		} else {
704
-			$sql .= 'AND `tag`.`category` = ? ';
705
-		}
706
-		$result = $this->connection->executeQuery(
707
-			$sql,
708
-			[
709
-				$this->getNumericStorageId(),
710
-				$userId,
711
-				$tag
712
-			]
713
-		);
714
-
715
-		$files = $result->fetchAll();
716
-
717
-		return array_map(function (array $data) {
718
-			return self::cacheEntryFromData($data, $this->mimetypeLoader);
719
-		}, $files);
720
-	}
721
-
722
-	/**
723
-	 * Re-calculate the folder size and the size of all parent folders
724
-	 *
725
-	 * @param string|boolean $path
726
-	 * @param array $data (optional) meta data of the folder
727
-	 */
728
-	public function correctFolderSize($path, $data = null) {
729
-		$this->calculateFolderSize($path, $data);
730
-		if ($path !== '') {
731
-			$parent = dirname($path);
732
-			if ($parent === '.' or $parent === '/') {
733
-				$parent = '';
734
-			}
735
-			$this->correctFolderSize($parent);
736
-		}
737
-	}
738
-
739
-	/**
740
-	 * calculate the size of a folder and set it in the cache
741
-	 *
742
-	 * @param string $path
743
-	 * @param array $entry (optional) meta data of the folder
744
-	 * @return int
745
-	 */
746
-	public function calculateFolderSize($path, $entry = null) {
747
-		$totalSize = 0;
748
-		if (is_null($entry) or !isset($entry['fileid'])) {
749
-			$entry = $this->get($path);
750
-		}
751
-		if (isset($entry['mimetype']) && $entry['mimetype'] === 'httpd/unix-directory') {
752
-			$id = $entry['fileid'];
753
-			$sql = 'SELECT SUM(`size`) AS f1, MIN(`size`) AS f2 ' .
754
-				'FROM `*PREFIX*filecache` ' .
755
-				'WHERE `parent` = ? AND `storage` = ?';
756
-			$result = $this->connection->executeQuery($sql, array($id, $this->getNumericStorageId()));
757
-			if ($row = $result->fetch()) {
758
-				$result->closeCursor();
759
-				list($sum, $min) = array_values($row);
760
-				$sum = 0 + $sum;
761
-				$min = 0 + $min;
762
-				if ($min === -1) {
763
-					$totalSize = $min;
764
-				} else {
765
-					$totalSize = $sum;
766
-				}
767
-				$update = array();
768
-				if ($entry['size'] !== $totalSize) {
769
-					$update['size'] = $totalSize;
770
-				}
771
-				if (count($update) > 0) {
772
-					$this->update($id, $update);
773
-				}
774
-			} else {
775
-				$result->closeCursor();
776
-			}
777
-		}
778
-		return $totalSize;
779
-	}
780
-
781
-	/**
782
-	 * get all file ids on the files on the storage
783
-	 *
784
-	 * @return int[]
785
-	 */
786
-	public function getAll() {
787
-		$sql = 'SELECT `fileid` FROM `*PREFIX*filecache` WHERE `storage` = ?';
788
-		$result = $this->connection->executeQuery($sql, array($this->getNumericStorageId()));
789
-		$ids = array();
790
-		while ($row = $result->fetch()) {
791
-			$ids[] = $row['fileid'];
792
-		}
793
-		return $ids;
794
-	}
795
-
796
-	/**
797
-	 * find a folder in the cache which has not been fully scanned
798
-	 *
799
-	 * If multiple incomplete folders are in the cache, the one with the highest id will be returned,
800
-	 * use the one with the highest id gives the best result with the background scanner, since that is most
801
-	 * likely the folder where we stopped scanning previously
802
-	 *
803
-	 * @return string|bool the path of the folder or false when no folder matched
804
-	 */
805
-	public function getIncomplete() {
806
-		$query = $this->connection->prepare('SELECT `path` FROM `*PREFIX*filecache`'
807
-			. ' WHERE `storage` = ? AND `size` = -1 ORDER BY `fileid` DESC', 1);
808
-		$query->execute([$this->getNumericStorageId()]);
809
-		if ($row = $query->fetch()) {
810
-			return $row['path'];
811
-		} else {
812
-			return false;
813
-		}
814
-	}
815
-
816
-	/**
817
-	 * get the path of a file on this storage by it's file id
818
-	 *
819
-	 * @param int $id the file id of the file or folder to search
820
-	 * @return string|null the path of the file (relative to the storage) or null if a file with the given id does not exists within this cache
821
-	 */
822
-	public function getPathById($id) {
823
-		$sql = 'SELECT `path` FROM `*PREFIX*filecache` WHERE `fileid` = ? AND `storage` = ?';
824
-		$result = $this->connection->executeQuery($sql, array($id, $this->getNumericStorageId()));
825
-		if ($row = $result->fetch()) {
826
-			// Oracle stores empty strings as null...
827
-			if ($row['path'] === null) {
828
-				return '';
829
-			}
830
-			return $row['path'];
831
-		} else {
832
-			return null;
833
-		}
834
-	}
835
-
836
-	/**
837
-	 * get the storage id of the storage for a file and the internal path of the file
838
-	 * unlike getPathById this does not limit the search to files on this storage and
839
-	 * instead does a global search in the cache table
840
-	 *
841
-	 * @param int $id
842
-	 * @deprecated use getPathById() instead
843
-	 * @return array first element holding the storage id, second the path
844
-	 */
845
-	static public function getById($id) {
846
-		$connection = \OC::$server->getDatabaseConnection();
847
-		$sql = 'SELECT `storage`, `path` FROM `*PREFIX*filecache` WHERE `fileid` = ?';
848
-		$result = $connection->executeQuery($sql, array($id));
849
-		if ($row = $result->fetch()) {
850
-			$numericId = $row['storage'];
851
-			$path = $row['path'];
852
-		} else {
853
-			return null;
854
-		}
855
-
856
-		if ($id = Storage::getStorageId($numericId)) {
857
-			return array($id, $path);
858
-		} else {
859
-			return null;
860
-		}
861
-	}
862
-
863
-	/**
864
-	 * normalize the given path
865
-	 *
866
-	 * @param string $path
867
-	 * @return string
868
-	 */
869
-	public function normalize($path) {
870
-
871
-		return trim(\OC_Util::normalizeUnicode($path), '/');
872
-	}
638
+        $mimetype = $this->mimetypeLoader->getId($mimetype);
639
+        $result = $this->connection->executeQuery($sql, array($mimetype, $this->getNumericStorageId()));
640
+
641
+        return $this->searchResultToCacheEntries($result);
642
+    }
643
+
644
+    public function searchQuery(ISearchQuery $searchQuery) {
645
+        $builder = \OC::$server->getDatabaseConnection()->getQueryBuilder();
646
+
647
+        $query = $builder->select(['fileid', 'storage', 'path', 'parent', 'name', 'mimetype', 'mimepart', 'size', 'mtime', 'storage_mtime', 'encrypted', 'etag', 'permissions', 'checksum'])
648
+            ->from('filecache', 'file');
649
+
650
+        $query->where($builder->expr()->eq('storage', $builder->createNamedParameter($this->getNumericStorageId())));
651
+
652
+        if ($this->querySearchHelper->shouldJoinTags($searchQuery->getSearchOperation())) {
653
+            $query
654
+                ->innerJoin('file', 'vcategory_to_object', 'tagmap', $builder->expr()->eq('file.fileid', 'tagmap.objid'))
655
+                ->innerJoin('tagmap', 'vcategory', 'tag', $builder->expr()->andX(
656
+                    $builder->expr()->eq('tagmap.type', 'tag.type'),
657
+                    $builder->expr()->eq('tagmap.categoryid', 'tag.id')
658
+                ))
659
+                ->andWhere($builder->expr()->eq('tag.type', $builder->createNamedParameter('files')))
660
+                ->andWhere($builder->expr()->eq('tag.uid', $builder->createNamedParameter($searchQuery->getUser()->getUID())));
661
+        }
662
+
663
+        $query->andWhere($this->querySearchHelper->searchOperatorToDBExpr($builder, $searchQuery->getSearchOperation()));
664
+
665
+        if ($searchQuery->getLimit()) {
666
+            $query->setMaxResults($searchQuery->getLimit());
667
+        }
668
+        if ($searchQuery->getOffset()) {
669
+            $query->setFirstResult($searchQuery->getOffset());
670
+        }
671
+
672
+        $result = $query->execute();
673
+        return $this->searchResultToCacheEntries($result);
674
+    }
675
+
676
+    /**
677
+     * Search for files by tag of a given users.
678
+     *
679
+     * Note that every user can tag files differently.
680
+     *
681
+     * @param string|int $tag name or tag id
682
+     * @param string $userId owner of the tags
683
+     * @return ICacheEntry[] file data
684
+     */
685
+    public function searchByTag($tag, $userId) {
686
+        $sql = 'SELECT `fileid`, `storage`, `path`, `parent`, `name`, ' .
687
+            '`mimetype`, `mimepart`, `size`, `mtime`, `storage_mtime`, ' .
688
+            '`encrypted`, `etag`, `permissions`, `checksum` ' .
689
+            'FROM `*PREFIX*filecache` `file`, ' .
690
+            '`*PREFIX*vcategory_to_object` `tagmap`, ' .
691
+            '`*PREFIX*vcategory` `tag` ' .
692
+            // JOIN filecache to vcategory_to_object
693
+            'WHERE `file`.`fileid` = `tagmap`.`objid` ' .
694
+            // JOIN vcategory_to_object to vcategory
695
+            'AND `tagmap`.`type` = `tag`.`type` ' .
696
+            'AND `tagmap`.`categoryid` = `tag`.`id` ' .
697
+            // conditions
698
+            'AND `file`.`storage` = ? ' .
699
+            'AND `tag`.`type` = \'files\' ' .
700
+            'AND `tag`.`uid` = ? ';
701
+        if (is_int($tag)) {
702
+            $sql .= 'AND `tag`.`id` = ? ';
703
+        } else {
704
+            $sql .= 'AND `tag`.`category` = ? ';
705
+        }
706
+        $result = $this->connection->executeQuery(
707
+            $sql,
708
+            [
709
+                $this->getNumericStorageId(),
710
+                $userId,
711
+                $tag
712
+            ]
713
+        );
714
+
715
+        $files = $result->fetchAll();
716
+
717
+        return array_map(function (array $data) {
718
+            return self::cacheEntryFromData($data, $this->mimetypeLoader);
719
+        }, $files);
720
+    }
721
+
722
+    /**
723
+     * Re-calculate the folder size and the size of all parent folders
724
+     *
725
+     * @param string|boolean $path
726
+     * @param array $data (optional) meta data of the folder
727
+     */
728
+    public function correctFolderSize($path, $data = null) {
729
+        $this->calculateFolderSize($path, $data);
730
+        if ($path !== '') {
731
+            $parent = dirname($path);
732
+            if ($parent === '.' or $parent === '/') {
733
+                $parent = '';
734
+            }
735
+            $this->correctFolderSize($parent);
736
+        }
737
+    }
738
+
739
+    /**
740
+     * calculate the size of a folder and set it in the cache
741
+     *
742
+     * @param string $path
743
+     * @param array $entry (optional) meta data of the folder
744
+     * @return int
745
+     */
746
+    public function calculateFolderSize($path, $entry = null) {
747
+        $totalSize = 0;
748
+        if (is_null($entry) or !isset($entry['fileid'])) {
749
+            $entry = $this->get($path);
750
+        }
751
+        if (isset($entry['mimetype']) && $entry['mimetype'] === 'httpd/unix-directory') {
752
+            $id = $entry['fileid'];
753
+            $sql = 'SELECT SUM(`size`) AS f1, MIN(`size`) AS f2 ' .
754
+                'FROM `*PREFIX*filecache` ' .
755
+                'WHERE `parent` = ? AND `storage` = ?';
756
+            $result = $this->connection->executeQuery($sql, array($id, $this->getNumericStorageId()));
757
+            if ($row = $result->fetch()) {
758
+                $result->closeCursor();
759
+                list($sum, $min) = array_values($row);
760
+                $sum = 0 + $sum;
761
+                $min = 0 + $min;
762
+                if ($min === -1) {
763
+                    $totalSize = $min;
764
+                } else {
765
+                    $totalSize = $sum;
766
+                }
767
+                $update = array();
768
+                if ($entry['size'] !== $totalSize) {
769
+                    $update['size'] = $totalSize;
770
+                }
771
+                if (count($update) > 0) {
772
+                    $this->update($id, $update);
773
+                }
774
+            } else {
775
+                $result->closeCursor();
776
+            }
777
+        }
778
+        return $totalSize;
779
+    }
780
+
781
+    /**
782
+     * get all file ids on the files on the storage
783
+     *
784
+     * @return int[]
785
+     */
786
+    public function getAll() {
787
+        $sql = 'SELECT `fileid` FROM `*PREFIX*filecache` WHERE `storage` = ?';
788
+        $result = $this->connection->executeQuery($sql, array($this->getNumericStorageId()));
789
+        $ids = array();
790
+        while ($row = $result->fetch()) {
791
+            $ids[] = $row['fileid'];
792
+        }
793
+        return $ids;
794
+    }
795
+
796
+    /**
797
+     * find a folder in the cache which has not been fully scanned
798
+     *
799
+     * If multiple incomplete folders are in the cache, the one with the highest id will be returned,
800
+     * use the one with the highest id gives the best result with the background scanner, since that is most
801
+     * likely the folder where we stopped scanning previously
802
+     *
803
+     * @return string|bool the path of the folder or false when no folder matched
804
+     */
805
+    public function getIncomplete() {
806
+        $query = $this->connection->prepare('SELECT `path` FROM `*PREFIX*filecache`'
807
+            . ' WHERE `storage` = ? AND `size` = -1 ORDER BY `fileid` DESC', 1);
808
+        $query->execute([$this->getNumericStorageId()]);
809
+        if ($row = $query->fetch()) {
810
+            return $row['path'];
811
+        } else {
812
+            return false;
813
+        }
814
+    }
815
+
816
+    /**
817
+     * get the path of a file on this storage by it's file id
818
+     *
819
+     * @param int $id the file id of the file or folder to search
820
+     * @return string|null the path of the file (relative to the storage) or null if a file with the given id does not exists within this cache
821
+     */
822
+    public function getPathById($id) {
823
+        $sql = 'SELECT `path` FROM `*PREFIX*filecache` WHERE `fileid` = ? AND `storage` = ?';
824
+        $result = $this->connection->executeQuery($sql, array($id, $this->getNumericStorageId()));
825
+        if ($row = $result->fetch()) {
826
+            // Oracle stores empty strings as null...
827
+            if ($row['path'] === null) {
828
+                return '';
829
+            }
830
+            return $row['path'];
831
+        } else {
832
+            return null;
833
+        }
834
+    }
835
+
836
+    /**
837
+     * get the storage id of the storage for a file and the internal path of the file
838
+     * unlike getPathById this does not limit the search to files on this storage and
839
+     * instead does a global search in the cache table
840
+     *
841
+     * @param int $id
842
+     * @deprecated use getPathById() instead
843
+     * @return array first element holding the storage id, second the path
844
+     */
845
+    static public function getById($id) {
846
+        $connection = \OC::$server->getDatabaseConnection();
847
+        $sql = 'SELECT `storage`, `path` FROM `*PREFIX*filecache` WHERE `fileid` = ?';
848
+        $result = $connection->executeQuery($sql, array($id));
849
+        if ($row = $result->fetch()) {
850
+            $numericId = $row['storage'];
851
+            $path = $row['path'];
852
+        } else {
853
+            return null;
854
+        }
855
+
856
+        if ($id = Storage::getStorageId($numericId)) {
857
+            return array($id, $path);
858
+        } else {
859
+            return null;
860
+        }
861
+    }
862
+
863
+    /**
864
+     * normalize the given path
865
+     *
866
+     * @param string $path
867
+     * @return string
868
+     */
869
+    public function normalize($path) {
870
+
871
+        return trim(\OC_Util::normalizeUnicode($path), '/');
872
+    }
873 873
 }
Please login to merge, or discard this patch.
lib/private/Files/Search/SearchQuery.php 1 patch
Indentation   +56 added lines, -56 removed lines patch added patch discarded remove patch
@@ -27,66 +27,66 @@
 block discarded – undo
27 27
 use OCP\IUser;
28 28
 
29 29
 class SearchQuery implements ISearchQuery {
30
-	/** @var  ISearchOperator */
31
-	private $searchOperation;
32
-	/** @var  integer */
33
-	private $limit;
34
-	/** @var  integer */
35
-	private $offset;
36
-	/** @var  ISearchOrder[] */
37
-	private $order;
38
-	/** @var IUser */
39
-	private $user;
30
+    /** @var  ISearchOperator */
31
+    private $searchOperation;
32
+    /** @var  integer */
33
+    private $limit;
34
+    /** @var  integer */
35
+    private $offset;
36
+    /** @var  ISearchOrder[] */
37
+    private $order;
38
+    /** @var IUser */
39
+    private $user;
40 40
 
41
-	/**
42
-	 * SearchQuery constructor.
43
-	 *
44
-	 * @param ISearchOperator $searchOperation
45
-	 * @param int $limit
46
-	 * @param int $offset
47
-	 * @param array $order
48
-	 * @param IUser $user
49
-	 */
50
-	public function __construct(ISearchOperator $searchOperation, $limit, $offset, array $order, IUser $user) {
51
-		$this->searchOperation = $searchOperation;
52
-		$this->limit = $limit;
53
-		$this->offset = $offset;
54
-		$this->order = $order;
55
-		$this->user = $user;
56
-	}
41
+    /**
42
+     * SearchQuery constructor.
43
+     *
44
+     * @param ISearchOperator $searchOperation
45
+     * @param int $limit
46
+     * @param int $offset
47
+     * @param array $order
48
+     * @param IUser $user
49
+     */
50
+    public function __construct(ISearchOperator $searchOperation, $limit, $offset, array $order, IUser $user) {
51
+        $this->searchOperation = $searchOperation;
52
+        $this->limit = $limit;
53
+        $this->offset = $offset;
54
+        $this->order = $order;
55
+        $this->user = $user;
56
+    }
57 57
 
58
-	/**
59
-	 * @return ISearchOperator
60
-	 */
61
-	public function getSearchOperation() {
62
-		return $this->searchOperation;
63
-	}
58
+    /**
59
+     * @return ISearchOperator
60
+     */
61
+    public function getSearchOperation() {
62
+        return $this->searchOperation;
63
+    }
64 64
 
65
-	/**
66
-	 * @return int
67
-	 */
68
-	public function getLimit() {
69
-		return $this->limit;
70
-	}
65
+    /**
66
+     * @return int
67
+     */
68
+    public function getLimit() {
69
+        return $this->limit;
70
+    }
71 71
 
72
-	/**
73
-	 * @return int
74
-	 */
75
-	public function getOffset() {
76
-		return $this->offset;
77
-	}
72
+    /**
73
+     * @return int
74
+     */
75
+    public function getOffset() {
76
+        return $this->offset;
77
+    }
78 78
 
79
-	/**
80
-	 * @return ISearchOrder[]
81
-	 */
82
-	public function getOrder() {
83
-		return $this->order;
84
-	}
79
+    /**
80
+     * @return ISearchOrder[]
81
+     */
82
+    public function getOrder() {
83
+        return $this->order;
84
+    }
85 85
 
86
-	/**
87
-	 * @return IUser
88
-	 */
89
-	public function getUser() {
90
-		return $this->user;
91
-	}
86
+    /**
87
+     * @return IUser
88
+     */
89
+    public function getUser() {
90
+        return $this->user;
91
+    }
92 92
 }
Please login to merge, or discard this patch.
apps/dav/lib/Files/FileSearchBackend.php 2 patches
Indentation   +199 added lines, -199 removed lines patch added patch discarded remove patch
@@ -49,223 +49,223 @@
 block discarded – undo
49 49
 use SearchDAV\XML\Order;
50 50
 
51 51
 class FileSearchBackend implements ISearchBackend {
52
-	/** @var Tree */
53
-	private $tree;
52
+    /** @var Tree */
53
+    private $tree;
54 54
 
55
-	/** @var IUser */
56
-	private $user;
55
+    /** @var IUser */
56
+    private $user;
57 57
 
58
-	/** @var IRootFolder */
59
-	private $rootFolder;
58
+    /** @var IRootFolder */
59
+    private $rootFolder;
60 60
 
61
-	/** @var IManager */
62
-	private $shareManager;
61
+    /** @var IManager */
62
+    private $shareManager;
63 63
 
64
-	/** @var View */
65
-	private $view;
64
+    /** @var View */
65
+    private $view;
66 66
 
67
-	/**
68
-	 * FileSearchBackend constructor.
69
-	 *
70
-	 * @param Tree $tree
71
-	 * @param IUser $user
72
-	 * @param IRootFolder $rootFolder
73
-	 * @param IManager $shareManager
74
-	 * @param View $view
75
-	 * @internal param IRootFolder $rootFolder
76
-	 */
77
-	public function __construct(Tree $tree, IUser $user, IRootFolder $rootFolder, IManager $shareManager, View $view) {
78
-		$this->tree = $tree;
79
-		$this->user = $user;
80
-		$this->rootFolder = $rootFolder;
81
-		$this->shareManager = $shareManager;
82
-		$this->view = $view;
83
-	}
67
+    /**
68
+     * FileSearchBackend constructor.
69
+     *
70
+     * @param Tree $tree
71
+     * @param IUser $user
72
+     * @param IRootFolder $rootFolder
73
+     * @param IManager $shareManager
74
+     * @param View $view
75
+     * @internal param IRootFolder $rootFolder
76
+     */
77
+    public function __construct(Tree $tree, IUser $user, IRootFolder $rootFolder, IManager $shareManager, View $view) {
78
+        $this->tree = $tree;
79
+        $this->user = $user;
80
+        $this->rootFolder = $rootFolder;
81
+        $this->shareManager = $shareManager;
82
+        $this->view = $view;
83
+    }
84 84
 
85
-	/**
86
-	 * Search endpoint will be remote.php/dav
87
-	 *
88
-	 * @return string
89
-	 */
90
-	public function getArbiterPath() {
91
-		return '';
92
-	}
85
+    /**
86
+     * Search endpoint will be remote.php/dav
87
+     *
88
+     * @return string
89
+     */
90
+    public function getArbiterPath() {
91
+        return '';
92
+    }
93 93
 
94
-	public function isValidScope($href, $depth, $path) {
95
-		// only allow scopes inside the dav server
96
-		if (is_null($path)) {
97
-			return false;
98
-		}
94
+    public function isValidScope($href, $depth, $path) {
95
+        // only allow scopes inside the dav server
96
+        if (is_null($path)) {
97
+            return false;
98
+        }
99 99
 
100
-		try {
101
-			$node = $this->tree->getNodeForPath($path);
102
-			return $node instanceof Directory;
103
-		} catch (NotFound $e) {
104
-			return false;
105
-		}
106
-	}
100
+        try {
101
+            $node = $this->tree->getNodeForPath($path);
102
+            return $node instanceof Directory;
103
+        } catch (NotFound $e) {
104
+            return false;
105
+        }
106
+    }
107 107
 
108
-	public function getPropertyDefinitionsForScope($href, $path) {
109
-		// all valid scopes support the same schema
108
+    public function getPropertyDefinitionsForScope($href, $path) {
109
+        // all valid scopes support the same schema
110 110
 
111
-		//todo dynamically load all propfind properties that are supported
112
-		return [
113
-			// queryable properties
114
-			new SearchPropertyDefinition('{DAV:}displayname', true, false, true),
115
-			new SearchPropertyDefinition('{DAV:}getcontenttype', true, true, true),
116
-			new SearchPropertyDefinition('{DAV:}getlastmodifed', true, true, true, SearchPropertyDefinition::DATATYPE_DATETIME),
117
-			new SearchPropertyDefinition(FilesPlugin::SIZE_PROPERTYNAME, true, true, true, SearchPropertyDefinition::DATATYPE_NONNEGATIVE_INTEGER),
118
-			new SearchPropertyDefinition(TagsPlugin::FAVORITE_PROPERTYNAME, true, true, true, SearchPropertyDefinition::DATATYPE_BOOLEAN),
111
+        //todo dynamically load all propfind properties that are supported
112
+        return [
113
+            // queryable properties
114
+            new SearchPropertyDefinition('{DAV:}displayname', true, false, true),
115
+            new SearchPropertyDefinition('{DAV:}getcontenttype', true, true, true),
116
+            new SearchPropertyDefinition('{DAV:}getlastmodifed', true, true, true, SearchPropertyDefinition::DATATYPE_DATETIME),
117
+            new SearchPropertyDefinition(FilesPlugin::SIZE_PROPERTYNAME, true, true, true, SearchPropertyDefinition::DATATYPE_NONNEGATIVE_INTEGER),
118
+            new SearchPropertyDefinition(TagsPlugin::FAVORITE_PROPERTYNAME, true, true, true, SearchPropertyDefinition::DATATYPE_BOOLEAN),
119 119
 
120
-			// select only properties
121
-			new SearchPropertyDefinition('{DAV:}resourcetype', false, true, false),
122
-			new SearchPropertyDefinition('{DAV:}getcontentlength', false, true, false),
123
-			new SearchPropertyDefinition(FilesPlugin::CHECKSUMS_PROPERTYNAME, false, true, false),
124
-			new SearchPropertyDefinition(FilesPlugin::PERMISSIONS_PROPERTYNAME, false, true, false),
125
-			new SearchPropertyDefinition(FilesPlugin::GETETAG_PROPERTYNAME, false, true, false),
126
-			new SearchPropertyDefinition(FilesPlugin::OWNER_ID_PROPERTYNAME, false, true, false),
127
-			new SearchPropertyDefinition(FilesPlugin::OWNER_DISPLAY_NAME_PROPERTYNAME, false, true, false),
128
-			new SearchPropertyDefinition(FilesPlugin::DATA_FINGERPRINT_PROPERTYNAME, false, true, false),
129
-			new SearchPropertyDefinition(FilesPlugin::HAS_PREVIEW_PROPERTYNAME, false, true, false, SearchPropertyDefinition::DATATYPE_BOOLEAN),
130
-			new SearchPropertyDefinition(FilesPlugin::INTERNAL_FILEID_PROPERTYNAME, false, true, false, SearchPropertyDefinition::DATATYPE_NONNEGATIVE_INTEGER),
131
-			new SearchPropertyDefinition(FilesPlugin::FILEID_PROPERTYNAME, false, true, false, SearchPropertyDefinition::DATATYPE_NONNEGATIVE_INTEGER),
132
-		];
133
-	}
120
+            // select only properties
121
+            new SearchPropertyDefinition('{DAV:}resourcetype', false, true, false),
122
+            new SearchPropertyDefinition('{DAV:}getcontentlength', false, true, false),
123
+            new SearchPropertyDefinition(FilesPlugin::CHECKSUMS_PROPERTYNAME, false, true, false),
124
+            new SearchPropertyDefinition(FilesPlugin::PERMISSIONS_PROPERTYNAME, false, true, false),
125
+            new SearchPropertyDefinition(FilesPlugin::GETETAG_PROPERTYNAME, false, true, false),
126
+            new SearchPropertyDefinition(FilesPlugin::OWNER_ID_PROPERTYNAME, false, true, false),
127
+            new SearchPropertyDefinition(FilesPlugin::OWNER_DISPLAY_NAME_PROPERTYNAME, false, true, false),
128
+            new SearchPropertyDefinition(FilesPlugin::DATA_FINGERPRINT_PROPERTYNAME, false, true, false),
129
+            new SearchPropertyDefinition(FilesPlugin::HAS_PREVIEW_PROPERTYNAME, false, true, false, SearchPropertyDefinition::DATATYPE_BOOLEAN),
130
+            new SearchPropertyDefinition(FilesPlugin::INTERNAL_FILEID_PROPERTYNAME, false, true, false, SearchPropertyDefinition::DATATYPE_NONNEGATIVE_INTEGER),
131
+            new SearchPropertyDefinition(FilesPlugin::FILEID_PROPERTYNAME, false, true, false, SearchPropertyDefinition::DATATYPE_NONNEGATIVE_INTEGER),
132
+        ];
133
+    }
134 134
 
135
-	/**
136
-	 * @param BasicSearch $search
137
-	 * @return SearchResult[]
138
-	 */
139
-	public function search(BasicSearch $search) {
140
-		if (count($search->from) !== 1) {
141
-			throw new \InvalidArgumentException('Searching more than one folder is not supported');
142
-		}
143
-		$query = $this->transformQuery($search);
144
-		$scope = $search->from[0];
145
-		if ($scope->path === null) {
146
-			throw new \InvalidArgumentException('Using uri\'s as scope is not supported, please use a path relative to the search arbiter instead');
147
-		}
148
-		$node = $this->tree->getNodeForPath($scope->path);
149
-		if (!$node instanceof Directory) {
150
-			throw new \InvalidArgumentException('Search is only supported on directories');
151
-		}
135
+    /**
136
+     * @param BasicSearch $search
137
+     * @return SearchResult[]
138
+     */
139
+    public function search(BasicSearch $search) {
140
+        if (count($search->from) !== 1) {
141
+            throw new \InvalidArgumentException('Searching more than one folder is not supported');
142
+        }
143
+        $query = $this->transformQuery($search);
144
+        $scope = $search->from[0];
145
+        if ($scope->path === null) {
146
+            throw new \InvalidArgumentException('Using uri\'s as scope is not supported, please use a path relative to the search arbiter instead');
147
+        }
148
+        $node = $this->tree->getNodeForPath($scope->path);
149
+        if (!$node instanceof Directory) {
150
+            throw new \InvalidArgumentException('Search is only supported on directories');
151
+        }
152 152
 
153
-		$fileInfo = $node->getFileInfo();
154
-		$folder = $this->rootFolder->get($fileInfo->getPath());
155
-		/** @var Folder $folder $results */
156
-		$results = $folder->search($query);
153
+        $fileInfo = $node->getFileInfo();
154
+        $folder = $this->rootFolder->get($fileInfo->getPath());
155
+        /** @var Folder $folder $results */
156
+        $results = $folder->search($query);
157 157
 
158
-		return array_map(function (Node $node) {
159
-			if ($node instanceof Folder) {
160
-				return new SearchResult(new \OCA\DAV\Connector\Sabre\Directory($this->view, $node, $this->tree, $this->shareManager), $this->getHrefForNode($node));
161
-			} else {
162
-				return new SearchResult(new \OCA\DAV\Connector\Sabre\File($this->view, $node, $this->shareManager), $this->getHrefForNode($node));
163
-			}
164
-		}, $results);
165
-	}
158
+        return array_map(function (Node $node) {
159
+            if ($node instanceof Folder) {
160
+                return new SearchResult(new \OCA\DAV\Connector\Sabre\Directory($this->view, $node, $this->tree, $this->shareManager), $this->getHrefForNode($node));
161
+            } else {
162
+                return new SearchResult(new \OCA\DAV\Connector\Sabre\File($this->view, $node, $this->shareManager), $this->getHrefForNode($node));
163
+            }
164
+        }, $results);
165
+    }
166 166
 
167
-	/**
168
-	 * @param Node $node
169
-	 * @return string
170
-	 */
171
-	private function getHrefForNode(Node $node) {
172
-		$base = '/files/' . $this->user->getUID();
173
-		return $base . $this->view->getRelativePath($node->getPath());
174
-	}
167
+    /**
168
+     * @param Node $node
169
+     * @return string
170
+     */
171
+    private function getHrefForNode(Node $node) {
172
+        $base = '/files/' . $this->user->getUID();
173
+        return $base . $this->view->getRelativePath($node->getPath());
174
+    }
175 175
 
176
-	/**
177
-	 * @param BasicSearch $query
178
-	 * @return ISearchQuery
179
-	 */
180
-	private function transformQuery(BasicSearch $query) {
181
-		// TODO offset, limit
182
-		$orders = array_map([$this, 'mapSearchOrder'], $query->orderBy);
183
-		return new SearchQuery($this->transformSearchOperation($query->where), 0, 0, $orders, $this->user);
184
-	}
176
+    /**
177
+     * @param BasicSearch $query
178
+     * @return ISearchQuery
179
+     */
180
+    private function transformQuery(BasicSearch $query) {
181
+        // TODO offset, limit
182
+        $orders = array_map([$this, 'mapSearchOrder'], $query->orderBy);
183
+        return new SearchQuery($this->transformSearchOperation($query->where), 0, 0, $orders, $this->user);
184
+    }
185 185
 
186
-	/**
187
-	 * @param Order $order
188
-	 * @return ISearchOrder
189
-	 */
190
-	private function mapSearchOrder(Order $order) {
191
-		return new SearchOrder($order->order === Order::ASC ? ISearchOrder::DIRECTION_ASCENDING : ISearchOrder::DIRECTION_DESCENDING, $this->mapPropertyNameToColumn($order->property));
192
-	}
186
+    /**
187
+     * @param Order $order
188
+     * @return ISearchOrder
189
+     */
190
+    private function mapSearchOrder(Order $order) {
191
+        return new SearchOrder($order->order === Order::ASC ? ISearchOrder::DIRECTION_ASCENDING : ISearchOrder::DIRECTION_DESCENDING, $this->mapPropertyNameToColumn($order->property));
192
+    }
193 193
 
194
-	/**
195
-	 * @param Operator $operator
196
-	 * @return ISearchOperator
197
-	 */
198
-	private function transformSearchOperation(Operator $operator) {
199
-		list(, $trimmedType) = explode('}', $operator->type);
200
-		switch ($operator->type) {
201
-			case Operator::OPERATION_AND:
202
-			case Operator::OPERATION_OR:
203
-			case Operator::OPERATION_NOT:
204
-				$arguments = array_map([$this, 'transformSearchOperation'], $operator->arguments);
205
-				return new SearchBinaryOperator($trimmedType, $arguments);
206
-			case Operator::OPERATION_EQUAL:
207
-			case Operator::OPERATION_GREATER_OR_EQUAL_THAN:
208
-			case Operator::OPERATION_GREATER_THAN:
209
-			case Operator::OPERATION_LESS_OR_EQUAL_THAN:
210
-			case Operator::OPERATION_LESS_THAN:
211
-			case Operator::OPERATION_IS_LIKE:
212
-				if (count($operator->arguments) !== 2) {
213
-					throw new \InvalidArgumentException('Invalid number of arguments for ' . $trimmedType . ' operation');
214
-				}
215
-				if (!is_string($operator->arguments[0])) {
216
-					throw new \InvalidArgumentException('Invalid argument 1 for ' . $trimmedType . ' operation, expected property');
217
-				}
218
-				if (!($operator->arguments[1] instanceof Literal)) {
219
-					throw new \InvalidArgumentException('Invalid argument 2 for ' . $trimmedType . ' operation, expected literal');
220
-				}
221
-				return new SearchComparison($trimmedType, $this->mapPropertyNameToColumn($operator->arguments[0]), $this->castValue($operator->arguments[0], $operator->arguments[1]->value));
222
-			case Operator::OPERATION_IS_COLLECTION:
223
-				return new SearchComparison('eq', 'mimetype', ICacheEntry::DIRECTORY_MIMETYPE);
224
-			default:
225
-				throw new \InvalidArgumentException('Unsupported operation ' . $trimmedType.  ' (' . $operator->type . ')');
226
-		}
227
-	}
194
+    /**
195
+     * @param Operator $operator
196
+     * @return ISearchOperator
197
+     */
198
+    private function transformSearchOperation(Operator $operator) {
199
+        list(, $trimmedType) = explode('}', $operator->type);
200
+        switch ($operator->type) {
201
+            case Operator::OPERATION_AND:
202
+            case Operator::OPERATION_OR:
203
+            case Operator::OPERATION_NOT:
204
+                $arguments = array_map([$this, 'transformSearchOperation'], $operator->arguments);
205
+                return new SearchBinaryOperator($trimmedType, $arguments);
206
+            case Operator::OPERATION_EQUAL:
207
+            case Operator::OPERATION_GREATER_OR_EQUAL_THAN:
208
+            case Operator::OPERATION_GREATER_THAN:
209
+            case Operator::OPERATION_LESS_OR_EQUAL_THAN:
210
+            case Operator::OPERATION_LESS_THAN:
211
+            case Operator::OPERATION_IS_LIKE:
212
+                if (count($operator->arguments) !== 2) {
213
+                    throw new \InvalidArgumentException('Invalid number of arguments for ' . $trimmedType . ' operation');
214
+                }
215
+                if (!is_string($operator->arguments[0])) {
216
+                    throw new \InvalidArgumentException('Invalid argument 1 for ' . $trimmedType . ' operation, expected property');
217
+                }
218
+                if (!($operator->arguments[1] instanceof Literal)) {
219
+                    throw new \InvalidArgumentException('Invalid argument 2 for ' . $trimmedType . ' operation, expected literal');
220
+                }
221
+                return new SearchComparison($trimmedType, $this->mapPropertyNameToColumn($operator->arguments[0]), $this->castValue($operator->arguments[0], $operator->arguments[1]->value));
222
+            case Operator::OPERATION_IS_COLLECTION:
223
+                return new SearchComparison('eq', 'mimetype', ICacheEntry::DIRECTORY_MIMETYPE);
224
+            default:
225
+                throw new \InvalidArgumentException('Unsupported operation ' . $trimmedType.  ' (' . $operator->type . ')');
226
+        }
227
+    }
228 228
 
229
-	/**
230
-	 * @param string $propertyName
231
-	 * @return string
232
-	 */
233
-	private function mapPropertyNameToColumn($propertyName) {
234
-		switch ($propertyName) {
235
-			case '{DAV:}displayname':
236
-				return 'name';
237
-			case '{DAV:}getcontenttype':
238
-				return 'mimetype';
239
-			case '{DAV:}getlastmodifed':
240
-				return 'mtime';
241
-			case FilesPlugin::SIZE_PROPERTYNAME:
242
-				return 'size';
243
-			case TagsPlugin::FAVORITE_PROPERTYNAME:
244
-				return 'favorite';
245
-			case TagsPlugin::TAGS_PROPERTYNAME:
246
-				return 'tagname';
247
-			default:
248
-				throw new \InvalidArgumentException('Unsupported property for search or order: ' . $propertyName);
249
-		}
250
-	}
229
+    /**
230
+     * @param string $propertyName
231
+     * @return string
232
+     */
233
+    private function mapPropertyNameToColumn($propertyName) {
234
+        switch ($propertyName) {
235
+            case '{DAV:}displayname':
236
+                return 'name';
237
+            case '{DAV:}getcontenttype':
238
+                return 'mimetype';
239
+            case '{DAV:}getlastmodifed':
240
+                return 'mtime';
241
+            case FilesPlugin::SIZE_PROPERTYNAME:
242
+                return 'size';
243
+            case TagsPlugin::FAVORITE_PROPERTYNAME:
244
+                return 'favorite';
245
+            case TagsPlugin::TAGS_PROPERTYNAME:
246
+                return 'tagname';
247
+            default:
248
+                throw new \InvalidArgumentException('Unsupported property for search or order: ' . $propertyName);
249
+        }
250
+    }
251 251
 
252
-	private function castValue($propertyName, $value) {
253
-		$allProps = $this->getPropertyDefinitionsForScope('', '');
254
-		foreach ($allProps as $prop) {
255
-			if ($prop->name === $propertyName) {
256
-				$dataType = $prop->dataType;
257
-				switch ($dataType) {
258
-					case SearchPropertyDefinition::DATATYPE_BOOLEAN:
259
-						return $value === 'yes';
260
-					case SearchPropertyDefinition::DATATYPE_DECIMAL:
261
-					case SearchPropertyDefinition::DATATYPE_INTEGER:
262
-					case SearchPropertyDefinition::DATATYPE_NONNEGATIVE_INTEGER:
263
-						return 0 + $value;
264
-					default:
265
-						return $value;
266
-				}
267
-			}
268
-		}
269
-		return $value;
270
-	}
252
+    private function castValue($propertyName, $value) {
253
+        $allProps = $this->getPropertyDefinitionsForScope('', '');
254
+        foreach ($allProps as $prop) {
255
+            if ($prop->name === $propertyName) {
256
+                $dataType = $prop->dataType;
257
+                switch ($dataType) {
258
+                    case SearchPropertyDefinition::DATATYPE_BOOLEAN:
259
+                        return $value === 'yes';
260
+                    case SearchPropertyDefinition::DATATYPE_DECIMAL:
261
+                    case SearchPropertyDefinition::DATATYPE_INTEGER:
262
+                    case SearchPropertyDefinition::DATATYPE_NONNEGATIVE_INTEGER:
263
+                        return 0 + $value;
264
+                    default:
265
+                        return $value;
266
+                }
267
+            }
268
+        }
269
+        return $value;
270
+    }
271 271
 }
Please login to merge, or discard this patch.
Spacing   +8 added lines, -8 removed lines patch added patch discarded remove patch
@@ -155,7 +155,7 @@  discard block
 block discarded – undo
155 155
 		/** @var Folder $folder $results */
156 156
 		$results = $folder->search($query);
157 157
 
158
-		return array_map(function (Node $node) {
158
+		return array_map(function(Node $node) {
159 159
 			if ($node instanceof Folder) {
160 160
 				return new SearchResult(new \OCA\DAV\Connector\Sabre\Directory($this->view, $node, $this->tree, $this->shareManager), $this->getHrefForNode($node));
161 161
 			} else {
@@ -169,8 +169,8 @@  discard block
 block discarded – undo
169 169
 	 * @return string
170 170
 	 */
171 171
 	private function getHrefForNode(Node $node) {
172
-		$base = '/files/' . $this->user->getUID();
173
-		return $base . $this->view->getRelativePath($node->getPath());
172
+		$base = '/files/'.$this->user->getUID();
173
+		return $base.$this->view->getRelativePath($node->getPath());
174 174
 	}
175 175
 
176 176
 	/**
@@ -210,19 +210,19 @@  discard block
 block discarded – undo
210 210
 			case Operator::OPERATION_LESS_THAN:
211 211
 			case Operator::OPERATION_IS_LIKE:
212 212
 				if (count($operator->arguments) !== 2) {
213
-					throw new \InvalidArgumentException('Invalid number of arguments for ' . $trimmedType . ' operation');
213
+					throw new \InvalidArgumentException('Invalid number of arguments for '.$trimmedType.' operation');
214 214
 				}
215 215
 				if (!is_string($operator->arguments[0])) {
216
-					throw new \InvalidArgumentException('Invalid argument 1 for ' . $trimmedType . ' operation, expected property');
216
+					throw new \InvalidArgumentException('Invalid argument 1 for '.$trimmedType.' operation, expected property');
217 217
 				}
218 218
 				if (!($operator->arguments[1] instanceof Literal)) {
219
-					throw new \InvalidArgumentException('Invalid argument 2 for ' . $trimmedType . ' operation, expected literal');
219
+					throw new \InvalidArgumentException('Invalid argument 2 for '.$trimmedType.' operation, expected literal');
220 220
 				}
221 221
 				return new SearchComparison($trimmedType, $this->mapPropertyNameToColumn($operator->arguments[0]), $this->castValue($operator->arguments[0], $operator->arguments[1]->value));
222 222
 			case Operator::OPERATION_IS_COLLECTION:
223 223
 				return new SearchComparison('eq', 'mimetype', ICacheEntry::DIRECTORY_MIMETYPE);
224 224
 			default:
225
-				throw new \InvalidArgumentException('Unsupported operation ' . $trimmedType.  ' (' . $operator->type . ')');
225
+				throw new \InvalidArgumentException('Unsupported operation '.$trimmedType.' ('.$operator->type.')');
226 226
 		}
227 227
 	}
228 228
 
@@ -245,7 +245,7 @@  discard block
 block discarded – undo
245 245
 			case TagsPlugin::TAGS_PROPERTYNAME:
246 246
 				return 'tagname';
247 247
 			default:
248
-				throw new \InvalidArgumentException('Unsupported property for search or order: ' . $propertyName);
248
+				throw new \InvalidArgumentException('Unsupported property for search or order: '.$propertyName);
249 249
 		}
250 250
 	}
251 251
 
Please login to merge, or discard this patch.