Browse Source

Retrieve summary and metric charts over https (issue-61)

tags/v1.3.0
James Moger 10 years ago
parent
commit
cf2ef78a8a

+ 1
- 0
releases.moxie View File

@@ -36,6 +36,7 @@ r17: {
- Fixed submodule diff display
changes:
- Retrieve summary and metric graphs from Google over https (issue-61)
- Persist originRepository (for forks) in the repository config instead of relying on parsing origin urls which are susceptible to filesystem relocation (issue 190)
- Improved error logging for servlet containers which provide a null contextFolder (issue 199)
- Improve Gerrit change ref decoration in the refs panel (issue 206)

+ 633
- 0
src/main/java/com/gitblit/wicket/charting/SecureChart.java View File

@@ -0,0 +1,633 @@
/*
* Copyright 2007 Daniel Spiewak.
* Copyright 2013 gitblit.com.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.gitblit.wicket.charting;
import java.awt.Color;
import java.awt.Dimension;
import java.io.Serializable;
import java.lang.reflect.InvocationTargetException;
import java.util.concurrent.locks.ReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;
import org.apache.wicket.markup.ComponentTag;
import org.apache.wicket.markup.html.WebComponent;
import org.wicketstuff.googlecharts.ChartDataEncoding;
import org.wicketstuff.googlecharts.IChartAxis;
import org.wicketstuff.googlecharts.IChartData;
import org.wicketstuff.googlecharts.IChartFill;
import org.wicketstuff.googlecharts.IChartGrid;
import org.wicketstuff.googlecharts.IChartProvider;
import org.wicketstuff.googlecharts.IFillArea;
import org.wicketstuff.googlecharts.ILineStyle;
import org.wicketstuff.googlecharts.ILinearGradientFill;
import org.wicketstuff.googlecharts.ILinearStripesFill;
import org.wicketstuff.googlecharts.IRangeMarker;
import org.wicketstuff.googlecharts.IShapeMarker;
import org.wicketstuff.googlecharts.ISolidFill;
import org.wicketstuff.googlecharts.Range;
/**
* This is a fork of org.wicketstuff.googlecharts.Chart whose only purpose
* is to build https urls instead of http urls.
*
* @author Daniel Spiewak
* @author James Moger
*/
public class SecureChart extends WebComponent implements Serializable {
private static final long serialVersionUID = 6286305912682861488L;
private IChartProvider provider;
private StringBuilder url;
private final ReadWriteLock lock = new ReentrantReadWriteLock();
public SecureChart(String id, IChartProvider provider) {
super(id);
this.provider = provider;
}
public void invalidate() {
lock.writeLock().lock();
try {
url = null;
} finally {
lock.writeLock().unlock();
}
}
public CharSequence constructURL() {
lock.writeLock().lock();
try {
if (url != null) {
return url;
}
url = new StringBuilder("https://chart.googleapis.com/chart?");
addParameter(url, "chs", render(provider.getSize()));
addParameter(url, "chd", render(provider.getData()));
addParameter(url, "cht", render(provider.getType()));
addParameter(url, "chbh", render(provider.getBarWidth(), provider.getBarGroupSpacing()));
addParameter(url, "chtt", render(provider.getTitle()));
addParameter(url, "chdl", render(provider.getLegend()));
addParameter(url, "chco", render(provider.getColors()));
IChartFill bgFill = provider.getBackgroundFill();
IChartFill fgFill = provider.getChartFill();
StringBuilder fillParam = new StringBuilder();
if (bgFill != null) {
fillParam.append("bg,").append(render(bgFill));
}
if (fgFill != null) {
if (fillParam.length() > 0) {
fillParam.append('|');
}
fillParam.append("c,").append(render(fgFill));
}
if (fillParam.toString().trim().equals("")) {
fillParam = null;
}
addParameter(url, "chf", fillParam);
IChartAxis[] axes = provider.getAxes();
addParameter(url, "chxt", renderTypes(axes));
addParameter(url, "chxl", renderLabels(axes));
addParameter(url, "chxp", renderPositions(axes));
addParameter(url, "chxr", renderRanges(axes));
addParameter(url, "chxs", renderStyles(axes));
addParameter(url, "chg", render(provider.getGrid()));
addParameter(url, "chm", render(provider.getShapeMarkers()));
addParameter(url, "chm", render(provider.getRangeMarkers()));
addParameter(url, "chls", render(provider.getLineStyles()));
addParameter(url, "chm", render(provider.getFillAreas()));
addParameter(url, "chl", render(provider.getPieLabels()));
return url;
} finally {
lock.writeLock().unlock();
}
}
private void addParameter(StringBuilder url, CharSequence param, CharSequence value) {
if (value == null || value.length() == 0) {
return;
}
if (url.charAt(url.length() - 1) != '?') {
url.append('&');
}
url.append(param).append('=').append(value);
}
private CharSequence convert(ChartDataEncoding encoding, double value, double max) {
switch (encoding) {
case TEXT:
return SecureChartDataEncoding.TEXT.convert(value, max);
case EXTENDED:
return SecureChartDataEncoding.EXTENDED.convert(value, max);
case SIMPLE:
default:
return SecureChartDataEncoding.SIMPLE.convert(value, max);
}
}
private CharSequence render(Dimension dim) {
if (dim == null) {
return null;
}
return new StringBuilder().append(dim.width).append('x').append(dim.height);
}
private CharSequence render(IChartData data) {
if (data == null) {
return null;
}
ChartDataEncoding encoding = data.getEncoding();
StringBuilder back = new StringBuilder();
back.append(render(encoding)).append(':');
for (double[] set : data.getData()) {
if (set == null || set.length == 0) {
back.append(convert(encoding, -1, data.getMax()));
} else {
for (double value : set) {
back.append(convert(encoding, value, data.getMax())).append(encoding.getValueSeparator());
}
if (back.substring(back.length() - encoding.getValueSeparator().length(),
back.length()).equals(encoding.getValueSeparator())) {
back.setLength(back.length() - encoding.getValueSeparator().length());
}
}
back.append(encoding.getSetSeparator());
}
if (back.substring(back.length() - encoding.getSetSeparator().length(),
back.length()).equals(encoding.getSetSeparator())) {
back.setLength(back.length() - encoding.getSetSeparator().length());
}
return back;
}
private CharSequence render(Enum<?> value) {
if (value == null) {
return null;
}
try {
Object back = value.getClass().getMethod("getRendering").invoke(value);
if (back != null) {
return back.toString();
}
} catch (IllegalArgumentException e) {
} catch (SecurityException e) {
} catch (IllegalAccessException e) {
} catch (InvocationTargetException e) {
} catch (NoSuchMethodException e) {
}
return null;
}
private CharSequence render(int barWidth, int groupSpacing) {
if (barWidth == -1) {
return null;
}
StringBuilder back = new StringBuilder(barWidth);
if (groupSpacing >= 0) {
back.append(',').append(groupSpacing);
}
return back;
}
private CharSequence render(String[] values) {
if (values == null) {
return null;
}
StringBuilder back = new StringBuilder();
for (String value : values) {
CharSequence toRender = render(value);
if (toRender == null) {
toRender = "";
}
back.append(toRender).append('|');
}
if (back.length() > 0) {
back.setLength(back.length() - 1);
}
return back;
}
private CharSequence render(String value) {
if (value == null) {
return value;
}
StringBuilder back = new StringBuilder();
for (char c : value.toCharArray()) {
if (c == ' ') {
back.append('+');
} else {
back.append(c);
}
}
return back;
}
private CharSequence render(Color[] values) {
if (values == null) {
return null;
}
StringBuilder back = new StringBuilder();
for (Color value : values) {
CharSequence toRender = render(value);
if (toRender == null) {
toRender = "";
}
back.append(toRender).append(',');
}
if (back.length() > 0) {
back.setLength(back.length() - 1);
}
return back;
}
private CharSequence render(Color value) {
if (value == null) {
return null;
}
StringBuilder back = new StringBuilder();
{
String toPad = Integer.toHexString(value.getRed());
if (toPad.length() == 1) {
back.append(0);
}
back.append(toPad);
}
{
String toPad = Integer.toHexString(value.getGreen());
if (toPad.length() == 1) {
back.append(0);
}
back.append(toPad);
}
{
String toPad = Integer.toHexString(value.getBlue());
if (toPad.length() == 1) {
back.append(0);
}
back.append(toPad);
}
{
String toPad = Integer.toHexString(value.getAlpha());
if (toPad.length() == 1) {
back.append(0);
}
back.append(toPad);
}
return back;
}
private CharSequence render(IChartFill fill) {
if (fill == null) {
return null;
}
StringBuilder back = new StringBuilder();
if (fill instanceof ISolidFill) {
ISolidFill solidFill = (ISolidFill) fill;
back.append("s,");
back.append(render(solidFill.getColor()));
} else if (fill instanceof ILinearGradientFill) {
ILinearGradientFill gradientFill = (ILinearGradientFill) fill;
back.append("lg,").append(gradientFill.getAngle()).append(',');
Color[] colors = gradientFill.getColors();
double[] offsets = gradientFill.getOffsets();
for (int i = 0; i < colors.length; i++) {
back.append(render(colors[i])).append(',').append(offsets[i]).append(',');
}
back.setLength(back.length() - 1);
} else if (fill instanceof ILinearStripesFill) {
ILinearStripesFill stripesFill = (ILinearStripesFill) fill;
back.append("ls,").append(stripesFill.getAngle()).append(',');
Color[] colors = stripesFill.getColors();
double[] widths = stripesFill.getWidths();
for (int i = 0; i < colors.length; i++) {
back.append(render(colors[i])).append(',').append(widths[i]).append(',');
}
back.setLength(back.length() - 1);
} else {
return null;
}
return back;
}
private CharSequence renderTypes(IChartAxis[] axes) {
if (axes == null) {
return null;
}
StringBuilder back = new StringBuilder();
for (IChartAxis axis : axes) {
back.append(render(axis.getType())).append(',');
}
if (back.length() > 0) {
back.setLength(back.length() - 1);
}
return back;
}
private CharSequence renderLabels(IChartAxis[] axes) {
if (axes == null) {
return null;
}
StringBuilder back = new StringBuilder();
for (int i = 0; i < axes.length; i++) {
if (axes[i] == null || axes[i].getLabels() == null) {
continue;
}
back.append(i).append(":|");
for (String label : axes[i].getLabels()) {
if (label == null) {
back.append('|');
continue;
}
back.append(render(label)).append('|');
}
if (i == axes.length - 1) {
back.setLength(back.length() - 1);
}
}
return back;
}
private CharSequence renderPositions(IChartAxis[] axes) {
if (axes == null) {
return null;
}
StringBuilder back = new StringBuilder();
for (int i = 0; i < axes.length; i++) {
if (axes[i] == null || axes[i].getPositions() == null) {
continue;
}
back.append(i).append(',');
for (double position : axes[i].getPositions()) {
back.append(position).append(',');
}
back.setLength(back.length() - 1);
back.append('|');
}
if (back.length() > 0) {
back.setLength(back.length() - 1);
}
return back;
}
private CharSequence renderRanges(IChartAxis[] axes) {
if (axes == null) {
return null;
}
StringBuilder back = new StringBuilder();
for (int i = 0; i < axes.length; i++) {
if (axes[i] == null || axes[i].getRange() == null) {
continue;
}
back.append(i).append(',');
Range range = axes[i].getRange();
back.append(range.getStart()).append(',').append(range.getEnd()).append('|');
}
if (back.length() > 0) {
back.setLength(back.length() - 1);
}
return back;
}
private CharSequence renderStyles(IChartAxis[] axes) {
if (axes == null) {
return null;
}
StringBuilder back = new StringBuilder();
for (int i = 0; i < axes.length; i++) {
if (axes[i] == null || axes[i].getColor() == null
|| axes[i].getFontSize() < 0 || axes[i].getAlignment() == null) {
continue;
}
back.append(i).append(',');
back.append(render(axes[i].getColor())).append(',');
back.append(axes[i].getFontSize()).append(',');
back.append(render(axes[i].getAlignment())).append('|');
}
if (back.length() > 0) {
back.setLength(back.length() - 1);
}
return back;
}
private CharSequence render(IChartGrid grid) {
if (grid == null) {
return null;
}
StringBuilder back = new StringBuilder();
back.append(grid.getXStepSize()).append(',');
back.append(grid.getYStepSize());
if (grid.getSegmentLength() >= 0) {
back.append(',').append(grid.getSegmentLength());
back.append(',').append(grid.getBlankLength());
}
return back;
}
private CharSequence render(IShapeMarker[] markers) {
if (markers == null) {
return null;
}
StringBuilder back = new StringBuilder();
for (IShapeMarker marker : markers) {
back.append(render(marker.getType())).append(',');
back.append(render(marker.getColor())).append(',');
back.append(marker.getIndex()).append(',');
back.append(marker.getPoint()).append(',');
back.append(marker.getSize()).append('|');
}
if (back.length() > 0) {
back.setLength(back.length() - 1);
}
return back;
}
private CharSequence render(IRangeMarker[] markers) {
if (markers == null) {
return null;
}
StringBuilder back = new StringBuilder();
for (IRangeMarker marker : markers) {
back.append(render(marker.getType())).append(',');
back.append(render(marker.getColor())).append(',');
back.append(0).append(',');
back.append(marker.getStart()).append(',');
back.append(marker.getEnd()).append('|');
}
if (back.length() > 0) {
back.setLength(back.length() - 1);
}
return back;
}
private CharSequence render(IFillArea[] areas) {
if (areas == null) {
return null;
}
StringBuilder back = new StringBuilder();
for (IFillArea area : areas) {
back.append(render(area.getType())).append(',');
back.append(render(area.getColor())).append(',');
back.append(area.getStartIndex()).append(',');
back.append(area.getEndIndex()).append(',');
back.append(0).append('|');
}
if (back.length() > 0) {
back.setLength(back.length() - 1);
}
return back;
}
private CharSequence render(ILineStyle[] styles) {
if (styles == null) {
return null;
}
StringBuilder back = new StringBuilder();
for (ILineStyle style : styles) {
if (style == null) {
back.append('|');
continue;
}
back.append(style.getThickness()).append(',');
back.append(style.getSegmentLength()).append(',');
back.append(style.getBlankLength()).append('|');
}
if (back.length() > 0) {
back.setLength(back.length() - 1);
}
return back;
}
@Override
protected void onComponentTag(ComponentTag tag) {
checkComponentTag(tag, "img");
super.onComponentTag(tag);
tag.put("src", constructURL());
}
}

+ 99
- 0
src/main/java/com/gitblit/wicket/charting/SecureChartDataEncoding.java View File

@@ -0,0 +1,99 @@
/*
* Copyright 2007 Daniel Spiewak.
* Copyright 2013 gitblit.com.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.gitblit.wicket.charting;
/**
* This class is a pristine fork of org.wicketstuff.googlecharts.ChartDataEncoding
* to bring the package-protected convert methods to SecureChart.
*
* @author Daniel Spiewak
*/
public enum SecureChartDataEncoding {
SIMPLE("s", "", ",") {
CharSequence convert(double value, double max) {
if (value < 0) {
return "_";
}
value = Math.round((CHARS.length() - 1) * value / max);
if (value > CHARS.length() - 1) {
throw new IllegalArgumentException(value + " is out of range for SIMPLE encoding");
}
return Character.toString(CHARS.charAt((int) value));
}
},
TEXT("t", ",", "|") {
CharSequence convert(double value, double max) {
if (value < 0) {
value = -1;
}
if (value > 100) {
throw new IllegalArgumentException(value + " is out of range for TEXT encoding");
}
return Double.toString(value);
}
},
EXTENDED("e", "", ",") {
CharSequence convert(double value, double max) {
if (value < 0) {
return "__";
}
value = Math.round(value);
if (value > (EXT_CHARS.length() - 1) * (EXT_CHARS.length() - 1)) {
throw new IllegalArgumentException(value + " is out of range for EXTENDED encoding");
}
int rem = (int) (value % EXT_CHARS.length());
int exp = (int) (value / EXT_CHARS.length());
return new StringBuilder().append(EXT_CHARS.charAt(exp)).append(EXT_CHARS.charAt(rem));
}
};
private static final String CHARS = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
private static final String EXT_CHARS = CHARS + "-_.";
private final String rendering, valueSeparator, setSeparator;
private SecureChartDataEncoding(String rendering, String valueSeparator, String setSeparator) {
this.rendering = rendering;
this.valueSeparator = valueSeparator;
this.setSeparator = setSeparator;
}
public String getRendering() {
return rendering;
}
public String getValueSeparator() {
return valueSeparator;
}
public String getSetSeparator() {
return setSeparator;
}
abstract CharSequence convert(double value, double max);
}

+ 4
- 4
src/main/java/com/gitblit/wicket/pages/MetricsPage.java View File

@@ -28,7 +28,6 @@ import java.util.List;
import org.apache.wicket.PageParameters;
import org.apache.wicket.markup.html.basic.Label;
import org.eclipse.jgit.lib.Repository;
import org.wicketstuff.googlecharts.Chart;
import org.wicketstuff.googlecharts.ChartAxis;
import org.wicketstuff.googlecharts.ChartAxisType;
import org.wicketstuff.googlecharts.ChartProvider;
@@ -42,6 +41,7 @@ import com.gitblit.models.Metric;
import com.gitblit.utils.MetricUtils;
import com.gitblit.utils.StringUtils;
import com.gitblit.wicket.WicketUtils;
import com.gitblit.wicket.charting.SecureChart;
public class MetricsPage extends RepositoryPage {
@@ -87,7 +87,7 @@ public class MetricsPage extends RepositoryPage {
provider.setLineStyles(new LineStyle[] { new LineStyle(2, 4, 0), new LineStyle(0, 4, 1) });
provider.addShapeMarker(new ShapeMarker(MarkerType.CIRCLE, Color.decode("#002060"), 1, -1, 5));
add(new Chart(wicketId, provider));
add(new SecureChart(wicketId, provider));
} else {
add(WicketUtils.newBlankImage(wicketId));
}
@@ -112,7 +112,7 @@ public class MetricsPage extends RepositoryPage {
String.valueOf((int) WicketUtils.maxValue(metrics)) });
provider.addAxis(commitAxis);
add(new Chart(wicketId, provider));
add(new SecureChart(wicketId, provider));
} else {
add(WicketUtils.newBlankImage(wicketId));
}
@@ -127,7 +127,7 @@ public class MetricsPage extends RepositoryPage {
}
ChartProvider provider = new ChartProvider(new Dimension(800, 200), ChartType.PIE, data);
provider.setPieLabels(labels.toArray(new String[labels.size()]));
add(new Chart(wicketId, provider));
add(new SecureChart(wicketId, provider));
} else {
add(WicketUtils.newBlankImage(wicketId));
}

+ 2
- 2
src/main/java/com/gitblit/wicket/pages/SummaryPage.java View File

@@ -32,7 +32,6 @@ import org.apache.wicket.markup.repeater.data.DataView;
import org.apache.wicket.markup.repeater.data.ListDataProvider;
import org.eclipse.jgit.lib.Repository;
import org.eclipse.jgit.revwalk.RevCommit;
import org.wicketstuff.googlecharts.Chart;
import org.wicketstuff.googlecharts.ChartAxis;
import org.wicketstuff.googlecharts.ChartAxisType;
import org.wicketstuff.googlecharts.ChartProvider;
@@ -53,6 +52,7 @@ import com.gitblit.utils.MarkdownUtils;
import com.gitblit.utils.StringUtils;
import com.gitblit.wicket.GitBlitWebSession;
import com.gitblit.wicket.WicketUtils;
import com.gitblit.wicket.charting.SecureChart;
import com.gitblit.wicket.panels.BranchesPanel;
import com.gitblit.wicket.panels.LinkPanel;
import com.gitblit.wicket.panels.LogPanel;
@@ -204,7 +204,7 @@ public class SummaryPage extends RepositoryPage {
provider.setLineStyles(new LineStyle[] { new LineStyle(2, 4, 0), new LineStyle(0, 4, 1) });
provider.addShapeMarker(new ShapeMarker(MarkerType.CIRCLE, Color.decode("#002060"), 1, -1, 5));
add(new Chart("commitsChart", provider));
add(new SecureChart("commitsChart", provider));
} else {
add(WicketUtils.newBlankImage("commitsChart"));
}

Loading…
Cancel
Save