4034 |
29 Jul 16 |
nicklas |
1 |
package net.sf.basedb.reggie.plugins; |
4034 |
29 Jul 16 |
nicklas |
2 |
|
4141 |
30 Sep 16 |
nicklas |
3 |
import java.util.ArrayList; |
4034 |
29 Jul 16 |
nicklas |
4 |
import java.util.Arrays; |
4034 |
29 Jul 16 |
nicklas |
5 |
import java.util.Collection; |
4034 |
29 Jul 16 |
nicklas |
6 |
import java.util.EnumSet; |
4034 |
29 Jul 16 |
nicklas |
7 |
import java.util.HashSet; |
4141 |
30 Sep 16 |
nicklas |
8 |
import java.util.List; |
4034 |
29 Jul 16 |
nicklas |
9 |
import java.util.Set; |
4034 |
29 Jul 16 |
nicklas |
10 |
|
4141 |
30 Sep 16 |
nicklas |
11 |
import net.sf.basedb.core.BioSource; |
4034 |
29 Jul 16 |
nicklas |
12 |
import net.sf.basedb.core.DbControl; |
4141 |
30 Sep 16 |
nicklas |
13 |
import net.sf.basedb.core.Directory; |
4141 |
30 Sep 16 |
nicklas |
14 |
import net.sf.basedb.core.File; |
4034 |
29 Jul 16 |
nicklas |
15 |
import net.sf.basedb.core.Group; |
4034 |
29 Jul 16 |
nicklas |
16 |
import net.sf.basedb.core.Item; |
4141 |
30 Sep 16 |
nicklas |
17 |
import net.sf.basedb.core.ItemSubtype; |
4034 |
29 Jul 16 |
nicklas |
18 |
import net.sf.basedb.core.Job; |
4034 |
29 Jul 16 |
nicklas |
19 |
import net.sf.basedb.core.MultiPermissions; |
4141 |
30 Sep 16 |
nicklas |
20 |
import net.sf.basedb.core.Nameable; |
4141 |
30 Sep 16 |
nicklas |
21 |
import net.sf.basedb.core.Path; |
4034 |
29 Jul 16 |
nicklas |
22 |
import net.sf.basedb.core.Permission; |
4034 |
29 Jul 16 |
nicklas |
23 |
import net.sf.basedb.core.PluginDefinition; |
4034 |
29 Jul 16 |
nicklas |
24 |
import net.sf.basedb.core.PluginExecutionRequest; |
4034 |
29 Jul 16 |
nicklas |
25 |
import net.sf.basedb.core.ProgressReporter; |
4141 |
30 Sep 16 |
nicklas |
26 |
import net.sf.basedb.core.Sample; |
4034 |
29 Jul 16 |
nicklas |
27 |
import net.sf.basedb.core.SharedItem; |
4141 |
30 Sep 16 |
nicklas |
28 |
import net.sf.basedb.core.Subtypable; |
4034 |
29 Jul 16 |
nicklas |
29 |
import net.sf.basedb.core.plugin.AbstractPlugin; |
4034 |
29 Jul 16 |
nicklas |
30 |
import net.sf.basedb.core.plugin.Permissions; |
4034 |
29 Jul 16 |
nicklas |
31 |
import net.sf.basedb.core.plugin.Request; |
4034 |
29 Jul 16 |
nicklas |
32 |
import net.sf.basedb.core.plugin.Response; |
4141 |
30 Sep 16 |
nicklas |
33 |
import net.sf.basedb.reggie.Reggie; |
4034 |
29 Jul 16 |
nicklas |
34 |
import net.sf.basedb.reggie.Site; |
4141 |
30 Sep 16 |
nicklas |
35 |
import net.sf.basedb.reggie.dao.Annotationtype; |
4034 |
29 Jul 16 |
nicklas |
36 |
import net.sf.basedb.reggie.dao.ReggieRole; |
4141 |
30 Sep 16 |
nicklas |
37 |
import net.sf.basedb.reggie.dao.Subtype; |
4034 |
29 Jul 16 |
nicklas |
38 |
|
4034 |
29 Jul 16 |
nicklas |
39 |
/** |
4034 |
29 Jul 16 |
nicklas |
Plug-in for setting permissions on items that should be published to |
4034 |
29 Jul 16 |
nicklas |
sites. By using a plug-in we can get elevated permissions |
4034 |
29 Jul 16 |
nicklas |
for sharing items to the site group, even though the logged |
4034 |
29 Jul 16 |
nicklas |
in user doesn't have permission to share items. |
4034 |
29 Jul 16 |
nicklas |
44 |
|
4034 |
29 Jul 16 |
nicklas |
This plug-in is not an interactable plug-in. The plug-in must be |
4034 |
29 Jul 16 |
nicklas |
used programmatically by some other code. To get access to the elevated |
4034 |
29 Jul 16 |
nicklas |
permissions it must be invoked via the BASE job API (eg. Job.execute() and |
4034 |
29 Jul 16 |
nicklas |
PluginExecutionRequest.invoke() methods). |
4034 |
29 Jul 16 |
nicklas |
49 |
|
4141 |
30 Sep 16 |
nicklas |
Parameters for the plug-in must be set on the PluginExecutionRequest |
4034 |
29 Jul 16 |
nicklas |
instance: |
4034 |
29 Jul 16 |
nicklas |
52 |
|
4141 |
30 Sep 16 |
nicklas |
* items: A Set with items that should be shared or unshared |
4141 |
30 Sep 16 |
nicklas |
Depdending on the type of item and the information |
4141 |
30 Sep 16 |
nicklas |
stored in it this plug-in will automatically determine |
4141 |
30 Sep 16 |
nicklas |
what should happen (if anything). |
4034 |
29 Jul 16 |
nicklas |
57 |
|
4141 |
30 Sep 16 |
nicklas |
Supported items are: |
4141 |
30 Sep 16 |
nicklas |
59 |
|
4141 |
30 Sep 16 |
nicklas |
Specimen (Sample): The YellowLabel annotation is checked. If it is set |
4141 |
30 Sep 16 |
nicklas |
the specimen, case and patient is shared to the site with READ permission. |
4141 |
30 Sep 16 |
nicklas |
If the annotation is not set the specimen is unshared from the site. The |
4141 |
30 Sep 16 |
nicklas |
sharing is not modified on the case or patient, since it may be needed |
4141 |
30 Sep 16 |
nicklas |
for other specimen tubes. |
4141 |
30 Sep 16 |
nicklas |
65 |
|
4034 |
29 Jul 16 |
nicklas |
66 |
*/ |
4034 |
29 Jul 16 |
nicklas |
67 |
public class SetPermissionsForDeliveryPlugin |
4034 |
29 Jul 16 |
nicklas |
68 |
extends AbstractPlugin |
4034 |
29 Jul 16 |
nicklas |
69 |
{ |
4034 |
29 Jul 16 |
nicklas |
70 |
|
4034 |
29 Jul 16 |
nicklas |
71 |
/** |
4034 |
29 Jul 16 |
nicklas |
Prepare a plug-in execution request for the given action. Other parameters |
4034 |
29 Jul 16 |
nicklas |
need to be set before calling PluginExecutionRequest.invoke(). See |
4034 |
29 Jul 16 |
nicklas |
class description. |
4034 |
29 Jul 16 |
nicklas |
75 |
*/ |
4034 |
29 Jul 16 |
nicklas |
76 |
public static final PluginExecutionRequest createExecutionRequest(DbControl dc, String action) |
4034 |
29 Jul 16 |
nicklas |
77 |
{ |
4034 |
29 Jul 16 |
nicklas |
78 |
PluginDefinition setPermissionPlugin = PluginDefinition.getByClassName(dc, SetPermissionsForDeliveryPlugin.class.getName()); |
4034 |
29 Jul 16 |
nicklas |
79 |
Job job = Job.getNew(dc, setPermissionPlugin, null, null); |
4034 |
29 Jul 16 |
nicklas |
80 |
job.setSendMessage(false); |
4034 |
29 Jul 16 |
nicklas |
81 |
job.setPrepared(null, null); |
4034 |
29 Jul 16 |
nicklas |
82 |
job.setName(action); |
4034 |
29 Jul 16 |
nicklas |
83 |
|
4034 |
29 Jul 16 |
nicklas |
84 |
PluginExecutionRequest request = job.execute(null, null); |
4034 |
29 Jul 16 |
nicklas |
85 |
return request; |
4034 |
29 Jul 16 |
nicklas |
86 |
} |
4034 |
29 Jul 16 |
nicklas |
87 |
|
4034 |
29 Jul 16 |
nicklas |
88 |
|
4034 |
29 Jul 16 |
nicklas |
89 |
private static final Set<Permissions> permissions = new HashSet<>(); |
4034 |
29 Jul 16 |
nicklas |
90 |
|
4034 |
29 Jul 16 |
nicklas |
91 |
@Override |
4034 |
29 Jul 16 |
nicklas |
92 |
public MainType getMainType() |
4034 |
29 Jul 16 |
nicklas |
93 |
{ |
4034 |
29 Jul 16 |
nicklas |
94 |
return MainType.OTHER; |
4034 |
29 Jul 16 |
nicklas |
95 |
} |
4034 |
29 Jul 16 |
nicklas |
96 |
|
4034 |
29 Jul 16 |
nicklas |
97 |
@Override |
4034 |
29 Jul 16 |
nicklas |
98 |
public boolean supportsConfigurations() |
4034 |
29 Jul 16 |
nicklas |
99 |
{ |
4034 |
29 Jul 16 |
nicklas |
100 |
return false; |
4034 |
29 Jul 16 |
nicklas |
101 |
} |
4034 |
29 Jul 16 |
nicklas |
102 |
|
4034 |
29 Jul 16 |
nicklas |
103 |
@Override |
4034 |
29 Jul 16 |
nicklas |
104 |
public boolean requiresConfiguration() |
4034 |
29 Jul 16 |
nicklas |
105 |
{ |
4034 |
29 Jul 16 |
nicklas |
106 |
return false; |
4034 |
29 Jul 16 |
nicklas |
107 |
} |
4034 |
29 Jul 16 |
nicklas |
108 |
|
4034 |
29 Jul 16 |
nicklas |
109 |
@Override |
4034 |
29 Jul 16 |
nicklas |
110 |
public Collection<Permissions> getPermissions() |
4034 |
29 Jul 16 |
nicklas |
111 |
{ |
4034 |
29 Jul 16 |
nicklas |
112 |
|
4034 |
29 Jul 16 |
nicklas |
113 |
if (permissions.isEmpty()) |
4034 |
29 Jul 16 |
nicklas |
114 |
{ |
4034 |
29 Jul 16 |
nicklas |
115 |
Set<Permission> grantSetPermission = EnumSet.of(Permission.SET_PERMISSION); |
4141 |
30 Sep 16 |
nicklas |
116 |
Set<Permission> readPermission = EnumSet.of(Permission.READ); |
4141 |
30 Sep 16 |
nicklas |
117 |
|
4034 |
29 Jul 16 |
nicklas |
// Items the we may need to set permission on |
4034 |
29 Jul 16 |
nicklas |
119 |
permissions.add(new Permissions(Item.BIOSOURCE, grantSetPermission, null)); |
4034 |
29 Jul 16 |
nicklas |
120 |
permissions.add(new Permissions(Item.SAMPLE, grantSetPermission, null)); |
4034 |
29 Jul 16 |
nicklas |
121 |
permissions.add(new Permissions(Item.EXTRACT, grantSetPermission, null)); |
4034 |
29 Jul 16 |
nicklas |
122 |
permissions.add(new Permissions(Item.PHYSICALBIOASSAY, grantSetPermission, null)); |
4034 |
29 Jul 16 |
nicklas |
123 |
permissions.add(new Permissions(Item.DERIVEDBIOASSAY, grantSetPermission, null)); |
4034 |
29 Jul 16 |
nicklas |
124 |
permissions.add(new Permissions(Item.RAWBIOASSAY, grantSetPermission, null)); |
4034 |
29 Jul 16 |
nicklas |
125 |
permissions.add(new Permissions(Item.FILE, grantSetPermission, null)); |
4034 |
29 Jul 16 |
nicklas |
126 |
permissions.add(new Permissions(Item.DIRECTORY, grantSetPermission, null)); |
4034 |
29 Jul 16 |
nicklas |
127 |
|
4034 |
29 Jul 16 |
nicklas |
// To be able to access the site groups |
4141 |
30 Sep 16 |
nicklas |
129 |
permissions.add(new Permissions(Item.GROUP, readPermission, null)); |
4141 |
30 Sep 16 |
nicklas |
130 |
|
4141 |
30 Sep 16 |
nicklas |
// To be able to access other information |
4141 |
30 Sep 16 |
nicklas |
132 |
permissions.add(new Permissions(Item.ITEMSUBTYPE, readPermission, null)); |
4141 |
30 Sep 16 |
nicklas |
133 |
permissions.add(new Permissions(Item.ANNOTATIONTYPE, null, readPermission)); |
4034 |
29 Jul 16 |
nicklas |
134 |
} |
4034 |
29 Jul 16 |
nicklas |
135 |
|
4034 |
29 Jul 16 |
nicklas |
136 |
return permissions; |
4034 |
29 Jul 16 |
nicklas |
137 |
} |
4034 |
29 Jul 16 |
nicklas |
138 |
|
4034 |
29 Jul 16 |
nicklas |
139 |
|
4034 |
29 Jul 16 |
nicklas |
140 |
@SuppressWarnings("unchecked") |
4034 |
29 Jul 16 |
nicklas |
141 |
@Override |
4034 |
29 Jul 16 |
nicklas |
142 |
public void run(Request request, Response response, ProgressReporter progress) |
4034 |
29 Jul 16 |
nicklas |
143 |
{ |
4034 |
29 Jul 16 |
nicklas |
144 |
|
4034 |
29 Jul 16 |
nicklas |
145 |
DbControl dc = sc.newDbControl(); |
4034 |
29 Jul 16 |
nicklas |
146 |
try |
4034 |
29 Jul 16 |
nicklas |
147 |
{ |
4141 |
30 Sep 16 |
nicklas |
148 |
Set<SharedItem> items = (Set<SharedItem>)request.getParameterValue("items"); |
4034 |
29 Jul 16 |
nicklas |
149 |
int numItems = 0; |
4034 |
29 Jul 16 |
nicklas |
150 |
|
4141 |
30 Sep 16 |
nicklas |
151 |
ItemSubtype specimenType = Subtype.SPECIMEN.load(dc); |
4141 |
30 Sep 16 |
nicklas |
152 |
|
4141 |
30 Sep 16 |
nicklas |
153 |
for (SharedItem i : items) |
4034 |
29 Jul 16 |
nicklas |
154 |
{ |
4141 |
30 Sep 16 |
nicklas |
// Reload in current transaction |
4141 |
30 Sep 16 |
nicklas |
156 |
i = (SharedItem)i.getType().getById(dc, i.getId()); |
4034 |
29 Jul 16 |
nicklas |
157 |
|
4141 |
30 Sep 16 |
nicklas |
158 |
if (i instanceof File) |
4034 |
29 Jul 16 |
nicklas |
159 |
{ |
4141 |
30 Sep 16 |
nicklas |
160 |
ensurePdfIsPublishedToSite(dc, (File)i); |
4034 |
29 Jul 16 |
nicklas |
161 |
} |
4141 |
30 Sep 16 |
nicklas |
162 |
else |
4034 |
29 Jul 16 |
nicklas |
163 |
{ |
4141 |
30 Sep 16 |
nicklas |
164 |
Site site = Site.findByCaseName(((Nameable)i).getName()); |
4141 |
30 Sep 16 |
nicklas |
165 |
if (site == Site.UNKNOWN) continue; // Do nothing with unknown sites |
4141 |
30 Sep 16 |
nicklas |
166 |
|
4141 |
30 Sep 16 |
nicklas |
167 |
ItemSubtype subtype = i instanceof Subtypable ? ((Subtypable)i).getItemSubtype() : null; |
4141 |
30 Sep 16 |
nicklas |
168 |
if (specimenType.equals(subtype)) |
4141 |
30 Sep 16 |
nicklas |
169 |
{ |
4141 |
30 Sep 16 |
nicklas |
// This is a specimen item |
4141 |
30 Sep 16 |
nicklas |
171 |
ensureSpecimenIsPublishedToSite(dc, (Sample)i, site); |
4141 |
30 Sep 16 |
nicklas |
172 |
numItems++; |
4141 |
30 Sep 16 |
nicklas |
173 |
} |
4034 |
29 Jul 16 |
nicklas |
174 |
} |
4034 |
29 Jul 16 |
nicklas |
175 |
} |
4034 |
29 Jul 16 |
nicklas |
176 |
|
4034 |
29 Jul 16 |
nicklas |
177 |
dc.commit(); |
4141 |
30 Sep 16 |
nicklas |
178 |
response.setDone(numItems + " items updated"); |
4034 |
29 Jul 16 |
nicklas |
179 |
} |
4034 |
29 Jul 16 |
nicklas |
180 |
catch (Throwable t) |
4034 |
29 Jul 16 |
nicklas |
181 |
{ |
4034 |
29 Jul 16 |
nicklas |
182 |
t.printStackTrace(System.out); |
4034 |
29 Jul 16 |
nicklas |
183 |
response.setError(t.getMessage(), Arrays.asList(t)); |
4034 |
29 Jul 16 |
nicklas |
184 |
} |
4034 |
29 Jul 16 |
nicklas |
185 |
finally |
4034 |
29 Jul 16 |
nicklas |
186 |
{ |
4034 |
29 Jul 16 |
nicklas |
187 |
if (dc != null) dc.close(); |
4034 |
29 Jul 16 |
nicklas |
188 |
} |
4034 |
29 Jul 16 |
nicklas |
189 |
} |
4141 |
30 Sep 16 |
nicklas |
190 |
|
4141 |
30 Sep 16 |
nicklas |
191 |
/** |
4141 |
30 Sep 16 |
nicklas |
Publish or unpublish the specimen to/from the given site. This method will |
4141 |
30 Sep 16 |
nicklas |
check the YellowLabel annotation. If set, the specimen and the parent case |
4141 |
30 Sep 16 |
nicklas |
and patient will be shared with READ permission to the site. If the |
4141 |
30 Sep 16 |
nicklas |
YellowLabel annotation is not set the specimen will be unshared from the site. |
4141 |
30 Sep 16 |
nicklas |
The case and patient is not modified in this case since there may be |
4141 |
30 Sep 16 |
nicklas |
other specimen which requires the share. |
4141 |
30 Sep 16 |
nicklas |
198 |
*/ |
4141 |
30 Sep 16 |
nicklas |
199 |
private void ensurePdfIsPublishedToSite(DbControl dc, File pdf) |
4141 |
30 Sep 16 |
nicklas |
200 |
{ |
4141 |
30 Sep 16 |
nicklas |
// The immediate parent directory has the same name as the specimen |
4141 |
30 Sep 16 |
nicklas |
// so we use that to find the site to share to |
4141 |
30 Sep 16 |
nicklas |
203 |
Directory d = pdf.getDirectory(); |
4141 |
30 Sep 16 |
nicklas |
204 |
Site site = Site.findByCaseName(d.getName()); |
4141 |
30 Sep 16 |
nicklas |
205 |
if (site == Site.UNKNOWN) return; // Ignore unknown sites |
4141 |
30 Sep 16 |
nicklas |
206 |
|
4141 |
30 Sep 16 |
nicklas |
207 |
Group siteGroup = site.load(dc); |
4141 |
30 Sep 16 |
nicklas |
208 |
Group patientCurator = ReggieRole.PATIENT_CURATOR.load(dc); |
4141 |
30 Sep 16 |
nicklas |
209 |
Directory deliveryRootDir = Directory.getNew(dc, new Path(Reggie.DELIVERY_DIR, Path.Type.DIRECTORY)); |
4141 |
30 Sep 16 |
nicklas |
210 |
|
4141 |
30 Sep 16 |
nicklas |
211 |
List<SharedItem> itemsToShare = new ArrayList<>(); |
4141 |
30 Sep 16 |
nicklas |
212 |
itemsToShare.add(pdf); |
4141 |
30 Sep 16 |
nicklas |
213 |
|
4141 |
30 Sep 16 |
nicklas |
214 |
while (d != null && !d.equals(deliveryRootDir)) |
4141 |
30 Sep 16 |
nicklas |
215 |
{ |
4141 |
30 Sep 16 |
nicklas |
216 |
itemsToShare.add(d); |
4141 |
30 Sep 16 |
nicklas |
217 |
d = d.getParent(); |
4141 |
30 Sep 16 |
nicklas |
218 |
} |
4141 |
30 Sep 16 |
nicklas |
219 |
|
4141 |
30 Sep 16 |
nicklas |
220 |
MultiPermissions mp = new MultiPermissions(itemsToShare); |
4141 |
30 Sep 16 |
nicklas |
221 |
mp.addPermissions(siteGroup, EnumSet.of(Permission.READ)); |
4141 |
30 Sep 16 |
nicklas |
222 |
mp.addPermissions(patientCurator, EnumSet.of(Permission.DELETE, Permission.SET_PERMISSION)); |
4141 |
30 Sep 16 |
nicklas |
223 |
mp.updateKeys(dc); |
4141 |
30 Sep 16 |
nicklas |
224 |
} |
4141 |
30 Sep 16 |
nicklas |
225 |
|
4141 |
30 Sep 16 |
nicklas |
226 |
/** |
4141 |
30 Sep 16 |
nicklas |
Publish the PDF report and make sure all intermediate directories |
4141 |
30 Sep 16 |
nicklas |
up to {@link Reggie#DELIVERY_DIR} is shared to the site. |
4141 |
30 Sep 16 |
nicklas |
229 |
*/ |
4141 |
30 Sep 16 |
nicklas |
230 |
private void ensureSpecimenIsPublishedToSite(DbControl dc, Sample specimen, Site site) |
4141 |
30 Sep 16 |
nicklas |
231 |
{ |
4141 |
30 Sep 16 |
nicklas |
232 |
Group siteGroup = site.load(dc); |
4141 |
30 Sep 16 |
nicklas |
233 |
Set<Permission> permissions = null; |
4141 |
30 Sep 16 |
nicklas |
234 |
List<SharedItem> itemsToShare = new ArrayList<>(); |
4141 |
30 Sep 16 |
nicklas |
235 |
itemsToShare.add(specimen); |
4141 |
30 Sep 16 |
nicklas |
236 |
|
4141 |
30 Sep 16 |
nicklas |
237 |
String yellow = (String)Annotationtype.YELLOW_LABEL.getAnnotationValue(dc, specimen); |
4141 |
30 Sep 16 |
nicklas |
238 |
if (yellow != null) |
4141 |
30 Sep 16 |
nicklas |
239 |
{ |
4141 |
30 Sep 16 |
nicklas |
// Share the specimen, case and patient with READ permission |
4141 |
30 Sep 16 |
nicklas |
241 |
permissions = EnumSet.of(Permission.READ); |
4141 |
30 Sep 16 |
nicklas |
242 |
Sample theCase = (Sample)specimen.getParent(); |
4141 |
30 Sep 16 |
nicklas |
243 |
if (theCase != null) |
4141 |
30 Sep 16 |
nicklas |
244 |
{ |
4141 |
30 Sep 16 |
nicklas |
245 |
itemsToShare.add(theCase); |
4141 |
30 Sep 16 |
nicklas |
246 |
BioSource patient = (BioSource)theCase.getParent(); |
4141 |
30 Sep 16 |
nicklas |
247 |
if (patient != null) |
4141 |
30 Sep 16 |
nicklas |
248 |
{ |
4141 |
30 Sep 16 |
nicklas |
249 |
itemsToShare.add(patient); |
4141 |
30 Sep 16 |
nicklas |
250 |
} |
4141 |
30 Sep 16 |
nicklas |
251 |
} |
4141 |
30 Sep 16 |
nicklas |
252 |
} |
4141 |
30 Sep 16 |
nicklas |
253 |
|
4141 |
30 Sep 16 |
nicklas |
254 |
MultiPermissions mp = new MultiPermissions(itemsToShare); |
4141 |
30 Sep 16 |
nicklas |
255 |
if (permissions != null) |
4141 |
30 Sep 16 |
nicklas |
256 |
{ |
4141 |
30 Sep 16 |
nicklas |
257 |
mp.addPermissions(siteGroup, permissions); |
4141 |
30 Sep 16 |
nicklas |
258 |
} |
4141 |
30 Sep 16 |
nicklas |
259 |
else |
4141 |
30 Sep 16 |
nicklas |
260 |
{ |
4141 |
30 Sep 16 |
nicklas |
261 |
mp.setPermissions(siteGroup, null); |
4141 |
30 Sep 16 |
nicklas |
262 |
} |
4141 |
30 Sep 16 |
nicklas |
263 |
|
4141 |
30 Sep 16 |
nicklas |
264 |
mp.updateKeys(dc); |
4141 |
30 Sep 16 |
nicklas |
265 |
} |
4034 |
29 Jul 16 |
nicklas |
266 |
|
4034 |
29 Jul 16 |
nicklas |
267 |
} |