# SINCE 0.5.0 \r
web.allowZipDownloads = true\r
\r
+# Use Clippy (Flash solution) to provide a copy-to-clipboard button.\r
+# If false, a button with a more primitive JavaScript-based prompt box will\r
+# offer a 3-step (click, ctrl+c, enter) copy-to-clipboard alternative.\r
+#\r
+# SINCE 0.8.0\r
+web.allowFlashCopyToClipboard = true\r
+\r
# Default number of entries to include in RSS Syndication links\r
#\r
# SINCE 0.5.0\r
The following dependencies are bundled with Gitblit.\r
\r
- [Bootstrap](http://twitter.github.com/bootstrap) (Apache 2.0)\r
+- [Clippy](https://github.com/mojombo/clippy) (MIT)\r
- [google-code-prettify](http://code.google.com/p/google-code-prettify) (Apache 2.0)\r
- [Commons Daemon](http://commons.apache.org/daemon) (Apache 2.0)\r
- magnifying glass search icon courtesy of [Gnome](http://gnome.org) (Creative Commons CC-BY)\r
**New:** *web.timeFormat = HH:mm* \r
**New:** *web.datestampLongFormat = EEEE, MMMM d, yyyy* \r
- fixed: several a bugs in FileUserService related to cleaning up old repository permissions on a rename or delete\r
-- added: primitive technique for manual *copy to clipboard* of the primary repository url\r
+- added: optional flash-based 1-step *copy to clipboard* of the primary repository url\r
+- added: javascript-based 3-step (click, ctrl+c, enter) *copy to clipboard* of the primary repository url\r
+ **New:** *web.allowFlashCopyToClipboard = true* \r
- improved: empty repositories now link to the *empty repository* page which gives some direction to the user for the next step in using Gitblit. This page displays the primary push/clone url of the repository and gives sample syntax for the git command-line client. (issue 31)\r
- improved: unit testing framework has been migrated to JUnit4 syntax and the test suite has been redesigned to run all unit tests, including rpc, federation, and git push/clone tests\r
\r
--- /dev/null
+/*\r
+ * Licensed to the Apache Software Foundation (ASF) under one or more\r
+ * contributor license agreements. See the NOTICE file distributed with\r
+ * this work for additional information regarding copyright ownership.\r
+ * The ASF licenses this file to You under the Apache License, Version 2.0\r
+ * (the "License"); you may not use this file except in compliance with\r
+ * the License. You may obtain a copy of the License at\r
+ *\r
+ * http://www.apache.org/licenses/LICENSE-2.0\r
+ *\r
+ * Unless required by applicable law or agreed to in writing, software\r
+ * distributed under the License is distributed on an "AS IS" BASIS,\r
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
+ * See the License for the specific language governing permissions and\r
+ * limitations under the License.\r
+ */\r
+package com.gitblit.wicket.panels;\r
+\r
+import java.util.List;\r
+\r
+import org.apache.wicket.Component;\r
+import org.apache.wicket.ResourceReference;\r
+import org.apache.wicket.Response;\r
+import org.apache.wicket.markup.ComponentTag;\r
+import org.apache.wicket.markup.MarkupStream;\r
+import org.apache.wicket.markup.html.WebMarkupContainer;\r
+import org.apache.wicket.markup.html.panel.Fragment;\r
+import org.apache.wicket.protocol.http.ClientProperties;\r
+import org.apache.wicket.protocol.http.WebRequestCycle;\r
+import org.apache.wicket.protocol.http.WebSession;\r
+import org.apache.wicket.protocol.http.request.WebClientInfo;\r
+import org.apache.wicket.request.ClientInfo;\r
+import org.apache.wicket.util.value.IValueMap;\r
+\r
+import com.gitblit.wicket.WicketUtils;\r
+\r
+/**\r
+ * https://cwiki.apache.org/WICKET/object-container-adding-flash-to-a-wicket-application.html\r
+ */\r
+public abstract class ObjectContainer extends WebMarkupContainer {\r
+\r
+ private static final long serialVersionUID = 1L;\r
+\r
+ // Some general attributes for the object tag:\r
+ private static final String ATTRIBUTE_CONTENTTYPE = "type";\r
+ private static final String ATTRIBUTE_CLASSID = "classid";\r
+ private static final String ATTRIBUTE_CODEBASE = "codebase";\r
+\r
+ // This is used for browser specific adjustments\r
+ private ClientProperties clientProperties = null;\r
+\r
+ public ObjectContainer(String id) {\r
+ super(id);\r
+ }\r
+\r
+ // Set an attribute/property\r
+ public abstract void setValue(String name, String value);\r
+\r
+ // Get an attribute/property\r
+ public abstract String getValue(String name);\r
+\r
+ // Set the object's content type\r
+ protected abstract String getContentType();\r
+\r
+ // Set the object's clsid (for IE)\r
+ protected abstract String getClsid();\r
+\r
+ // Where to get the browser plugin (for IE)\r
+ protected abstract String getCodebase();\r
+\r
+ // Object's valid attribute names\r
+ protected abstract List<String> getAttributeNames();\r
+\r
+ // Object's valid parameter names\r
+ protected abstract List<String> getParameterNames();\r
+\r
+ // Utility function to get the URL for the object's data\r
+ protected String resolveResource(String src) {\r
+ // if it's an absolute path, return it:\r
+ if (src.startsWith("/") || src.startsWith("http://") || src.startsWith("https://"))\r
+ return (src);\r
+\r
+ // use the parent container class to resolve the resource reference\r
+ Component parent = getParent();\r
+ if (parent instanceof Fragment) {\r
+ // must check for fragment, otherwise we end up in Wicket namespace\r
+ parent = parent.getParent();\r
+ } \r
+ if (parent != null) {\r
+ ResourceReference resRef = new ResourceReference(parent.getClass(), src, false);\r
+ return (urlFor(resRef).toString());\r
+ }\r
+\r
+ return (src);\r
+ }\r
+\r
+ public void onComponentTag(ComponentTag tag) {\r
+ super.onComponentTag(tag);\r
+\r
+ // get the attributes from the html-source\r
+ IValueMap attributeMap = tag.getAttributes();\r
+\r
+ // set the content type\r
+ String contentType = getContentType();\r
+ if (contentType != null && !"".equals(contentType))\r
+ attributeMap.put(ATTRIBUTE_CONTENTTYPE, contentType);\r
+\r
+ // set clsid and codebase for IE\r
+ if (getClientProperties().isBrowserInternetExplorer()) {\r
+ String clsid = getClsid();\r
+ String codeBase = getCodebase();\r
+\r
+ if (clsid != null && !"".equals(clsid))\r
+ attributeMap.put(ATTRIBUTE_CLASSID, clsid);\r
+ if (codeBase != null && !"".equals(codeBase))\r
+ attributeMap.put(ATTRIBUTE_CODEBASE, codeBase);\r
+ }\r
+\r
+ // add all attributes\r
+ for (String name : getAttributeNames()) {\r
+ String value = getValue(name);\r
+ if (value != null)\r
+ attributeMap.put(name, value);\r
+ }\r
+ }\r
+\r
+ public void onComponentTagBody(MarkupStream markupStream, ComponentTag openTag) {\r
+ Response response = getResponse();\r
+ response.write("\n");\r
+\r
+ // add all object's parameters:\r
+ for (String name : getParameterNames()) {\r
+ String value = getValue(name);\r
+ if (value != null) {\r
+ response.write("<param name=\"");\r
+ response.write(name);\r
+ response.write("\" value=\"");\r
+ response.write(value);\r
+ response.write("\"/>\n");\r
+ }\r
+ }\r
+\r
+ super.onComponentTagBody(markupStream, openTag);\r
+ }\r
+\r
+ // shortcut to the client properties:\r
+ protected ClientProperties getClientProperties() {\r
+ if (clientProperties == null) {\r
+ ClientInfo clientInfo = WebSession.get().getClientInfo();\r
+\r
+ if (clientInfo == null || !(clientInfo instanceof WebClientInfo)) {\r
+ clientInfo = new WebClientInfo((WebRequestCycle) getRequestCycle());\r
+ WebSession.get().setClientInfo(clientInfo);\r
+ }\r
+\r
+ clientProperties = ((WebClientInfo) clientInfo).getProperties();\r
+ }\r
+ return (clientProperties);\r
+ }\r
+}
\ No newline at end of file
</wicket:head>\r
\r
<wicket:panel>\r
- <span wicket:id="repositoryUrl" style="color: blue;">[repository url]</span><span style="padding-left:5px;"><span class="btn" style="padding:0px 3px;vertical-align:middle;"><img wicket:id="copyIcon" style="padding-top:1px;"></img></span></span> \r
-</wicket:panel> \r
+ <span wicket:id="repositoryUrl" style="color: blue;">[repository url]</span><span wicket:id="copyFunction"></span>\r
+ \r
+ <!-- Plain JavaScript manual copy & paste -->\r
+ <wicket:fragment wicket:id="jsPanel">\r
+ <span class="btn" style="padding:0px 3px 0px 3px;vertical-align:middle;">\r
+ <img wicket:id="copyIcon" style="padding-top:1px;"></img>\r
+ </span>\r
+ </wicket:fragment>\r
+ \r
+ <!-- flash-based button-press copy & paste -->\r
+ <wicket:fragment wicket:id="clippyPanel">\r
+ <object style="padding:0px 2px;vertical-align:middle;"\r
+ wicket:id="clippy"\r
+ width="110" \r
+ height="14"\r
+ bgcolor="#ffffff" \r
+ quality="high"\r
+ wmode="transparent"\r
+ allowScriptAccess="always"></object>\r
+ </wicket:fragment>\r
+</wicket:panel>\r
</html>
\ No newline at end of file
\r
import org.apache.wicket.markup.html.basic.Label;\r
import org.apache.wicket.markup.html.image.ContextImage;\r
+import org.apache.wicket.markup.html.panel.Fragment;\r
\r
+import com.gitblit.GitBlit;\r
+import com.gitblit.Keys;\r
+import com.gitblit.utils.StringUtils;\r
import com.gitblit.wicket.WicketUtils;\r
\r
public class RepositoryUrlPanel extends BasePanel {\r
public RepositoryUrlPanel(String wicketId, String url) {\r
super(wicketId);\r
add(new Label("repositoryUrl", url));\r
- ContextImage img = WicketUtils.newImage("copyIcon", "clipboard_13x13.png");\r
- WicketUtils.setHtmlTooltip(img, "Manual Copy to Clipboard");\r
- img.add(new JavascriptTextPrompt("onclick", "Copy to Clipboard (Ctrl+C, Enter)", url));\r
- add(img);\r
+ if (GitBlit.getBoolean(Keys.web.allowFlashCopyToClipboard, true)) {\r
+ // clippy: flash-based copy & paste\r
+ Fragment fragment = new Fragment("copyFunction", "clippyPanel", this);\r
+ String baseUrl = WicketUtils.getGitblitURL(getRequest());\r
+ ShockWaveComponent clippy = new ShockWaveComponent("clippy", baseUrl + "/clippy.swf");\r
+ clippy.setValue("flashVars", "text=" + StringUtils.encodeURL(url));\r
+ fragment.add(clippy);\r
+ add(fragment);\r
+ } else {\r
+ // javascript: manual copy & paste with modal browser prompt dialog\r
+ Fragment fragment = new Fragment("copyFunction", "jsPanel", this);\r
+ ContextImage img = WicketUtils.newImage("copyIcon", "clipboard_13x13.png");\r
+ WicketUtils.setHtmlTooltip(img, "Manual Copy to Clipboard");\r
+ img.add(new JavascriptTextPrompt("onclick", "Copy to Clipboard (Ctrl+C, Enter)", url));\r
+ fragment.add(img);\r
+ add(fragment);\r
+ }\r
}\r
}\r
--- /dev/null
+/*\r
+ * Licensed to the Apache Software Foundation (ASF) under one or more\r
+ * contributor license agreements. See the NOTICE file distributed with\r
+ * this work for additional information regarding copyright ownership.\r
+ * The ASF licenses this file to You under the Apache License, Version 2.0\r
+ * (the "License"); you may not use this file except in compliance with\r
+ * the License. You may obtain a copy of the License at\r
+ *\r
+ * http://www.apache.org/licenses/LICENSE-2.0\r
+ *\r
+ * Unless required by applicable law or agreed to in writing, software\r
+ * distributed under the License is distributed on an "AS IS" BASIS,\r
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
+ * See the License for the specific language governing permissions and\r
+ * limitations under the License.\r
+ */\r
+package com.gitblit.wicket.panels;\r
+\r
+import java.util.ArrayList;\r
+import java.util.Arrays;\r
+import java.util.HashMap;\r
+import java.util.List;\r
+import java.util.Map;\r
+\r
+import org.apache.wicket.Response;\r
+import org.apache.wicket.markup.ComponentTag;\r
+import org.apache.wicket.markup.MarkupStream;\r
+import org.apache.wicket.util.value.IValueMap;\r
+\r
+/**\r
+ * https://cwiki.apache.org/WICKET/object-container-adding-flash-to-a-wicket-application.html\r
+ * \r
+ * @author Jan Kriesten\r
+ * @author manuelbarzi\r
+ * @author James Moger\r
+ * \r
+ */\r
+public class ShockWaveComponent extends ObjectContainer {\r
+ private static final long serialVersionUID = 1L;\r
+\r
+ private static final String CONTENTTYPE = "application/x-shockwave-flash";\r
+ private static final String CLSID = "clsid:D27CDB6E-AE6D-11cf-96B8-444553540000";\r
+ private static final String CODEBASE = "http://fpdownload.adobe.com/pub/shockwave/cabs/flash/swflash.cab#version=7,0,0,0";\r
+\r
+ // valid attributes\r
+ private static final List<String> attributeNames = Arrays.asList(new String[] { "classid",\r
+ "width", "height", "codebase", "align", "base", "data", "flashvars" });\r
+ // valid parameters\r
+ private static final List<String> parameterNames = Arrays.asList(new String[] { "devicefont",\r
+ "movie", "play", "loop", "quality", "bgcolor", "scale", "salign", "menu", "wmode",\r
+ "allowscriptaccess", "seamlesstabbing", "flashvars" });\r
+\r
+ // combined options (to iterate over them)\r
+ private static final List<String> optionNames = new ArrayList<String>(attributeNames.size()\r
+ + parameterNames.size());\r
+ static {\r
+ optionNames.addAll(attributeNames);\r
+ optionNames.addAll(parameterNames);\r
+ }\r
+\r
+ private Map<String, String> attributes;\r
+ private Map<String, String> parameters;\r
+\r
+ public ShockWaveComponent(String id) {\r
+ super(id);\r
+\r
+ attributes = new HashMap<String, String>();\r
+ parameters = new HashMap<String, String>();\r
+ }\r
+ \r
+ public ShockWaveComponent(String id, String movie) {\r
+ this(id);\r
+ setValue("movie", movie);\r
+ }\r
+\r
+ public ShockWaveComponent(String id, String movie, String width, String height) {\r
+ this(id);\r
+\r
+ setValue("movie", movie);\r
+ setValue("width", width);\r
+ setValue("height", height);\r
+ }\r
+\r
+ public void setValue(String name, String value) {\r
+ // IE and other browsers handle movie/data differently. So movie is used\r
+ // for IE, whereas\r
+ // data is used for all other browsers. The class uses movie parameter\r
+ // to handle url and\r
+ // puts the values to the maps depending on the browser information\r
+ String parameter = name.toLowerCase();\r
+ if ("data".equals(parameter))\r
+ parameter = "movie";\r
+\r
+ if ("movie".equals(parameter) && !getClientProperties().isBrowserInternetExplorer())\r
+ attributes.put("data", value);\r
+\r
+ if (attributeNames.contains(parameter))\r
+ attributes.put(parameter, value);\r
+ else if (parameterNames.contains(parameter))\r
+ parameters.put(parameter, value);\r
+ }\r
+\r
+ public String getValue(String name) {\r
+ String parameter = name.toLowerCase();\r
+ String value = null;\r
+\r
+ if ("data".equals(parameter)) {\r
+ if (getClientProperties().isBrowserInternetExplorer())\r
+ return null;\r
+ parameter = "movie";\r
+ }\r
+\r
+ if (attributeNames.contains(parameter))\r
+ value = attributes.get(parameter);\r
+ else if (parameterNames.contains(parameter))\r
+ value = parameters.get(parameter);\r
+\r
+ // special treatment of movie to resolve to the url\r
+ if (value != null && parameter.equals("movie"))\r
+ value = resolveResource(value);\r
+\r
+ return value;\r
+ }\r
+\r
+ public void onComponentTag(ComponentTag tag) {\r
+ // get options from the markup\r
+ IValueMap valueMap = tag.getAttributes();\r
+\r
+ // Iterate over valid options\r
+ for (String s : optionNames) {\r
+ if (valueMap.containsKey(s)) {\r
+ // if option isn't set programmatically, set value from markup\r
+ if (!attributes.containsKey(s) && !parameters.containsKey(s))\r
+ setValue(s, valueMap.getString(s));\r
+ // remove attribute - they are added in super.onComponentTag()\r
+ // to\r
+ // the right place as attribute or param\r
+ valueMap.remove(s);\r
+ }\r
+ }\r
+\r
+ super.onComponentTag(tag);\r
+ }\r
+\r
+ public void onComponentTagBody(MarkupStream markupStream, ComponentTag openTag) {\r
+\r
+ super.onComponentTagBody(markupStream, openTag);\r
+\r
+ Response response = getResponse();\r
+\r
+ // add all object's parameters in embed tag too:\r
+ response.write("<embed");\r
+ addParameter(response, "type", CONTENTTYPE);\r
+ for (String name : getParameterNames()) {\r
+ String value = getValue(name);\r
+ if (value != null) {\r
+ name = "movie".equals(name) ? "src" : name;\r
+ addParameter(response, name, value);\r
+ }\r
+ }\r
+ for (String name : getAttributeNames()) {\r
+ if ("width".equals(name) || "height".equals(name)) {\r
+ String value = getValue(name);\r
+ if (value != null) {\r
+ addParameter(response, name, value);\r
+ }\r
+ }\r
+ }\r
+ response.write(" />\n");\r
+\r
+ }\r
+\r
+ private void addParameter(Response response, String name, String value) {\r
+ response.write(" ");\r
+ response.write(name);\r
+ response.write("=\"");\r
+ response.write(value);\r
+ response.write("\"");\r
+ }\r
+\r
+ @Override\r
+ protected String getClsid() {\r
+ return CLSID;\r
+ }\r
+\r
+ @Override\r
+ protected String getCodebase() {\r
+ return CODEBASE;\r
+ }\r
+\r
+ @Override\r
+ protected String getContentType() {\r
+ return CONTENTTYPE;\r
+ }\r
+\r
+ @Override\r
+ protected List<String> getAttributeNames() {\r
+ return attributeNames;\r
+ }\r
+\r
+ @Override\r
+ protected List<String> getParameterNames() {\r
+ return parameterNames;\r
+ }\r
+}
\ No newline at end of file