aboutsummaryrefslogtreecommitdiffstats
path: root/models/repo_indexer.go
blob: 4877d339f8cc249269c3bf69dadb63b62c138abb (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
// Copyright 2017 The Gitea Authors. All rights reserved.
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file.

package models

import (
	"io/ioutil"
	"os"
	"path"
	"strconv"
	"strings"

	"code.gitea.io/git"
	"code.gitea.io/gitea/modules/base"
	"code.gitea.io/gitea/modules/indexer"
	"code.gitea.io/gitea/modules/log"
	"code.gitea.io/gitea/modules/setting"

	"github.com/Unknwon/com"
)

// RepoIndexerStatus status of a repo's entry in the repo indexer
// For now, implicitly refers to default branch
type RepoIndexerStatus struct {
	ID        int64  `xorm:"pk autoincr"`
	RepoID    int64  `xorm:"INDEX"`
	CommitSha string `xorm:"VARCHAR(40)"`
}

func (repo *Repository) getIndexerStatus() error {
	if repo.IndexerStatus != nil {
		return nil
	}
	status := &RepoIndexerStatus{RepoID: repo.ID}
	has, err := x.Get(status)
	if err != nil {
		return err
	} else if !has {
		status.CommitSha = ""
	}
	repo.IndexerStatus = status
	return nil
}

func (repo *Repository) updateIndexerStatus(sha string) error {
	if err := repo.getIndexerStatus(); err != nil {
		return err
	}
	if len(repo.IndexerStatus.CommitSha) == 0 {
		repo.IndexerStatus.CommitSha = sha
		_, err := x.Insert(repo.IndexerStatus)
		return err
	}
	repo.IndexerStatus.CommitSha = sha
	_, err := x.ID(repo.IndexerStatus.ID).Cols("commit_sha").
		Update(repo.IndexerStatus)
	return err
}

type repoIndexerOperation struct {
	repo    *Repository
	deleted bool
}

var repoIndexerOperationQueue chan repoIndexerOperation

// InitRepoIndexer initialize the repo indexer
func InitRepoIndexer() {
	if !setting.Indexer.RepoIndexerEnabled {
		return
	}
	indexer.InitRepoIndexer(populateRepoIndexer)
	repoIndexerOperationQueue = make(chan repoIndexerOperation, setting.Indexer.UpdateQueueLength)
	go processRepoIndexerOperationQueue()
}

// populateRepoIndexer populate the repo indexer with data
func populateRepoIndexer() error {
	log.Info("Populating repository indexer (this may take a while)")
	for page := 1; ; page++ {
		repos, _, err := SearchRepositoryByName(&SearchRepoOptions{
			Page:     page,
			PageSize: 10,
			OrderBy:  SearchOrderByID,
			Private:  true,
		})
		if err != nil {
			return err
		} else if len(repos) == 0 {
			return nil
		}
		for _, repo := range repos {
			if err = updateRepoIndexer(repo); err != nil {
				// only log error, since this should not prevent
				// gitea from starting up
				log.Error(4, "updateRepoIndexer: repoID=%d, %v", repo.ID, err)
			}
		}
	}
}

func updateRepoIndexer(repo *Repository) error {
	changes, err := getRepoChanges(repo)
	if err != nil {
		return err
	} else if changes == nil {
		return nil
	}

	batch := indexer.RepoIndexerBatch()
	for _, filename := range changes.UpdatedFiles {
		if err := addUpdate(filename, repo, batch); err != nil {
			return err
		}
	}
	for _, filename := range changes.RemovedFiles {
		if err := addDelete(filename, repo, batch); err != nil {
			return err
		}
	}
	if err = batch.Flush(); err != nil {
		return err
	}
	return updateLastIndexSync(repo)
}

// repoChanges changes (file additions/updates/removals) to a repo
type repoChanges struct {
	UpdatedFiles []string
	RemovedFiles []string
}

// getRepoChanges returns changes to repo since last indexer update
func getRepoChanges(repo *Repository) (*repoChanges, error) {
	repoWorkingPool.CheckIn(com.ToStr(repo.ID))
	defer repoWorkingPool.CheckOut(com.ToStr(repo.ID))

	if err := repo.UpdateLocalCopyBranch(""); err != nil {
		return nil, err
	} else if !git.IsBranchExist(repo.LocalCopyPath(), repo.DefaultBranch) {
		// repo does not have any commits yet, so nothing to update
		return nil, nil
	} else if err = repo.UpdateLocalCopyBranch(repo.DefaultBranch); err != nil {
		return nil, err
	} else if err = repo.getIndexerStatus(); err != nil {
		return nil, err
	}

	if len(repo.IndexerStatus.CommitSha) == 0 {
		return genesisChanges(repo)
	}
	return nonGenesisChanges(repo)
}

func addUpdate(filename string, repo *Repository, batch *indexer.Batch) error {
	filepath := path.Join(repo.LocalCopyPath(), filename)
	if stat, err := os.Stat(filepath); err != nil {
		return err
	} else if stat.Size() > setting.Indexer.MaxIndexerFileSize {
		return nil
	} else if stat.IsDir() {
		// file could actually be a directory, if it is the root of a submodule.
		// We do not index submodule contents, so don't do anything.
		return nil
	}
	fileContents, err := ioutil.ReadFile(filepath)
	if err != nil {
		return err
	} else if !base.IsTextFile(fileContents) {
		return nil
	}
	return batch.Add(indexer.RepoIndexerUpdate{
		Filepath: filename,
		Op:       indexer.RepoIndexerOpUpdate,
		Data: &indexer.RepoIndexerData{
			RepoID:  repo.ID,
			Content: string(fileContents),
		},
	})
}

func addDelete(filename string, repo *Repository, batch *indexer.Batch) error {
	return batch.Add(indexer.RepoIndexerUpdate{
		Filepath: filename,
		Op:       indexer.RepoIndexerOpDelete,
		Data: &indexer.RepoIndexerData{
			RepoID: repo.ID,
		},
	})
}

// genesisChanges get changes to add repo to the indexer for the first time
func genesisChanges(repo *Repository) (*repoChanges, error) {
	var changes repoChanges
	stdout, err := git.NewCommand("ls-files").RunInDir(repo.LocalCopyPath())
	if err != nil {
		return nil, err
	}
	for _, line := range strings.Split(stdout, "\n") {
		filename := strings.TrimSpace(line)
		if len(filename) == 0 {
			continue
		} else if filename[0] == '"' {
			filename, err = strconv.Unquote(filename)
			if err != nil {
				return nil, err
			}
		}
		changes.UpdatedFiles = append(changes.UpdatedFiles, filename)
	}
	return &changes, nil
}

// nonGenesisChanges get changes since the previous indexer update
func nonGenesisChanges(repo *Repository) (*repoChanges, error) {
	diffCmd := git.NewCommand("diff", "--name-status",
		repo.IndexerStatus.CommitSha, "HEAD")
	stdout, err := diffCmd.RunInDir(repo.LocalCopyPath())
	if err != nil {
		// previous commit sha may have been removed by a force push, so
		// try rebuilding from scratch
		if err = indexer.DeleteRepoFromIndexer(repo.ID); err != nil {
			return nil, err
		}
		return genesisChanges(repo)
	}
	var changes repoChanges
	for _, line := range strings.Split(stdout, "\n") {
		line = strings.TrimSpace(line)
		if len(line) == 0 {
			continue
		}
		filename := strings.TrimSpace(line[1:])
		if len(filename) == 0 {
			continue
		} else if filename[0] == '"' {
			filename, err = strconv.Unquote(filename)
			if err != nil {
				return nil, err
			}
		}

		switch status := line[0]; status {
		case 'M', 'A':
			changes.UpdatedFiles = append(changes.UpdatedFiles, filename)
		case 'D':
			changes.RemovedFiles = append(changes.RemovedFiles, filename)
		default:
			log.Warn("Unrecognized status: %c (line=%s)", status, line)
		}
	}
	return &changes, nil
}

func updateLastIndexSync(repo *Repository) error {
	stdout, err := git.NewCommand("rev-parse", "HEAD").RunInDir(repo.LocalCopyPath())
	if err != nil {
		return err
	}
	sha := strings.TrimSpace(stdout)
	return repo.updateIndexerStatus(sha)
}

func processRepoIndexerOperationQueue() {
	for {
		op := <-repoIndexerOperationQueue
		if op.deleted {
			if err := indexer.DeleteRepoFromIndexer(op.repo.ID); err != nil {
				log.Error(4, "DeleteRepoFromIndexer: %v", err)
			}
		} else {
			if err := updateRepoIndexer(op.repo); err != nil {
				log.Error(4, "updateRepoIndexer: %v", err)
			}
		}
	}
}

// DeleteRepoFromIndexer remove all of a repository's entries from the indexer
func DeleteRepoFromIndexer(repo *Repository) {
	addOperationToQueue(repoIndexerOperation{repo: repo, deleted: true})
}

// UpdateRepoIndexer update a repository's entries in the indexer
func UpdateRepoIndexer(repo *Repository) {
	addOperationToQueue(repoIndexerOperation{repo: repo, deleted: false})
}

func addOperationToQueue(op repoIndexerOperation) {
	if !setting.Indexer.RepoIndexerEnabled {
		return
	}
	select {
	case repoIndexerOperationQueue <- op:
		break
	default:
		go func() {
			repoIndexerOperationQueue <- op
		}()
	}
}
-4.1.3 Nextcloud server, a safe home for all your data: https://github.com/nextcloud/serverwww-data
summaryrefslogtreecommitdiffstats
path: root/apps/files/l10n/en_GB.js
blob: ac3119057bc95410b815f1f7478df4852ac1672f (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
OC.L10N.register(
    "files",
    {
    "File could not be found" : "File could not be found",
    "Move or copy" : "Move or copy",
    "Download" : "Download",
    "Delete" : "Delete",
    "Tags" : "Tags",
    "Show list view" : "Show list view",
    "Show grid view" : "Show grid view",
    "Home" : "Home",
    "Close" : "Close",
    "Favorites" : "Favourites",
    "Could not create folder \"{dir}\"" : "Could not create folder \"{dir}\"",
    "This will stop your current uploads." : "This will stop your current uploads.",
    "Upload cancelled." : "Upload cancelled.",
    "Processing files …" : "Processing files …",
    "…" : "…",
    "Unable to upload {filename} as it is a directory or has 0 bytes" : "Unable to upload {filename} as it is a directory or has 0 bytes",
    "Not enough free space, you are uploading {size1} but only {size2} is left" : "Not enough free space, you are uploading {size1} but only {size2} is left",
    "Target folder \"{dir}\" does not exist any more" : "Target folder \"{dir}\" does not exist any more",
    "Not enough free space" : "Not enough free space",
    "An unknown error has occurred" : "An unknown error has occurred",
    "File could not be uploaded" : "File could not be uploaded",
    "Uploading …" : "Uploading …",
    "{loadedSize} of {totalSize} ({bitrate})" : "{loadedSize} of {totalSize} ({bitrate})",
    "Uploading that item is not supported" : "Uploading that item is not supported",
    "Target folder does not exist any more" : "Target folder does not exist any more",
    "Operation is blocked by access control" : "Operation is blocked by access control",
    "Error when assembling chunks, status code {status}" : "Error when assembling chunks, status code {status}",
    "Actions" : "Actions",
    "Rename" : "Rename",
    "Move" : "Move",
    "Copy" : "Copy",
    "Choose target folder" : "Choose target folder",
    "Edit locally" : "Edit locally",
    "Open" : "Open",
    "Delete file" : "Delete file",
    "Delete folder" : "Delete folder",
    "Disconnect storage" : "Disconnect storage",
    "Leave this share" : "Leave this share",
    "Could not load info for file \"{file}\"" : "Could not load info for file \"{file}\"",
    "Files" : "Files",
    "Details" : "Details",
    "Please select tag(s) to add to the selection" : "Please select tag(s) to add to the selection",
    "Apply tag(s) to selection" : "Apply tag(s) to selection",
    "Select directory \"{dirName}\"" : "Select directory \"{dirName}\"",
    "Select file \"{fileName}\"" : "Select file \"{fileName}\"",
    "Pending" : "Pending",
    "Unable to determine date" : "Unable to determine date",
    "This operation is forbidden" : "This operation is forbidden",
    "This directory is unavailable, please check the logs or contact the administrator" : "This directory is unavailable, please check the logs or contact the administrator",
    "Storage is temporarily not available" : "Storage is temporarily not available",
    "Could not move \"{file}\", target exists" : "Could not move \"{file}\", target exists",
    "Could not move \"{file}\"" : "Could not move \"{file}\"",
    "copy" : "copy",
    "Could not copy \"{file}\", target exists" : "Could not copy \"{file}\", target exists",
    "Could not copy \"{file}\"" : "Could not copy \"{file}\"",
    "Copied {origin} inside {destination}" : "Copied {origin} inside {destination}",
    "Copied {origin} and {nbfiles} other files inside {destination}" : "Copied {origin} and {nbfiles} other files inside {destination}",
    "Failed to redirect to client" : "Failed to redirect to client",
    "{newName} already exists" : "{newName} already exists",
    "Could not rename \"{fileName}\", it does not exist any more" : "Could not rename \"{fileName}\", it does not exist any more",
    "The name \"{targetName}\" is already used in the folder \"{dir}\". Please choose a different name." : "The name \"{targetName}\" is already used in the folder \"{dir}\". Please choose a different name.",
    "Could not rename \"{fileName}\"" : "Could not rename \"{fileName}\"",
    "Could not create file \"{file}\"" : "Could not create file \"{file}\"",
    "Could not create file \"{file}\" because it already exists" : "Could not create file \"{file}\" because it already exists",
    "Could not create folder \"{dir}\" because it already exists" : "Could not create folder \"{dir}\" because it already exists",
    "Could not fetch file details \"{file}\"" : "Could not fetch file details \"{file}\"",
    "Error deleting file \"{fileName}\"." : "Error deleting file \"{fileName}\".",
    "No search results in other folders for {tag}{filter}{endtag}" : "No search results in other folders for {tag}{filter}{endtag}",
    "Enter more than two characters to search in other folders" : "Enter more than two characters to search in other folders",
    "Name" : "Name",
    "Size" : "Size",
    "Modified" : "Modified",
    "_%n folder_::_%n folders_" : ["%n folder","%n folders"],
    "_%n file_::_%n files_" : ["%n file","%n files"],
    "{dirs} and {files}" : "{dirs} and {files}",
    "_including %n hidden_::_including %n hidden_" : ["including %n hidden","including %n hidden"],
    "You do not have permission to upload or create files here" : "You do not have permission to upload or create files here",
    "_Uploading %n file_::_Uploading %n files_" : ["Uploading %n file","Uploading %n files"],
    "New file/folder menu" : "New file/folder menu",
    "Select file range" : "Select file range",
    "{used}%" : "{used}%",
    "{used} of {quota} used" : "{used} of {quota} used",
    "{used} used" : "{used} used",
    "\"{name}\" is an invalid file name." : "\"{name}\" is an invalid file name.",
    "File name cannot be empty." : "File name cannot be empty.",
    "\"/\" is not allowed inside a file name." : "\"/\" is not allowed inside a file name.",
    "\"{name}\" is not an allowed filetype" : "\"{name}\" is not an allowed filetype",
    "Storage of {owner} is full, files cannot be updated or synced anymore!" : "Storage of {owner} is full, files cannot be updated or synced anymore!",
    "Group folder \"{mountPoint}\" is full, files cannot be updated or synced anymore!" : "Group folder \"{mountPoint}\" is full, files cannot be updated or synced anymore!",
    "External storage \"{mountPoint}\" is full, files cannot be updated or synced anymore!" : "External storage \"{mountPoint}\" is full, files cannot be updated or synced anymore!",
    "Your storage is full, files cannot be updated or synced anymore!" : "Your storage is full, files cannot be updated or synced anymore!",
    "Storage of {owner} is almost full ({usedSpacePercent}%)." : "Storage of {owner} is almost full ({usedSpacePercent}%).",
    "Group folder \"{mountPoint}\" is almost full ({usedSpacePercent}%)." : "Group folder \"{mountPoint}\" is almost full ({usedSpacePercent}%).",
    "External storage \"{mountPoint}\" is almost full ({usedSpacePercent}%)." : "External storage \"{mountPoint}\" is almost full ({usedSpacePercent}%).",
    "Your storage is almost full ({usedSpacePercent}%)." : "Your storage is almost full ({usedSpacePercent}%).",
    "_matches \"{filter}\"_::_match \"{filter}\"_" : ["matches \"{filter}\"","match \"{filter}\""],
    "View in folder" : "View in folder",
    "Direct link was copied (only works for users who have access to this file/folder)" : "Direct link was copied (only works for users who have access to this file/folder)",
    "Path" : "Path",
    "_%n byte_::_%n bytes_" : ["%n byte","%n bytes"],
    "Favorited" : "Favourited",
    "Favorite" : "Favourite",
    "Copy direct link (only works for users who have access to this file/folder)" : "Copy direct link (only works for users who have access to this file/folder)",
    "You can only favorite a single file or folder at a time" : "You can only favourite a single file or folder at a time",
    "New folder" : "New folder",
    "Create new folder" : "Create new folder",
    "Upload file" : "Upload file",
    "Recent" : "Recent",
    "Not favorited" : "Not favourited",
    "Remove from favorites" : "Remove from favourites",
    "Add to favorites" : "Add to favourites",
    "An error occurred while trying to update the tags" : "An error occurred whilst trying to update the tags",
    "Added to favorites" : "Added to favourites",
    "Removed from favorites" : "Removed from favourites",
    "You added {file} to your favorites" : "You added {file} to your favourites",
    "You removed {file} from your favorites" : "You removed {file} from your favourites",
    "File changes" : "File changes",
    "Created by {user}" : "Created by {user}",
    "Changed by {user}" : "Changed by {user}",
    "Deleted by {user}" : "Deleted by {user}",
    "Restored by {user}" : "Restored by {user}",
    "Renamed by {user}" : "Renamed by {user}",
    "Moved by {user}" : "Moved by {user}",
    "\"remote user\"" : "\"remote user\"",
    "You created {file}" : "You created {file}",
    "You created an encrypted file in {file}" : "You created an encrypted file in {file}",
    "{user} created {file}" : "{user} created {file}",
    "{user} created an encrypted file in {file}" : "{user} created an encrypted file in {file}",
    "{file} was created in a public folder" : "{file} was created in a public folder",
    "You changed {file}" : "You changed {file}",
    "You changed an encrypted file in {file}" : "You changed an encrypted file in {file}",
    "{user} changed {file}" : "{user} changed {file}",
    "{user} changed an encrypted file in {file}" : "{user} changed an encrypted file in {file}",
    "You deleted {file}" : "You deleted {file}",
    "You deleted an encrypted file in {file}" : "You deleted an encrypted file in {file}",
    "{user} deleted {file}" : "{user} deleted {file}",
    "{user} deleted an encrypted file in {file}" : "{user} deleted an encrypted file in {file}",
    "You restored {file}" : "You restored {file}",
    "{user} restored {file}" : "{user} restored {file}",
    "You renamed {oldfile} (hidden) to {newfile} (hidden)" : "You renamed {oldfile} (hidden) to {newfile} (hidden)",
    "You renamed {oldfile} (hidden) to {newfile}" : "You renamed {oldfile} (hidden) to {newfile}",
    "You renamed {oldfile} to {newfile} (hidden)" : "You renamed {oldfile} to {newfile} (hidden)",
    "You renamed {oldfile} to {newfile}" : "You renamed {oldfile} to {newfile}",
    "{user} renamed {oldfile} (hidden) to {newfile} (hidden)" : "{user} renamed {oldfile} (hidden) to {newfile} (hidden)",
    "{user} renamed {oldfile} (hidden) to {newfile}" : "{user} renamed {oldfile} (hidden) to {newfile}",
    "{user} renamed {oldfile} to {newfile} (hidden)" : "{user} renamed {oldfile} to {newfile} (hidden)",
    "{user} renamed {oldfile} to {newfile}" : "{user} renamed {oldfile} to {newfile}",
    "You moved {oldfile} to {newfile}" : "You moved {oldfile} to {newfile}",
    "{user} moved {oldfile} to {newfile}" : "{user} moved {oldfile} to {newfile}",
    "A file has been added to or removed from your <strong>favorites</strong>" : "A file has been added to or removed from your <strong>favourites</strong>",
    "A file or folder has been <strong>changed</strong>" : "A file or folder has been <strong>changed</strong>",
    "A favorite file or folder has been <strong>changed</strong>" : "A favourite file or folder has been <strong>changed</strong>",
    "All files" : "All files",
    "Upload (max. %s)" : "Upload (max. %s)",
    "Accept" : "Accept",
    "Reject" : "Reject",
    "Incoming ownership transfer from {user}" : "Incoming ownership transfer from {user}",
    "Do you want to accept {path}?\n\nNote: The transfer process after accepting may take up to 1 hour." : "Do you want to accept {path}?\n\nNote: The transfer process after accepting may take up to 1 hour.",
    "Ownership transfer failed" : "Ownership transfer failed",
    "Your ownership transfer of {path} to {user} failed." : "Your ownership transfer of {path} to {user} failed.",
    "The ownership transfer of {path} from {user} failed." : "The ownership transfer of {path} from {user} failed.",
    "Ownership transfer done" : "Ownership transfer done",
    "Your ownership transfer of {path} to {user} has completed." : "Your ownership transfer of {path} to {user} has completed.",
    "The ownership transfer of {path} from {user} has completed." : "The ownership transfer of {path} from {user} has completed.",
    "in %s" : "in %s",
    "File Management" : "File Management",
    "Reload current directory" : "Reload current directory",
    "Go to the \"{dir}\" directory" : "Go to the \"{dir}\" directory",
    "Select the row for {displayName}" : "Select the row for {displayName}",
    "Open folder {name}" : "Open folder {name}",
    "Download file {name}" : "Download file {name}",
    "\"{displayName}\" action executed successfully" : "\"{displayName}\" action executed successfully",
    "\"{displayName}\" action failed" : "\"{displayName}\" action failed",
    "Total rows summary" : "Total rows summary",
    "Select all" : "Select all",
    "Unselect all" : "Unselect all",
    "\"{displayName}\" failed on some elements " : "\"{displayName}\" failed on some elements ",
    "\"{displayName}\" batch action executed successfully" : "\"{displayName}\" batch action executed successfully",
    "ascending" : "ascending",
    "descending" : "descending",
    "Sort list by {column} ({direction})" : "Sort list by {column} ({direction})",
    "This list is not fully rendered for performances reasons. The files will be rendered as you navigate through the list." : "This list is not fully rendered for performances reasons. The files will be rendered as you navigate through the list.",
    "Storage informations" : "Storage informations",
    "{usedQuotaByte} used" : "{usedQuotaByte} used",
    "{relative}% used" : "{relative}% used",
    "Could not refresh storage stats" : "Could not refresh storage stats",
    "Transfer ownership of a file or folder" : "Transfer ownership of a file or folder",
    "Choose file or folder to transfer" : "Choose file or folder to transfer",
    "Change" : "Change",
    "New owner" : "New owner",
    "Search for an account" : "Search for an account",
    "Choose a file or folder to transfer" : "Choose a file or folder to transfer",
    "Transfer" : "Transfer",
    "Transfer {path} to {userid}" : "Transfer {path} to {userid}",
    "Invalid path selected" : "Invalid path selected",
    "Unknown error" : "Unknown error",
    "Ownership transfer request sent" : "Ownership transfer request sent",
    "Cannot transfer ownership of a file or folder you do not own" : "Cannot transfer ownership of a file or folder you do not own",
    "Select file or folder to link to" : "Select file or folder to link to",
    "Loading current folder" : "Loading current folder",
    "No files in here" : "No files in here",
    "No files or folders have been deleted yet" : "No files or folders have been deleted yet",
    "Go to the previous folder" : "Go to the previous folder",
    "Go back" : "Go back",
    "Open the files app settings" : "Open the files app settings",
    "Files settings" : "Files settings",
    "File cannot be accessed" : "File cannot be accessed",
    "You might not have have permissions to view it, ask the sender to share it" : "You might not have have permissions to view it, ask the sender to share it",
    "Show hidden files" : "Show hidden files",
    "Crop image previews" : "Crop image previews",
    "Additional settings" : "Additional settings",
    "WebDAV" : "WebDAV",
    "Copy to clipboard" : "Copy to clipboard",
    "Use this address to access your Files via WebDAV" : "Use this address to access your Files via WebDAV",
    "If you have enabled 2FA, you must create and use a new app password by clicking here." : "If you have enabled 2FA, you must create and use a new app password by clicking here.",
    "Clipboard is not available" : "Clipboard is not available",
    "WebDAV URL copied to clipboard" : "WebDAV URL copied to clipboard",
    "Unable to change the favourite state of the file" : "Unable to change the favourite state of the file",
    "Error while loading the file data" : "Error while loading the file data",
    "Pick a template for {name}" : "Pick a template for {name}",
    "Cancel" : "Cancel",
    "Create" : "Create",
    "Create a new file with the selected template" : "Create a new file with the selected template",
    "Creating file" : "Creating file",
    "Blank" : "Blank",
    "Unable to create new file from template" : "Unable to create new file from template",
    "Delete permanently" : "Delete permanently",
    "Set up templates folder" : "Set up templates folder",
    "Templates" : "Templates",
    "Unable to initialize the templates directory" : "Unable to initialize the templates directory",
    "Toggle %1$s sublist" : "Toggle %1$s sublist",
    "Toggle grid view" : "Toggle grid view",
    "Upload some content or sync with your devices!" : "Upload some content or sync with your devices!",
    "No entries found in this folder" : "No entries found in this folder",
    "Upload too large" : "Upload too large",
    "The files you are trying to upload exceed the maximum size for file uploads on this server." : "The files you are trying to upload exceed the maximum size for file uploads on this server.",
    "No favorites yet" : "No favourites yet",
    "Files and folders you mark as favorite will show up here" : "Files and folders you mark as favourite will show up here",
    "Shares" : "Shares",
    "Shared with others" : "Shared with others",
    "Shared with you" : "Shared with you",
    "Shared by link" : "Shared by link",
    "Deleted shares" : "Deleted shares",
    "Pending shares" : "Pending shares",
    "Text file" : "Text file",
    "New text file.txt" : "New text file.txt",
    "Storage invalid" : "Storage invalid",
    "Select" : "Select",
    "You don’t have permission to upload or create files here" : "You don’t have permission to upload or create files here",
    "New" : "New",
    "Copied!" : "Copied!",
    "Unlimited" : "Unlimited",
    "Search users" : "Search users",
    "Cannot transfer ownership of a file or folder you don't own" : "Cannot transfer ownership of a file or folder you don't own",
    "%s used" : "%s used",
    "%s%%" : "%s%%",
    "%1$s of %2$s used" : "%1$s of %2$s used",
    "Settings" : "Settings",
    "Deleted files" : "Deleted files"
},
"nplurals=2; plural=(n != 1);");