Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

OSOE-70: Split the Orchard Core specific methods into its own project #127

Merged
merged 36 commits into from
Mar 26, 2022
Merged
Show file tree
Hide file tree
Changes from 31 commits
Commits
Show all changes
36 commits
Select commit Hold shift + click to select a range
45706f8
Removing unneeded Lucene.Net reference (perhaps fixed an issue in CI?)
Piedone Mar 25, 2022
65c387a
Removing unneeded package references
Piedone Mar 25, 2022
914cae9
Removing unnecessary RootNamespace config
Piedone Mar 25, 2022
7170e98
Removing unneeded Razor SDK references
Piedone Mar 25, 2022
b7bc11e
Conventional sub-library titles
Piedone Mar 25, 2022
5e5c9a1
Removing unneeded FrameworkReferences
Piedone Mar 25, 2022
93411b3
Grammar
Piedone Mar 25, 2022
226d7d4
Removing dead end AddCoreOrchardServiceImplementations()
Piedone Mar 25, 2022
30c2ec4
Typo
Piedone Mar 25, 2022
8f2af64
Code styling
Piedone Mar 25, 2022
bcc9300
Moving non-Orchard ASP.NET Core features to a new project
Piedone Mar 25, 2022
b17c295
Adding NuGet shield
Piedone Mar 25, 2022
be382d7
Removing unnecessarily broad Microsoft.AspNetCore.App reference from …
Piedone Mar 26, 2022
fc76414
Removing Lombiq.HelpfulLibraries.Targets, Lombiq.HelpfulLibraries is …
Piedone Mar 26, 2022
7e53775
Typo
Piedone Mar 26, 2022
edbf21a
Common Libraries foundations
Piedone Mar 26, 2022
8b9caee
Orchard Core Libraries foundations and standardizing Readmes and names
Piedone Mar 26, 2022
74b2b58
Missing project reference
Piedone Mar 26, 2022
8ab4030
Moving everything remaining out of Lombiq.HelpfulLibraries to sub-lib…
Piedone Mar 26, 2022
abcd375
Removing unused references
Piedone Mar 26, 2022
44ff992
Adding DisplayManagement and ResourceManagement references to make su…
Piedone Mar 26, 2022
7c3dffc
Moving the Tests project out to the root since it needn't be in a sub…
Piedone Mar 26, 2022
38ebae6
Fixing project reference
Piedone Mar 26, 2022
22f5049
Missing docs link
Piedone Mar 26, 2022
7422229
Standardizing docs
Piedone Mar 26, 2022
27e4240
Moving Moq-using extension to a separate project so it needn't be dep…
Piedone Mar 26, 2022
7440412
NuGet shield
Piedone Mar 26, 2022
d9d6cd4
Suppress unnecessary analyzer violation
Piedone Mar 26, 2022
c2ceccf
New CommonStereotypes constants
Piedone Mar 26, 2022
59ecd20
Fixing analyzer violation
Piedone Mar 26, 2022
580e088
Code cleanup
Piedone Mar 26, 2022
2eeca28
Typo
Piedone Mar 26, 2022
f136c5b
Typo
Piedone Mar 26, 2022
b70f9f1
Clarifying how ICachingUserManager works
Piedone Mar 26, 2022
a154c67
Getting rid of "CMS" when talking about Orchard Core
Piedone Mar 26, 2022
00f0aa3
Removing unnecessary reference
Piedone Mar 26, 2022
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 6 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
@@ -1,4 +1,9 @@
.vs/
obj/
bin/
artifacts/
wwwroot/
node_modules/
*.user
artifacts/
.pnpm-debug.log
.editorconfig
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
# ASP.NET Core Libraries Documentation
# Lombiq Helpful Libraries - ASP.NET Core Libraries - Extensions


Please see the inline documentation of each extension methods learn more.


- `ForwardedHeadersApplicationBuilderExtensions`: Provides `UseForwardedHeadersForCloudflareAndAzure()` that forwards proxied headers onto the current request with settings suitable for an app behind Cloudflare and hosted in an Azure App Service.
- `CookieHttpContextExtensions`: Provides shortcuts for some cookie-related operations.
- `DateTimeHttpContextExtensions`: Makes it possible to set or get IANA time-zone IDs in the HTTP context.
- `EnvironmentHttpContextExtensions`: Provides shortcuts to determine information about the current hosting environment, like whether the app is running in Development mode.
- `ForwardedHeadersApplicationBuilderExtensions`: Provides `UseForwardedHeadersForCloudflareAndAzure()` that forwards proxied headers onto the current request with settings suitable for an app behind Cloudflare and hosted in an Azure App Service.
- `JsonStringExtensions`: Adds JSON related extensions for the `string` type. (E.g. `JsonHtmlContent` which safely serializes a string for use in `<script>` elements.)
- `NonEmptyTagHelper`: An attribute tag helper that conditionally hides its element if the provided collection is null or empty. This eliminates a bulky wrapping `@if(collection?.Count > 1) { ... }` expression that would needlessly increase the document's indentation too.
- `TemporaryResponseWrapper`: An `IAsyncDisposable` that replaces the `HttpContext`'s response stream at creation with a `MemoryStream` and copies its content back into the real response stream during disposal.
8 changes: 8 additions & 0 deletions Lombiq.HelpfulLibraries.AspNetCore/Docs/Localization.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
# Lombiq Helpful Libraries - ASP.NET Core Libraries - Localization



## Extensions

- `HttpContext` extensions: Adds extension methods to `HttpContext` objects that can be used to fetch localization-specific values such as the current request's UI culture.
- `LocalizedHtmlString` extensions: Adds extension methods to `LocalizedHtmlString` objects to make view localization more convenient.
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# Middlewares Documentation
# Lombiq Helpful Libraries - ASP.NET Core Libraries - Middlewares


Contains ASP.NET Core middlewares and middleware-related services.
Expand All @@ -10,6 +10,3 @@ Implement the `IDeferredTask` interface if you want to create a service that exe
Also use the `app.UseDeferredTasks()` extension method to enable the necessary `DeferredTaskMiddleware`. You only have to do that once.

Note that deferred tasks won't execute on calls that aren't part of the middleware pipeline, such as deployment and setup tasks in Orchard Core. You can use the `IDeferredTask.IsScheduled` property to check if you can expect the deferred task to execute in the current scope.


Please see the inline documentation of each type or extension method to learn more.
10 changes: 10 additions & 0 deletions Lombiq.HelpfulLibraries.AspNetCore/Docs/Mvc.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
# Lombiq Helpful Libraries - ASP.NET Core Libraries - MVC



- `ActionResultHelpers`: For returning common specialized `IActionResult`s from `Controller`s, such as `ZipFile`.
- Attributes for controllers:
- `DevelopmentOnlyAttribute`: Enforces the Development environment on controllers.
- `DevelopmentAndLocalhostOnlyAttribute`: Enforces the Development environment as well as localhost.
- `Controller` extensions: Adds extension methods to `Controller` objects like `.RedirectToLocal(redirectUrl)`.
- `ResultExecutingContextExtensions` to get some shortcuts to usual context operations in `IAsyncResultFilter`s.
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
using System;

namespace Microsoft.AspNetCore.Http;

public static class CookieHttpContextExtensions
{
/// <summary>
/// Sets the cookie with the given name with a maximal expiration time.
/// </summary>
public static void SetCookieForever(this HttpContext httpContext, string name, string value) =>
httpContext.Response.Cookies.Append(name, value, new CookieOptions
{
Expires = DateTimeOffset.MaxValue,
Secure = true,
HttpOnly = true,
});

/// <summary>
/// Sets the cookie with the given name with a maximal expiration time.
/// </summary>
public static void SetCookieForever(this IHttpContextAccessor httpContextAccessor, string name, string value) =>
httpContextAccessor.HttpContext.SetCookieForever(name, value);
}
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
using Lombiq.HelpfulLibraries.Libraries.DateTime;
using Lombiq.HelpfulLibraries.AspNetCore.DateTime;

namespace Microsoft.AspNetCore.Http;

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
namespace Lombiq.HelpfulLibraries.AspNetCore.DateTime;

public static class HttpContextKeys
{
public const string TimeZoneIdKey = "TimeZoneId";
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<TargetFramework>net6.0</TargetFramework>
<DefaultItemExcludes>$(DefaultItemExcludes);.git*</DefaultItemExcludes>
</PropertyGroup>

<PropertyGroup>
<Title>Lombiq Helpful Libraries - ASP.NET Core Libraries</Title>
<Authors>Lombiq Technologies</Authors>
<Copyright>Copyright © 2011, Lombiq Technologies Ltd.</Copyright>
<Description>Lombiq Helpful Libraries - ASP.NET Core Libraries: See the project website for detailed documentation.</Description>
<PackageTags>OrchardCore;Lombiq;AspNetCore;</PackageTags>
<PackageIcon>NuGetIcon.png</PackageIcon>
<RepositoryUrl>https:/Lombiq/Helpful-Libraries</RepositoryUrl>
<PackageProjectUrl>https:/Lombiq/Helpful-Libraries/blob/dev/Lombiq.HelpfulLibraries.AspNetCore/Readme.md</PackageProjectUrl>
<PackageLicenseFile>License.md</PackageLicenseFile>
</PropertyGroup>

<ItemGroup>
<None Include="License.md" Pack="true" PackagePath="" />
<None Include="Readme.md" />
<None Include="NuGetIcon.png" Pack="true" PackagePath="" />
</ItemGroup>

<ItemGroup>
<FrameworkReference Include="Microsoft.AspNetCore.App" />
</ItemGroup>

<ItemGroup>
<ProjectReference Include="..\Lombiq.HelpfulLibraries.Common\Lombiq.HelpfulLibraries.Common.csproj" />
</ItemGroup>

</Project>
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
using System.Collections.Generic;
using System.Threading.Tasks;

namespace Lombiq.HelpfulLibraries.Libraries.Middlewares;
namespace Lombiq.HelpfulLibraries.AspNetCore.Middlewares;

public class DeferredTaskMiddleware
{
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
using Microsoft.AspNetCore.Http;
using System.Threading.Tasks;

namespace Lombiq.HelpfulLibraries.Libraries.Middlewares;
namespace Lombiq.HelpfulLibraries.AspNetCore.Middlewares;

/// <summary>
/// A task that can be executed in the return edge of the middleware pipeline.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
using System.IO.Compression;
using static System.Net.Mime.MediaTypeNames.Application;

namespace Lombiq.HelpfulLibraries.Libraries.Mvc;
namespace Lombiq.HelpfulLibraries.AspNetCore.Mvc;

public static class ActionResultHelpers
{
Expand Down
17 changes: 17 additions & 0 deletions Lombiq.HelpfulLibraries.AspNetCore/Mvc/ControllerExtensions.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
namespace Microsoft.AspNetCore.Mvc;

public static class ControllerExtensions
{
/// <summary>
/// Will redirect to the given URL if that is local. Otherwise it will redirect to "~/".
/// </summary>
/// <param name="redirectUrl">Local URL to redirect to.</param>
/// <returns>Redirect action result.</returns>
/// <remarks>
/// <para>
/// Could be part of Orchard but <see href="https:/OrchardCMS/OrchardCore/issues/2830">it won't</see>.
/// </para>
/// </remarks>
public static RedirectResult RedirectToLocal(this Controller controller, string redirectUrl) =>
controller.Redirect(controller.Url.IsLocalUrl(redirectUrl) ? redirectUrl : "~/");
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
using Microsoft.Extensions.Hosting;
using System;

namespace Lombiq.HelpfulLibraries.Libraries.Mvc;
namespace Lombiq.HelpfulLibraries.AspNetCore.Mvc;

/// <summary>
/// Enforces the Development environment as well as localhost. When put on a controller or an action it'll set a <see
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
using Microsoft.Extensions.Hosting;
using System;

namespace Lombiq.HelpfulLibraries.Libraries.Mvc;
namespace Lombiq.HelpfulLibraries.AspNetCore.Mvc;

/// <summary>
/// Enforces the Development environment. When put on a controller or an action it'll set a <see cref="NotFoundResult"/>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
using Microsoft.AspNetCore.Mvc.RazorPages;

namespace Microsoft.AspNetCore.Mvc.Filters;

public static class ResultExecutingContextExtensions
{
/// <summary>
/// Indicates if the current result is a full view rendering result (i.e. where you can properly inject shapes into
/// the Layout).
/// </summary>
/// <remarks>
/// <para>The URL /Admin/Media/MediaApplication from OrchardCore.Media will be a full view rendering though.</para>
/// </remarks>
public static bool IsNotFullViewRendering(this ResultExecutingContext context) =>
context.Result is not ViewResult and not PageResult;
}
19 changes: 19 additions & 0 deletions Lombiq.HelpfulLibraries.AspNetCore/Readme.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
# Lombiq Helpful Libraries - ASP.NET Core Libraries



## About

Some useful extensions and other helpers for ASP.NET Core.

For general details about and on using the Helpful Libraries see the [root Readme](../Readme.md).


## Documentation

Please see the inline documentation of each piece of the API to learn more.

- [Extensions](Docs/Extensions.md)
- [Localization](Docs/Localization.md)
- [Middlewares](Docs/Middlewares.md)
- [MVC](Docs/Mvc.md)
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.DependencyInjection.Extensions;
using System;

namespace Lombiq.HelpfulLibraries.Common.DependencyInjection;

public static class ServiceCollectionExtensions
{
// This implementation is based on this StackOverflow answer: https://stackoverflow.com/a/45775657/4611736
public static void AddLazyInjectionSupport(this IServiceCollection services) =>
services.TryAddTransient(typeof(Lazy<>), typeof(Lazier<>));

private sealed class Lazier<T> : Lazy<T>
where T : class
{
public Lazier(IServiceProvider provider)
: base(() => provider.GetRequiredService<T>()) { }
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
using Microsoft.Extensions.DependencyInjection;

namespace System;

public static class ServiceProviderExtensions
{
/// <summary>
/// Returns a <see cref="Lazy{T}"/> accessor for the service so you can access services with a shorter lifecyle in
/// your service implementation without storing a service provider which is an anti-pattern.
/// </summary>
/// <typeparam name="T">The type of the required service.</typeparam>
public static Lazy<T> GetLazyService<T>(this IServiceProvider serviceProvider) =>
new(serviceProvider.GetRequiredService<T>);
}
16 changes: 16 additions & 0 deletions Lombiq.HelpfulLibraries.Common/Docs/DependencyInjection.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
# Lombiq Helpful Libraries - Common Libraries - Dependency Injection



## Lazy injection support

Using the `.AddLazyInjectionSupport()` extension will allow you to inject lazy dependencies like `Lazy<IMyService> myService`.

Usage:

```csharp
public override void ConfigureServices(IServiceCollection services)
{
services.AddLazyInjectionSupport();
}
```
Original file line number Diff line number Diff line change
@@ -1,30 +1,17 @@
# Utilities Documentation
# Lombiq Helpful Libraries - Common Libraries - Extensions


## Extensions

- `CollectionExtensions`: Adds `ICollection<T>` extensions. (eg. `CartesianProduct` for producing all pairs of two collections or `RemoveAll` to filter an existing collection in-place).
- `ConfigurationExtensions`: Shortcuts for `IConfiguration` operations.
- `ContentOrchardHelperExtensions`: Extensions for managing content items better via `IOrchardHelper`.
- `DictionaryExtensions`: Adds `IDictionary<TKey, TValue>` extensions. (eg. `GetMaybe` for safely retrieving an item if it's in the dictionary or returning `default` without throwing an exception).
- `EnumerableExtensions`: Adds `IEnumerable<T>` extensions. (eg. `AwaitEachAsync` for performing async operations on a collection sequentially, and `AsList` for casting to `List<T>` without necessarily creating a new list).
- `EnumExtensions`: Adds extensions for working with `enum` types. (eg. `UnknownEnumException` which can be used to raise a standardized exception on the default arm of a `switch`).
- `ExceptionHelpers`: Using these helpers, arguments can be tested without writing `if` statements. There are Orchard Core content-specific helpers as well for example checks if the `ContentItem` has a the given part attached to it).
- `ExpressionExtensions`: Adds `System.Linq.Expressions`. (E.g. `StripResult` turns a `Func<T1, T2>` expression int an `Action<T1>` one).
- `HttpContextExtensions`: Some shortcuts for managing cookies.
- `IoExtensions`: Adds extensions for `String.IO` types. (E.g. `TextWriter.WriteLineInvariant` writes interpolated string in a culture invariant manner.)
- `JsonHelpers`: JSON syntax can be validated with the `ValidateJsonIfNotNull` helper method.
- `JsonStringExtensions`: Adds JSON related extensions for the `string` type. (E.g. `JsonHtmlContent` which safely serializes a string for use in `<script>` elements.)
- `MemoryCacheExtensions`: Adds extensions for `IMemoryCache` manipulation. (E.g. `GetOrNew<T>` type-safely returns the item or creates a new instance.)
- `MulticastDelegateExtensions`: Extensions for `MulticastDelegate`s, e.g. to invoke async delegates in a safe fashion.
- `NonSecurityRandomizer`: A wrapper around `System.Random` for explicitly not security-related usage-cases.
- `NumberExtensions`: Adds extensions for primitive numeric types. (E.g. `ToTechnicalString` converts `int` into culture invariant `string`.)
- `OrchardCoreBuilderExtensions`: Shortcuts when initializing Orchard with `OrchardCoreBuilder`, i.e. `AddOrchardCms()`.
- `RandomNumberGeneratorExtensions`: Shortcuts for retrieving cryptographically secure random numbers.
- `Sha256Helper`: A static helper class with the `ComputeHash` utility function that converts text into [SHA-256](https://en.wikipedia.org/wiki/SHA-256) hash string.
- `StringExtensions`: Adds common useful extensions to the `string` type. (E.g. `SplitByCommas` and `ContainsLoose`.)
- `StringHelper`: A static helper class with utility functions for concatenating and generating strings, particularly in a culture invariant manner.
- `Union`: A container type which is a union of two different types. Only either of the two types can be set at the same time.
- `UserServiceExtensions`: Adds extensions for `IUserService`. (E.g. `GetOrchardUserAsync` retrieves the user by user name or throws an exception if none found.)

Please see the inline documentation of each extension method to learn more.
10 changes: 10 additions & 0 deletions Lombiq.HelpfulLibraries.Common/Docs/Utilities.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
# Lombiq Helpful Libraries - Common Libraries - Utilities



- `ExceptionHelpers`: Using these helpers, arguments can be tested without writing `if` statements.
- `JsonHelpers`: JSON syntax can be validated with the `ValidateJsonIfNotNull` helper method.
- `NonSecurityRandomizer`: A wrapper around `System.Random` for explicitly not security-related usage-cases.
- `Sha256Helper`: A static helper class with the `ComputeHash` utility function that converts text into [SHA-256](https://en.wikipedia.org/wiki/SHA-256) hash string.
- `StringHelper`: A static helper class with utility functions for concatenating and generating strings, particularly in a culture invariant manner.
- `Union`: A container type which is a union of two different types. Only either of the two types can be set at the same time.
8 changes: 8 additions & 0 deletions Lombiq.HelpfulLibraries.Common/Docs/Validation.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
# Lombiq Helpful Libraries - Common Libraries - Validation



## Attributes

- `CommaSeparatedEmailAddresses`: Validates the given property to check if it's a comma-separated list containing valid email addresses.
- `EmailValidationHelpers`: Helpers to validate e-mail addresses.
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
using OrchardCore.ContentManagement;
using System.Diagnostics.CodeAnalysis;
using System.Linq;
using System.Threading.Tasks;
Expand Down Expand Up @@ -227,24 +226,6 @@ public static string JoinNotNullOrEmpty(this IEnumerable<string> strings, string
public static string Join(this IEnumerable<string> values, string separator = " ") =>
string.Join(separator, values ?? Enumerable.Empty<string>());

/// <summary>
/// Re-flattens <see cref="ILookup{TKey, ContentItem}"/> or <c>GroupBy</c> collections and eliminates duplicates
/// using <see cref="ContentItem.ContentItemVersionId"/>.
/// </summary>
public static IEnumerable<ContentItem> GetUniqueValues<TKey>(
this IEnumerable<IGrouping<TKey, ContentItem>> lookup) =>
lookup
.SelectMany(grouping => grouping)
.Unique(contentItem => contentItem.ContentItemVersionId);

/// <summary>
/// Re-flattens <see cref="ILookup{TKey, ContentItem}"/> or <c>GroupBy</c> collections and ensures that each
/// grouping only had one item (i.e. one-to-one relationships).
/// </summary>
public static IEnumerable<ContentItem> GetSingleValues<TKey>(
this IEnumerable<IGrouping<TKey, ContentItem>> lookup) =>
lookup.Select(item => item.Single());

/// <summary>
/// A simple conditional enumeration where the items are <see langword="yield"/> ed from the <paramref
/// name="collection"/> if the <paramref name="negativePredicate"/> returns <see langword="false"/>.
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
using Lombiq.HelpfulLibraries.Libraries.Utilities;
using Lombiq.HelpfulLibraries.Common.Utilities;
using System.Globalization;
using System.Threading.Tasks;

Expand Down
15 changes: 15 additions & 0 deletions Lombiq.HelpfulLibraries.Common/License.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
Copyright © 2011, [Lombiq Technologies Ltd.](https://lombiq.com)

All rights reserved.

For more information and requests about licensing please [contact us through our website](https://lombiq.com/contact-us).

Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:

* Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.

* Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.

* Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
Loading