Merge branch '1.9.x' into dev

Conflicts:
	src/Orchard.Web/Modules/Orchard.ContentPermissions/Drivers/ContentPermissionsPartDriver.cs
	src/Orchard.Web/Modules/Orchard.Scripting.CSharp/Orchard.Scripting.CSharp.csproj
This commit is contained in:
Sebastien Ros
2016-01-14 08:43:56 -08:00
18 changed files with 181 additions and 112 deletions

View File

@@ -90,7 +90,10 @@ namespace Orchard.Core.Contents.Controllers {
model.TypeDisplayName = !string.IsNullOrWhiteSpace(contentTypeDefinition.DisplayName)
? contentTypeDefinition.DisplayName
: contentTypeDefinition.Name;
query = query.ForType(model.TypeName);
// We display a specific type even if it's not listable so that admin pages
// can reuse the Content list page for specific types.
query = _contentManager.Query(versionOptions, model.TypeName);
}
switch (model.Options.OrderBy) {

View File

@@ -93,16 +93,15 @@ namespace Orchard.Core.Navigation.Services {
var schemes = new[] { "http", "https", "tel", "mailto" };
if (!string.IsNullOrEmpty(url) && _urlHelper.RequestContext.HttpContext != null &&
!(url.StartsWith("/") || schemes.Any(scheme => url.StartsWith(scheme + ":")))) {
if (url.StartsWith("~/")) {
if (!String.IsNullOrEmpty(_shellSettings.RequestUrlPrefix)) {
url = _shellSettings.RequestUrlPrefix + "/" + url.Substring(2);
}
else {
url = url.Substring(2);
}
}
if (!url.StartsWith("#")) {
if (url.StartsWith("~/")) {
if (!String.IsNullOrEmpty(_shellSettings.RequestUrlPrefix)) {
url = _shellSettings.RequestUrlPrefix + "/" + url.Substring(2);
}
else {
url = url.Substring(2);
}
}
var appPath = _urlHelper.RequestContext.HttpContext.Request.ApplicationPath;
if (appPath == "/")
appPath = "";

View File

@@ -41,8 +41,6 @@ namespace Lucene.Services {
// TODO: (sebros) Find a common way to get where tenant's specific files should go. "Sites/Tenant" is hard coded in multiple places
_basePath = _appDataFolder.Combine("Sites", shellSettings.Name, "Indexes");
Logger = NullLogger.Instance;
// Ensures the directory exists
EnsureDirectoryExists();

View File

@@ -44,7 +44,7 @@ namespace Orchard.Blogs.Controllers {
var blogPost = Services.ContentManager.New<BlogPostPart>("BlogPost");
blogPost.BlogPart = blog;
if (!Services.Authorizer.Authorize(Permissions.EditBlogPost, blog, T("Not allowed to create blog post")))
if (!Services.Authorizer.Authorize(Permissions.EditBlogPost, blogPost, T("Not allowed to create blog post")))
return new HttpUnauthorizedResult();
var model = Services.ContentManager.BuildEditor(blogPost);
@@ -76,7 +76,7 @@ namespace Orchard.Blogs.Controllers {
var blogPost = Services.ContentManager.New<BlogPostPart>("BlogPost");
blogPost.BlogPart = blog;
if (!Services.Authorizer.Authorize(Permissions.EditBlogPost, blog, T("Couldn't create blog post")))
if (!Services.Authorizer.Authorize(Permissions.EditBlogPost, blogPost, T("Couldn't create blog post")))
return new HttpUnauthorizedResult();
Services.ContentManager.Create(blogPost, VersionOptions.Draft);
@@ -88,7 +88,7 @@ namespace Orchard.Blogs.Controllers {
}
if (publish) {
if (!Services.Authorizer.Authorize(Permissions.PublishBlogPost, blog.ContentItem, T("Couldn't publish blog post")))
if (!Services.Authorizer.Authorize(Permissions.PublishBlogPost, blogPost.ContentItem, T("Couldn't publish blog post")))
return new HttpUnauthorizedResult();
Services.ContentManager.Publish(blogPost.ContentItem);

View File

@@ -119,6 +119,11 @@ namespace Orchard.ContentPermissions.Drivers {
}
protected override DriverResult Editor(ContentPermissionsPart part, IUpdateModel updater, dynamic shapeHelper) {
// ensure the current user is allowed to define permissions
if (!_authorizer.Authorize(Permissions.GrantPermission)) {
return null;
}
var model = new ContentPermissionsPartViewModel();
if (!updater.TryUpdateModel(model, Prefix, null, null)) {
@@ -223,4 +228,4 @@ namespace Orchard.ContentPermissions.Drivers {
}
}
}
}
}

View File

@@ -90,6 +90,7 @@ namespace Orchard.ContentPicker.Drivers {
if (field.Ids.Any()) {
var contentItemIds = field.Ids
.Select(x => _contentManager.Get(x))
.Where(x => x != null)
.Select(x => _contentManager.GetItemMetadata(x).Identity.ToString())
.ToArray();

View File

@@ -141,13 +141,15 @@ namespace Orchard.DesignerTools.Services {
}
foreach (var member in members) {
if ((o is ContentItem && (member.Name == "ContentManager"
|| member.Name == "Parts"
|| member.Name == "Record"
|| member.Name == "VersionRecord"
|| member.Name == "TypeDefinition"
|| member.Name == "TypePartDefinition"
|| member.Name == "PartDefinition"))
if ((o is ContentItem && (
member.Name == "Content" ||
member.Name == "ContentManager" ||
member.Name == "Parts" ||
member.Name == "Record" ||
member.Name == "VersionRecord" ||
member.Name == "TypeDefinition" ||
member.Name == "TypePartDefinition" ||
member.Name == "PartDefinition"))
|| o is Delegate
|| o is Type
) {

View File

@@ -22,7 +22,9 @@
tagBuilder.AddCssClass("input-validation-error");
}
tagBuilder.SetInnerText(Model.ProcessedValue);
if (!String.IsNullOrWhiteSpace((string)Model.ProcessedValue)) {
tagBuilder.SetInnerText(Model.ProcessedValue);
}
}
@if (element.ShowLabel) {
<label for="@element.HtmlId">@Model.ProcessedLabel</label>

View File

@@ -86,7 +86,7 @@
</ol>
<ol class="layout-editor-toolbar-group">
<li>
<a class="layout-editor-help-link" href="#"><i class="fa fa-info-circle"></i> Clipboard, keyboard shortcuts, etc.</a>
<a class="layout-editor-help-link" href="#"><i class="fa fa-info-circle"></i> @T("Clipboard, keyboard shortcuts, etc.")</a>
</li>
@if (Model.Templates.Any()) {
var options = Model.Templates.Select(x => new SelectListItem { Text = Html.ItemDisplayText(x).ToString(), Value = x.Id.ToString(CultureInfo.InvariantCulture), Selected = x.Id == Model.TemplateId });
@@ -105,125 +105,125 @@
<orc-layout-editor model="window.layoutEditor" ng-app="LayoutEditor" />
</div>
@Display.DialogTemplate(Name: "Layout")
<div class="layout-editor-help-dialog" title="Layout editor help">
<div class="layout-editor-help-dialog" title="@T("Layout editor help")">
<div class="help-row">
<h3>Clipboard</h3>
<h3>@T("Clipboard")</h3>
<div class="help-column-full">
<p>Elements (including containers) can be cut, copied and pasted using the standard clipboard shortcuts (<code>Ctrl+X</code> / <code>Ctrl+C</code> / <code>Ctrl-V</code> on Windows, <code>⌘+X</code> / <code>⌘+C</code> / <code>⌘+V</code> on Mac OS).</p>
<p>On browsers that support native clipboard events, clipboard operations can be performed across different layout editor instances, in different tabs or browser windows. Text content can also be pasted into other applications.</p>
<p>On other browsers, clipboard operations work only within the same layout editor instance.</p>
<p>@T("Elements (including containers) can be cut, copied and pasted using the standard clipboard shortcuts (<code>Ctrl+X</code> / <code>Ctrl+C</code> / <code>Ctrl-V</code> on Windows, <code>⌘+X</code> / <code>⌘+C</code> / <code>⌘+V</code> on Mac OS).")</p>
<p>@T("On browsers that support native clipboard events, clipboard operations can be performed across different layout editor instances, in different tabs or browser windows. Text content can also be pasted into other applications.")</p>
<p>@T("On other browsers, clipboard operations work only within the same layout editor instance.")</p>
</div>
</div>
<div class="help-row">
<h3>Keyboard shortcuts</h3>
<h3>@T("Keyboard shortcuts")</h3>
<div class="help-column-half">
<h4>Resizing columns</h4>
<h4>@T("Resizing columns")</h4>
<table>
<tbody>
<tr>
<td><code>Alt+Left</code></td>
<td>Moves the left edge of the focused column left</td>
<td><code>@T("Alt+Left")</code></td>
<td>@T("Moves the left edge of the focused column left")</td>
</tr>
<tr>
<td><code>Alt+Right</code></td>
<td>Moves the left edge of the focused column right</td>
<td><code>@T("Alt+Right")</code></td>
<td>@T("Moves the left edge of the focused column right")</td>
</tr>
<tr>
<td><code>Shift+Left</code></td>
<td>Moves the right edge of the focused column left</td>
<td><code>@T("Shift+Left")</code></td>
<td>@T("Moves the right edge of the focused column left")</td>
</tr>
<tr>
<td><code>Shift+Right</code></td>
<td>Moves the right edge of the focused column right</td>
<td><code>@T("Shift+Right")</code></td>
<td>@T("Moves the right edge of the focused column right")</td>
</tr>
</tbody>
</table>
<p>The <code>Alt</code> and <code>Shift</code> keys can also be combined to move both edges simultaneously.</p>
<p>@T("The <code>Alt</code> and <code>Shift</code> keys can also be combined to move both edges simultaneously.")</p>
</div>
<div class="help-column-half">
<h4>Focus</h4>
<h4>@T("Focus")</h4>
<table>
<tbody>
<tr>
<td><code>Up</code></td>
<td>Moves focus to the previous element (above)</td>
<td><code>@T("Up")</code></td>
<td>@T("Moves focus to the previous element (above)")</td>
</tr>
<tr>
<td><code>Down</code></td>
<td>Moves focus to the next element (below)</td>
<td><code>@T("Down")</code></td>
<td>@T("Moves focus to the next element (below)")</td>
</tr>
<tr>
<td><code>Left</code></td>
<td>Moves focus to the previous column (to the left)</td>
<td><code>@T("Left")</code></td>
<td>@T("Moves focus to the previous column (to the left)")</td>
</tr>
<tr>
<td><code>Right</code></td>
<td>Moves focus to the next column (to the right)</td>
<td><code>@T("Right")</code></td>
<td>@T("Moves focus to the next column (to the right)")</td>
</tr>
<tr>
<td><code>Alt+Up</code></td>
<td>Moves focus to the parent element</td>
<td><code>@T("Alt+Up")</code></td>
<td>@T("Moves focus to the parent element")</td>
</tr>
<tr>
<td><code>Alt+Down</code></td>
<td>Moves focus to the first child element</td>
<td><code>@T("Alt+Down")</code></td>
<td>@T("Moves focus to the first child element")</td>
</tr>
</tbody>
</table>
</div>
<div class="help-column-half">
<h4>Editing</h4>
<h4>@T("Editing")</h4>
<table>
<tbody>
<tr>
<td><code>Enter</code></td>
<td>Opens the content editor of the focused element</td>
<td><code>@T("Enter")</code></td>
<td>@T("Opens the content editor of the focused element")</td>
</tr>
<tr>
<td><code>Space</code></td>
<td>Opens the properties popup of the focused element</td>
<td><code>@T("Space")</code></td>
<td>@T("Opens the properties popup of the focused element")</td>
</tr>
<tr>
<td><code>Esc</code></td>
<td>Closes the properties popup of the focused element</td>
<td><code>@T("Esc")</code></td>
<td>@T("Closes the properties popup of the focused element")</td>
</tr>
<tr>
<td><code>Del</code></td>
<td>Deletes the focused element</td>
<td><code>@T("Del")</code></td>
<td>@T("Deletes the focused element")</td>
</tr>
</tbody>
</table>
</div>
<div class="help-column-half">
<h4>Moving</h4>
<h4>@T("Moving")</h4>
<table>
<tbody>
<tr>
<td><code>Ctrl+Up</code></td>
<td>Moves (reorders) the focused element up</td>
<td><code>@T("Ctrl+Up")</code></td>
<td>@T("Moves (reorders) the focused element up")</td>
</tr>
<tr>
<td><code>Ctrl+Down</code></td>
<td>Moves (reorders) the focused element down</td>
<td><code>@T("Ctrl+Down")</code></td>
<td>@T("Moves (reorders) the focused element down")</td>
</tr>
<tr>
<td><code>Ctrl+Left</code></td>
<td>Moves (reorders) the focused column left</td>
<td><code>@T("Ctrl+Left")</code></td>
<td>@T("Moves (reorders) the focused column left")</td>
</tr>
<tr>
<td><code>Ctrl+Right</code></td>
<td>Moves (reorders) the focused column right</td>
<td><code>@T("Ctrl+Right")</code></td>
<td>@T("Moves (reorders) the focused column right")</td>
</tr>
</tbody>
</table>
</div>
</div>
<div class="help-row">
<h3>Drag and drop</h3>
<h3>@T("Drag and drop")</h3>
<section class="help-column-full">
<p>Drag any existing element to reorder within its parent.</p>
<p>Drag a new element from the toolbox and drop it within a compatible container.</p>
<p>Drag the left and right edges of a focused column to resize the column. By default any adjacent column will be attached and resized accordingly; holding down the <code>Alt</code> key while resizing unattaches from the adjacent column and instead modifies the offset between.</p>
<p>@T("Drag any existing element to reorder within its parent.")</p>
<p>@T("Drag a new element from the toolbox and drop it within a compatible container.")</p>
<p>@T("Drag the left and right edges of a focused column to resize the column. By default any adjacent column will be attached and resized accordingly; holding down the <code>Alt</code> key while resizing unattaches from the adjacent column and instead modifies the offset between.")</p>
</section>
</div>
</div>

View File

@@ -65,7 +65,7 @@ namespace Orchard.MediaLibrary.Drivers {
}
if (settings.Required && field.Ids.Length == 0) {
updater.AddModelError("Id", T("The field {0} is mandatory", field.Name.CamelFriendly()));
updater.AddModelError("Id", T("The field {0} is mandatory", field.DisplayName));
}
return Editor(part, field, shapeHelper);
@@ -87,6 +87,7 @@ namespace Orchard.MediaLibrary.Drivers {
if (field.Ids.Any()) {
var contentItemIds = field.Ids
.Select(x => _contentManager.Get(x))
.Where(x => x != null)
.Select(x => _contentManager.GetItemMetadata(x).Identity.ToString())
.ToArray();
@@ -99,4 +100,4 @@ namespace Orchard.MediaLibrary.Drivers {
.Member(null, typeof(string), T("Ids"), T("A formatted list of the ids, e.g., {1},{42}"));
}
}
}
}

View File

@@ -21,12 +21,15 @@
var refreshIds = function() {
var id = element.find('.selected-ids');
id.val('');
element.find(".media-library-picker-item").each(function() {
id.val(id.val() + "," + $(this).attr("data-id"));
var ids = [];
element.find(".media-library-picker-item").each(function () {
ids.push($(this).attr("data-id"));
});
var itemsCount = element.find(".media-library-picker-item").length;
id.val(ids.join());
var itemsCount = ids.length;
if(!multiple && itemsCount > 0) {
addButton.hide();

View File

@@ -1,5 +1,4 @@
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.Globalization;
@@ -26,11 +25,10 @@ using Orchard.UI.Admin;
using Orchard.Utility.Extensions;
namespace Orchard.OutputCache.Filters {
public class OutputCacheFilter : FilterProvider, IActionFilter, IResultFilter {
public class OutputCacheFilter : FilterProvider, IActionFilter, IResultFilter, IDisposable {
private static string _refreshKey = "__r";
private static long _epoch = new DateTime(2014, DateTimeKind.Utc).Ticks;
private static readonly ConcurrentDictionary<string, object> _cacheKeyLocks = new ConcurrentDictionary<string, object>();
// Dependencies.
private readonly ICacheManager _cacheManager;
@@ -43,6 +41,8 @@ namespace Orchard.OutputCache.Filters {
private readonly ICacheService _cacheService;
private readonly ISignals _signals;
private readonly ShellSettings _shellSettings;
private bool _isDisposed = false;
public ILogger Logger { get; set; }
public OutputCacheFilter(
@@ -98,15 +98,11 @@ namespace Orchard.OutputCache.Filters {
return;
// Computing the cache key after we know that the request is cacheable means that we are only performing this calculation on requests that require it
_cacheKey = ComputeCacheKey(filterContext, GetCacheKeyParameters(filterContext));
_cacheKey = String.Intern(ComputeCacheKey(filterContext, GetCacheKeyParameters(filterContext)));
_invariantCacheKey = ComputeCacheKey(filterContext, null);
Logger.Debug("Cache key '{0}' was created.", _cacheKey);
// The cache key lock for a given cache key is used to synchronize requests to
// ensure only a single request is regenerating the item.
var cacheKeyLock = _cacheKeyLocks.GetOrAdd(_cacheKey, x => new object());
try {
// Is there a cached item, and are we allowed to serve it?
@@ -120,7 +116,7 @@ namespace Orchard.OutputCache.Filters {
if (cacheItem.IsInGracePeriod(_now)) {
// Render the content unless another request is already doing so.
if (Monitor.TryEnter(cacheKeyLock)) {
if (Monitor.TryEnter(_cacheKey)) {
Logger.Debug("Item '{0}' is in grace period and not currently being rendered; rendering item...", _cacheKey);
BeginRenderItem(filterContext);
return;
@@ -137,7 +133,7 @@ namespace Orchard.OutputCache.Filters {
// No cached item found, or client doesn't want it; acquire the cache key
// lock to render the item.
Logger.Debug("Item '{0}' was not found in cache or client refuses it. Acquiring cache key lock...", _cacheKey);
if (Monitor.TryEnter(cacheKeyLock, TimeSpan.FromSeconds(20))) {
if (Monitor.TryEnter(_cacheKey)) {
Logger.Debug("Cache key lock for item '{0}' was acquired.", _cacheKey);
// Item might now have been rendered and cached by another request; if so serve it from cache.
@@ -145,7 +141,7 @@ namespace Orchard.OutputCache.Filters {
cacheItem = GetCacheItem(_cacheKey);
if (cacheItem != null) {
Logger.Debug("Item '{0}' was now found; releasing cache key lock and serving from cache.", _cacheKey);
Monitor.Exit(cacheKeyLock);
Monitor.Exit(_cacheKey);
ServeCachedItem(filterContext, cacheItem);
return;
}
@@ -161,8 +157,7 @@ namespace Orchard.OutputCache.Filters {
catch {
// Remember to release the cache key lock in the event of an exception!
Logger.Debug("Exception occurred for item '{0}'; releasing any acquired lock.", _cacheKey);
if (Monitor.IsEntered(cacheKeyLock))
Monitor.Exit(cacheKeyLock);
ReleaseCacheKeyLock();
throw;
}
}
@@ -179,11 +174,11 @@ namespace Orchard.OutputCache.Filters {
var captureHandlerIsAttached = false;
try {
// This filter is not reentrant (multiple executions within the same request are
// not supported) so child actions are ignored completely.
if (filterContext.IsChildAction || !_isCachingRequest)
return;
return;
Logger.Debug("Item '{0}' was rendered.", _cacheKey);
@@ -360,7 +355,7 @@ namespace Orchard.OutputCache.Filters {
protected virtual IDictionary<string, object> GetCacheKeyParameters(ActionExecutingContext filterContext) {
var result = new Dictionary<string, object>();
// Vary by action parameters.
foreach (var p in filterContext.ActionParameters)
result.Add("PARAM:" + p.Key, p.Value);
@@ -383,7 +378,7 @@ namespace Orchard.OutputCache.Filters {
result["HEADER:" + varyByRequestHeader] = requestHeaders[varyByRequestHeader];
}
// Vary by request culture if configured.
if (CacheSettings.VaryByCulture) {
result["culture"] = _workContext.CurrentCulture.ToLowerInvariant();
@@ -474,7 +469,7 @@ namespace Orchard.OutputCache.Filters {
var response = filterContext.HttpContext.Response;
// Fix for missing charset in response headers
response.Charset = response.Charset;
response.Charset = response.Charset;
// Adds some caching information to the output if requested.
if (CacheSettings.DebugMode) {
@@ -540,12 +535,10 @@ namespace Orchard.OutputCache.Filters {
}
private void ReleaseCacheKeyLock() {
if (_cacheKey != null) {
object cacheKeyLock;
if (_cacheKeyLocks.TryGetValue(_cacheKey, out cacheKeyLock) && Monitor.IsEntered(cacheKeyLock)) {
Logger.Debug("Releasing cache key lock for item '{0}'.", _cacheKey);
Monitor.Exit(cacheKeyLock);
}
if (_cacheKey != null && Monitor.IsEntered(_cacheKey)) {
Logger.Debug("Releasing cache key lock for item '{0}'.", _cacheKey);
Monitor.Exit(_cacheKey);
_cacheKey = null;
}
}
@@ -599,12 +592,37 @@ namespace Orchard.OutputCache.Filters {
var cacheItem = _cacheStorageProvider.GetCacheItem(key);
return cacheItem;
}
catch(Exception e) {
catch (Exception e) {
Logger.Error(e, "An unexpected error occured while reading a cache entry");
}
return null;
}
public void Dispose() {
Dispose(true);
GC.SuppressFinalize(this);
}
protected virtual void Dispose(bool disposing) {
if (!_isDisposed) {
if (disposing) {
// Free other state (managed objects).
}
if (_cacheKey != null && Monitor.IsEntered(_cacheKey)) {
Monitor.Exit(_cacheKey);
}
_isDisposed = true;
}
}
~OutputCacheFilter() {
// Ensure locks are released even after an unexpected exception
Dispose(false);
}
}
public class ViewDataContainer : IViewDataContainer {

View File

@@ -125,6 +125,7 @@
<Compile Include="Models\UserRolesPartRecord.cs" />
<Compile Include="Permissions.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="RuleEngine\RoleRuleProvider.cs" />
<Compile Include="Services\IRoleService.cs" />
<Compile Include="Services\RolesBasedAuthorizationService.cs" />
<Compile Include="Services\RoleService.cs" />

View File

@@ -0,0 +1,36 @@
using Orchard.ContentManagement;
using Orchard.Events;
using Orchard.Roles.Models;
using Orchard.Security;
using System;
using System.Linq;
namespace Orchard.Roles.RuleEngine {
public interface IRuleProvider : IEventHandler {
void Process(dynamic ruleContext);
}
public class RoleRuleProvider : IRuleProvider {
private readonly IAuthenticationService _authenticationService;
public RoleRuleProvider(IAuthenticationService authenticationService) {
_authenticationService = authenticationService;
}
public void Process(dynamic ruleContext) {
if (!String.Equals(ruleContext.FunctionName, "role", StringComparison.OrdinalIgnoreCase)) {
return;
}
var user = _authenticationService.GetAuthenticatedUser();
if (user == null) {
ruleContext.Result = false;
return;
}
var roles = ((object[])ruleContext.Arguments).Cast<string>();
var userRoles = user.As<IUserRoles>();
ruleContext.Result = userRoles != null ? userRoles.Roles.Intersect(roles).Count() > 0 : false;
}
}
}

View File

@@ -53,8 +53,9 @@
<HintPath>..\..\..\packages\Microsoft.Web.Infrastructure.1.0.0.0\lib\net40\Microsoft.Web.Infrastructure.dll</HintPath>
<Private>True</Private>
</Reference>
<Reference Include="Mono.CSharp">
<HintPath>Lib\Mono.CSharp.dll</HintPath>
<Reference Include="Mono.CSharp, Version=4.0.0.0, Culture=neutral, PublicKeyToken=0738eb9f132ed756, processorArchitecture=MSIL">
<SpecificVersion>False</SpecificVersion>
<HintPath>..\..\..\..\lib\mono\Mono.CSharp.dll</HintPath>
</Reference>
<Reference Include="System" />
<Reference Include="System.Data" />

View File

@@ -10,8 +10,8 @@
<ToolsOptionsCategory name="Environment">
<ToolsOptionsSubCategory name="TaskList">
<PropertyValue name="CommentTokens" ArrayType="VT_VARIANT" ArrayElementCount="4">
<PropertyValue name="0">HACK:2</PropertyValue>
<PropertyValue name="1">TODO:2</PropertyValue>
<PropertyValue name="0">TODO:2</PropertyValue>
<PropertyValue name="1">HACK:2</PropertyValue>
<PropertyValue name="2">UNDONE:2</PropertyValue>
<PropertyValue name="3">UnresolvedMergeConflict:3</PropertyValue>
</PropertyValue>