Merge pull request #7 from StevanFreeborn/stevanfreeborn/chore/add-terms-and-privacy-pages
feat: add terms and privacy pages
This commit is contained in:
@@ -66,5 +66,7 @@ jobs:
|
||||
username: ${{ secrets.SSH_USERNAME }}
|
||||
key: ${{ secrets.SSH_KEY }}
|
||||
script: |
|
||||
export GOOGLE_ANALYTICS_TAG="${{ secrets.GOOGLE_ANALYTICS_TAG }}"
|
||||
chmod +x blog.stevanfreeborn.com/deploy.ps1
|
||||
sudo pwsh ./blog.stevanfreeborn.com/deploy.ps1 ${{ needs.build.outputs.version }}
|
||||
unset GOOGLE_ANALYTICS_TAG
|
||||
+2
-1
@@ -8,7 +8,8 @@ function StartContainer {
|
||||
|
||||
$containerName = "blog.stevanfreeborn.com.$containerColor"
|
||||
$postDirEnv = "FilePostServiceOptions__PostsDirectory=wwwroot/posts"
|
||||
$containerId = docker run -d -p 8080 --name $containerName --env $postDirEnv $dockerTag
|
||||
$googleTagEnv = "GoogleAnalyticsTag=$env:GOOGLE_ANALYTICS_TAG"
|
||||
$containerId = docker run -d -p 8080 --name $containerName --env $postDirEnv --env $googleTagEnv $dockerTag
|
||||
|
||||
if ($LASTEXITCODE -ne 0)
|
||||
{
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Blazor-Analytics" Version="3.12.0" />
|
||||
<PackageReference Include="Markdig" Version="0.36.2" />
|
||||
<PackageReference Include="System.ServiceModel.Syndication" Version="8.0.0" />
|
||||
<PackageReference Include="TestableIO.System.IO.Abstractions.Wrappers" Version="21.0.2" />
|
||||
|
||||
@@ -8,14 +8,17 @@
|
||||
<link rel="stylesheet" href="app.css" />
|
||||
<link rel="stylesheet" href="Blog.styles.css" />
|
||||
<link rel="shortcut icon" href="favicon.ico" type="image/x-icon">
|
||||
<HeadOutlet @rendermode="InteractiveServer" />
|
||||
<HeadOutlet />
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<Routes @rendermode="InteractiveServer" />
|
||||
<Routes />
|
||||
<NavigationTracker />
|
||||
<script src="https://cdn.jsdelivr.net/npm/anchor-js/anchor.min.js"></script>
|
||||
<script src="https://cdn.jsdelivr.net/npm/clipboard@2.0.11/dist/clipboard.min.js"></script>
|
||||
<script src="prism.js"></script>
|
||||
<script src="_framework/blazor.web.js"></script>
|
||||
<script src="_content/Blazor-Analytics/blazor-analytics.js"></script>
|
||||
</body>
|
||||
|
||||
</html>
|
||||
|
||||
@@ -0,0 +1,7 @@
|
||||
@namespace Blog.Components.Layout
|
||||
|
||||
<footer>
|
||||
<a href="https://github.com/StevanFreeborn/blog.stevanfreeborn.com">Code</a>
|
||||
<NavLink href="/terms">Terms</NavLink>
|
||||
<NavLink href="/privacy">Privacy</NavLink>
|
||||
</footer>
|
||||
@@ -0,0 +1,6 @@
|
||||
footer {
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
width: 100%;
|
||||
gap: 1rem;
|
||||
}
|
||||
@@ -2,9 +2,9 @@
|
||||
|
||||
<header class="container">
|
||||
<div class="title-container">
|
||||
<a href="/">
|
||||
<NavLink href="/">
|
||||
<h1>journal</h1>
|
||||
</a>
|
||||
</NavLink>
|
||||
</div>
|
||||
<div class="author-container">
|
||||
<span>by</span>
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
@inherits LayoutComponentBase
|
||||
@rendermode InteractiveServer
|
||||
|
||||
<div class="container">
|
||||
<Header />
|
||||
<main>
|
||||
@Body
|
||||
</main>
|
||||
<Footer />
|
||||
</div>
|
||||
|
||||
<div id="blazor-error-ui">
|
||||
@@ -13,3 +13,10 @@
|
||||
<a href="" class="reload">Reload</a>
|
||||
<a class="dismiss">🗙</a>
|
||||
</div>
|
||||
|
||||
<div id="components-reconnect-modal">
|
||||
<div>
|
||||
<h3>Connection lost</h3>
|
||||
<p>Attempting to reconnect...</p>
|
||||
</div>
|
||||
</div>
|
||||
@@ -6,12 +6,14 @@
|
||||
padding: 1.25rem;
|
||||
width: 100%;
|
||||
gap: 4rem;
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
main {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
width: 100%;
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
#blazor-error-ui {
|
||||
@@ -32,3 +34,11 @@ main {
|
||||
right: 0.75rem;
|
||||
top: 0.5rem;
|
||||
}
|
||||
|
||||
#components-reconnect-modal {
|
||||
display: none;
|
||||
position: fixed;
|
||||
bottom: 0;
|
||||
right: 0;
|
||||
padding: 0.5rem;
|
||||
}
|
||||
@@ -15,7 +15,8 @@
|
||||
}
|
||||
</div>
|
||||
|
||||
@code{
|
||||
@code
|
||||
{
|
||||
[CascadingParameter]
|
||||
private HttpContext? HttpContext { get; set; }
|
||||
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
@page "/{slug:nonfile}"
|
||||
@implements IAsyncDisposable
|
||||
@inject IPostService PostService
|
||||
@inject NavigationManager NavigationManager
|
||||
@inject IJSRuntime JSRuntime
|
||||
@@ -13,11 +12,12 @@ else
|
||||
{
|
||||
<PageTitle>@BlogPost.Title - journal</PageTitle>
|
||||
|
||||
<PageScript Src="./Components/Pages/Post.razor.js" />
|
||||
|
||||
<HeadContent>
|
||||
<meta name="description" content="@BlogPost.Lead">
|
||||
<link rel="stylesheet" href="markdown.css">
|
||||
<link rel="stylesheet" href="prism.css">
|
||||
<script src="prism.js"></script>
|
||||
</HeadContent>
|
||||
|
||||
<article>
|
||||
@@ -31,11 +31,11 @@ else
|
||||
</article>
|
||||
}
|
||||
|
||||
@code {
|
||||
@code
|
||||
{
|
||||
[Parameter]
|
||||
public string Slug { get; set; } = string.Empty;
|
||||
private PostWithContent? BlogPost;
|
||||
private IJSObjectReference? module;
|
||||
|
||||
protected override async Task OnInitializedAsync()
|
||||
{
|
||||
@@ -49,32 +49,4 @@ else
|
||||
|
||||
BlogPost = post;
|
||||
}
|
||||
|
||||
protected override async Task OnAfterRenderAsync(bool firstRender)
|
||||
{
|
||||
if (firstRender)
|
||||
{
|
||||
module = await JSRuntime.InvokeAsync<IJSObjectReference>("import", "./Components/Pages/Post.razor.js");
|
||||
await module.InvokeVoidAsync("addAnchors");
|
||||
await module.InvokeVoidAsync("addClipboard");
|
||||
}
|
||||
}
|
||||
|
||||
async ValueTask IAsyncDisposable.DisposeAsync()
|
||||
{
|
||||
try
|
||||
{
|
||||
if (module is not null)
|
||||
{
|
||||
await module.DisposeAsync();
|
||||
}
|
||||
}
|
||||
catch (Exception ex) when (ex is JSDisconnectedException)
|
||||
{
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Logger.LogError(ex, "Error disposing of the module");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,9 +1,37 @@
|
||||
let initialized = false;
|
||||
|
||||
export function onLoad() {
|
||||
if (initialized) {
|
||||
return;
|
||||
}
|
||||
|
||||
init();
|
||||
}
|
||||
|
||||
export function onUpdate() {
|
||||
if (initialized) {
|
||||
return;
|
||||
}
|
||||
|
||||
init();
|
||||
}
|
||||
|
||||
export function onDispose() {
|
||||
initialized = false;
|
||||
}
|
||||
|
||||
function init(){
|
||||
addAnchors();
|
||||
addClipboard();
|
||||
Prism.highlightAll();
|
||||
initialized = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @summary Adds anchors to each heading in the post
|
||||
* so that users can link directly to them.
|
||||
*/
|
||||
export function addAnchors() {
|
||||
function addAnchors() {
|
||||
const selectors = [
|
||||
'.markdown-body h2',
|
||||
'.markdown-body h3',
|
||||
@@ -21,7 +49,7 @@ export function addAnchors() {
|
||||
* @summary Adds a button to each code block that
|
||||
* allows users to copy the code to their clipboard.
|
||||
*/
|
||||
export function addClipboard() {
|
||||
function addClipboard() {
|
||||
const codeBlocks = document.querySelectorAll('pre');
|
||||
|
||||
codeBlocks.forEach((block) => {
|
||||
|
||||
@@ -0,0 +1,115 @@
|
||||
@page "/privacy"
|
||||
|
||||
<div class="container">
|
||||
<h2>Privacy Policy for journal</h2>
|
||||
|
||||
<section>
|
||||
<p>At journal, accessible from <a href="https://blog.stevanfreeborn.com">https://blog.stevanfreeborn.com</a>, one of
|
||||
our main priorities is the privacy of our visitors. This Privacy Policy document contains types of information
|
||||
that
|
||||
is collected and recorded by journal and how we use it.</p>
|
||||
|
||||
<p>
|
||||
If you have additional questions or require more information about our Privacy Policy, do not hesitate to contact
|
||||
us.
|
||||
</p>
|
||||
</section>
|
||||
|
||||
<section>
|
||||
<h3>Log Files</h3>
|
||||
|
||||
<p>
|
||||
journal follows a standard procedure of using log files. These files log visitors when they visit websites. All
|
||||
hosting companies do this and a part of hosting services' analytics. The information collected by log files
|
||||
include
|
||||
internet protocol (IP) addresses, browser type, Internet Service Provider (ISP), date and time stamp,
|
||||
referring/exit
|
||||
pages, and possibly the number of clicks. These are not linked to any information that is personally identifiable.
|
||||
The purpose of the information is for analyzing trends, administering the site, tracking users' movement on the
|
||||
website, and gathering demographic information. Our Privacy Policy was created with the help of the <a
|
||||
href="https://www.privacypolicyonline.com/privacy-policy-generator/">Privacy Policy Generator</a>.
|
||||
</p>
|
||||
</section>
|
||||
|
||||
<section>
|
||||
<h3>Cookies and Web Beacons</h3>
|
||||
|
||||
<p>
|
||||
Like any other website, journal uses "cookies". These cookies are used to store information including visitors'
|
||||
preferences, and the pages on the website that the visitor accessed or visited. The information is used to
|
||||
optimize
|
||||
the users' experience by customizing our web page content based on visitors' browser type and/or other
|
||||
information.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
For more general information on cookies, please read <a
|
||||
href="https://www.privacypolicyonline.com/what-are-cookies/">the "Cookies" article from the Privacy Policy
|
||||
Generator</a>.
|
||||
</p>
|
||||
</section>
|
||||
|
||||
<section>
|
||||
<h3>Privacy Policies</h3>
|
||||
|
||||
<p>You may consult this list to find the Privacy Policy for each of the advertising partners of journal.</p>
|
||||
|
||||
<p>
|
||||
Third-party ad servers or ad networks uses technologies like cookies, JavaScript, or Web Beacons that are used in
|
||||
their respective advertisements and links that appear on journal, which are sent directly to users' browser. They
|
||||
automatically receive your IP address when this occurs. These technologies are used to measure the effectiveness
|
||||
of
|
||||
their advertising campaigns and/or to personalize the advertising content that you see on websites that you visit.
|
||||
</p>
|
||||
|
||||
<p>Note that journal has no access to or control over these cookies that are used by third-party advertisers.</p>
|
||||
|
||||
</section>
|
||||
|
||||
<section>
|
||||
<h3>Third Party Privacy Policies</h3>
|
||||
|
||||
<p>
|
||||
journal's Privacy Policy does not apply to other advertisers or websites. Thus, we are advising you to consult the
|
||||
respective Privacy Policies of these third-party ad servers for more detailed information. It may include their
|
||||
practices and instructions about how to opt-out of certain options.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
You can choose to disable cookies through your individual browser options. To know more detailed information about
|
||||
cookie management with specific web browsers, it can be found at the browsers' respective websites. What Are
|
||||
Cookies?
|
||||
</p>
|
||||
</section>
|
||||
|
||||
<section>
|
||||
<h3>Children's Information</h3>
|
||||
|
||||
<p>
|
||||
Another part of our priority is adding protection for children while using the internet. We encourage parents and
|
||||
guardians to observe, participate in, and/or monitor and guide their online activity.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
journal does not knowingly collect any Personal Identifiable Information from children under the age of 13. If you
|
||||
think that your child provided this kind of information on our website, we strongly encourage you to contact us
|
||||
immediately and we will do our best efforts to promptly remove such information from our records.
|
||||
</p>
|
||||
</section>
|
||||
|
||||
<section>
|
||||
<h3>Online Privacy Policy Only</h3>
|
||||
|
||||
<p>
|
||||
This Privacy Policy applies only to our online activities and is valid for visitors to our website with regards to
|
||||
the information that they shared and/or collect in journal. This policy is not applicable to any information
|
||||
collected offline or via channels other than this website.
|
||||
</p>
|
||||
</section>
|
||||
|
||||
<section>
|
||||
<h3>Consent</h3>
|
||||
|
||||
<p>By using our website, you hereby consent to our Privacy Policy and agree to its Terms and Conditions.</p>
|
||||
</section>
|
||||
</div>
|
||||
@@ -0,0 +1,16 @@
|
||||
.container {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 2rem;
|
||||
|
||||
section {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 1rem;
|
||||
|
||||
ul {
|
||||
list-style-type: disc;
|
||||
padding-left: 1rem;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,269 @@
|
||||
@page "/terms"
|
||||
|
||||
<div class="container">
|
||||
<h2><strong>Terms and Conditions</strong></h2>
|
||||
|
||||
<section>
|
||||
<p>Welcome to journal!</p>
|
||||
|
||||
<p>These terms and conditions outline the rules and regulations for the use of journal's Website, located at
|
||||
<a href="https://blog.stevanfreeborn.com">https://blog.stevanfreeborn.com</a>.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
By accessing this website we assume you accept these terms and conditions. Do not continue to use journal if you
|
||||
do
|
||||
not agree to take all of the terms and conditions stated on this page.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
The following terminology applies to these Terms and Conditions, Privacy Statement and Disclaimer Notice and all
|
||||
Agreements: "Client", "You" and "Your" refers to you, the person log on this website and compliant to the
|
||||
Company's
|
||||
terms and conditions. "The Company", "Ourselves", "We", "Our" and "Us", refers to our Company. "Party", "Parties",
|
||||
or "Us", refers to both the Client and ourselves. All terms refer to the offer, acceptance and consideration of
|
||||
payment
|
||||
necessary to undertake the process of our assistance to the Client in the most appropriate manner for the express
|
||||
purpose of meeting the Client's needs in respect of provision of the Company's stated services, in accordance with
|
||||
and subject to, prevailing law of Netherlands. Any use of the above terminology or other words in the singular,
|
||||
plural,
|
||||
capitalization and/or he/she or they, are taken as interchangeable and therefore as referring to same. Our Terms
|
||||
and
|
||||
Conditions were created with the help of the <a
|
||||
href="https://www.privacypolicyonline.com/terms-conditions-generator/">Terms & Conditions Generator</a>.
|
||||
</p>
|
||||
</section>
|
||||
|
||||
<section>
|
||||
<h3><strong>Cookies</strong></h3>
|
||||
|
||||
<p>
|
||||
We employ the use of cookies. By accessing journal, you agreed to use cookies in agreement with the journal's
|
||||
Privacy Policy.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
Most interactive websites use cookies to let us retrieve the user's details for each visit. Cookies are used by
|
||||
our
|
||||
website to enable the functionality of certain areas to make it easier for people visiting our website. Some of
|
||||
our
|
||||
affiliate/advertising partners may also use cookies.
|
||||
</p>
|
||||
</section>
|
||||
|
||||
<section>
|
||||
<h3><strong>License</strong></h3>
|
||||
|
||||
<p>
|
||||
Unless otherwise stated, journal and/or its licensors own the intellectual property rights for all material on
|
||||
journal. All intellectual property rights are reserved. You may access this from journal for your own personal use
|
||||
subjected to restrictions set in these terms and conditions.
|
||||
</p>
|
||||
|
||||
<p>You must not:</p>
|
||||
<ul>
|
||||
<li>Republish material from journal</li>
|
||||
<li>Sell, rent or sub-license material from journal</li>
|
||||
<li>Reproduce, duplicate or copy material from journal</li>
|
||||
<li>Redistribute content from journal</li>
|
||||
</ul>
|
||||
|
||||
<p>This Agreement shall begin on the date hereof.</p>
|
||||
|
||||
<p>
|
||||
Parts of this website offer an opportunity for users to post and exchange opinions and information in certain
|
||||
areas
|
||||
of the website. journal does not filter, edit, publish or review Comments prior to their presence on the website.
|
||||
Comments do not reflect the views and opinions of journal,its agents and/or affiliates. Comments reflect the views
|
||||
and opinions of the person who post their views and opinions. To the extent permitted by applicable laws, journal
|
||||
shall
|
||||
not be liable for the Comments or for any liability, damages or expenses caused and/or suffered as a result of any
|
||||
use of and/or posting of and/or appearance of the Comments on this website.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
journal reserves the right to monitor all Comments and to remove any Comments which can be considered
|
||||
inappropriate, offensive or causes breach of these Terms and Conditions.
|
||||
</p>
|
||||
|
||||
<p>You warrant and represent that:</p>
|
||||
|
||||
<ul>
|
||||
<li>You are entitled to post the Comments on our website and have all necessary licenses and consents to do so;
|
||||
</li>
|
||||
<li>
|
||||
The Comments do not invade any intellectual property right, including without limitation copyright, patent or
|
||||
trademark of any third party;
|
||||
</li>
|
||||
<li>
|
||||
The Comments do not contain any defamatory, libelous, offensive, indecent or otherwise unlawful material which
|
||||
is
|
||||
an invasion of privacy
|
||||
</li>
|
||||
<li>
|
||||
The Comments will not be used to solicit or promote business or custom or present commercial activities or
|
||||
unlawful activity.
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
<p>
|
||||
You hereby grant journal a non-exclusive license to use, reproduce, edit and authorize others to use, reproduce
|
||||
and
|
||||
edit any of your Comments in any and all forms, formats or media.
|
||||
</p>
|
||||
</section>
|
||||
|
||||
<section>
|
||||
<h3><strong>Hyperlinking to our Content</strong></h3>
|
||||
|
||||
<p>
|
||||
The following organizations may link to our Website without prior written approval:
|
||||
</p>
|
||||
|
||||
<ul>
|
||||
<li>Government agencies;</li>
|
||||
<li>Search engines;</li>
|
||||
<li>News organizations;</li>
|
||||
<li>
|
||||
Online directory distributors may link to our Website in the same manner as they hyperlink to the Websites of
|
||||
other listed businesses; and
|
||||
</li>
|
||||
<li>
|
||||
System wide Accredited Businesses except soliciting non-profit organizations, charity shopping malls, and
|
||||
charity fundraising groups which may not hyperlink to our Web site.
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
<p>
|
||||
These organizations may link to our home page, to publications or to other Website information so long as the
|
||||
link:
|
||||
(a) is not in any way deceptive; (b) does not falsely imply sponsorship, endorsement or approval of the linking
|
||||
party and its products and/or services; and (c) fits within the context of the linking party's site.
|
||||
</p>
|
||||
|
||||
<p>We may consider and approve other link requests from the following types of organizations:</p>
|
||||
|
||||
<ul>
|
||||
<li>commonly-known consumer and/or business information sources;</li>
|
||||
<li>dot.com community sites;</li>
|
||||
<li>associations or other groups representing charities;</li>
|
||||
<li>online directory distributors;</li>
|
||||
<li>internet portals;</li>
|
||||
<li>accounting, law and consulting firms; and</li>
|
||||
<li>educational institutions and trade associations.</li>
|
||||
</ul>
|
||||
|
||||
<p>
|
||||
We will approve link requests from these organizations if we decide that: (a) the link would not make us look
|
||||
unfavorably to ourselves or to our accredited businesses; (b) the organization does not have any negative records
|
||||
with us; (c) the benefit to us from the visibility of the hyperlink compensates the absence of journal; and (d)
|
||||
the
|
||||
link
|
||||
is in the context of general resource information.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
These organizations may link to our home page so long as the link: (a) is not in any way deceptive; (b) does not
|
||||
falsely imply sponsorship, endorsement or approval of the linking party and its products or services; and (c) fits
|
||||
within the context of the linking party's site.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
If you are one of the organizations listed in paragraph 2 above and are interested in linking to our website, you
|
||||
must inform us by sending an e-mail to journal. Please include your name, your organization name, contact
|
||||
information as well as the URL of your site, a list of any URLs from which you intend to link to our Website, and
|
||||
a
|
||||
list of the
|
||||
URLs on our site to which you would like to link. Wait 2-3 weeks for a response.
|
||||
</p>
|
||||
|
||||
<p>Approved organizations may hyperlink to our Website as follows:</p>
|
||||
|
||||
<ul>
|
||||
<li>By use of our corporate name; or</li>
|
||||
<li>By use of the uniform resource locator being linked to; or</li>
|
||||
<li>
|
||||
By use of any other description of our Website being linked to that makes sense within the context and format of
|
||||
content on the linking party's site.
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
<p>No use of journal's logo or other artwork will be allowed for linking absent a trademark license agreement.</p>
|
||||
</section>
|
||||
|
||||
<section>
|
||||
<h3><strong>iFrames</strong></h3>
|
||||
|
||||
<p>
|
||||
Without prior approval and written permission, you may not create frames around our Webpages that alter in any way
|
||||
the visual presentation or appearance of our Website.
|
||||
</p>
|
||||
</section>
|
||||
|
||||
<section>
|
||||
<h3><strong>Content Liability</strong></h3>
|
||||
|
||||
<p>
|
||||
We shall not be hold responsible for any content that appears on your Website. You agree to protect and defend us
|
||||
against all claims that is rising on your Website. No link(s) should appear on any Website that may be interpreted
|
||||
as libelous, obscene or criminal, or which infringes, otherwise violates, or advocates the infringement or other
|
||||
violation of, any third party rights.
|
||||
</p>
|
||||
</section>
|
||||
|
||||
<section>
|
||||
<h3><strong>Reservation of Rights</strong></h3>
|
||||
|
||||
<p>
|
||||
We reserve the right to request that you remove all links or any particular link to our Website. You approve to
|
||||
immediately remove all links to our Website upon request. We also reserve the right to amen these terms and
|
||||
conditions and it's linking policy at any time. By continuously linking to our Website, you agree to be bound to
|
||||
and
|
||||
follow
|
||||
these linking terms and conditions.
|
||||
</p>
|
||||
</section>
|
||||
|
||||
<section>
|
||||
<h3><strong>Removal of links from our website</strong></h3>
|
||||
|
||||
<p>
|
||||
If you find any link on our Website that is offensive for any reason, you are free to contact and inform us any
|
||||
moment. We will consider requests to remove links but we are not obligated to or so or to respond to you directly.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
We do not ensure that the information on this website is correct, we do not warrant its completeness or accuracy;
|
||||
nor do we promise to ensure that the website remains available or that the material on the website is kept up to
|
||||
date.
|
||||
</p>
|
||||
</section>
|
||||
|
||||
<section>
|
||||
<h3><strong>Disclaimer</strong></h3>
|
||||
|
||||
<p>
|
||||
To the maximum extent permitted by applicable law, we exclude all representations, warranties and conditions
|
||||
relating to our website and the use of this website. Nothing in this disclaimer will:
|
||||
</p>
|
||||
|
||||
<ul>
|
||||
<li>limit or exclude our or your liability for death or personal injury;</li>
|
||||
<li>limit or exclude our or your liability for fraud or fraudulent misrepresentation;</li>
|
||||
<li>limit any of our or your liabilities in any way that is not permitted under applicable law; or</li>
|
||||
<li>exclude any of our or your liabilities that may not be excluded under applicable law.</li>
|
||||
</ul>
|
||||
|
||||
<p>
|
||||
The limitations and prohibitions of liability set in this Section and elsewhere in this disclaimer: (a) are
|
||||
subject
|
||||
to the preceding paragraph; and (b) govern all liabilities arising under the disclaimer, including liabilities
|
||||
arising in contract, in tort and for breach of statutory duty.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
As long as the website and the information and services on the website are provided free of charge, we will not be
|
||||
liable for any loss or damage of any nature.
|
||||
</p>
|
||||
</section>
|
||||
</div>
|
||||
@@ -0,0 +1,16 @@
|
||||
.container {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 2rem;
|
||||
|
||||
section {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 1rem;
|
||||
|
||||
ul {
|
||||
list-style-type: disc;
|
||||
padding-left: 1rem;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -18,20 +18,23 @@
|
||||
<section role="feed" class="container">
|
||||
@foreach (var post in Posts)
|
||||
{
|
||||
<a href="/@post.Slug">
|
||||
<article>
|
||||
<article>
|
||||
<NavLink href="@GetLink(post)">
|
||||
<h3 title="@post.Title" >@post.Title</h3>
|
||||
<p class="date">@post.PublishedAt.ToString("MMMM d, yyyy")</p>
|
||||
<p class="lead">@post.Lead</p>
|
||||
</article>
|
||||
</a>
|
||||
</NavLink>
|
||||
</article>
|
||||
}
|
||||
</section>
|
||||
}
|
||||
|
||||
@code {
|
||||
@code
|
||||
{
|
||||
private List<Post>? Posts;
|
||||
|
||||
private string GetLink(Post post) => $"/{post.Slug}";
|
||||
|
||||
protected override async Task OnInitializedAsync()
|
||||
{
|
||||
Posts = await postService.GetPostsAsync();
|
||||
|
||||
@@ -4,43 +4,45 @@
|
||||
width: 100%;
|
||||
gap: 2rem;
|
||||
|
||||
& a {
|
||||
padding: 1rem;
|
||||
border-radius: 1rem;
|
||||
background-color: var(--color-background-soft);
|
||||
transition-property: transform;
|
||||
transition-duration: 100ms;
|
||||
transition-timing-function: ease-in-out
|
||||
}
|
||||
|
||||
& a:active {
|
||||
transform: scale(0.95)
|
||||
}
|
||||
|
||||
& article {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 0.25rem;
|
||||
|
||||
& h3 {
|
||||
background: linear-gradient(to right top, #db3e3e, #df3741, #e32e44, #e72347, #eb124b);
|
||||
background-clip: text;
|
||||
-webkit-background-clip: text;
|
||||
-webkit-text-fill-color: transparent;
|
||||
font-size: 2rem;
|
||||
font-weight: 900;
|
||||
& a {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 0.25rem;
|
||||
padding: 1rem;
|
||||
border-radius: 1rem;
|
||||
background-color: var(--color-background-soft);
|
||||
transition-property: transform;
|
||||
transition-duration: 100ms;
|
||||
transition-timing-function: ease-in-out;
|
||||
|
||||
& h3 {
|
||||
background: linear-gradient(to right top, #db3e3e, #df3741, #e32e44, #e72347, #eb124b);
|
||||
background-clip: text;
|
||||
-webkit-background-clip: text;
|
||||
-webkit-text-fill-color: transparent;
|
||||
font-size: 2rem;
|
||||
font-weight: 900;
|
||||
}
|
||||
|
||||
& .date {
|
||||
font-size: 0.85rem;
|
||||
}
|
||||
|
||||
& h3,
|
||||
& .date,
|
||||
& .lead {
|
||||
text-wrap: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
}
|
||||
|
||||
& .date {
|
||||
font-size: 0.85rem;
|
||||
}
|
||||
|
||||
& h3,
|
||||
& .date,
|
||||
& .lead {
|
||||
text-wrap: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
& a:active {
|
||||
transform: scale(0.95)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
@namespace Blog.Components.Utils
|
||||
|
||||
@*
|
||||
custom web-component defined in:
|
||||
Blog.lib.module.js
|
||||
|
||||
Manages loading colocated scripts
|
||||
for pages and invoking expected
|
||||
lifecycle methods to support
|
||||
enhanced page navigation.
|
||||
*@
|
||||
<page-script src="@Src"></page-script>
|
||||
|
||||
@code
|
||||
{
|
||||
[Parameter]
|
||||
[EditorRequired]
|
||||
public string Src { get; set; } = default!;
|
||||
}
|
||||
@@ -8,3 +8,6 @@
|
||||
@using Microsoft.JSInterop
|
||||
@using Blog
|
||||
@using Blog.Components
|
||||
@using Blog.Components.Utils
|
||||
@using Blazor.Analytics
|
||||
@using Blazor.Analytics.Components
|
||||
+7
-7
@@ -1,12 +1,15 @@
|
||||
using Blazor.Analytics;
|
||||
|
||||
var builder = WebApplication.CreateBuilder(args);
|
||||
|
||||
builder.Services.ConfigureOptions<FilePostServiceOptionsSetup>();
|
||||
builder.Services.AddSingleton<IFileSystem, FileSystem>();
|
||||
builder.Services.AddScoped<IPostService, FilePostService>();
|
||||
|
||||
builder.Services
|
||||
.AddRazorComponents()
|
||||
.AddInteractiveServerComponents();
|
||||
builder.Services.AddRazorComponents();
|
||||
|
||||
var googleTag = builder.Configuration["GoogleAnalyticsTag"];
|
||||
builder.Services.AddGoogleAnalytics(googleTag);
|
||||
|
||||
var app = builder.Build();
|
||||
|
||||
@@ -88,10 +91,7 @@ app
|
||||
.WithDisplayName("RSS Feed")
|
||||
.WithDescription("RSS feed for the blog");
|
||||
|
||||
app
|
||||
.MapRazorComponents<App>()
|
||||
.AddInteractiveServerRenderMode();
|
||||
|
||||
app.MapRazorComponents<App>();
|
||||
|
||||
app.Run();
|
||||
|
||||
|
||||
@@ -10,5 +10,6 @@
|
||||
"FilePostServiceOptions": {
|
||||
"PostsDirectory": "wwwroot/posts"
|
||||
},
|
||||
"DetailedErrors": "true"
|
||||
"DetailedErrors": "true",
|
||||
"GoogleAnalyticsTag": ""
|
||||
}
|
||||
@@ -0,0 +1,95 @@
|
||||
const pageScriptInfoBySrc = new Map();
|
||||
|
||||
function registerPageScriptElement(src) {
|
||||
if (!src) {
|
||||
throw new Error('Must provide a non-empty value for the "src" attribute.');
|
||||
}
|
||||
|
||||
let pageScriptInfo = pageScriptInfoBySrc.get(src);
|
||||
|
||||
if (pageScriptInfo) {
|
||||
pageScriptInfo.referenceCount++;
|
||||
return;
|
||||
}
|
||||
|
||||
pageScriptInfo = { referenceCount: 1, module: null };
|
||||
pageScriptInfoBySrc.set(src, pageScriptInfo);
|
||||
initializePageScriptModule(src, pageScriptInfo);
|
||||
}
|
||||
|
||||
function unregisterPageScriptElement(src) {
|
||||
if (!src) {
|
||||
return;
|
||||
}
|
||||
|
||||
const pageScriptInfo = pageScriptInfoBySrc.get(src);
|
||||
|
||||
if (!pageScriptInfo) {
|
||||
return;
|
||||
}
|
||||
|
||||
pageScriptInfo.referenceCount--;
|
||||
}
|
||||
|
||||
async function initializePageScriptModule(src, pageScriptInfo) {
|
||||
// If the path is relative, normalize it by by making it an absolute URL
|
||||
// with document's the base HREF.
|
||||
if (src.startsWith("./")) {
|
||||
src = new URL(src.substr(2), document.baseURI).toString();
|
||||
}
|
||||
|
||||
const module = await import(src);
|
||||
|
||||
if (pageScriptInfo.referenceCount <= 0) {
|
||||
// All page-script elements with the same 'src' were
|
||||
// unregistered while we were loading the module.
|
||||
return;
|
||||
}
|
||||
|
||||
pageScriptInfo.module = module;
|
||||
module.onLoad?.();
|
||||
module.onUpdate?.();
|
||||
}
|
||||
|
||||
function onEnhancedLoad() {
|
||||
// Start by invoking 'onDispose' on any modules that are no longer referenced.
|
||||
for (const [src, { module, referenceCount }] of pageScriptInfoBySrc) {
|
||||
if (referenceCount <= 0) {
|
||||
module?.onDispose?.();
|
||||
pageScriptInfoBySrc.delete(src);
|
||||
}
|
||||
}
|
||||
|
||||
// Then invoke 'onUpdate' on the remaining modules.
|
||||
for (const { module } of pageScriptInfoBySrc.values()) {
|
||||
module?.onUpdate?.();
|
||||
}
|
||||
}
|
||||
|
||||
export function afterWebStarted(blazor) {
|
||||
customElements.define(
|
||||
"page-script",
|
||||
class extends HTMLElement {
|
||||
static observedAttributes = ["src"];
|
||||
|
||||
// We use attributeChangedCallback instead of connectedCallback
|
||||
// because a page-script element might get reused between enhanced
|
||||
// navigations.
|
||||
attributeChangedCallback(name, oldValue, newValue) {
|
||||
if (name !== "src") {
|
||||
return;
|
||||
}
|
||||
|
||||
this.src = newValue;
|
||||
unregisterPageScriptElement(oldValue);
|
||||
registerPageScriptElement(newValue);
|
||||
}
|
||||
|
||||
disconnectedCallback() {
|
||||
unregisterPageScriptElement(this.src);
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
blazor.addEventListener("enhancedload", onEnhancedLoad);
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
@inherits Bunit.TestContext
|
||||
|
||||
@code {
|
||||
[Test]
|
||||
public void Footer_WhenRendered_ItShouldRenderFooterWithNavLinks()
|
||||
{
|
||||
var ctx = new TestContext();
|
||||
|
||||
var cut = ctx.Render(@<Footer />);
|
||||
|
||||
var footer = cut.Find("footer");
|
||||
footer.Should().NotBeNull();
|
||||
|
||||
var navLinks = cut.FindAll("a");
|
||||
navLinks.Count.Should().Be(3);
|
||||
|
||||
var navLinkTexts = navLinks.Select(x => x.TextContent);
|
||||
|
||||
navLinkTexts.Should().Contain("Code");
|
||||
navLinkTexts.Should().Contain("Terms");
|
||||
navLinkTexts.Should().Contain("Privacy");
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user