Contact Form
How Insert Update Delete curd step by Step in Angular 9 with api Asp.net Core EF DB First approach
Step .1
app.module.ts
import { BrowserModule } from "@angular/platform-browser";
import { NgModule } from "@angular/core";
import { HttpClientModule } from "@angular/common/http";
import { ReactiveFormsModule } from "@angular/forms";
import { AppRoutingModule } from "./app-routing.module";
import { AppComponent } from "./app.component";
import { BlogPostsComponent } from "./Components/blog-posts/blog-posts.component";
import { BlogPostComponent } from "./Components/blog-post/blog-post.component";
import { BlogPostAddEditComponent } from "./Components/blog-post-add-edit/blog-post-add-edit.component";
import { BlogPostService } from "./services/blog-post.service";
@NgModule({
declarations: [
AppComponent,
BlogPostsComponent,
BlogPostComponent,
BlogPostAddEditComponent,
],
imports: [
BrowserModule,
HttpClientModule,
AppRoutingModule,
ReactiveFormsModule,
],
providers: [BlogPostService],
bootstrap: [AppComponent],
})
export class AppModule {}
app.component.html
<div class="container">
<a [routerLink]="['/']" class="btn btn-danger">Home</a>
<router-outlet></router-outlet>
</div>
blog-posts.component.html
blog-posts.component.ts<h1>Blog posts</h1> <p *ngIf="!(blogPosts$ | async)"><em>Loading...</em></p> <p> <a [routerLink]="['/add']" class="btn btn-primary float-right mb-3">New post</a> </p> <table class="table table-sm table-hover" *ngIf="(blogPosts$ | async)?.length>0"> <thead> <tr> <th>#</th> <th>Title</th> <th>Creator</th> <th>Date</th> <th></th> <th></th> </tr> </thead> <tbody> <tr *ngFor="let blogPost of (blogPosts$ | async)"> <td>{{ blogPost.employeeId }}</td> <td><a [routerLink]="['/blogpost/', blogPost.employeeId]">{{ blogPost.employeeName }}</a></td> <td>{{ blogPost.phoneNumber }}</td> <td>{{ blogPost.skillId }}</td> <td><a [routerLink]="['/blogpost/edit/', blogPost.employeeId]" class="btn btn-primary btn-sm float-right">Edit</a></td> <td><a [routerLink]="" (click)="delete(blogPost.employeeId)" class="btn btn-danger btn-sm float-right">Delete</a></td> </tr> </tbody> </table>
blog-post-add-edit.component.htmlimport { Component, OnInit } from '@angular/core'; import { BlogPost } from 'src/app/services/models/BlogPos'; import { Observable } from 'rxjs'; import { BlogPostService } from 'src/app/services/blog-post.service'; @Component({ selector: 'app-blog-posts', templateUrl: './blog-posts.component.html', styleUrls: ['./blog-posts.component.css'] }) export class BlogPostsComponent implements OnInit { blogPosts$: Observable<BlogPost[]>; constructor(private blogPostService: BlogPostService) { } ngOnInit() { this.loadBlogPosts(); } loadBlogPosts() { this.blogPosts$ = this.blogPostService.getBlogPosts(); } delete(postId) { const ans = confirm('Do you want to delete blog post with id: ' + postId); if (ans) { this.blogPostService.deleteBlogPost(postId).subscribe((data) => { this.loadBlogPosts(); }); } } }
blog-post-add-edit.component.ts<h1>{{actionType}} blog post</h1> <form [formGroup]="form" (ngSubmit)="save()" #formDir="ngForm" novalidate> <div class="form-group row"> <label class=" control-label col-md-12">employee Name</label> <div class="col-md-12"> <input class="form-control" type="text" formControlName="employeeName"> </div> <span class="text-danger ml-3" *ngIf="employeeName.invalid && formDir.submitted"> employee Name is required. </span> </div> <div class="form-group row"> <label class="control-label col-md-12" for="phoneNumber">phone Number</label> <div class="col-md-12"> <textarea class="form-control" rows="15" formControlName="phoneNumber"></textarea> </div> <span class="text-danger ml-3" *ngIf="phoneNumber.invalid && formDir.submitted"> phone Number is required. </span> </div> <div class="form-group row"> <label class=" control-label col-md-12">skillId</label> <div class="col-md-12"> <input class="form-control" type="text" formControlName="skillId"> </div> <span class="text-danger ml-3" *ngIf="skillId.invalid && formDir.submitted"> skillId is required. </span> </div> <div class="form-group row"> <label class=" control-label col-md-12">yearsExperience</label> <div class="col-md-12"> <input class="form-control" type="text" formControlName="yearsExperience"> </div> <span class="text-danger ml-3" *ngIf="yearsExperience.invalid && formDir.submitted"> yearsExperience is required. </span> </div> <div class="form-group"> <button type="submit" class="btn btn-success float-right">Save</button> <button class="btn btn-secondary float-left" (click)="cancel()">Cancel</button> </div> </form>
BlogPos.tsimport { Component, OnInit } from '@angular/core'; import { BlogPost } from 'src/app/services/models/BlogPos'; import { FormBuilder, FormGroup, Validators } from '@angular/forms'; import { Router, ActivatedRoute } from '@angular/router'; import { BlogPostService } from 'src/app/services/blog-post.service'; @Component({ selector: 'app-blog-post-add-edit', templateUrl: './blog-post-add-edit.component.html', styleUrls: ['./blog-post-add-edit.component.css'] }) export class BlogPostAddEditComponent implements OnInit { form: FormGroup; actionType: string; formemployeeName: string; formphoneNumber: string; formskillId: string; formyearsExperience: string; employeeId: number; errorMessage: any; existingBlogPost: BlogPost; // tslint:disable-next-line: max-line-length constructor(private blogPostService: BlogPostService, private formBuilder: FormBuilder, private avRoute: ActivatedRoute, private router: Router) { const idParam = 'id'; this.actionType = 'Add'; this.formemployeeName = 'employeeName'; this.formphoneNumber = 'phoneNumber'; this.formskillId = 'skillId'; this.formyearsExperience = 'yearsExperience'; if (this.avRoute.snapshot.params[idParam]) { this.employeeId = this.avRoute.snapshot.params[idParam]; } this.form = this.formBuilder.group( { employeeIdemployeeId: 0, employeeName: ['', [Validators.required]], phoneNumber: ['', [Validators.required]], skillId: ['', [Validators.required]], yearsExperience: ['', [Validators.required]] } ); } ngOnInit() { if (this.employeeId > 0) { this.actionType = 'Edit'; this.blogPostService.getBlogPost(this.employeeId) .subscribe(data => ( this.existingBlogPost = data, this.form.controls[this.formemployeeName].setValue(data.employeeName), this.form.controls[this.formphoneNumber].setValue(data.phoneNumber), this.form.controls[this.formskillId].setValue(data.skillId), this.form.controls[this.formyearsExperience].setValue(data.yearsExperience) )); } } save() { if (!this.form.valid) { return; } if (this.actionType === 'Add') { const blogPost: BlogPost = { employeeName: this.form.get(this.formemployeeName).value, phoneNumber: this.form.get(this.formphoneNumber).value, skillId: this.form.get(this.formskillId).value, yearsExperience: this.form.get(this.formyearsExperience).value }; this.blogPostService.saveBlogPost(blogPost) .subscribe((data) => { this.router.navigate(['/blogpost', data.employeeId]); }); } if (this.actionType === 'Edit') { const blogPost: BlogPost = { employeeId: this.existingBlogPost.employeeId, employeeName: this.form.get(this.formemployeeName).value, phoneNumber: this.form.get(this.formphoneNumber).value, skillId: this.form.get(this.formskillId).value, yearsExperience: this.form.get(this.formyearsExperience).value }; this.blogPostService.updateBlogPost(blogPost.employeeId, blogPost) .subscribe((data) => { this.router.navigate([this.router.url]); }); } } cancel() { this.router.navigate(['/']); } get employeeName() { return this.form.get(this.formemployeeName); } get phoneNumber() { return this.form.get(this.formphoneNumber); } get skillId() { return this.form.get(this.formskillId); } get yearsExperience() { return this.form.get(this.formyearsExperience); } }
blog-post.service.tsexport class BlogPost { employeeId?: number; employeeName: string; phoneNumber: string; skillId: number; yearsExperience: number; }
app-routing.module.tsimport { Injectable } from '@angular/core'; import { HttpClient, HttpHeaders } from '@angular/common/http'; import { Observable, throwError } from 'rxjs'; import { retry, catchError } from 'rxjs/operators'; import { environment } from 'src/environments/environment'; import { BlogPost } from './models/BlogPos'; @Injectable({ providedIn: 'root' }) export class BlogPostService { myAppUrl: string; myApiUrl: string; httpOptions = { headers: new HttpHeaders({ 'Content-Type': 'application/json; charset=utf-8' }) }; constructor(private http: HttpClient) { this.myAppUrl = environment.appUrl; this.myApiUrl = 'api/TblEmployees/'; } getBlogPosts(): Observable<BlogPost[]> { return this.http.get<BlogPost[]>(this.myAppUrl + this.myApiUrl) .pipe( retry(1), catchError(this.errorHandler) ); } getBlogPost(postId: number): Observable<BlogPost> { return this.http.get<BlogPost>(this.myAppUrl + this.myApiUrl + postId) .pipe( retry(1), catchError(this.errorHandler) ); } saveBlogPost(blogPost): Observable<BlogPost> { return this.http.post<BlogPost>(this.myAppUrl + this.myApiUrl, JSON.stringify(blogPost), this.httpOptions) .pipe( retry(1), catchError(this.errorHandler) ); } updateBlogPost(postId: number, blogPost): Observable<BlogPost> { return this.http.put<BlogPost>(this.myAppUrl + this.myApiUrl + postId, JSON.stringify(blogPost), this.httpOptions) .pipe( retry(1), catchError(this.errorHandler) ); } deleteBlogPost(postId: number): Observable<BlogPost> { return this.http.delete<BlogPost>(this.myAppUrl + this.myApiUrl + postId) .pipe( retry(1), catchError(this.errorHandler) ); } errorHandler(error) { let errorMessage = ''; if (error.error instanceof ErrorEvent) { // Get client-side error errorMessage = error.error.message; } else { // Get server-side error errorMessage = `Error Code: ${error.status}\nMessage: ${error.message}`; } console.log(errorMessage); return throwError(errorMessage); } }
import { NgModule } from '@angular/core';
import { Routes, RouterModule } from '@angular/router';
import { BlogPostsComponent } from './Components/blog-posts/blog-posts.component';
import { BlogPostComponent } from './Components/blog-post/blog-post.component';
import { BlogPostAddEditComponent } from './Components/blog-post-add-edit/blog-post-add-edit.component';
const routes: Routes = [
{ path: '', component: BlogPostsComponent, pathMatch: 'full' },
{ path: 'blogpost/:id', component: BlogPostComponent },
{ path: 'add', component: BlogPostAddEditComponent },
{ path: 'blogpost/edit/:id', component: BlogPostAddEditComponent },
{ path: '**', redirectTo: '/' }
];
@NgModule({
imports: [RouterModule.forRoot(routes)],
exports: [RouterModule]
})
export class AppRoutingModule { }
API
Install-Package Microsoft.EntityFrameworkCore.SqlServer
1. Scaffold-DbContext "Server=.;Database=MbkTest;Trusted_Connection=True;" Microsoft.EntityFrameworkCore.SqlServer -OutputDir Models
2. Scaffold-DbContext "Server=.;Database=MbkTest;Trusted_Connection=True;" Microsoft.EntityFrameworkCore.SqlServer -OutputDir Models -Tables tblSkills -Force
Scaffold-DbContext [-Connection] [-Provider] [-OutputDir] [-Context] [-Schemas>] [-Tables>]
[-DataAnnotations] [-Force] [-Project] [-StartupProject] [<CommonParameters>]
Startup.cs
appsettings.jsonusing System; using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; using CurdAPP2.Models; using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Hosting; using Microsoft.AspNetCore.HttpsPolicy; using Microsoft.AspNetCore.Mvc; using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Logging; using Microsoft.Extensions.Options; namespace CurdAPP2 { public class Startup { public Startup(IConfiguration configuration) { Configuration = configuration; } public IConfiguration Configuration { get; } // This method gets called by the runtime. Use this method to add services to the container. public void ConfigureServices(IServiceCollection services) { services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_2); services.AddDbContext<MbkTestContext>(options => options.UseSqlServer(Configuration.GetConnectionString("MbkTestCon"))); services.AddCors(options => { options.AddPolicy("CorsPolicy", builder => builder.AllowAnyOrigin() .AllowAnyMethod() .AllowAnyHeader() .AllowCredentials()); }); } // This method gets called by the runtime. Use this method to configure the HTTP request pipeline. public void Configure(IApplicationBuilder app, IHostingEnvironment env) { if (env.IsDevelopment()) { app.UseDeveloperExceptionPage(); } else { // The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts. app.UseHsts(); } //app.UseMvc(); app.UseCors("CorsPolicy"); app.UseHttpsRedirection(); app.UseStaticFiles(); //app.UseSpaStaticFiles(); app.UseMvc(routes => { routes.MapRoute( name: "default", template: "{controller}/{action=Index}/{id?}"); }); } } }
TblEmployeesController.cs{ "Logging": { "LogLevel": { "Default": "Warning" } }, "AllowedHosts": "*", "ConnectionStrings": { "MbkTestCon": "Server=.;Database=MbkTest;Trusted_Connection=True;MultipleActiveResultSets=true" } }
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Microsoft.EntityFrameworkCore;
using CurdAPP2.Models;
namespace CurdAPP2.Controllers
{
[Route("api/[controller]")]
[ApiController]
public class TblEmployeesController : ControllerBase
{
private readonly MbkTestContext _context;
public TblEmployeesController(MbkTestContext context)
{
_context = context;
}
// GET: api/TblEmployees
[HttpGet]
public async Task<ActionResult<IEnumerable<TblEmployees>>> GetTblEmployees()
{
return await _context.TblEmployees.ToListAsync();
}
// GET: api/TblEmployees/5
[HttpGet("{id}")]
public async Task<ActionResult<TblEmployees>> GetTblEmployees(int id)
{
var tblEmployees = await _context.TblEmployees.FindAsync(id);
if (tblEmployees == null)
{
return NotFound();
}
return tblEmployees;
}
// PUT: api/TblEmployees/5
[HttpPut("{id}")]
public async Task<IActionResult> PutTblEmployees(int id, TblEmployees tblEmployees)
{
if (id != tblEmployees.EmployeeId)
{
return BadRequest();
}
_context.Entry(tblEmployees).State = EntityState.Modified;
try
{
await _context.SaveChangesAsync();
}
catch (DbUpdateConcurrencyException)
{
if (!TblEmployeesExists(id))
{
return NotFound();
}
else
{
throw;
}
}
return NoContent();
}
// POST: api/TblEmployees
[HttpPost]
public async Task<ActionResult<TblEmployees>> PostTblEmployees(TblEmployees tblEmployees)
{
_context.TblEmployees.Add(tblEmployees);
await _context.SaveChangesAsync();
return CreatedAtAction("GetTblEmployees", new { id = tblEmployees.EmployeeId }, tblEmployees);
}
// DELETE: api/TblEmployees/5
[HttpDelete("{id}")]
public async Task<ActionResult<TblEmployees>> DeleteTblEmployees(int id)
{
var tblEmployees = await _context.TblEmployees.FindAsync(id);
if (tblEmployees == null)
{
return NotFound();
}
_context.TblEmployees.Remove(tblEmployees);
await _context.SaveChangesAsync();
return tblEmployees;
}
private bool TblEmployeesExists(int id)
{
return _context.TblEmployees.Any(e => e.EmployeeId == id);
}
}
}
CREATE TABLE [dbo].[tblEmployees](
[EmployeeID] [int] IDENTITY(1,1) NOT FOR REPLICATION NOT NULL,
[EmployeeName] [varchar](50) NULL,
[PhoneNumber] [varchar](50) NULL,
[SkillID] [int] NULL,
[YearsExperience] [int] NULL,
PRIMARY KEY CLUSTERED
(
[EmployeeID] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY]
GO
SET ANSI_PADDING OFF
GO
SET IDENTITY_INSERT [dbo].[tblEmployees] ON
INSERT [dbo].[tblEmployees] ([EmployeeID], [EmployeeName], [PhoneNumber], [SkillID], [YearsExperience]) VALUES (1, N'Suhana Kalla', N'9869569634', 2, 11111)
INSERT [dbo].[tblEmployees] ([EmployeeID], [EmployeeName], [PhoneNumber], [SkillID], [YearsExperience]) VALUES (2, N'Ashish Kalla', N'9869166077', 8, 14)
INSERT [dbo].[tblEmployees] ([EmployeeID], [EmployeeName], [PhoneNumber], [SkillID], [YearsExperience]) VALUES (3, N'Manoj Kalla', N'9869569634', 1, 24)
INSERT [dbo].[tblEmployees] ([EmployeeID], [EmployeeName], [PhoneNumber], [SkillID], [YearsExperience]) VALUES (6, N'Murli Vyas', N'9261166012', 5, 18)
INSERT [dbo].[tblEmployees] ([EmployeeID], [EmployeeName], [PhoneNumber], [SkillID], [YearsExperience]) VALUES (8, N'wer', N'1', 1, 1)
SET IDENTITY_INSERT [dbo].[tblEmployees] OFF
Configuration React JS ( Installation ) in Windows
Install NodeJS and NPM:
Go to
https://nodejs.org/en/
Download latest version of NodeJS LTS with Administrator privileges.
Download latest version of NodeJS LTS with Administrator privileges.
Install Visual Studio
Code:
Go to
https://code.visualstudio.com/
Downloaded latest version.
Run VS Code.
Downloaded latest version.
Run VS Code.
It should recommend to
install Git. Follow instructions to install Git for Windows.
After installation,
should be able to get version of git.
Open terminal and type:
COPY
git --version
Followed by:
COPY
node --version
Followed by:
COPY
npm --version
Each should give
versions installed on Windows.
Install React from
terminal:
COPY
npm install -g create-react-app
If successful, you
should be able to get version:
COPY
create-react-app --version
Create react project:
COPY
create-react-app <projectname>
Run project:
COPY
cd <projectname>
npm start
Expected output in
terminal (and should auto open browser to port 3000):
COPY
Compiled successfully!
You can now view <project_name> in the browser.
Local: http://localhost:3000/
On Your Network: http://<your_ip_address>:3000/
Note that the development build is not optimized.
To create a production build, use yarn build.
how To block Facebook and youtube on Mikrotik on layer 7
Regular expression rule at Layer7 Protocols Press + Sign to add
^.+(facebook.com|youtube).*$
Create Filter Rule as below steps
1. choose Foward at General tab
2. add Regular expression layer 7
3. Action Tab action Drop
It's working 100%
How to Create Script port forwarding from local using mikrotik
Just run this Script, to change your IP Scheme and interface and Port
/ip firewall nat
add action=dst-nat chain=dstnat disabled=no dst-port=80 in-interface=ether1
protocol=tcp to-addresses=192.168.1.20 to-ports=80
How to Create Mikrotik Queue Script of all IP
To run this Script ,Just open Mikrotik Router terminal and run
Your can Change download and upload limit and add your ip Scheme
:for x from 1 to 254 do={/queue simple add name="queue-$x" max-limit=1M/1M target="192.168.10.$x"}
Your can Change download and upload limit and add your ip Scheme
how to WAN combine in Mikrotik
/ip address
add address=192.168.0.1/24 network=192.168.0.0 broadcast=192.168.0.255 interface=Local
add address=192.168.1.2/24 network=192.168.1.0 broadcast=192.168.1.255 interface=WAN1
add address=192.168.2.2/24 network=192.168.2.0 broadcast=192.168.2.255 interface=WAN2
/ip dns set allow-remote-requests=yes cache-max-ttl=1w cache-size=5000KiB max-udp-packet-size=512 servers=8.8.8.8
/ip firewall mangle
add chain=input in-interface=WAN1 action=mark-connection new-connection-mark=WAN1_conn
add chain=input in-interface=WAN2 action=mark-connection new-connection-mark=WAN2_conn
add chain=output connection-mark=WAN1_conn action=mark-routing new-routing-mark=to_WAN1
add chain=output connection-mark=WAN2_conn action=mark-routing new-routing-mark=to_WAN2
add chain=prerouting dst-address=192.168.1.0/24 action=accept in-interface=Local
add chain=prerouting dst-address=192.168.2.0/24 action=accept in-interface=Local
add chain=prerouting dst-address-type=!local in-interface=Local per-connection-classifier=both-addresses-and-ports:2/0 action=mark-connection new-connection-mark=WAN1_conn passthrough=yes
add chain=prerouting dst-address-type=!local in-interface=Local per-connection-classifier=both-addresses-and-ports:2/1 action=mark-connection new-connection-mark=WAN2_conn passthrough=yes
add chain=prerouting connection-mark=WAN1_conn in-interface=Local action=mark-routing new-routing-mark=to_WAN1
add chain=prerouting connection-mark=WAN2_conn in-interface=Local action=mark-routing new-routing-mark=to_WAN2
/ip route
add dst-address=0.0.0.0/0 gateway=192.168.1.1 routing-mark=to_WAN1 check-gateway=ping
add dst-address=0.0.0.0/0 gateway=192.168.2.1 routing-mark=to_WAN2 check-gateway=ping
add dst-address=0.0.0.0/0 gateway=192.168.1.1 distance=1 check-gateway=ping
add dst-address=0.0.0.0/0 gateway=192.168.2.1 distance=2 check-gateway=ping
/ip firewall nat
add chain=srcnat out-interface=WAN1 action=masquerade
add chain=srcnat out-interface=WAN2 action=masquerade
add address=192.168.0.1/24 network=192.168.0.0 broadcast=192.168.0.255 interface=Local
add address=192.168.1.2/24 network=192.168.1.0 broadcast=192.168.1.255 interface=WAN1
add address=192.168.2.2/24 network=192.168.2.0 broadcast=192.168.2.255 interface=WAN2
/ip dns set allow-remote-requests=yes cache-max-ttl=1w cache-size=5000KiB max-udp-packet-size=512 servers=8.8.8.8
/ip firewall mangle
add chain=input in-interface=WAN1 action=mark-connection new-connection-mark=WAN1_conn
add chain=input in-interface=WAN2 action=mark-connection new-connection-mark=WAN2_conn
add chain=output connection-mark=WAN1_conn action=mark-routing new-routing-mark=to_WAN1
add chain=output connection-mark=WAN2_conn action=mark-routing new-routing-mark=to_WAN2
add chain=prerouting dst-address=192.168.1.0/24 action=accept in-interface=Local
add chain=prerouting dst-address=192.168.2.0/24 action=accept in-interface=Local
add chain=prerouting dst-address-type=!local in-interface=Local per-connection-classifier=both-addresses-and-ports:2/0 action=mark-connection new-connection-mark=WAN1_conn passthrough=yes
add chain=prerouting dst-address-type=!local in-interface=Local per-connection-classifier=both-addresses-and-ports:2/1 action=mark-connection new-connection-mark=WAN2_conn passthrough=yes
add chain=prerouting connection-mark=WAN1_conn in-interface=Local action=mark-routing new-routing-mark=to_WAN1
add chain=prerouting connection-mark=WAN2_conn in-interface=Local action=mark-routing new-routing-mark=to_WAN2
/ip route
add dst-address=0.0.0.0/0 gateway=192.168.1.1 routing-mark=to_WAN1 check-gateway=ping
add dst-address=0.0.0.0/0 gateway=192.168.2.1 routing-mark=to_WAN2 check-gateway=ping
add dst-address=0.0.0.0/0 gateway=192.168.1.1 distance=1 check-gateway=ping
add dst-address=0.0.0.0/0 gateway=192.168.2.1 distance=2 check-gateway=ping
/ip firewall nat
add chain=srcnat out-interface=WAN1 action=masquerade
add chain=srcnat out-interface=WAN2 action=masquerade
Subscribe to:
Posts
(
Atom
)



























