mirror of
				https://github.com/go-gitea/gitea.git
				synced 2025-10-29 10:57:44 +09:00 
			
		
		
		
	Display SVG files as images instead of text (#14101)
* Change to display SVG files as images * Remove unsafe styles from SVG CSP * Add integration test to test SVG headers * Add config setting to disable SVG rendering * Add test for img tag when loading SVG image * Remove the Raw view button for svg files since we don't fully support this * Fix copyright year * Rename and move config setting * Add setting to cheat sheet in docs * Fix so that comment matches cheat sheet * Add allowing styles in CSP based on pull request feedback * Re-enable raw button since we show SVG styles now * Change so that SVG files are editable * Add UI to toggle between source and rendered image for SVGs * Change to show blame button for SVG images * Fix to update ctx data * Add test for DetectContentType when file is longer than sniffLen Co-authored-by: Jonathan Tran <jon@allspice.io> Co-authored-by: Kyle D <kdumontnu@gmail.com>
This commit is contained in:
		| @@ -244,6 +244,10 @@ TIMEOUT_STEP = 10s | ||||
| ; If the browser client supports EventSource and SharedWorker, a SharedWorker will be used in preference to polling notification. Set to -1 to disable the EventSource | ||||
| EVENT_SOURCE_UPDATE_TIME = 10s | ||||
|  | ||||
| [ui.svg] | ||||
| ; Whether to render SVG files as images.  If SVG rendering is disabled, SVG files are displayed as text and cannot be embedded in markdown files as images. | ||||
| ENABLE_RENDER = true | ||||
|  | ||||
| [markdown] | ||||
| ; Render soft line breaks as hard line breaks, which means a single newline character between | ||||
| ; paragraphs will cause a line break and adding trailing whitespace to paragraphs is not | ||||
|   | ||||
| @@ -194,6 +194,10 @@ Values containing `#` or `;` must be quoted using `` ` `` or `"""`. | ||||
| - `TIMEOUT_STEP`: **10s**. | ||||
| - `EVENT_SOURCE_UPDATE_TIME`: **10s**: This setting determines how often the database is queried to update notification counts. If the browser client supports `EventSource` and `SharedWorker`, a `SharedWorker` will be used in preference to polling notification endpoint. Set to **-1** to disable the `EventSource`. | ||||
|  | ||||
| ### UI - SVG Images (`ui.svg`) | ||||
|  | ||||
| - `ENABLE_RENDER`: **true**: Whether to render SVG files as images.  If SVG rendering is disabled, SVG files are displayed as text and cannot be embedded in markdown files as images. | ||||
|  | ||||
| ## Markdown (`markdown`) | ||||
|  | ||||
| - `ENABLE_HARD_LINE_BREAK_IN_COMMENTS`: **true**: Render soft line breaks as hard line breaks in comments, which | ||||
|   | ||||
| @@ -23,6 +23,20 @@ func TestDownloadByID(t *testing.T) { | ||||
| 	assert.Equal(t, "# repo1\n\nDescription for repo1", resp.Body.String()) | ||||
| } | ||||
|  | ||||
| func TestDownloadByIDForSVGUsesSecureHeaders(t *testing.T) { | ||||
| 	defer prepareTestEnv(t)() | ||||
|  | ||||
| 	session := loginUser(t, "user2") | ||||
|  | ||||
| 	// Request raw blob | ||||
| 	req := NewRequest(t, "GET", "/user2/repo2/raw/blob/6395b68e1feebb1e4c657b4f9f6ba2676a283c0b") | ||||
| 	resp := session.MakeRequest(t, req, http.StatusOK) | ||||
|  | ||||
| 	assert.Equal(t, "default-src 'none'; style-src 'unsafe-inline'; sandbox", resp.HeaderMap.Get("Content-Security-Policy")) | ||||
| 	assert.Equal(t, "image/svg+xml", resp.HeaderMap.Get("Content-Type")) | ||||
| 	assert.Equal(t, "nosniff", resp.HeaderMap.Get("X-Content-Type-Options")) | ||||
| } | ||||
|  | ||||
| func TestDownloadByIDMedia(t *testing.T) { | ||||
| 	defer prepareTestEnv(t)() | ||||
|  | ||||
| @@ -34,3 +48,17 @@ func TestDownloadByIDMedia(t *testing.T) { | ||||
|  | ||||
| 	assert.Equal(t, "# repo1\n\nDescription for repo1", resp.Body.String()) | ||||
| } | ||||
|  | ||||
| func TestDownloadByIDMediaForSVGUsesSecureHeaders(t *testing.T) { | ||||
| 	defer prepareTestEnv(t)() | ||||
|  | ||||
| 	session := loginUser(t, "user2") | ||||
|  | ||||
| 	// Request raw blob | ||||
| 	req := NewRequest(t, "GET", "/user2/repo2/media/blob/6395b68e1feebb1e4c657b4f9f6ba2676a283c0b") | ||||
| 	resp := session.MakeRequest(t, req, http.StatusOK) | ||||
|  | ||||
| 	assert.Equal(t, "default-src 'none'; style-src 'unsafe-inline'; sandbox", resp.HeaderMap.Get("Content-Security-Policy")) | ||||
| 	assert.Equal(t, "image/svg+xml", resp.HeaderMap.Get("Content-Type")) | ||||
| 	assert.Equal(t, "nosniff", resp.HeaderMap.Get("X-Content-Type-Options")) | ||||
| } | ||||
|   | ||||
| @@ -0,0 +1 @@ | ||||
| ref: refs/heads/master | ||||
| @@ -0,0 +1,4 @@ | ||||
| [core] | ||||
| 	repositoryformatversion = 0 | ||||
| 	filemode = true | ||||
| 	bare = true | ||||
| @@ -0,0 +1 @@ | ||||
| Unnamed repository; edit this file 'description' to name the repository. | ||||
| @@ -0,0 +1,15 @@ | ||||
| #!/bin/sh | ||||
| # | ||||
| # An example hook script to check the commit log message taken by | ||||
| # applypatch from an e-mail message. | ||||
| # | ||||
| # The hook should exit with non-zero status after issuing an | ||||
| # appropriate message if it wants to stop the commit.  The hook is | ||||
| # allowed to edit the commit message file. | ||||
| # | ||||
| # To enable this hook, rename this file to "applypatch-msg". | ||||
|  | ||||
| . git-sh-setup | ||||
| commitmsg="$(git rev-parse --git-path hooks/commit-msg)" | ||||
| test -x "$commitmsg" && exec "$commitmsg" ${1+"$@"} | ||||
| : | ||||
							
								
								
									
										24
									
								
								integrations/gitea-repositories-meta/user2/repo2.git/hooks/commit-msg.sample
									
									
									
									
									
										Executable file
									
								
							
							
						
						
									
										24
									
								
								integrations/gitea-repositories-meta/user2/repo2.git/hooks/commit-msg.sample
									
									
									
									
									
										Executable file
									
								
							| @@ -0,0 +1,24 @@ | ||||
| #!/bin/sh | ||||
| # | ||||
| # An example hook script to check the commit log message. | ||||
| # Called by "git commit" with one argument, the name of the file | ||||
| # that has the commit message.  The hook should exit with non-zero | ||||
| # status after issuing an appropriate message if it wants to stop the | ||||
| # commit.  The hook is allowed to edit the commit message file. | ||||
| # | ||||
| # To enable this hook, rename this file to "commit-msg". | ||||
|  | ||||
| # Uncomment the below to add a Signed-off-by line to the message. | ||||
| # Doing this in a hook is a bad idea in general, but the prepare-commit-msg | ||||
| # hook is more suited to it. | ||||
| # | ||||
| # SOB=$(git var GIT_AUTHOR_IDENT | sed -n 's/^\(.*>\).*$/Signed-off-by: \1/p') | ||||
| # grep -qs "^$SOB" "$1" || echo "$SOB" >> "$1" | ||||
|  | ||||
| # This example catches duplicate Signed-off-by lines. | ||||
|  | ||||
| test "" = "$(grep '^Signed-off-by: ' "$1" | | ||||
| 	 sort | uniq -c | sed -e '/^[ 	]*1[ 	]/d')" || { | ||||
| 	echo >&2 Duplicate Signed-off-by lines. | ||||
| 	exit 1 | ||||
| } | ||||
| @@ -0,0 +1,8 @@ | ||||
| #!/bin/sh | ||||
| # | ||||
| # An example hook script to prepare a packed repository for use over | ||||
| # dumb transports. | ||||
| # | ||||
| # To enable this hook, rename this file to "post-update". | ||||
|  | ||||
| exec git update-server-info | ||||
| @@ -0,0 +1,14 @@ | ||||
| #!/bin/sh | ||||
| # | ||||
| # An example hook script to verify what is about to be committed | ||||
| # by applypatch from an e-mail message. | ||||
| # | ||||
| # The hook should exit with non-zero status after issuing an | ||||
| # appropriate message if it wants to stop the commit. | ||||
| # | ||||
| # To enable this hook, rename this file to "pre-applypatch". | ||||
|  | ||||
| . git-sh-setup | ||||
| precommit="$(git rev-parse --git-path hooks/pre-commit)" | ||||
| test -x "$precommit" && exec "$precommit" ${1+"$@"} | ||||
| : | ||||
							
								
								
									
										49
									
								
								integrations/gitea-repositories-meta/user2/repo2.git/hooks/pre-commit.sample
									
									
									
									
									
										Executable file
									
								
							
							
						
						
									
										49
									
								
								integrations/gitea-repositories-meta/user2/repo2.git/hooks/pre-commit.sample
									
									
									
									
									
										Executable file
									
								
							| @@ -0,0 +1,49 @@ | ||||
| #!/bin/sh | ||||
| # | ||||
| # An example hook script to verify what is about to be committed. | ||||
| # Called by "git commit" with no arguments.  The hook should | ||||
| # exit with non-zero status after issuing an appropriate message if | ||||
| # it wants to stop the commit. | ||||
| # | ||||
| # To enable this hook, rename this file to "pre-commit". | ||||
|  | ||||
| if git rev-parse --verify HEAD >/dev/null 2>&1 | ||||
| then | ||||
| 	against=HEAD | ||||
| else | ||||
| 	# Initial commit: diff against an empty tree object | ||||
| 	against=4b825dc642cb6eb9a060e54bf8d69288fbee4904 | ||||
| fi | ||||
|  | ||||
| # If you want to allow non-ASCII filenames set this variable to true. | ||||
| allownonascii=$(git config --bool hooks.allownonascii) | ||||
|  | ||||
| # Redirect output to stderr. | ||||
| exec 1>&2 | ||||
|  | ||||
| # Cross platform projects tend to avoid non-ASCII filenames; prevent | ||||
| # them from being added to the repository. We exploit the fact that the | ||||
| # printable range starts at the space character and ends with tilde. | ||||
| if [ "$allownonascii" != "true" ] && | ||||
| 	# Note that the use of brackets around a tr range is ok here, (it's | ||||
| 	# even required, for portability to Solaris 10's /usr/bin/tr), since | ||||
| 	# the square bracket bytes happen to fall in the designated range. | ||||
| 	test $(git diff --cached --name-only --diff-filter=A -z $against | | ||||
| 	  LC_ALL=C tr -d '[ -~]\0' | wc -c) != 0 | ||||
| then | ||||
| 	cat <<\EOF | ||||
| Error: Attempt to add a non-ASCII file name. | ||||
|  | ||||
| This can cause problems if you want to work with people on other platforms. | ||||
|  | ||||
| To be portable it is advisable to rename the file. | ||||
|  | ||||
| If you know what you are doing you can disable this check using: | ||||
|  | ||||
|   git config hooks.allownonascii true | ||||
| EOF | ||||
| 	exit 1 | ||||
| fi | ||||
|  | ||||
| # If there are whitespace errors, print the offending file names and fail. | ||||
| exec git diff-index --check --cached $against -- | ||||
							
								
								
									
										53
									
								
								integrations/gitea-repositories-meta/user2/repo2.git/hooks/pre-push.sample
									
									
									
									
									
										Executable file
									
								
							
							
						
						
									
										53
									
								
								integrations/gitea-repositories-meta/user2/repo2.git/hooks/pre-push.sample
									
									
									
									
									
										Executable file
									
								
							| @@ -0,0 +1,53 @@ | ||||
| #!/bin/sh | ||||
|  | ||||
| # An example hook script to verify what is about to be pushed.  Called by "git | ||||
| # push" after it has checked the remote status, but before anything has been | ||||
| # pushed.  If this script exits with a non-zero status nothing will be pushed. | ||||
| # | ||||
| # This hook is called with the following parameters: | ||||
| # | ||||
| # $1 -- Name of the remote to which the push is being done | ||||
| # $2 -- URL to which the push is being done | ||||
| # | ||||
| # If pushing without using a named remote those arguments will be equal. | ||||
| # | ||||
| # Information about the commits which are being pushed is supplied as lines to | ||||
| # the standard input in the form: | ||||
| # | ||||
| #   <local ref> <local sha1> <remote ref> <remote sha1> | ||||
| # | ||||
| # This sample shows how to prevent push of commits where the log message starts | ||||
| # with "WIP" (work in progress). | ||||
|  | ||||
| remote="$1" | ||||
| url="$2" | ||||
|  | ||||
| z40=0000000000000000000000000000000000000000 | ||||
|  | ||||
| while read local_ref local_sha remote_ref remote_sha | ||||
| do | ||||
| 	if [ "$local_sha" = $z40 ] | ||||
| 	then | ||||
| 		# Handle delete | ||||
| 		: | ||||
| 	else | ||||
| 		if [ "$remote_sha" = $z40 ] | ||||
| 		then | ||||
| 			# New branch, examine all commits | ||||
| 			range="$local_sha" | ||||
| 		else | ||||
| 			# Update to existing branch, examine new commits | ||||
| 			range="$remote_sha..$local_sha" | ||||
| 		fi | ||||
|  | ||||
| 		# Check for WIP commit | ||||
| 		commit=`git rev-list -n 1 --grep '^WIP' "$range"` | ||||
| 		if [ -n "$commit" ] | ||||
| 		then | ||||
| 			echo >&2 "Found WIP commit in $local_ref, not pushing" | ||||
| 			exit 1 | ||||
| 		fi | ||||
| 	fi | ||||
| done | ||||
|  | ||||
| exit 0 | ||||
							
								
								
									
										169
									
								
								integrations/gitea-repositories-meta/user2/repo2.git/hooks/pre-rebase.sample
									
									
									
									
									
										Executable file
									
								
							
							
						
						
									
										169
									
								
								integrations/gitea-repositories-meta/user2/repo2.git/hooks/pre-rebase.sample
									
									
									
									
									
										Executable file
									
								
							| @@ -0,0 +1,169 @@ | ||||
| #!/bin/sh | ||||
| # | ||||
| # Copyright (c) 2006, 2008 Junio C Hamano | ||||
| # | ||||
| # The "pre-rebase" hook is run just before "git rebase" starts doing | ||||
| # its job, and can prevent the command from running by exiting with | ||||
| # non-zero status. | ||||
| # | ||||
| # The hook is called with the following parameters: | ||||
| # | ||||
| # $1 -- the upstream the series was forked from. | ||||
| # $2 -- the branch being rebased (or empty when rebasing the current branch). | ||||
| # | ||||
| # This sample shows how to prevent topic branches that are already | ||||
| # merged to 'next' branch from getting rebased, because allowing it | ||||
| # would result in rebasing already published history. | ||||
|  | ||||
| publish=next | ||||
| basebranch="$1" | ||||
| if test "$#" = 2 | ||||
| then | ||||
| 	topic="refs/heads/$2" | ||||
| else | ||||
| 	topic=`git symbolic-ref HEAD` || | ||||
| 	exit 0 ;# we do not interrupt rebasing detached HEAD | ||||
| fi | ||||
|  | ||||
| case "$topic" in | ||||
| refs/heads/??/*) | ||||
| 	;; | ||||
| *) | ||||
| 	exit 0 ;# we do not interrupt others. | ||||
| 	;; | ||||
| esac | ||||
|  | ||||
| # Now we are dealing with a topic branch being rebased | ||||
| # on top of master.  Is it OK to rebase it? | ||||
|  | ||||
| # Does the topic really exist? | ||||
| git show-ref -q "$topic" || { | ||||
| 	echo >&2 "No such branch $topic" | ||||
| 	exit 1 | ||||
| } | ||||
|  | ||||
| # Is topic fully merged to master? | ||||
| not_in_master=`git rev-list --pretty=oneline ^master "$topic"` | ||||
| if test -z "$not_in_master" | ||||
| then | ||||
| 	echo >&2 "$topic is fully merged to master; better remove it." | ||||
| 	exit 1 ;# we could allow it, but there is no point. | ||||
| fi | ||||
|  | ||||
| # Is topic ever merged to next?  If so you should not be rebasing it. | ||||
| only_next_1=`git rev-list ^master "^$topic" ${publish} | sort` | ||||
| only_next_2=`git rev-list ^master           ${publish} | sort` | ||||
| if test "$only_next_1" = "$only_next_2" | ||||
| then | ||||
| 	not_in_topic=`git rev-list "^$topic" master` | ||||
| 	if test -z "$not_in_topic" | ||||
| 	then | ||||
| 		echo >&2 "$topic is already up-to-date with master" | ||||
| 		exit 1 ;# we could allow it, but there is no point. | ||||
| 	else | ||||
| 		exit 0 | ||||
| 	fi | ||||
| else | ||||
| 	not_in_next=`git rev-list --pretty=oneline ^${publish} "$topic"` | ||||
| 	/usr/bin/perl -e ' | ||||
| 		my $topic = $ARGV[0]; | ||||
| 		my $msg = "* $topic has commits already merged to public branch:\n"; | ||||
| 		my (%not_in_next) = map { | ||||
| 			/^([0-9a-f]+) /; | ||||
| 			($1 => 1); | ||||
| 		} split(/\n/, $ARGV[1]); | ||||
| 		for my $elem (map { | ||||
| 				/^([0-9a-f]+) (.*)$/; | ||||
| 				[$1 => $2]; | ||||
| 			} split(/\n/, $ARGV[2])) { | ||||
| 			if (!exists $not_in_next{$elem->[0]}) { | ||||
| 				if ($msg) { | ||||
| 					print STDERR $msg; | ||||
| 					undef $msg; | ||||
| 				} | ||||
| 				print STDERR " $elem->[1]\n"; | ||||
| 			} | ||||
| 		} | ||||
| 	' "$topic" "$not_in_next" "$not_in_master" | ||||
| 	exit 1 | ||||
| fi | ||||
|  | ||||
| <<\DOC_END | ||||
|  | ||||
| This sample hook safeguards topic branches that have been | ||||
| published from being rewound. | ||||
|  | ||||
| The workflow assumed here is: | ||||
|  | ||||
|  * Once a topic branch forks from "master", "master" is never | ||||
|    merged into it again (either directly or indirectly). | ||||
|  | ||||
|  * Once a topic branch is fully cooked and merged into "master", | ||||
|    it is deleted.  If you need to build on top of it to correct | ||||
|    earlier mistakes, a new topic branch is created by forking at | ||||
|    the tip of the "master".  This is not strictly necessary, but | ||||
|    it makes it easier to keep your history simple. | ||||
|  | ||||
|  * Whenever you need to test or publish your changes to topic | ||||
|    branches, merge them into "next" branch. | ||||
|  | ||||
| The script, being an example, hardcodes the publish branch name | ||||
| to be "next", but it is trivial to make it configurable via | ||||
| $GIT_DIR/config mechanism. | ||||
|  | ||||
| With this workflow, you would want to know: | ||||
|  | ||||
| (1) ... if a topic branch has ever been merged to "next".  Young | ||||
|     topic branches can have stupid mistakes you would rather | ||||
|     clean up before publishing, and things that have not been | ||||
|     merged into other branches can be easily rebased without | ||||
|     affecting other people.  But once it is published, you would | ||||
|     not want to rewind it. | ||||
|  | ||||
| (2) ... if a topic branch has been fully merged to "master". | ||||
|     Then you can delete it.  More importantly, you should not | ||||
|     build on top of it -- other people may already want to | ||||
|     change things related to the topic as patches against your | ||||
|     "master", so if you need further changes, it is better to | ||||
|     fork the topic (perhaps with the same name) afresh from the | ||||
|     tip of "master". | ||||
|  | ||||
| Let's look at this example: | ||||
|  | ||||
| 		   o---o---o---o---o---o---o---o---o---o "next" | ||||
| 		  /       /           /           / | ||||
| 		 /   a---a---b A     /           / | ||||
| 		/   /               /           / | ||||
| 	       /   /   c---c---c---c B         / | ||||
| 	      /   /   /             \         / | ||||
| 	     /   /   /   b---b C     \       / | ||||
| 	    /   /   /   /             \     / | ||||
|     ---o---o---o---o---o---o---o---o---o---o---o "master" | ||||
|  | ||||
|  | ||||
| A, B and C are topic branches. | ||||
|  | ||||
|  * A has one fix since it was merged up to "next". | ||||
|  | ||||
|  * B has finished.  It has been fully merged up to "master" and "next", | ||||
|    and is ready to be deleted. | ||||
|  | ||||
|  * C has not merged to "next" at all. | ||||
|  | ||||
| We would want to allow C to be rebased, refuse A, and encourage | ||||
| B to be deleted. | ||||
|  | ||||
| To compute (1): | ||||
|  | ||||
| 	git rev-list ^master ^topic next | ||||
| 	git rev-list ^master        next | ||||
|  | ||||
| 	if these match, topic has not merged in next at all. | ||||
|  | ||||
| To compute (2): | ||||
|  | ||||
| 	git rev-list master..topic | ||||
|  | ||||
| 	if this is empty, it is fully merged to "master". | ||||
|  | ||||
| DOC_END | ||||
| @@ -0,0 +1,36 @@ | ||||
| #!/bin/sh | ||||
| # | ||||
| # An example hook script to prepare the commit log message. | ||||
| # Called by "git commit" with the name of the file that has the | ||||
| # commit message, followed by the description of the commit | ||||
| # message's source.  The hook's purpose is to edit the commit | ||||
| # message file.  If the hook fails with a non-zero status, | ||||
| # the commit is aborted. | ||||
| # | ||||
| # To enable this hook, rename this file to "prepare-commit-msg". | ||||
|  | ||||
| # This hook includes three examples.  The first comments out the | ||||
| # "Conflicts:" part of a merge commit. | ||||
| # | ||||
| # The second includes the output of "git diff --name-status -r" | ||||
| # into the message, just before the "git status" output.  It is | ||||
| # commented because it doesn't cope with --amend or with squashed | ||||
| # commits. | ||||
| # | ||||
| # The third example adds a Signed-off-by line to the message, that can | ||||
| # still be edited.  This is rarely a good idea. | ||||
|  | ||||
| case "$2,$3" in | ||||
|   merge,) | ||||
|     /usr/bin/perl -i.bak -ne 's/^/# /, s/^# #/#/ if /^Conflicts/ .. /#/; print' "$1" ;; | ||||
|  | ||||
| # ,|template,) | ||||
| #   /usr/bin/perl -i.bak -pe ' | ||||
| #      print "\n" . `git diff --cached --name-status -r` | ||||
| #	 if /^#/ && $first++ == 0' "$1" ;; | ||||
|  | ||||
|   *) ;; | ||||
| esac | ||||
|  | ||||
| # SOB=$(git var GIT_AUTHOR_IDENT | sed -n 's/^\(.*>\).*$/Signed-off-by: \1/p') | ||||
| # grep -qs "^$SOB" "$1" || echo "$SOB" >> "$1" | ||||
							
								
								
									
										128
									
								
								integrations/gitea-repositories-meta/user2/repo2.git/hooks/update.sample
									
									
									
									
									
										Executable file
									
								
							
							
						
						
									
										128
									
								
								integrations/gitea-repositories-meta/user2/repo2.git/hooks/update.sample
									
									
									
									
									
										Executable file
									
								
							| @@ -0,0 +1,128 @@ | ||||
| #!/bin/sh | ||||
| # | ||||
| # An example hook script to block unannotated tags from entering. | ||||
| # Called by "git receive-pack" with arguments: refname sha1-old sha1-new | ||||
| # | ||||
| # To enable this hook, rename this file to "update". | ||||
| # | ||||
| # Config | ||||
| # ------ | ||||
| # hooks.allowunannotated | ||||
| #   This boolean sets whether unannotated tags will be allowed into the | ||||
| #   repository.  By default they won't be. | ||||
| # hooks.allowdeletetag | ||||
| #   This boolean sets whether deleting tags will be allowed in the | ||||
| #   repository.  By default they won't be. | ||||
| # hooks.allowmodifytag | ||||
| #   This boolean sets whether a tag may be modified after creation. By default | ||||
| #   it won't be. | ||||
| # hooks.allowdeletebranch | ||||
| #   This boolean sets whether deleting branches will be allowed in the | ||||
| #   repository.  By default they won't be. | ||||
| # hooks.denycreatebranch | ||||
| #   This boolean sets whether remotely creating branches will be denied | ||||
| #   in the repository.  By default this is allowed. | ||||
| # | ||||
|  | ||||
| # --- Command line | ||||
| refname="$1" | ||||
| oldrev="$2" | ||||
| newrev="$3" | ||||
|  | ||||
| # --- Safety check | ||||
| if [ -z "$GIT_DIR" ]; then | ||||
| 	echo "Don't run this script from the command line." >&2 | ||||
| 	echo " (if you want, you could supply GIT_DIR then run" >&2 | ||||
| 	echo "  $0 <ref> <oldrev> <newrev>)" >&2 | ||||
| 	exit 1 | ||||
| fi | ||||
|  | ||||
| if [ -z "$refname" -o -z "$oldrev" -o -z "$newrev" ]; then | ||||
| 	echo "usage: $0 <ref> <oldrev> <newrev>" >&2 | ||||
| 	exit 1 | ||||
| fi | ||||
|  | ||||
| # --- Config | ||||
| allowunannotated=$(git config --bool hooks.allowunannotated) | ||||
| allowdeletebranch=$(git config --bool hooks.allowdeletebranch) | ||||
| denycreatebranch=$(git config --bool hooks.denycreatebranch) | ||||
| allowdeletetag=$(git config --bool hooks.allowdeletetag) | ||||
| allowmodifytag=$(git config --bool hooks.allowmodifytag) | ||||
|  | ||||
| # check for no description | ||||
| projectdesc=$(sed -e '1q' "$GIT_DIR/description") | ||||
| case "$projectdesc" in | ||||
| "Unnamed repository"* | "") | ||||
| 	echo "*** Project description file hasn't been set" >&2 | ||||
| 	exit 1 | ||||
| 	;; | ||||
| esac | ||||
|  | ||||
| # --- Check types | ||||
| # if $newrev is 0000...0000, it's a commit to delete a ref. | ||||
| zero="0000000000000000000000000000000000000000" | ||||
| if [ "$newrev" = "$zero" ]; then | ||||
| 	newrev_type=delete | ||||
| else | ||||
| 	newrev_type=$(git cat-file -t $newrev) | ||||
| fi | ||||
|  | ||||
| case "$refname","$newrev_type" in | ||||
| 	refs/tags/*,commit) | ||||
| 		# un-annotated tag | ||||
| 		short_refname=${refname##refs/tags/} | ||||
| 		if [ "$allowunannotated" != "true" ]; then | ||||
| 			echo "*** The un-annotated tag, $short_refname, is not allowed in this repository" >&2 | ||||
| 			echo "*** Use 'git tag [ -a | -s ]' for tags you want to propagate." >&2 | ||||
| 			exit 1 | ||||
| 		fi | ||||
| 		;; | ||||
| 	refs/tags/*,delete) | ||||
| 		# delete tag | ||||
| 		if [ "$allowdeletetag" != "true" ]; then | ||||
| 			echo "*** Deleting a tag is not allowed in this repository" >&2 | ||||
| 			exit 1 | ||||
| 		fi | ||||
| 		;; | ||||
| 	refs/tags/*,tag) | ||||
| 		# annotated tag | ||||
| 		if [ "$allowmodifytag" != "true" ] && git rev-parse $refname > /dev/null 2>&1 | ||||
| 		then | ||||
| 			echo "*** Tag '$refname' already exists." >&2 | ||||
| 			echo "*** Modifying a tag is not allowed in this repository." >&2 | ||||
| 			exit 1 | ||||
| 		fi | ||||
| 		;; | ||||
| 	refs/heads/*,commit) | ||||
| 		# branch | ||||
| 		if [ "$oldrev" = "$zero" -a "$denycreatebranch" = "true" ]; then | ||||
| 			echo "*** Creating a branch is not allowed in this repository" >&2 | ||||
| 			exit 1 | ||||
| 		fi | ||||
| 		;; | ||||
| 	refs/heads/*,delete) | ||||
| 		# delete branch | ||||
| 		if [ "$allowdeletebranch" != "true" ]; then | ||||
| 			echo "*** Deleting a branch is not allowed in this repository" >&2 | ||||
| 			exit 1 | ||||
| 		fi | ||||
| 		;; | ||||
| 	refs/remotes/*,commit) | ||||
| 		# tracking branch | ||||
| 		;; | ||||
| 	refs/remotes/*,delete) | ||||
| 		# delete tracking branch | ||||
| 		if [ "$allowdeletebranch" != "true" ]; then | ||||
| 			echo "*** Deleting a tracking branch is not allowed in this repository" >&2 | ||||
| 			exit 1 | ||||
| 		fi | ||||
| 		;; | ||||
| 	*) | ||||
| 		# Anything else (is there anything else?) | ||||
| 		echo "*** Update hook: unknown type of update to ref $refname of type $newrev_type" >&2 | ||||
| 		exit 1 | ||||
| 		;; | ||||
| esac | ||||
|  | ||||
| # --- Finished | ||||
| exit 0 | ||||
| @@ -0,0 +1,6 @@ | ||||
| # git ls-files --others --exclude-from=.git/info/exclude | ||||
| # Lines that start with '#' are comments. | ||||
| # For a project mostly in C, the following would be a good set of | ||||
| # exclude patterns (uncomment them if you want to use them): | ||||
| # *.[oa] | ||||
| # *~ | ||||
| @@ -0,0 +1 @@ | ||||
| 205ac761f3326a7ebe416e8673760016450b5cec	refs/heads/master | ||||
										
											Binary file not shown.
										
									
								
							| @@ -0,0 +1,2 @@ | ||||
| x<01><><EFBFBD>m<EFBFBD> D<>M<15><>Y<EFBFBD><59><1A><>(<28>J<01>`<60>5<EFBFBD>ɜ-<2D>K*Ki,Hi!?<3F><><<3C>i<EFBFBD>Vki0Z<30><5A>XH<58>D(Z6ĨG<C4A8>Sb<53><62>3<EFBFBD>JD<4A>h<EFBFBD><68>!<21>uB<1D><>DaJp<>	<09><><02><>F<EFBFBD>Lƹ4+~<7E><>v<EFBFBD>;<3B><><EFBFBD> | ||||
| e<><65><EFBFBD><EFBFBD><EFBFBD>[Nx>K<><4B><1C><><EFBFBD>_s<5F><73>q<>/<2F>]09MHpѤ<70><D1A4>k<EFBFBD><6B><EFBFBD>_d<5F>-%<25>풇۞<ED9287><DB9E>	v<>_<>]<5D><>^<5E>/<2F>I[t | ||||
| @@ -0,0 +1 @@ | ||||
| x+)JMU07b040031Q<31>*HM<48><4D>*Hg(<28>(<28><>=<3D><><EFBFBD><EFBFBD><EFBFBD>AvNA<4E><41><EFBFBD>6<><36><EFBFBD><EFBFBD><EFBFBD><EFBFBD>K<EFBFBD>+.KgH<67><48><EFBFBD>O<EFBFBD><4F>n9<6E><39>j<EFBFBD><6A>ًҳ4l<34><16><1B> | ||||
										
											Binary file not shown.
										
									
								
							
										
											Binary file not shown.
										
									
								
							
										
											Binary file not shown.
										
									
								
							| @@ -0,0 +1 @@ | ||||
| x<15>M<0E>@<0C><><EFBFBD>M<><19><17><08>r<EFBFBD><EFBFBD>6<EFBFBD><36>&&&<26><>9Le<4C>św<C59B><77><EFBFBD><EFBFBD>t<#<23><><EFBFBD>͡<>mv-<2D><>0w<30>b<EFBFBD><62>jy̖<79><CC96>ڗ~[<5B><><EFBFBD><EFBFBD><EFBFBD>=H<15><0C>.<2E>"<22><><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ǁ= | ||||
										
											Binary file not shown.
										
									
								
							
										
											Binary file not shown.
										
									
								
							
										
											Binary file not shown.
										
									
								
							| @@ -0,0 +1,2 @@ | ||||
| x<01><><EFBFBD>m<EFBFBD>0<10><><EFBFBD>)n<><06><>t2<14>S<07><04><><EFBFBD>`ņ<><C586><EFBFBD>e<EFBFBD>,VY<>/H<>#<23>[)<29><>E<EFBFBD><45>@N<>q<EFBFBD><71>툎<EFBFBD>r2<19>)D<><03>0<EFBFBD><30>j<EFBFBD>C<EFBFBD><1D><>L<07><>aC<61><43>&<26>4B<34>v]$E<><45><EFBFBD><1D>Iӑe<D391><65><EFBFBD><EFBFBD>P<EFBFBD>r<EFBFBD>I<EFBFBD>s<EFBFBD>e<EFBFBD>z<EFBFBD>˳~_ | ||||
| <EFBFBD><EFBFBD><EFBFBD>[y<03><><EFBFBD><EFBFBD>v<EFBFBD><76>W<EFBFBD><57>V=헛<>˘<EFBFBD>H	vZ~s<>@݉%<25><><EFBFBD><EFBFBD>?T<>ZH | ||||
										
											Binary file not shown.
										
									
								
							
										
											Binary file not shown.
										
									
								
							
										
											Binary file not shown.
										
									
								
							| @@ -0,0 +1,2 @@ | ||||
| P pack-a2f7ad943b3d857eb3ebdb4b35eeef38f63cf5d2.pack | ||||
|  | ||||
										
											Binary file not shown.
										
									
								
							
										
											Binary file not shown.
										
									
								
							
										
											Binary file not shown.
										
									
								
							| @@ -0,0 +1,2 @@ | ||||
| # pack-refs with: peeled fully-peeled sorted  | ||||
| 205ac761f3326a7ebe416e8673760016450b5cec refs/heads/master | ||||
| @@ -0,0 +1 @@ | ||||
| 205ac761f3326a7ebe416e8673760016450b5cec | ||||
							
								
								
									
										26
									
								
								integrations/view_test.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										26
									
								
								integrations/view_test.go
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,26 @@ | ||||
| // Copyright 2020 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 integrations | ||||
|  | ||||
| import ( | ||||
| 	"net/http" | ||||
| 	"testing" | ||||
|  | ||||
| 	"github.com/stretchr/testify/assert" | ||||
| ) | ||||
|  | ||||
| func TestRenderFileSVGIsInImgTag(t *testing.T) { | ||||
| 	defer prepareTestEnv(t)() | ||||
|  | ||||
| 	session := loginUser(t, "user2") | ||||
|  | ||||
| 	req := NewRequest(t, "GET", "/user2/repo2/src/branch/master/line.svg") | ||||
| 	resp := session.MakeRequest(t, req, http.StatusOK) | ||||
|  | ||||
| 	doc := NewHTMLParser(t, resp.Body) | ||||
| 	src, exists := doc.doc.Find(".file-view img").Attr("src") | ||||
| 	assert.True(t, exists, "The SVG image should be in an <img> tag so that scripts in the SVG are not run") | ||||
| 	assert.Equal(t, "/user2/repo2/raw/branch/master/line.svg", src) | ||||
| } | ||||
| @@ -15,6 +15,7 @@ import ( | ||||
| 	"net/http" | ||||
| 	"os" | ||||
| 	"path/filepath" | ||||
| 	"regexp" | ||||
| 	"runtime" | ||||
| 	"strconv" | ||||
| 	"strings" | ||||
| @@ -28,6 +29,15 @@ import ( | ||||
| 	"github.com/dustin/go-humanize" | ||||
| ) | ||||
|  | ||||
| // Use at most this many bytes to determine Content Type. | ||||
| const sniffLen = 512 | ||||
|  | ||||
| // SVGMimeType MIME type of SVG images. | ||||
| const SVGMimeType = "image/svg+xml" | ||||
|  | ||||
| var svgTagRegex = regexp.MustCompile(`(?s)\A\s*(?:<!--.*?-->\s*)*<svg\b`) | ||||
| var svgTagInXMLRegex = regexp.MustCompile(`(?s)\A<\?xml\b.*?\?>\s*(?:<!--.*?-->\s*)*<svg\b`) | ||||
|  | ||||
| // EncodeMD5 encodes string to md5 hex value. | ||||
| func EncodeMD5(str string) string { | ||||
| 	m := md5.New() | ||||
| @@ -265,32 +275,61 @@ func IsLetter(ch rune) bool { | ||||
| 	return 'a' <= ch && ch <= 'z' || 'A' <= ch && ch <= 'Z' || ch == '_' || ch >= 0x80 && unicode.IsLetter(ch) | ||||
| } | ||||
|  | ||||
| // DetectContentType extends http.DetectContentType with more content types. | ||||
| func DetectContentType(data []byte) string { | ||||
| 	ct := http.DetectContentType(data) | ||||
|  | ||||
| 	if len(data) > sniffLen { | ||||
| 		data = data[:sniffLen] | ||||
| 	} | ||||
|  | ||||
| 	if setting.UI.SVG.Enabled && | ||||
| 		((strings.Contains(ct, "text/plain") || strings.Contains(ct, "text/html")) && svgTagRegex.Match(data) || | ||||
| 			strings.Contains(ct, "text/xml") && svgTagInXMLRegex.Match(data)) { | ||||
|  | ||||
| 		// SVG is unsupported.  https://github.com/golang/go/issues/15888 | ||||
| 		return SVGMimeType | ||||
| 	} | ||||
| 	return ct | ||||
| } | ||||
|  | ||||
| // IsRepresentableAsText returns true if file content can be represented as | ||||
| // plain text or is empty. | ||||
| func IsRepresentableAsText(data []byte) bool { | ||||
| 	return IsTextFile(data) || IsSVGImageFile(data) | ||||
| } | ||||
|  | ||||
| // IsTextFile returns true if file content format is plain text or empty. | ||||
| func IsTextFile(data []byte) bool { | ||||
| 	if len(data) == 0 { | ||||
| 		return true | ||||
| 	} | ||||
| 	return strings.Contains(http.DetectContentType(data), "text/") | ||||
| 	return strings.Contains(DetectContentType(data), "text/") | ||||
| } | ||||
|  | ||||
| // IsImageFile detects if data is an image format | ||||
| func IsImageFile(data []byte) bool { | ||||
| 	return strings.Contains(http.DetectContentType(data), "image/") | ||||
| 	return strings.Contains(DetectContentType(data), "image/") | ||||
| } | ||||
|  | ||||
| // IsSVGImageFile detects if data is an SVG image format | ||||
| func IsSVGImageFile(data []byte) bool { | ||||
| 	return strings.Contains(DetectContentType(data), SVGMimeType) | ||||
| } | ||||
|  | ||||
| // IsPDFFile detects if data is a pdf format | ||||
| func IsPDFFile(data []byte) bool { | ||||
| 	return strings.Contains(http.DetectContentType(data), "application/pdf") | ||||
| 	return strings.Contains(DetectContentType(data), "application/pdf") | ||||
| } | ||||
|  | ||||
| // IsVideoFile detects if data is an video format | ||||
| func IsVideoFile(data []byte) bool { | ||||
| 	return strings.Contains(http.DetectContentType(data), "video/") | ||||
| 	return strings.Contains(DetectContentType(data), "video/") | ||||
| } | ||||
|  | ||||
| // IsAudioFile detects if data is an video format | ||||
| func IsAudioFile(data []byte) bool { | ||||
| 	return strings.Contains(http.DetectContentType(data), "audio/") | ||||
| 	return strings.Contains(DetectContentType(data), "audio/") | ||||
| } | ||||
|  | ||||
| // EntryIcon returns the octicon class for displaying files/directories | ||||
|   | ||||
| @@ -183,11 +183,63 @@ func TestIsLetter(t *testing.T) { | ||||
| 	assert.False(t, IsLetter('$')) | ||||
| } | ||||
|  | ||||
| func TestDetectContentTypeLongerThanSniffLen(t *testing.T) { | ||||
| 	// Pre-condition: Shorter than sniffLen detects SVG. | ||||
| 	assert.Equal(t, "image/svg+xml", DetectContentType([]byte(`<!-- Comment --><svg></svg>`))) | ||||
| 	// Longer than sniffLen detects something else. | ||||
| 	assert.Equal(t, "text/plain; charset=utf-8", DetectContentType([]byte(`<!-- | ||||
| Comment Comment Comment Comment Comment Comment Comment Comment Comment Comment | ||||
| Comment Comment Comment Comment Comment Comment Comment Comment Comment Comment | ||||
| Comment Comment Comment Comment Comment Comment Comment Comment Comment Comment | ||||
| Comment Comment Comment Comment Comment Comment Comment Comment Comment Comment | ||||
| Comment Comment Comment Comment Comment Comment Comment Comment Comment Comment | ||||
| Comment Comment Comment Comment Comment Comment Comment Comment Comment Comment | ||||
| Comment Comment Comment --><svg></svg>`))) | ||||
| } | ||||
|  | ||||
| func TestIsTextFile(t *testing.T) { | ||||
| 	assert.True(t, IsTextFile([]byte{})) | ||||
| 	assert.True(t, IsTextFile([]byte("lorem ipsum"))) | ||||
| } | ||||
|  | ||||
| func TestIsSVGImageFile(t *testing.T) { | ||||
| 	assert.True(t, IsSVGImageFile([]byte("<svg></svg>"))) | ||||
| 	assert.True(t, IsSVGImageFile([]byte("    <svg></svg>"))) | ||||
| 	assert.True(t, IsSVGImageFile([]byte(`<svg width="100"></svg>`))) | ||||
| 	assert.True(t, IsSVGImageFile([]byte("<svg/>"))) | ||||
| 	assert.True(t, IsSVGImageFile([]byte(`<?xml version="1.0" encoding="UTF-8"?><svg></svg>`))) | ||||
| 	assert.True(t, IsSVGImageFile([]byte(`<!-- Comment --> | ||||
| 	<svg></svg>`))) | ||||
| 	assert.True(t, IsSVGImageFile([]byte(`<!-- Multiple --> | ||||
| 	<!-- Comments --> | ||||
| 	<svg></svg>`))) | ||||
| 	assert.True(t, IsSVGImageFile([]byte(`<!-- Multiline | ||||
| 	Comment --> | ||||
| 	<svg></svg>`))) | ||||
| 	assert.True(t, IsSVGImageFile([]byte(`<?xml version="1.0" encoding="UTF-8"?> | ||||
| 	<!-- Comment --> | ||||
| 	<svg></svg>`))) | ||||
| 	assert.True(t, IsSVGImageFile([]byte(`<?xml version="1.0" encoding="UTF-8"?> | ||||
| 	<!-- Multiple --> | ||||
| 	<!-- Comments --> | ||||
| 	<svg></svg>`))) | ||||
| 	assert.True(t, IsSVGImageFile([]byte(`<?xml version="1.0" encoding="UTF-8"?> | ||||
| 	<!-- Multline | ||||
| 	Comment --> | ||||
| 	<svg></svg>`))) | ||||
| 	assert.False(t, IsSVGImageFile([]byte{})) | ||||
| 	assert.False(t, IsSVGImageFile([]byte("svg"))) | ||||
| 	assert.False(t, IsSVGImageFile([]byte("<svgfoo></svgfoo>"))) | ||||
| 	assert.False(t, IsSVGImageFile([]byte("text<svg></svg>"))) | ||||
| 	assert.False(t, IsSVGImageFile([]byte("<html><body><svg></svg></body></html>"))) | ||||
| 	assert.False(t, IsSVGImageFile([]byte(`<script>"<svg></svg>"</script>`))) | ||||
| 	assert.False(t, IsSVGImageFile([]byte(`<!-- <svg></svg> inside comment --> | ||||
| 	<foo></foo>`))) | ||||
| 	assert.False(t, IsSVGImageFile([]byte(`<?xml version="1.0" encoding="UTF-8"?> | ||||
| 	<!-- <svg></svg> inside comment --> | ||||
| 	<foo></foo>`))) | ||||
| } | ||||
|  | ||||
| func TestFormatNumberSI(t *testing.T) { | ||||
| 	assert.Equal(t, "125", FormatNumberSI(int(125))) | ||||
| 	assert.Equal(t, "1.3k", FormatNumberSI(int64(1317))) | ||||
|   | ||||
| @@ -190,6 +190,10 @@ var ( | ||||
| 			EventSourceUpdateTime time.Duration | ||||
| 		} `ini:"ui.notification"` | ||||
|  | ||||
| 		SVG struct { | ||||
| 			Enabled bool `ini:"ENABLE_RENDER"` | ||||
| 		} `ini:"ui.svg"` | ||||
|  | ||||
| 		Admin struct { | ||||
| 			UserPagingNum   int | ||||
| 			RepoPagingNum   int | ||||
| @@ -230,6 +234,11 @@ var ( | ||||
| 			MaxTimeout:            60 * time.Second, | ||||
| 			EventSourceUpdateTime: 10 * time.Second, | ||||
| 		}, | ||||
| 		SVG: struct { | ||||
| 			Enabled bool `ini:"ENABLE_RENDER"` | ||||
| 		}{ | ||||
| 			Enabled: true, | ||||
| 		}, | ||||
| 		Admin: struct { | ||||
| 			UserPagingNum   int | ||||
| 			RepoPagingNum   int | ||||
|   | ||||
| @@ -823,6 +823,8 @@ tag = Tag | ||||
| released_this = released this | ||||
| file_raw = Raw | ||||
| file_history = History | ||||
| file_view_source = View Source | ||||
| file_view_rendered = View Rendered | ||||
| file_view_raw = View Raw | ||||
| file_permalink = Permalink | ||||
| file_too_large = The file is too large to be shown. | ||||
|   | ||||
| @@ -46,6 +46,11 @@ func ServeData(ctx *context.Context, name string, reader io.Reader) error { | ||||
| 	} else if base.IsImageFile(buf) || base.IsPDFFile(buf) { | ||||
| 		ctx.Resp.Header().Set("Content-Disposition", fmt.Sprintf(`inline; filename="%s"`, name)) | ||||
| 		ctx.Resp.Header().Set("Access-Control-Expose-Headers", "Content-Disposition") | ||||
| 		if base.IsSVGImageFile(buf) { | ||||
| 			ctx.Resp.Header().Set("Content-Security-Policy", "default-src 'none'; style-src 'unsafe-inline'; sandbox") | ||||
| 			ctx.Resp.Header().Set("X-Content-Type-Options", "nosniff") | ||||
| 			ctx.Resp.Header().Set("Content-Type", base.SVGMimeType) | ||||
| 		} | ||||
| 	} else { | ||||
| 		ctx.Resp.Header().Set("Content-Disposition", fmt.Sprintf(`attachment; filename="%s"`, name)) | ||||
| 		ctx.Resp.Header().Set("Access-Control-Expose-Headers", "Content-Disposition") | ||||
|   | ||||
| @@ -114,9 +114,9 @@ func editFile(ctx *context.Context, isNewFile bool) { | ||||
| 		n, _ := dataRc.Read(buf) | ||||
| 		buf = buf[:n] | ||||
|  | ||||
| 		// Only text file are editable online. | ||||
| 		if !base.IsTextFile(buf) { | ||||
| 			ctx.NotFound("base.IsTextFile", nil) | ||||
| 		// Only some file types are editable online as text. | ||||
| 		if !base.IsRepresentableAsText(buf) { | ||||
| 			ctx.NotFound("base.IsRepresentableAsText", nil) | ||||
| 			return | ||||
| 		} | ||||
|  | ||||
|   | ||||
| @@ -279,14 +279,19 @@ func LFSFileGet(ctx *context.Context) { | ||||
| 	} | ||||
| 	buf = buf[:n] | ||||
|  | ||||
| 	isTextFile := base.IsTextFile(buf) | ||||
| 	ctx.Data["IsTextFile"] = isTextFile | ||||
| 	ctx.Data["IsTextFile"] = base.IsTextFile(buf) | ||||
| 	isRepresentableAsText := base.IsRepresentableAsText(buf) | ||||
|  | ||||
| 	fileSize := meta.Size | ||||
| 	ctx.Data["FileSize"] = meta.Size | ||||
| 	ctx.Data["RawFileLink"] = fmt.Sprintf("%s%s.git/info/lfs/objects/%s/%s", setting.AppURL, ctx.Repo.Repository.FullName(), meta.Oid, "direct") | ||||
| 	switch { | ||||
| 	case isTextFile: | ||||
| 	case isRepresentableAsText: | ||||
| 		// This will be true for SVGs. | ||||
| 		if base.IsImageFile(buf) { | ||||
| 			ctx.Data["IsImageFile"] = true | ||||
| 		} | ||||
|  | ||||
| 		if fileSize >= setting.UI.MaxDisplayFileSize { | ||||
| 			ctx.Data["IsFileTooLarge"] = true | ||||
| 			break | ||||
|   | ||||
| @@ -396,6 +396,20 @@ func renderFile(ctx *context.Context, entry *git.TreeEntry, treeLink, rawLink st | ||||
| 	isLFSFile := false | ||||
| 	ctx.Data["IsTextFile"] = isTextFile | ||||
|  | ||||
| 	isDisplayingSource := ctx.Query("display") == "source" | ||||
| 	isDisplayingRendered := !isDisplayingSource | ||||
| 	isRepresentableAsText := base.IsRepresentableAsText(buf) | ||||
| 	ctx.Data["IsRepresentableAsText"] = isRepresentableAsText | ||||
| 	if !isRepresentableAsText { | ||||
| 		// If we can't show plain text, always try to render. | ||||
| 		isDisplayingSource = false | ||||
| 		isDisplayingRendered = true | ||||
| 	} | ||||
| 	ctx.Data["IsDisplayingSource"] = isDisplayingSource | ||||
| 	ctx.Data["IsDisplayingRendered"] = isDisplayingRendered | ||||
|  | ||||
| 	ctx.Data["IsTextSource"] = isTextFile || isDisplayingSource | ||||
|  | ||||
| 	//Check for LFS meta file | ||||
| 	if isTextFile && setting.LFS.StartServer { | ||||
| 		meta := lfs.IsPointerFile(&buf) | ||||
| @@ -451,12 +465,18 @@ func renderFile(ctx *context.Context, entry *git.TreeEntry, treeLink, rawLink st | ||||
| 	// Assume file is not editable first. | ||||
| 	if isLFSFile { | ||||
| 		ctx.Data["EditFileTooltip"] = ctx.Tr("repo.editor.cannot_edit_lfs_files") | ||||
| 	} else if !isTextFile { | ||||
| 	} else if !isRepresentableAsText { | ||||
| 		ctx.Data["EditFileTooltip"] = ctx.Tr("repo.editor.cannot_edit_non_text_files") | ||||
| 	} | ||||
|  | ||||
| 	switch { | ||||
| 	case isTextFile: | ||||
| 	case isRepresentableAsText: | ||||
| 		// This will be true for SVGs. | ||||
| 		if base.IsImageFile(buf) { | ||||
| 			ctx.Data["IsImageFile"] = true | ||||
| 			ctx.Data["HasSourceRenderedToggle"] = true | ||||
| 		} | ||||
|  | ||||
| 		if fileSize >= setting.UI.MaxDisplayFileSize { | ||||
| 			ctx.Data["IsFileTooLarge"] = true | ||||
| 			break | ||||
|   | ||||
| @@ -32,12 +32,18 @@ | ||||
| 		</div> | ||||
| 		{{if not .ReadmeInList}} | ||||
| 		<div class="file-header-right file-actions df ac"> | ||||
| 			{{if .HasSourceRenderedToggle}} | ||||
| 				<div class="ui compact icon buttons"> | ||||
| 					<a href="{{$.Link}}?display=source" class="ui tiny basic button poping up {{if .IsDisplayingSource}}active{{end}}" data-content="{{.i18n.Tr "repo.file_view_source"}}" data-position="bottom center" data-variation="tiny inverted">{{svg "octicon-code"}}</a> | ||||
| 					<a href="{{$.Link}}" class="ui tiny basic button poping up {{if .IsDisplayingRendered}}active{{end}}" data-content="{{.i18n.Tr "repo.file_view_rendered"}}" data-position="bottom center" data-variation="tiny inverted">{{svg "octicon-file"}}</a> | ||||
| 				</div> | ||||
| 			{{end}} | ||||
| 			<div class="ui buttons mr-2"> | ||||
| 				<a class="ui mini basic button" href="{{EscapePound $.RawFileLink}}">{{.i18n.Tr "repo.file_raw"}}</a> | ||||
| 				{{if not .IsViewCommit}} | ||||
| 					<a class="ui mini basic button" href="{{.RepoLink}}/src/commit/{{.CommitID}}/{{EscapePound .TreePath}}">{{.i18n.Tr "repo.file_permalink"}}</a> | ||||
| 				{{end}} | ||||
| 				{{if .IsTextFile}} | ||||
| 				{{if .IsRepresentableAsText}} | ||||
| 					<a class="ui mini basic button" href="{{.RepoLink}}/blame/{{EscapePound .BranchNameSubURL}}/{{EscapePound .TreePath}}">{{.i18n.Tr "repo.blame"}}</a> | ||||
| 				{{end}} | ||||
| 				<a class="ui mini basic button" href="{{.RepoLink}}/commits/{{EscapePound .BranchNameSubURL}}/{{EscapePound .TreePath}}">{{.i18n.Tr "repo.file_history"}}</a> | ||||
| @@ -58,12 +64,12 @@ | ||||
| 		{{end}} | ||||
| 	</h4> | ||||
| 	<div class="ui attached table unstackable segment"> | ||||
| 		<div class="file-view {{if .IsMarkup}}{{.MarkupType}} markdown{{else if .IsRenderedHTML}}plain-text{{else if .IsTextFile}}code-view{{end}}"> | ||||
| 		<div class="file-view {{if .IsMarkup}}{{.MarkupType}} markdown{{else if .IsRenderedHTML}}plain-text{{else if .IsTextSource}}code-view{{end}}"> | ||||
| 			{{if .IsMarkup}} | ||||
| 				{{if .FileContent}}{{.FileContent | Safe}}{{end}} | ||||
| 			{{else if .IsRenderedHTML}} | ||||
| 				<pre>{{if .FileContent}}{{.FileContent | Str2html}}{{end}}</pre> | ||||
| 			{{else if not .IsTextFile}} | ||||
| 			{{else if not .IsTextSource}} | ||||
| 				<div class="view-raw ui center"> | ||||
| 					{{if .IsImageFile}} | ||||
| 						<img src="{{EscapePound $.RawFileLink}}"> | ||||
|   | ||||
		Reference in New Issue
	
	Block a user