- آشنایی با Angular CLI
- آشنایی با مسیریابیها در Angular
همچنین اگر پیشتر Angular CLI را نصب کردهاید، قسمت «به روز رسانی Angular CLI» ذکر شدهی در مطلب «Angular CLI - قسمت اول - نصب و راه اندازی» را نیز اعمال کنید. در این سری از angular/cli: 1.1.2@ استفاده شدهاست.
فناوریهای مختلف کار با فرمها در Angular
Angular (که خلاصه شدهی نام تمام نگارشهای پس از Angular 2 است)، به همراه دو فناوری توکار کار با فرمها است:
الف) فرمهای مبتنی بر قالبها یا Template driven forms
در اینجا عمدهی کار تعاریف فرمها، در قالبهای HTML ایی کامپوننتها به همراه data binding انجام میشود. کار با آن سادهتر است و به همراه حداقل کدنویسی در قسمت کامپوننتهای برنامه است؛ چون two-way data binding بسیاری از مسایل را به صورت خودکار مدیریت میکند. همچنین این روش برای کسانیکه با Angular 1.x کار کرده باشند، آشناتر است.
ب) فرمهای واکنشی یا Reactive forms
در اینجا نیز همانند حالت الف کار تعریف ابتدایی فرم در قالبهای HTML ایی کامپوننتها انجام میشود. اما در اینجا نیاز است مدل فرم را توسط کدهای TypeScript کامپوننت نیز ایجاد کرد و با قالب HTML ایی هماهنگ نمود (به همین جهت به آن model driven forms هم میگویند). مزیت این روش نسبت به حالت الف، سادگی Unit testing و همچنین امکان تعریف اعتبارسنجیهای پیچیدهاست. به علاوه در این حالت میتوان فرمهای پویایی را نیز طراحی کرد.
ما در این سری حالت Template driven forms را بررسی خواهیم کرد.
ایجاد ساختار اولیهی مثال این سری
در ادامه، یک پروژهی جدید مبتنی بر Angular CLI را به نام angular-template-driven-forms-lab به همراه تنظیمات ابتدایی مسیریابی آن ایجاد میکنیم:
> ng new angular-template-driven-forms-lab --routing
> npm install bootstrap --save
"apps": [ { "styles": [ "../node_modules/bootstrap/dist/css/bootstrap.min.css", "styles.css" ],
در ادامه برای تکمیل مثال جاری، دو کامپوننت جدید خوشآمدگویی و همچنین یافتن نشدن مسیرها را به برنامه اضافه میکنیم:
>ng g c welcome >ng g c PageNotFound
@NgModule({ declarations: [ AppComponent, WelcomeComponent, PageNotFoundComponent ],
سپس فایل src\app\app-routing.module.ts را به نحو ذیل تکمیل نمائید:
import { PageNotFoundComponent } from './page-not-found/page-not-found.component'; import { WelcomeComponent } from './welcome/welcome.component'; import { NgModule } from '@angular/core'; import { Routes, RouterModule } from '@angular/router'; const routes: Routes = [ { path: 'welcome', component: WelcomeComponent }, { path: '', redirectTo: 'welcome', pathMatch: 'full' }, { path: '**', component: PageNotFoundComponent } ]; @NgModule({ imports: [RouterModule.forRoot(routes)], exports: [RouterModule] }) export class AppRoutingModule { }
همچنین مدیریت مسیریابی آدرسهای ناموجود در سایت نیز با تعریف ** صورت گرفتهاست.
زمانیکه یک کامپوننت فعالسازی میشود، قالب آن در router-outlet نمایش داده خواهد شد. برای این منظور فایل src\app\app.component.html را گشوده و به نحو ذیل تغییر دهید:
<nav class="navbar navbar-default"> <div class="container-fluid"> <a class="navbar-brand">{{title}}</a> <ul class="nav navbar-nav"> <li> <a [routerLink]="['/']">Home</a> </li> </ul> </div> </nav> <div class="container"> <router-outlet></router-outlet> </div>
افزودن ماژول فرمها به برنامه
پس از ایجاد ساختار اولیه برنامه، اولین کاری را که در جهت استفادهی از فرمهای مبتنی بر قالبها باید انجام داد، افزودن ماژول فرمها به ماژول اصلی برنامه است. برای این منظور فایل src\app\app.module.ts را گشوده و تغییرات ذیل را به آن اعمال کنید:
import { FormsModule } from '@angular/forms'; @NgModule({ imports: [ BrowserModule, FormsModule, AppRoutingModule ]
ایجاد ماژول و کامپوننت فرم ثبت نام کارمندان
در ادامه میخواهیم فرم ثبت نام یک کارمند را تکمیل کنیم. بنابراین ماژول جدید کارمندان را به همراه تنظیمات ابتدایی مسیریابی آن ایجاد میکنیم:
>ng g m Employee -m app.module --routing
installing module create src\app\employee\employee-routing.module.ts create src\app\employee\employee.module.ts update src\app\app.module.ts
import { EmployeeRoutingModule } from './employee/employee-routing.module'; @NgModule({ imports: [ BrowserModule, FormsModule, AppRoutingModule, EmployeeRoutingModule ]
همچنین برای اینکه کامپوننتهای این ماژول نیز در حین مسیریابی در دسترس باشند، نیاز است بجای EmployeeRoutingModule، خود EmployeeModule را ذکر کرد که حاوی تعاریف مسیریابی (ذکر EmployeeRoutingModule در قسمت imports آن) نیز میباشد. بنابراین فایل app.module.ts چنین تعاریفی را پیدا میکند:
import { EmployeeModule } from './employee/employee.module'; @NgModule({ imports: [ BrowserModule, FormsModule, EmployeeModule, AppRoutingModule ]
در ادامه کامپوننت جدید ثبت یک کارمند را به این ماژول اضافه میکنیم:
>ng g c employee/employee-register
installing component create src\app\employee\employee-register\employee-register.component.css create src\app\employee\employee-register\employee-register.component.html create src\app\employee\employee-register\employee-register.component.spec.ts create src\app\employee\employee-register\employee-register.component.ts update src\app\employee\employee.module.ts
import { EmployeeRegisterComponent } from './employee-register/employee-register.component'; @NgModule({ declarations: [EmployeeRegisterComponent]
در ادامه میخواهیم قالب این کامپوننت را در منوی اصلی سایت قابل دسترسی کنیم. به همین جهت به فایل src\app\employee\employee-routing.module.ts مراجعه کرده و مسیریابی این کامپوننت را تعریف میکنیم:
import { EmployeeRegisterComponent } from './employee-register/employee-register.component'; const routes: Routes = [ { path: 'register', component: EmployeeRegisterComponent } ];
سپس میخواهیم لینکی را به این مسیریابی جدید اضافه کنیم. به همین جهت به فایل src\app\app.component.html مراجعه کرده و routerLink آنرا اضافه میکنیم:
<ul class="nav navbar-nav"> <li> <a [routerLink]="['/']">Home</a> </li> <li> <a [routerLink]="['/register']">Register</a> </li> </ul>
در قسمت بعد، ایجاد اولین فرم مبتنی بر قالبها را پیگیری میکنیم.
کدهای کامل این قسمت را از اینجا میتوانید دریافت کنید: angular-template-driven-forms-lab-01.zip
برای اجرای آن فرض بر این است که پیشتر Angular CLI را نصب کردهاید. سپس از طریق خط فرمان به ریشهی پروژه وارد شده و دستور npm install را صادر کنید تا وابستگیهای آن دریافت و نصب شوند. در آخر با اجرای دستور ng serve -o برنامه ساخته شده و در مرورگر پیش فرض سیستم نمایش داده خواهد شد.
smc.version = "0"
defaults write com.apple.finder AppleShowAllFiles YES
defaults write com.apple.CoreSimulator.IndigoFramebufferServices FramebufferEmulationHint 1
امکان تست بر روی بر روی آخرین نسخه iOS یعنی 12 بر روی iPhone 5s تا iPhone XS Max وجود دارد. علاوه بر این میتوانید iOS 12 را روی iPad نسل پنج و شش و iPad Air 1 و 2 و iPad Pro تست کنید. اگر قصد تست روی نسخههای قدیمیتر iOS، چون iOS 11 یا سایر دستگاهها را دارید، باید ابتدا در XCode، از منوی Window به Devices and simulators بروید و در تب Simulators روی + کلیک کنید. در قسمت OS version میتوانید نسخههای قدیمیتر را دانلود کنید یا برای Apple TV و Apple Watch نیز Simulator بسازید.
برنامه را روی iPhone 6 یا یک گوشی سبک و ساده دیگر گذاشته و برنامه را اجرا کنید و تست بگیرید. دقت کنید که Simulator روی ویندوز اجرا میشود و نیازی به سوئیچ مداوم بین ویندوز و Mac نیست. ولی اگر Simulator روی ویندوز از لحاظ UI ای عملکرد مناسبی را نداشت، در ویژوال استودیو به منوی Tools رفته و از Options > Xamarin > iOS settings تیک Remote simulator to Windows را بردارید که باعث میشود Simulator در Mac اجرا شود. در هر بار عوض کردن Simulator از گوشی ای به گوشی دیگر، پروژه را Clean - Rebuild کنید.
در سریهای بعد که ویژوال استودیو را باز میکنید، از منوی Tools > iOS گزینه Pair Mac را بزنید و به Mac خود متصل شوید.
برای اینکه بتوانید روی گوشی تست بگیرید، در همین عکس بالا، به جای iPhoneSimulator، گزینه iPhone را انتخاب کنید. بهتر است گوشی به آخرین نسخه iOS آپدیت شده باشد. همچنین iTunes for windows را نصب کنید تا ابتدا ویندوز، گوشی شما را که با کابل به کامپیوتر وصل کردهاید بشناسد. سپس در VM Ware درخواست کنید که گوشی به جای ویندوز، به Virtual Machine مک شما وصل شود. در قسمت پایین - سمت راست VM Ware برای هر سخت افزار متصل به کامپیوتر، یک گزینه هست که آنهایی که پر رنگ هستند، سخت افزارهای متصل به Virtual Mac بوده و کمرنگها را Mac نمیبیند. روی سخت افزار گوشی خود کلیک کنید و Connect را بزنید. حال باید بتوانید در iTunes موجود در Mac، اطلاعات مربوط به گوشی خود را مشاهده کنید.
به ویژوال استودیو برگشته و در قسمت Properties پروژه XamApp.iOS، به تب iOS Bundle signing رفته و ضمن انتخاب Automatic provisioning، در قسمت Team، تیم خود را انتخاب کنید. این Team را در زمان ساخت Apple Developer account ایجاد کردهاید و همانطور که قبلا در این آموزش گفته شد، اگر در ویژوال استودیو با آن لاگین کرده باشید، میتوانید آن را ببینید. در صورتی که Apple Developer account ندارید، بر اساس این آموزش پیش بروید.
زمانیکه برنامه را روی گوشی اجرا میکنید، ممکن است در Mac دیالوگ گرفتن نام کاربری و رمز عبور کاربر Mac باز شود، پس نیم نگاهی به آن داشته باشید. پس از اولین اجرای موفق روی گوشی میتوانید در XCode به منوی Window رفته و سپس Devices & simulators را باز کنید و گوشی خود را در قسمت Devices انتخاب کرده و تیک Connect via network را بزنید تا از این به بعد، بدون کابل نیز بتوانید روی گوشی خود تست کنید. البته گوشی، ویندوز و مک، باید در یک شبکه باشند.
نحوه پابلیش پروژه را در مقاله مربوط به App Center خواهیم نوشت. اما به صورت خلاصه حجم فایل ipa حدود 10 مگ است که شامل تمامی مواردی است که در قسمت Android توضیح دادیم و پروژه نهایی شما حجمی در همین حدود خواهد داشت و با اضافه کردن چندین فرم حجم اضافه نمیشود. همانطور که در قسمت توضیحات پیشرفته پروژه اندروید توضیح دادیم، اینجا نیز از AOT و LLVM برای دستیابی به بالاترین سرعت ممکن کدهای Native استفاده شده و کدهای اضافه از پروژه Link (حذف) میشوند. برای اینکار، در ویژوال استودیو iPhone - Release را انتخاب کنید و پروژه را بیلد کنید. فایل ipa درون Mac در فولدر
Library ▸ Caches ▸ Xamarin ▸ mtbs ▸ builds ▸ XamApp.iOS ▸ e9979ba2348d1c5a87390643d62c4a1b ▸ bin ▸ iPhone ▸ Release ▸ XamApp.iOS.ipa
ساخته میشود که Library فولدری در User شما بوده و مقدار Guid مربوطه، Random است. همچنین XamApp.iOS نام پروژه است.
حالتهای متصور برای عکس بالا عبارتند از [Debug - iPhoneSimulator] و [Debug - iPhone] و [Release - iPhone] که دو تای اول برای تست روی Simulator و Device بوده و حالت سوم برای Release کردن فایل ipa. طبیعی است که تنظیم [Release - Simulator] معنی نمیدهد.
با توجه به اینکه محیط توسعه برنامه را آماده کردهایم، از قسمت بعد، به آموزش کدنویسی خواهیم پرداخت.
کامپوننت mat-table
کار کامپوننت mat-table نمایش اطلاعات در ردیفها و ستونها است. به همراه آن mat-paginator برای نمایش UI صفحه بندی اطلاعات، دایرکتیو matSort و mat-sort-header برای افزودن رابط کاربری مرتب سازی اطلاعات و امکان تغییر منبع داده آن برای فیلتر کردن دادهها، نیز وجود دارند.
افزودن کامپوننت جدید notes برای نمایش یادداشتهای کاربران
برای نمایش لیست یادداشتهای هر شخص، کامپوننت جدید Notes را به صورت زیر در پوشهی components ایجاد میکنیم:
ng g c contact-manager/components/notes --no-spec
<mat-tab-group> <mat-tab label="Bio"> <p> {{user.bio}} </p> </mat-tab> <mat-tab label="Notes"> <app-notes [notes]="user.userNotes"></app-notes> </mat-tab> </mat-tab-group>
import { Component, Input, OnInit } from "@angular/core"; import { UserNote } from "../../models/user-note"; @Component({ selector: "app-notes", templateUrl: "./notes.component.html", styleUrls: ["./notes.component.css"] }) export class NotesComponent implements OnInit { @Input() notes: UserNote[];
<p> {{notes | json}} </p>
تکمیل کامپوننت Notes توسط یک data table
در ادامه قصد داریم این اطلاعات خام را توسط یک data table نمایش دهیم. به همین جهت ابتدا به مستندات mat-table مراجعه کرده و همانند قبل، مثالی را پیدا میکنیم که به منظور ما نزدیکتر باشد. سپس کدهای آنرا به برنامه اضافه کرده و سفارشی سازی میکنیم. در ابتدا مثال basic آنرا دقیقا به همان نحوی که هست کپی کرده و سپس آنرا تغییر میدهیم:
محتوای فایل notes.component.ts
import { Component, Input, OnInit } from "@angular/core"; import { MatTableDataSource } from "@angular/material"; import { UserNote } from "../../models/user-note"; @Component({ selector: "app-notes", templateUrl: "./notes.component.html", styleUrls: ["./notes.component.css"] }) export class NotesComponent implements OnInit { @Input() notes: UserNote[]; displayedColumns = ["position", "title", "date"]; dataSource: MatTableDataSource<UserNote>; constructor() { } ngOnInit() { this.dataSource = new MatTableDataSource<UserNote>(this.notes); } }
سپس این منبع داده در قسمت ngOnInit بر اساس ورودی آرایهی notes که از کامپوننت main-content مقدار دهی میشود، تامین خواهد شد.
displayedColumns نیز لیست ستونها را مشخص میکند.
محتوای فایل notes.component.html
<div class="example-container mat-elevation-z8" fxLayout="column"> <mat-table #table [dataSource]="dataSource"> <ng-container matColumnDef="position"> <mat-header-cell *matHeaderCellDef> No. </mat-header-cell> <mat-cell *matCellDef="let note"> {{note.id}} </mat-cell> </ng-container> <ng-container matColumnDef="title"> <mat-header-cell *matHeaderCellDef> Title </mat-header-cell> <mat-cell *matCellDef="let note"> {{note.title}} </mat-cell> </ng-container> <ng-container matColumnDef="date"> <mat-header-cell *matHeaderCellDef> Date </mat-header-cell> <mat-cell *matCellDef="let note"> {{note.date | date:'yyyy-MM-dd'}} </mat-cell> </ng-container> <mat-header-row *matHeaderRowDef="displayedColumns"></mat-header-row> <mat-row *matRowDef="let row; columns: displayedColumns;"></mat-row> </mat-table> </div>
سپس به ازای هر ستون، یک ng-container اضافه شدهاست. matColumnDef معادل نامهای displayedColumns خواهد بود. matCellDef نیز بر اساس متغیر حلقهای که بر روی منبع داده تشکیل میشود، تعریف خواهد شد. این تعریف امکان دسترسی به مقدار آنرا در ادامه میسر میکند.
در این حالت اگر برنامه را اجرا کنیم، خروجی زیر قابل مشاهده خواهد بود:
افزودن صفحه بندی به mat-table یادداشتهای یک کاربر
اگر مجددا به مستندات mat-table مراجعه کنیم، مثالی در مورد mat-paginator نیز دارد که جهت نمایش رابط کاربری صفحه بندی مورد استفاده قرار میگیرد. بنابراین از مثال آن جهت تکمیل این قسمت ایده میگیریم:
</mat-table> <mat-paginator #paginator [pageSize]="2" [pageSizeOptions]="[2, 4, 6]"> </mat-paginator> </div>
در ادامه به کدهای کامپوننت مراجعه کرده و توسط ViewChild به template reference variable ایی به نام paginator دسترسی پیدا میکنیم:
export class NotesComponent implements OnInit, AfterViewInit { dataSource: MatTableDataSource<UserNote>; @ViewChild(MatPaginator) paginator: MatPaginator; ngAfterViewInit() { this.dataSource.paginator = this.paginator; } }
اکنون اگر برنامه را اجرا کنیم، صفحه بندی فعال شدهاست:
افزودن جستجو و فیلتر کردن اطلاعات به mat-table یادداشتهای یک کاربر
مستندات mat-table به همراه مثال filtering نیز هست که از آن جهت تکمیل این قسمت به نحو ذیل ایده خواهیم گرفت:
ابتدا فیلد ورود اطلاعات جستجو، پیش از Mat-table به قالب کامپوننت اضافه میشود:
<div class="example-container mat-elevation-z8" fxLayout="column"> <div class="example-header"> <mat-form-field> <input matInput (keyup)="applyFilter($event.target.value)" placeholder="Filter"> </mat-form-field> </div>
applyFilter(filterValue: string) { this.dataSource.filter = filterValue.trim().toLowerCase(); // MatTableDataSource defaults to lowercase matches }
افزودن مرتب سازی اطلاعات به mat-table یادداشتهای یک کاربر
مستندات mat-table به همراه مثال sorting نیز هست که از آن جهت تکمیل این قسمت به نحو ذیل ایده خواهیم گرفت:
برای فعالسازی مرتب سازی اطلاعات، در قالب کامپوننت، به mat-table، دایرکتیو matSort و به هر ستونی که نیاز است مرتب سازی شود، دایرکتیو mat-sort-header را به mat-headerها اضافه میکنیم:
<mat-table #table [dataSource]="dataSource" matSort> <ng-container matColumnDef="position"> <mat-header-cell *matHeaderCellDef mat-sort-header> No. </mat-header-cell>
export class NotesComponent implements OnInit, AfterViewInit { dataSource: MatTableDataSource<UserNote>; @ViewChild(MatSort) sort: MatSort; ngAfterViewInit() { this.dataSource.sort = this.sort; } }
کدهای کامل این قسمت را از اینجا میتوانید دریافت کنید: MaterialAngularClient-04.zip
برای اجرای آن:
الف) ابتدا به پوشهی src\MaterialAngularClient وارد شده و فایلهای restore.bat و ng-build-dev.bat را اجرا کنید.
ب) سپس به پوشهی src\MaterialAspNetCoreBackend\MaterialAspNetCoreBackend.WebApp وارد شده و فایلهای restore.bat و dotnet_run.bat را اجرا کنید.
اکنون برنامه در آدرس https://localhost:5001 قابل دسترسی است.
- فعال سازی فشرده سازی
- انتقال لاگها به یک سیستم راه دور
- حذف لاگ فایلهای قدیمی از طریق اسکریپت نویسی
- حذف لاگ فایلهای قدیمی توسط IIS Log File Cleaner
فشرده سازی دایرکتوری لاگ فایل ها
%SystemDrive%\inetpub\logs\LogFiles
این روش سادهترین روش موجود برای مدیریت لاگ هاست ولی روش نهایی نیست و باز به مرور زمان این روش هم کارایی خودش را از دست خواهد داد. این روش بیشتر شبیه خرید زمان میباشد تا اینکه یک راه حل نهایی برای حل مشکل باشد. البته این را هم باید مدنظر داشت که موقع تیک زدن گزینه بالا عملیات فشرده سازی باعث کند شدن سرعت کامپیوتر در حین آغاز عمل ذخیره سازی لاگ فایلها هم خواهد شد. پس اگر قصد چنین کاری ذا دارید در ساعاتی که سرور کمترین فشار از طرف کاربران را دارد یا اصطلاحا پیک کاری آن پایین است انجامش دهید.
انتقال لاگ فایلها به یک سیستم راه دور
همانطور که در بالا اشاره کردیم محل پیش فرض ذخیره سازی لاگها درمسیر
%SystemDrive%\inetpub\logs\LogFiles
قرار دارد و این محل ذخیره سازی برای هر سرور یا حتی یک وب سایت خاص در صفحه تنظیمات Logging مشخص شده است و شما در میتوانید این لاگها را حتی برای کل سرور یا مربوط به یک سایت خاص، به سروری دیگر انتقال دهید. این امکان میتواند به امنیت سیستم هم کمک فراوانی کند تا اگر دیسک محلی Local Disk هم دچار مشکل شد، باز خواندن لاگ فایلها میسر باشد و با استفاده از ابزارهای تحلیل لاگ فایل ها، آنها را مورد بررسی قرار دهیم. برای تغییر محل ذخیره سازی لاگها به یک سیستم راه دور، راه حل زیر را طی کنید.
در IIS وب سایتی را که میخواهید لاگ آن انتقال یابد، انتخاب کنید؛ یا اگر لاگ کل سیستم IIS را میخواهید انتقال بدهید نام سرور را در لیست درختی انتخاب کنید و از ماژولهای سمت راست، ماژول Logging را انتخاب کنید و در قسمت Directory که محل ذخیره سازی فعلی لاگها را نوشته شده است، به صورت UNC آدرس دهی کنید. در آدرس زیر اولی نام سرور است Contoso-server1\\ و دومی هم Logs نام پوشهای که به اشتراک گذاشته شده است.
حذف لاگ فایلهای قدیمی با استفاده از اسکریپت
با این روش میتوانید لاگ فایل هایی را که بعد از مدتی معین که دلخواه شما هست، از سیستم حذف نمایید و اگر این اسکریپت را زمان بندی خودکار نمایید، میتوانید از مراقبت مداوم و ثابت این کار نیز رها شوید.
با ستفاده از VBScript بررسی میکنیم که اگر مثلا عمر لاگ فایل به 30 روز رسیده است، باید حذف شوند. خط دوم کد زیر نهایت عمر یک لاگ فایل را مشخص میکند:
sLogFolder = "c:\inetpub\logs\LogFiles" iMaxAge = 30 'in days Set objFSO = CreateObject("Scripting.FileSystemObject") set colFolder = objFSO.GetFolder(sLogFolder) For Each colSubfolder in colFolder.SubFolders Set objFolder = objFSO.GetFolder(colSubfolder.Path) Set colFiles = objFolder.Files For Each objFile in colFiles iFileAge = now-objFile.DateCreated if iFileAge > (iMaxAge+1) then objFSO.deletefile objFile, True end if Next Next
اسکریپت بالا تمامی subfolderها را برای همه سایتها بررسی کرده و لاگهای آنان را حذف میکند. ولی اگر دوست دارید این عملیات را تنها به یک وب سایت محدود کنید، باید مسیر را در خط اول دقیقتر مشخص کنید.
برای اجرای دستی اسکریپت در cmd تایپ کنید:
cscript.exe c:\scripts\retentionscript.vbs
ولی اگر میخواهید این اسکریپت در هر دورهی زمانی خاص اجرا شود، یا زمان بندی Scheduling گردد، دیگر مجبور نیستید هر بار به فکر نگهداری از لاگها باشید.
زمان بندی اجرای اسکریپت
server manager (قابل تست در ویندوزهای سرور) را باز کرده و از منوی Tools گزینه Task Scheduler را انتخاب کنید و در قسمت Actions گزینه Create Task را انتخاب نمایید. در کادر باز شده نام "Delete Log Files " را برای مثال برگزینید و در قسمت Security هم کاربری که اجازه اجرای اسکریپت را دارد مشخص کنید.
برگه Triggers را انتخاب کرده و گزینه New را انتخاب کنید و عملیات زمان بندی را تنظیم کنید و حتما بعد از زمان بندی مطمئن باشید که تیک Enabled فعال است.
در برگه Actions هم گزینه New را انتخاب کنید؛ در کادر باز شده از لیست Start a program را انتخاب کرده و در قسمت Program\script، دستور cscript را ذکر نمایید و به عنوان آرگومان ورودی Add arguments هم مسیر اسکریپت خود را ذکر نمایید و کادر را تایید کنید.
برای آغاز زمان بندی در لیست وظیفههای فعال active task pane، وظیفه ای که الان ساخته اید را اجرا کرده و به مسیر ذخیره لاگها رفته و میبینید که لاگهای مورد نظر حذف شدهاند؛ پس از صحت اجرای اسکریپت مطمئن میشویم. دوباره به لیست وظایف رفته و گزینه End را بزنید تا وظیفه، در حالت Ready قرار گیرد تا از همین الان فرایند زمان بندی اجرای اسکریپت آغاز شود.
حذف لاگ فایلها با استفاده از IIS Log Cleaner Tools
سادهترین ابزار برای مدیریت حذف لاگ فایل هاست که هر یک ساعت یکبار اجرا شده و لاگ فایلهای تاریخ گذشته را که زمانش را شما تعیین میکنید، به سمت سطل زباله که البته درستش بازیافت است Recycle Bin انتقال میدهد تا از ضرر از دست دادن لاگها جلوگیری کند که بعدا شما میتوانید آنها را به صورت دستی حذف کنید. همچنین عملیات خودکار حذف را نیز میتوان متوقف نمود.
ابتدا برنامه را از اینجا دانلود کنید. موقعیکه برنامه را اجرا کنید، در نوتیفیکیشن taskbar مینشیند و برنامه با یک پیغام به شما اعلام میکند، این اولین بار است که برنامه را باز کردهاید. پس یک سر به setting آن بزنید؛ با انتخاب گزینهی settings برنامه بسته شده و فایل Settings.txt برای شما باز میشود که مدت زمان عمر لاگ فایل و مسیر ذخیره آنها، از شما پرسیده میشود که مقدار عمر هر لاگ فایل به طور پیش فرض 30 روز و مسیر ذخیرهی لاگها همان مسیر پیش فرض IIS است که اگر شما دستی آن را تغییر داده اید، با پرسیدن آن، از محل لاگها اطمینان کسب میکند. در صورتی که قصد تغییری را در فایل، دارید آن را تغییر داده و ذخیره کنید و برنامه را مجددا اجرا کنید.
نکات نهایی در مورد این برنامه :
- اگر از ابزار IIS Cleaner Tool استفاده میکنید باید دستی سطل بازیافت را هم پاک کنید و هم اینکه میتوانید یک محدودیت حجمی برای Recycle Bin قرار دهید که اگر به یک حدی رسید، خودکار پاک کند تا مشکلی برای سیستم عامل ایجاد نشود که البته به طور پیش فرض چنین است.
- برنامه بالا به طور پیش فرض ریشهی لاگها را حذف میکند. پس اگر میخواهید فقط سایت خاصی را مد نظر داشته باشد، آدرس دایرکتوری آن را اضافه کنید. البته چون این برنامه فقط روی یک دایرکتوری کار میکند و شما چند وب سایت دارید و مثلا میخواهید سه تای آنها را پاکسازی کنید، چارهی جز استفاده از اسکریپتهای با زمان بندی ندارید.
- برنامهی بالا فقط فایل هایی با پسوند log را به سطل بازیافت انتقال میدهد.
- برنامهی بالا یک سرویس نیست و باید به طور دستی توسط کاربر اجرا گردد. پس اگر ریست هم شد باید دستی اجرا شود یا آن را به داخل پوشه startup بکشید.
- برنامه برای اجرایش نیاز به لاگین کاربر و مجوز نوشتن در آن پوشه را دارد تا به درستی کار کند.
شماره 29 رادار فناوری Thoughtworks
همان طور که میدونید مکانیزم عملکرد اکثر پلاگینهای lightbox به صورت زیر است:
<a href="orginal size of image directory"><img src="thumb(smaller size) image directory" /></a>
خب معلوم میشه که آدرس عکسی که به صورت کوچیک)اصطلاحا بند انگشتی) به نمایش در میاد باید داخل تگ img ، وآدرس عکس با سایز اصلی ، داخل تگ a قرار بگیره.
پس تقریبا معلوم شد که چه باید بکنیم.
ابتدا باید عکسی که آپلود شده را به صورت خودکار به اندازه کوچکتر تغییر ابعاد داده و سپس آدرس عکس تغییر ابعاد داده شده ، را به عنوان آدرس عکس آپلود شده به ckeditor باز گردانیم که در شکل زیر آن را نشان داده ام:
ولی ما فقط آدرس عکس تغییر ابعاد داده شده متعلق به تگ img را ، به ckeditor داده ایم و برای اینکه با lightbox به خوبی کار کند، احتیاج داریم که آدرس عکس با سایز اصلی را داخل تگ a قرار دهیم، که در ckeditor میتوانیم از قسمت پیوندها این کار را انجام دهیم، و همهی این عملیات باید به صورت خودکار انجام شود.خب تا اینجا خلاصه ای از آنچه باید انجام شود را گفتم.
در کل منطقی که باید پیاده شود بدین گونه است:
1- پیاده سازی ckeditor
2- فعال کردن قسمت آپلود عکس ckeditor
3- آپلود کردن عکس و تغییر اندازه آن به کمک کلاس WebImage
4- برگرداندن آدرس عکسهای آپلود شده به ckeditor و روشهای پیاده سازی آن
نکات:
الف) من در اینجا از ASP.NET MVC استفاده میکنم .شما نیز میتوانید منطق پیاده سازی شده را به راحتی و با کمی تغییرات به پروژه خود اعمال کنید.
ب) کدهایی که من نوشتم 100% بهینه نیستند و کاملا هم بدون اشکال نیستند ولی نیازهای شما را برآورده میکند.
ج)آدرس دادن فایلهای من کاملا صحیح نیستند و بهتر است شما از T4MVC استفاده کنید.
مرحله اول: دانلود و پیاده سازی ckeditor در صفحه
ابتدا به سایت ckeditor.com مراجعه و از قسمت دانلود، آن را دانلود کرده و سپس از حالت فشرده درآورده، در فولدری مثلا به نام Scripts در پروژهی خود بریزید.
همان طور که میدانید ckeditor با یک textarea یکپارچه میشود ، که من سادهترین آن را برای شما شرح میدهم.
اول از همه شما باید یک فایل جاوا اسکریپت متعلق به ckeditor را در صفحه ای که از آن میخواهید استفاده کنید به صفحه ضمیمه کنید. نام این فایل ckeditor.js هست که در فایل دانلودی در داخل پوشهی ckeditor دیده میشود.
<script src="/Scripts/ckeditor/ckeditor.js" type="text/javascript"></script>
خب فرض کنید که یک textarea به شکل زیر داریم. برای اینکه این textarea با ckeditor یکپارچه شود کافیست که class آن را بر روی ckeditor قرار دهید.
<textarea id="content" name="content" class="ckeditor" ></textarea>
تا به اینجای کار توانستیم ckeditor را اجرا کنیم.
در مرحله بعد سراغ فعال سازی آپلود عکس در آن میرویم.
مرحله دوم: فعال سازی آپلود عکس در ckeditor
از شواهد این طور به نظر میاد که به صورت پیشفرض این امکان غیر فعال است و باید به صورت دستی آن را فعال کنیم.
برای این کار کافیست که از مستندات ckeditor کمک بگیریم.
خب یکی از راحتترین این روشها این است که قبل از بسته شدن تگ بادی فایل جاوا اسکریپت زیر را بنویسیم:
<script type="text/javascript"> CKEDITOR.replace( 'content' , { filebrowserImageUploadUrl: '/Admin/UploadImage' } ); </script>
توضیحات:
آرگومان اول CKEDITOR.replace که در اینجا content است ، در واقع id همان textarea ای هست که میخواهیم ckeditor روی آن اعمال شود.
آرگومان دوم نام کنترلر و اکشن را برای آپلود فایل مشخص میکنه.(از منطق ASP.Net MVC استفاده کردم)
خب تا اینجا اگر تست بگیرید میبینید که قسمت زیر برایتان فعال شده.
مرحله سوم: آپلود کردن عکس و تغییر اندازه آن به کمک کلاس WebImage
برای این که ببینم که این فرم آپلود ckeditor ، چه پارامترهایی را به اکشن متد من ارسال میکنه ، از فایرباگ کمک گرفتم:
همان طور که مییبینید به خوبی آدرس post شدن اطلاعات به اکشن متدی که من برایش مشخص کردم را فهمیده.
اما سه پارامتر دیگر نیز به اکشن متد ما ارسال میکنه: CKEditor و CKeditorFuncNum و langCod
برای اینکه با این پارامترهای ارسالی بیشتر آشنا شوید ، توصیه میکنم این صفحه را ببینید.
آنچه که از این پارامترها برای ما مهم هست ، من در اکشن متد تعریفی خود لحاظ کرده ام.
خب همون طور که یپش از این گفته بودم ما نیاز داریم که عکس آپلود شده را به ابعاد کوچکتر تغییر اندازه داده و اصطلاحا از آن به عنوان تصویر بند انگشتی استفاده کنیم. آدرس این عکس کوچک شده ، همانیست که در آدرس تگ img قرار میگیره و در ابتدا به کاربر نمایش داده میشه.
برای انجام این عمل من کلاسی را برای کار کردن با عکس برایتان معرفی میکنم، به نام WebImage که در داخل فضای نامی System.Web.Helpers قرار گرفته است.
از طریق این کلاس میتوان کلیه عملیات دریافت فایل آپلود شده، ویرایش ، تغییر اندازه ، چرخاندن ، بریدن و حتی watermark کردن و در نهایت ذخیره عکس را به آسانی انجام داد.
من کدهای متد UploadImage را برایتان قرار میدهم که زیاد هم بهینه نیست و سپس برایتان توضیح میدهم.
public ActionResult UploadImage(string CKEditorFuncNum, string CKEditor, string langCode) { string message; // message to display when file upload successfully(optional) string thumbPath = ""; // the directory for thumb file that should resize var db = new MyDbContext(); // make new instance from my context // here logic to upload image // and get file path of the image var file = WebImage.GetImageFromRequest(); // get the uploaded file from request var ext = Path.GetExtension(file.FileName); // get the path of file //get the file name without extension var fileName = Path.GetFileNameWithoutExtension(file.FileName); //add time to file name to avoid same name file overwrite, and then add extension to it fileName += DateTime.Now.ToFileTime() + ext; //choose the path for the original size of image var path = Path.Combine(Server.MapPath("~/Content/UploadedImages/Default"), fileName); file.Save(path); //save the original size of the image db.Images.Add(new Image { RealName = file.FileName, FileName = fileName, UploadDate = DateTime.Now }); // save image info to db db.SaveChanges(); // submit changes to db string defaultPath = "/Content/UploadedImages/Default/" + fileName; //path for original size of //images if (file.Width > 400) // if width of image bigger than 400 px do resize { file.Resize(400, 400, true); //resize the image , third argument is aspect ratio string thumbName = "Thumb-" + fileName; // resized image name //path for resized image file string path2 = Server.MapPath("~/Content/UploadedImages/Thumbs/" + thumbName); thumbPath = "/Content/UploadedImages/Thumbs/" + thumbName; //save resized image file file.Save(path2); } else { thumbPath = defaultPath; // if the size not bigger than 400px the thumb, path = default path } // passing message success/failure message = "Image was saved correctly"; // since it is an ajax request it requires this string //java script that return files path to ckeditor string output = @"<script>window.parent.CKEDITOR.tools.callFunction(" + CKEditorFuncNum + ", \"" + thumbPath + "\", \"" + message + "\");window.parent.document.getElementById('cke_145_textInput').value='" + defaultPath + "';window.parent.document.getElementById('cke_125_textInput').value=0;</script>"; return Content(output); }
توضیحات:
ابتدا یه رشته به نام message در نظر گرفتم ، برای هنگامی که آپلود شد، ckeditor به کاربر نشان بده.
سپس منطق من به این صورت بوده که مسیری برای ذخیره سازی فایلهای تغییر اندازه داده شده ، و نیز مسیری برای فایلهای با اندازه اصلی در نظر گرفتم.
همچنین من در اینجا من از بانک اطلاعاتی برای ذخیره سازی اطلاعاتی از عکس استفاده کردم، که در اینجا بحث اصلی ما نیست.
سپس به کمک WebImage.GetImageFromRequest فایل آپلود شده را دریافت کردم.این متد به اندازه کافی باهوش هست که بفهمد ، چه فایلی آپلود شده.
سپس پسوند فایل را از نام فایل جدا کردم، و تاریخ کنونی را به شکل رشته در آورده و به انتهای نام عکس اضافه کرده تا از تکراری نبودن نام عکسها مطمئن باشم.
سپس پسوند فایل را نیز دوباره به نام فایل اضافه کردم و به کمک متد Save کلاس WebImage عکس را ذخیره کردم.
سپس چک کردم که اگر عرض عکس بیشتر از 400 پیکسل هست ، آن را تغییر اندازه بده و ذخیره کنه، و در غیر این صورت آدرس عکسی که قرار بود تغییر اندازه داده بشه با آدرس عکس اصلی یکی میشه.
قسمت مهم:
نکته مهم اینه که ما آدرسهای عکسهای آپلود شده را چگونه به ckeditor برگردانیم.
همان طوری که در قسمت آخر هم مشاهده میکنید ، ما سه دستور جاوا اسکریپت به مرورگر برگردوندیمم:
اولیش:
window.parent.CKEDITOR.tools.callFunction(" + CKEditorFuncNum + ", \"" + thumbPath + "\", \"" + message + "\");
در حقیقت ما در اینجا ما از apiهای ckeditor و همچنین پارامترهای ارسالی از طرف ckeditor استفاده کردیم ، تا قسمت آدرس عکس را با آدرس عکس تغییر اندازه داده شده و کوچک شده پر میکنیم.
سوال؟
حالا چگونه قسمت پیوند را پر کنیم؟ این را دیگر من پیدا نکردم ، تا دست به دامن دوست و یا شایدم دشمن قدیمیمون جاوا اسکریپت شدم.
اول رفتم به کمک فایرباگ دیدم که id فیلد پیوندها چیه؟
همان طور که معلومه id این فیلد cke_145_textInput هست و به کمک یه خط js میتوان این فیلد را با آدرس عکس آپلود شده با سایز اصلی پر کرد.
اولش من این را نوشتم:
document.getElementsById("cke_145_textInput").value = defaultPath;
اما بازم js شروع کرد به بدقلق شدن. هرچی توی کنسول دیباگش کردم خطای null بودن را میداد. بعد از یه ساعت سرو کله زدن و تقریبا ناامید شدن ، چشمم به قسمت اول کد api خود ادیتور که اولش را با window.parent شروع کرده افتاد ، و من هم کد خودم را به شکل زیر تغییر دادم:
window.parent.document.getElementsById("cke_145_textInput").value = defaultPath;
موفقیت در این قسمت از کد باعث شد که من دست به کدتر شوم و مشکل border عکس ها، که به صورت دیفالت در IE یا همون دشمن همیشگی وجود داره ، را حل کنم ومقدار border را به صورت پیش فرض صفر کنم.
window.parent.document.getElementsById('cke_125_textInput').value=0;
خب همهی این دردسرها را ما تحمل کردیم تا به سادهترین شکل ممکن هر عکسی را که آپلود شد ، برای مکانیزم lightbox آماده کنیم و به راحتی یه گالری عکس خوب داشته باشیم.
خب حتما میپرسید که از چه پلاگینی برای ایجاد lightbox استفاده کنیم:
من به شخصه پلاگین colorbox را پیشنهاد میدم.
با انجام یک سرچ ساده سایتش را پیدا کنید و با مستندات و ویژگیهای آن آشنا شوید.
یک پیشنهاد:
برای انجام سلکت زدن برای عناصری که باید پلاگین colorbox روی آنها اعمال شوند من سلکت زدن به شیوهی زیر را پیشنهاد میکنم:
$(".container [href$='.jpg']").colorbox({ maxWidth: 800, opacity: 0.5, rel: 'gal' }); $(".container [href$='.png']").colorbox({ maxWidth: 800, opacity: 0.5, rel: 'gal' }); $(".container [href$='.gif']").colorbox({ maxWidth: 800, opacity: 0.5, rel: 'gal' });
فرض کنید div ی که متن و عکسهای ما را شامل میشه ، کلاسش container باشه . با کمک [href$='.jpg'] میتوان گفت هر لینکی که، پسوند فایلی که به آن اشاره میکند، .jpg هست، ویژگی colorbox را به خود بگیرید.
یک پیشنهاد برای تشکیل گالری عکس:
همان طور که من در بالا اشاره کردم ، rel را بر روی gal قرار دادم، تا هر تگی که ویژگی rel را داشته باشد، تشکیل یک گروه برای گالری عکس را بدهد.
برای اینکه بتوانیم این ویژگی را به عناصر مورد نظر خود اعمال کنیم ، بازم دست به دامان jQuery میشویم:
$(".container [href$='.jpg']").attr("rel", "gal");
خب مثل اینکه دیگر کار تمام شده و امیدوارم برای شما مفید بوده باشه.
موفق باشید...